The Perl Advent Calendar needs more articles for 2022. Submit your idea today!

NAME

Perl::Dist::APPerl - Actually Portable Perl

DESCRIPTION

Actually Portable Perl (APPerl) is a distribution of Perl the runs on several x86_64 operating systems via the same binary. It builds to a single binary with perl modules packed inside of it.

Cross-platform, single binary, standalone Perl applications can be made by building custom versions of APPerl, with and without compiling Perl from scratch, so it can be used an alternative to PAR::Packer. APPerl could also easily be added to development SDKs, carried on your USB drive, or just allow you to run the exact same perl on all your PCs multiple computers.

This package documentation covers the apperlm tool for building APPerl, APPerl usage, and how to create applications with APPerl. To handle the chicken-and egg-situation of needing Perl to build APPerl, APPerl may be bootstrapped from an existing build of APPerl. See README.md for instructions.

Information on the creation of APPerl can be found in this blog post: https://computoid.com/posts/Perl-is-Actually-Portable.html.

SYNOPSIS

    apperlm install-build-deps
    apperlm-list
    apperlm configure
    apperlm build
    ./perl.com /zip/bin/perldoc Perl::Dist::APPerl
    cp perl.com perl
    ./perl --assimilate
    ln -s perl perldoc
    ./perldoc perlcosmo

To build small APPerl from scratch:

    apperlm install-build-deps
    apperlm checkout small
    apperlm configure
    apperlm build

To start an APPerl project from an existing APPerl and build it:

    mkdir src
    mv perl.com src/
    apperlm init --name your_config_name --base nobuild-v0.1.0
    apperlm build

To start an APPerl project and build from scratch:

    apperlm install-build-deps
    apperlm init --name your_config_name --base v5.36.0-small-v0.1.0
    apperlm configure
    apperlm build

apperlm

The apperlm (APPerl Manager) script is a CLI interface to configuring and building APPerl.

COMMAND REFERENCE

  • apperlm install-build-deps installs APPerl build dependencies, currently, a fork of the perl5 source and the Cosmopolitan Libc. This is only necessary if you are building APPerl from scratch (not using a nobuild configuration). Initialization of the repos can be skipped by passing the path to them locally. The cosmopolitan repo initialization can be skipped with -c <path_to_repo> . The perl5 repo initialization can be skipped with -p <path_to_repo>. This install is done user specific, installs to $XDG_CONFIG_HOME/apperl .

  • apperlm init creates an APPerl project, apperl-project.json. The project default configuration may to specified with -n <name>. If the configuration does not exist, a new configuration will be created, and then the base of the configuration may be specified with -b <base_config_name>. The default configuration is then checked out.

  • apperlm list lists the available APPerl configs. If a current config is set it is denoted with a *. Project configs are labeled PROJECT. The exact configuration of a STABLE config may change from release to release of Perl::Dist::APPerl, but only non-breaking changes should occur. ROLLING configurations are always the latest STABLE configurations, but breaking changes may occur from release to release of Perl::Dist::APPerl.

  • apperlm checkout sets the current APPerl config, this includes a make veryclean in the Perl repo and git checkout in both Perl and cosmo repos. The current config name is written to .apperl/user-project.json .

  • apperlm new-config creates a new config and adds to to the project config. -n specifies the name of the new config and must be provided. -b specifies the base of the new config.

  • apperlm configure builds cosmopolitan for the current APPerl config and runs Perl's Configure

  • apperlm build makes perl and builds apperl. The output binary by default is copied to perl.com in the current directory, set dest in apperl-project.json to customize output binary path and name. A zip binary is required to build, see README.md for details. The zip binary path may be explictly set by passing in --zippath <zip_binary_path> .

USAGE

APPerl doesn't need to be installed, the output perl.com binary can be copied between computers and ran without installation. However, in certain cases such as magic (modifying $0, etc.) The binary must be assimilated for it to work properly. Note, you likely want to copy before this operation as it modifies the binary in-place to be bound to the current environment. cp perl.com perl ./perl --assimilate

For the most part, APPerl works like normal perl, however it has a couple additional features.

  • /zip/ filesystem - The APPerl binary is also a ZIP file. Paths starting with /zip/ refer to files compressed in the binary itself. At runtime the zip filesystem is readonly, but additional modules and scripts can be added just by adding them to the zip file. For example, perldoc and the other standard scripts are shipped inside of /zip/bin

      ./perl.com /zip/bin/perldoc perlcosmo
  • argv[0] script execution - this allows making single binary perl applications! APPerl built with the APPerl additions (found in cosmo-apperl branches) attempts to load the argv[0] basename without extension from /zip/bin

      ln -s perl.com perldoc.com
      ./perldoc.com perlcosmo

CREATING APPLICATIONS WITH APPERL

RATONALE

APPerl wasn't developed to be the 'hack of the day', but provide real world utility by easing using Perl in user environments.

Unfortunately, scripting languages are often a second class citizen on user environments due to them not being installed by default or only broken/old/incomplete versions installed, and sometimes not being the easiest to install. Providing native perl binaries with solutions like PAR::Packer is possible, but that requires juggling binaries for every desired target and packing.

The idea of APPerl applications is that you can handcraft the desired Perl environment with your application and then ship it as one portable binary for all targets.

Building an APPerl application does nothing to ofuscate or hide your source code, it is a feature that APPerl binaries are also zip files, allowing for easy retrieval of Perl scripts and modules.

BUILDING AN APPLICATION FROM EXISTING APPERL

The easiest way to build an APPerl application is to build it from existing APPerl. If your application doesn't depend on non-standard C or XS extensions, it can be built from one of the official APPerl builds, skipping the need for building Perl from scratch.

Enter your projects directory, create it if it doesn't exists. Download or copy in an existing version of APPerl you wish to build off of. Official builds are available on the APPerl web page: https://computoid.com/APPerl/. Create a new nobuild APPerl project and build it.

  cd projectdir
  mkdir src
  cp ./perl.com src/
  apperlm init --name my_nobuild_config
  apperlm build

Now you should have a newly built perl.com inside the current directory. However, this isn't very exciting as it's identical to the one you copied into src. Let's create a script.

  printf "%s\n" \
  '#!/usr/bin/perl' \
  'use strict; use warnings;' \
  'print "Hello, World!\n";' > src/hello

To add it open apperl-project.json and add the following to my_nobuild_config:

  "zip_extra_files" : { "bin" : ["src/hello"] }

Rebuild and try loading the newly added script

   apperlm build
   ./perl.com /zip/bin/hello

You have embedded a script inside APPerl, however running it is a little awkward. What if you could run it by the name of the script?

  ln -s perl.com hello
  ./hello

More details on the argv[0] script execution is in "USAGE". Now, what about Perl modules? Perl modules can be packed in the same way, but to ease setting the correct directory to packing them into, the magic prefix __perllib__ can be used in the destination. Note, you may have to add items to the MANIFEST key if the MANIFEST isn't set permissively already.

  "zip_extra_files" : { "__perllib__/Your" : ["Module.pm"] }

BUILDING AN APPLICATION FROM SCRATCH

If your application requires non-standard C or XS extensions, APPerl must be built from scratch as it does not support dynamic libraries, only static linking. Note, this process can only be completed on Linux as building the Cosmopolitan Libc from scratch is only supported on Linux and APPerl uses the unix-like Configure to configure perl. This tutorial assumes you already have an APPerl project, possibly from following the "BUILDING AN APPLICATION FROM EXISTING APPERL" tutorial.

First install the APPerl build dependencies and create a new config based on the current small config, checkout, configure, and build.

  apperlm install-build-deps
  apperlm new-config --name my_src_build_config --base v5.36.0-small-v0.1.0
  apperlm checkout my_src_build_config
  apperlm configure
  apperlm build

If all goes well you should have compiled APPerl from source!

  ./perl-small.com -V
  stat perl-small.com

Now let's create a very basic C extension.

  mkdir MyCExtension
  printf "%s\n" \
  "package MyCExtension;" \
  "use strict; use warnings;" \
  "our \$VERSION = '0.0';" \
  "require XSLoader;" \
  'XSLoader::load("MyCExtension", $VERSION);' \
  "1;" > MyCExtension/MyCExtension.pm
  printf "%s\n" \
  '#define PERL_NO_GET_CONTEXT' \
  '#include "EXTERN.h"' \
  '#include "perl.h"' \
  '#include "XSUB.h"' \
  '#include <stdio.h>' \
  '' \
  'MODULE = MyCExtension    PACKAGE = MyCExtension' \
  '' \
  'void' \
  'helloworld()' \
  '    CODE:' \
  '        printf("Hello, World!\n");' > MyCExtension/MyCExtension.xs

Add it to my_src_build_config in apperl-project.json . Keys that begin with '+' will be merged with the non-plus variant of the parent config. Keys the begin with '-' will be removed from the non-minus variant of the parent config.

  "perl_repo_files" : { "ext" : [
      "MyCExtension"
  ]},
  "+MANIFEST" : ["__perlarchlib__/MyCExtension.pm"],
  "+perl_onlyextensions" : ["MyCExtension"]

Build it and try it out. apperlm checkout is needed as Perl must be rebuilt from scratch as the Configure flags changed and new files were added to the perl5 repo.

  apperlm checkout my_src_build_config
  apperlm configure
  apperlm build
  ./perl-small.com -MMyCExtension -e 'MyCExtension::helloworld();'

Now for completeness sake, let's turn this custom build of APPerl into an application that calls the extension function we just added. First make the application main script.

  printf "%s\n" \
  '#!/usr/bin/perl' \
  'use strict; use warnings;' \
  'use MyCExtension;' \
  'MyCExtension::helloworld();' > helloext

Then, add it the project config and set the dest binary name to match the script so that it will launch the script.

  "dest" : "helloext.com",
  "+MANIFEST" : ["__perlarchlib__/MyCExtension.pm", "bin/helloext"],
  "zip_extra_files" : { "bin" : ["helloext"] }

Build and test it.

  apperlm build
  ./helloext.com

SUPPORT AND DOCUMENTATION

APPerl web page: https://computoid.com/APPerl/

Support and bug reports can be found at the repository https://github.com/G4Vi/Perl-Dist-APPerl

ACKNOWLEDGEMENTS

The Cosmopolitan Libc (https://github.com/jart/cosmopolitan) contributors, especially Justine Tunney (https://justine.lol/) and Gautham Venkatasubramanian (https://ahgamut.github.io). APPerl wouldn't be possible without Actually Portable Executables and polyfills of several Linux and POSIX APIs for other platforms. Gautham's Python port (https://ahgamut.github.io/2021/07/13/ape-python/) inspired this project.

AUTHOR

Gavin Hayes, <gahayes at cpan.org>

LICENSE AND COPYRIGHT

This software is copyright (c) 2022 by Gavin Hayes.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.