In No-Dependency Land
While the proliferation of solutions like local::lib and cpanminus has made it a breeze to manage dependencies, there are still some rare occassions in which we need to be able to ship code that has no external non-core dependencies.
There are a few existing solutions for them, but we're going to concentrate on a new one called FatPacker.
Of course, we just happen to have a sample application we want to pack. It downloads various pages from our website and compiles a statistics report. It uses HTTP::Tiny as a user agent. Our application begins with the lines:
Our app is, surprisingly, saved as the file ourapp.pl.
Packing the deps
App::FatPacker comes with an application called fatpack. You'll use fatpack to get at all of App::FatPacker's features. There are four simple steps for packing your dependencies. Let's go over them.
To find out what dependencies our code has, we
trace our app. This will create a file called fatpacker.trace, which includes a list of modules that fatpack has discovered.
In case some modules aren't successfully traced, you can ask
fatpack to include them:
If we open the fatpacker.trace file, we can see it collected a few modules, including both HTTP/Tiny.pm and Carp.pm (which HTTP::Tiny uses).
Packlists are files that distributions install. They contain information on which modules are included in the distribution. FatPacker needs to find the packlist for each module in order to make sure it includes all dependencies recursively and does not miss anything. One module is likely to use another module, which might use another module in turn, and so on.
We can call
packlists-for with a list of modules, or we can feed it the content of the trace output we created with the previous command. It will print out a list of all the packlists, which we'll simply redirect to a file so we can reuse this information.
Forming the tree
In this step FatPacker collects all the dependencies recursively into a directory called fatlib, which it will then be able to pack together.
tree needs a list of packlists. Lucky for us, we saved the packlists that our previous command has found in a file called packlists. Let's just call
tree and feed it that file.
Taking a look at our fatlib directory, we'll see the following structure:
fatlib/ ├── Carp │ └── Heavy.pm ├── Carp.pm └── HTTP └── Tiny.pm
Once we have all our dependencies in a directory, we can finally pack it all nicely using the last command:
file. This command packs all the modules in the current fatlib directory. It will also try to pack any lib directory that exists in the current directory. If none is present, you will need to create it.
Since the command only packs the modules, we're still missing our code that uses them, so we will concatenate that as well. We will also print this to a new file so we could ship it.
Stick a shebang line at the top of ourapp.packed.pl and that's all there is to it!
You can now ship ourapp.packed.pl to any location, and it will include all dependencies recursively.
You can open our newly-packed application file and see the way it has packed everything together:
It's already being used!
There is at least one famous project which uses this method to create a self-contained program: cpanminus proved this method to be useful for beginners and seasoned system administrators in providing a self-contained full-fledged CPAN client, always available at your finger-tips without any installations required (other than having a Perl interpreter, of course).
You can always download a packed
cpanminus program and use it, wherever you are, using the following command:
There are some considerations still:
Compile time code will be run
If you have any compile-time code (think
BEGIN blocks), they will be run as part of the tracing step. Generally, these aren't recommended for most use cases anyway.
If you have any compile-time code which shouldn't run upon tracing, you might want to consider refactoring it into run-time code.
Lazily loaded modules won't be found
Any modules that are loaded lazily (such as
require statements) will not be traced successfully. You can, however, provide them as additional modules for the
trace command, as described above.
XS modules are not supported
App::FatPacker only supports Pure-Perl modules, so if you're using any XS modules, you'll need to have them installed remotely.