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 small
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 amake veryclean
in the Perl repo andgit 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'sConfigure
apperlm build
make
s perl and builds apperl. The output binary by default is copied toperl.com
in the current directory, set dest inapperl-project.json
to customize output binary path and name. Azip
binary is required to build, see README.md for details. Thezip
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.
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 variableAPPERL_SCRIPTNAME
is set, APPerl attempts to load the basename ofAPPERL_SCRIPTNAME
without file extension from/zip/bin
or opens the perl interpreter like normal if it isperl
.APPERL_SCRIPTNAME=perldoc ./perl.com perlcosmo
argv[0]
- IfAPPERL_SCRIPTNAME
is not set, APPerl attempts to load the basename ofargv[0]
without file extension from/zip/bin
or opens the perl interpreter like normal if it isperl
. 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
- Ifargv[0]
doesn't yield a valid target either, if theAPPERL_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 upargv[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 forAPPERL_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 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
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) 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.