Thu Dec 11 21:20:00 2014

mstpan 9 - Library deployment

I roll to disbelieve in dependency heaven.

Note that I'm going to be discussing tools to manage installation in general, because I've concluded over the years that regarding development dependency management as a (possible temporary, often high churn) subset of deployment helps me come up with better approaches.

cpan as root

Yeah. Um.

People? It's 2014. You have better approaches available.

The real problem with this is that you end up with a completely untracked set of modules, at which point you're probably going to have to break out Dist::Surveyor to work out wtf's actually there.

I did, back in the mists of time, do this strategically by running cpan as root on one system, tarballing the results, and using that both to deploy to other servers and to reset the development box when I decided I wanted to go backwards. You can still totally do that, or something like it.

However, I'd suggest that if you've got as far as realising that would be helpful, you should probably try and go as far as doing something better.

Vendor Packages

Depending on what you're doing, this is potentially a completely viable approach. You need to accept having a machine per deployment set, and if the vendor provided versions of some modules you want are old or missing entirely you're going to need to pick a way to build your own.

On the other hand, if everything else you're doing is deployed that way, and you value having a standard platform across apps over being able to update their dependencies independently, then it's perfectly sane.


At this point, CPANPLUS itself seems to have fallen out of favour. I never entirely got on with it, but the cpan2dist script is potentially useful to build packages for various OSen in a (somewhat) uniform way.

I suspect that in that case you should also look at fpm, but I've not actually tried it myself yet.


If you're using the standard cpan client, make sure you know about distroprefs. Distroprefs are awesome, and a big reason you may want to consider sticking to using cpan.


Originally a shining example of an 80% solution done right, cpanminus has managed to evolve into a 95%-ish solution without getting harder to use in the simple cases. I want to clone the interface-designing part of miyagawa's brain, I really do. Unless you have a specific reason to use something else, use it.

Remember that you should probably pick a mirror and set mirror-only to get consistent results - PERL_CPANM_OPT is your friend for such things.


Figures out what directory your executable is in, so you can do a 'use lib' for something like $FindBin::Bin/../lib - which is usually really quite useful, but occasionally manages to be a footgun. If your code is expected to be deployed as a git clone, this is generally a good idea. If it's expected to be installed as a dist, it probably isn't.


This sets up environment variables that make perl look in a particular directory for libraries, and install them there. This makes avoiding the site_perl-installed-as-root approach much less painful.

At this point, both cpan and cpanminus have built-in support for it, and the ability to stack (and deactivate) multiple trees makes for a lot of flexibility.

If you're attempting to have 'a directory with these libraries' in it, you're almost certain to be using it indirectly, if not directly.


Carton is basically "miyagawa got annoyed of there not being a bundler for perl, and wrote one". This is A Good Thing.

It takes a cpanfile, installs a local::lib based on it, and then writes a cpanfile.snapshot that records exactly what it did, so you can reproduce the build process later.

One note is that if you're trying to keep in sync with other developers, you probably want 'carton install --deployment', which is a little bit odd until you consider my notes at the start of the article.

I've spent some years now preferring a deployment style of "ship a local::lib of the dependencies alongside each version of the app". I used to do this using a snapshot cpan mirror to provide reproducability; now I do it using carton.


If you want single file deployment, and have only pure perl dependencies, App::FatPacker is worth a look. It basically takes a local::lib and stuffs the .pm files in it into a single script, using some @INC trickery to have them load like files from disk. It explicitly does not and never will support XS modules, because if you try and achieve that, you end up with PAR. If you've ever tried to use PAR, you already know why this isn't a good idea.

You do have to be careful about modules that are only in core on some of your target perls, but fatpacker does its best to remain stupid so it's easy to debug. Also, getting things with pure perl and XS versions in the same dist to install the pure perl version can sometimes be a pain. Spinning up a build VM and not installing a compiler may save you some hassle.

CPAN::Mini / Pinto

Maximum underkill and maximum overkill respectively for building a local cpan mirror. If you have a decent number of shared dists, being able to release them into a local cpan is extremely helpful. If your apps depend only on CPAN modules, cpanfile.snapshot probably already provides the value.

-- mst, out.