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 list
apperlm checkout full
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 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
apperlm build

To start an APPerl project and build from scratch:

apperlm init --name your_config_name --base small
apperlm configure
apperlm build

apperlm

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

COMMAND REFERENCE

  • 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, including cleaning or reestablishing the build dirs. 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. Alternatively, you can modify apperl-project.json directly.

  • 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> .

  • apperlm install-build-deps installs APPerl build git dependencies. This is now unnecessary unless you are doing vista builds or developing APPerl itself. Note, vista builds are deprecated, see Changes. 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 .

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.

EMBEDDED SCRIPTS

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

For convenience, APPerl has some other ways of invoking embedded scripts:

  • APPERL_SCRIPTNAME - When the environment variable APPERL_SCRIPTNAME is set, APPerl attempts to load the basename of APPERL_SCRIPTNAME without file extension from /zip/bin or opens the perl interpreter like normal if it is perl.

    APPERL_SCRIPTNAME=perldoc ./perl.com perlcosmo
  • argv[0] - If APPERL_SCRIPTNAME is not set, APPerl attempts to load the basename of argv[0] without file extension from /zip/bin or opens the perl interpreter like normal if it is perl. This enables making single binary perl applications, with a symlink, move, or copy!

    ln -s perl.com perldoc.com
    ./perldoc.com perlcosmo
  • APPERL_DEFAULT_SCRIPT - If argv[0] doesn't yield a valid target either, if the APPERL_DEFAULT_SCRIPT field inside of the binary is set, APPerl will attempt to load that. This way is meant for APPerl application authors to protect against accidental rename messing up argv[0] script execution. "BUILDING AN APPLICATION FROM EXISTING APPERL" shows how to set it with "default_script", but you could also set/change it, by searching for APPERL_DEFAULT_SCRIPT in a hex editor and modifying it.

If a valid target is not found via the script execution methods, the perl interpreter is invoked like normal.

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? APPerl has argv[0] script execution, enabling the following:

ln -s perl.com hello
./hello

Now, you may still wish for your application to be run, even if the executable is renamed. Add default_script to your config to set a fallback script:

"default_script" : "/zip/bin/hello"

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, or you would like to install CPAN distributions through their standard mechanisms (Makefile.PL or Build.PL), APPerl must be built from scratch as it only supports static linking and installing distributions may require adding in extensions. 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 full config, checkout, configure, and build.

apperlm install-build-deps
apperlm new-config --name my_src_build_config --base full
apperlm checkout my_src_build_config
apperlm configure
apperlm build

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

./perl.com -V
stat perl.com

ADDING CPAN DISTRIBUTIONS

The recommended way of adding CPAN distributions or C or XS extensions is via the "install_modules" mechanism.

Currently, no CPAN client is used, so you must download or make available or disk otherwise the needed distributions for apperlm to be able to build and install them into your APPerl. It supports folder paths and paths to tarballs such as the one's directly downloaded from CPAN.

For example to install Geo::Calc:XS, download its tarball from CPAN and add to to my_src_build_config in apperl-project.json:

"install_modules" : ["Geo-Calc-XS-0.33.tar.gz"]

Then, build and test it:

apperlm build
./perl.com -MGeo::Calc::XS -e 'print $Geo::Calc::XS::VERSION'

Distributions in "install_modules" are installed in order, so modules with dependencies just need them to be installed before in order for them to be added.

ADDING VIA PERL BUILD

This method is NOT RECOMMENDED as many modules/extensions cannot be built this way, it only works for modules that can be built with miniperl, do not have dependencies, and requires reconfiguring Perl. The method listed in "ADDING CPAN DISTRIBUTIONS" supersedes this method.

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 build dir.

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

DEBUGGING

APPerl binaries as with other Actually Portable Executables built with the Cosmopolitan Libc have some nice debug features:

Syscall logging can be performed just by running with --strace as the first argument:

./perl.com --strace /zip/bin/perldoc perlcosmo

Function call logging can be performed if you have the accompanying .com.dbg file in the same directory as your APPerl binary:

./perl.com --ftrace /zip/bin/perldoc perlcosmo

In theory, it should be possible to use gdb with APPerl binaries, but the author has had great difficulty getting this to work. The dbg APPerl config is available to build Cosmopolitan with MODE=dbg and Perl with -Doptimize=-g3 -gdwarf-4.

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) 2024 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.