Managing Perl Code

TL;DR: we recommend using Dist::Zilla for managing Perl packages. It is worth to invest time in learning this tool. The other tools mentioned on this page set the context and are important if you want to form a well reasoned opinion.

Motivation

If you maintain Perl code you probably want to manage your software as packages. You can use autoconf as described in Managing a Software Package with the GNU Autotools. However, there is a whole ecosystem for managing packages within the Perl community that started with the CPAN. It is recommended that you use those tools for Perl code.

True to their motto There is more than one way to do it (TIMTOWTDI) the Perl community has over the years developed several tools for the task. As you browse the available literature you may get confused as to when to use one or the other tool. Depending on when an article was written it may even be that some of the issues mentioned do not apply anymore.

The goal of this writeup is to show how the most frequently used Perl packaging tools relate to each other. This may help you decide how you want to package your own code.

ExtUtils::MakeMaker

With the invention of CPAN there needed to be a well defined way to install a package. At the time very often there were parts of the package written in C that needed to be built along the Perl code. Using make was a given but the Makefile needed to conform to certain conventions. To ensure these conventions a script Makefile.PL was written using the ExtUtils::MakeMaker module. With this the sequence to install a Perl package became:

perl Makefile.PL
make
make test
make install

If you need to maintain a package written using ExtUtils::MakeMaker you may want to read the ExtUtils::MakeMaker::Tutorial and then the extensive manual. However, if you start a new project you should use one of the newer tools mentioned below.

Module::Build

Module::Build was written to replace ExtUtils::MakeMaker with the goal of getting rid of the dependency on make. This allows Module::Build to be based purely on Perl. Once Perl itself is installed it already provides a certain standard across the platform that it supports. Module::Build profits from this standard and is therefore much more reliable. Instead of a Makefile it uses a Perl script called Build which is generated by a script Build.PL. The latter is written using the Module::Build module. Installing a package becomes:

perl Build.PL
./Build
./Build test
./Build install

The similarity to the use of ExtUtils::MakeMaker above is intentional. For most simple projects both approaches require about the same effort and both are as easy to understand as the other.

Ever since Module::Build became available there has been controversy as to which approach is better. As it turns out, having to rely on make is often not a curse but a blessing. make allows dry runs with make -n which Module::Build does not support. For all relevant platforms make is fundamental enough that there is at least one version of make available that supports all important modern features.

As the situation presents itself as of this writing there are plans to remove Module::Build from Perl core (sometime after version 5.020). ExtUtils::MakeMaker is now the preferred tool and is still used and maintained actively.

To quote from the ExtUtils::MakeMaker::FAQ:

Module::Build was long the official heir apparent to MakeMaker. The rate of both its development and adoption has slowed in recent years, though, and it is unclear what the future holds for it. That said, Module::Build set the stage for something to become the heir to MakeMaker. MakeMaker's maintainers have long said that it is a dead end and should be kept functioning, but not extended with new features. It's complicated enough as it is!

For all practical purposes there is a way out. Read on.

Module::Starter

This could be the end of the story were it not for CPAN. A package needs more than just a Build.PL (or Makefile.PL) file before it can be submitted to the CPAN. You need to add at least MANIFEST, META.yml (or META.json, or META.yaml, or ...), LICENSE, and README or some variations thereof. This can be a bit daunting so Module::Starter was written with the module-starter command line tool.

module-starter will create a skeleton of a Perl package. You can choose between Module::Build and ExtUtils::MakeMaker and set a few other options. Once created you can change the files the way you want them to be and do the actual work of writing the code and documentation that makes up your package.

Module::Starter does in now way obsolete either ExtUtils::MakeMaker or Module::Build but rather gives you a head start to use it properly for CPAN conforming Perl packages. Plus, it allows you to choose which one to use when you start the project.

Dist::Zilla

While module-starter gives you a nice skeleton for your package you still need to maintain consistency and update all the boilerplate over the lifetime of your package. This is where Dist::Zilla shines. It collects all the relevant information in a file called dist.ini and puts the documentation close to where it belongs (e.g., as POD text inside Perl code or in designated .pod files). The goal is to avoid redundancies whenever possible and it is impressive how far that can go. If you need to update a piece of information (e.g., a version number) you edit it in exactly one place and the rest is generated by Dist::Zilla.

The typical workflow for, say, an internally used module Foo::Bar at version 1.4 becomes like this:

dzil build
cd Foo-Bar-1.4
perl Build.PL
./Build
./Build test
./Build install

Or if you want to submit to CPAN you will use the .tar.gz file generated by dzil build directly or even use dzil to upload to CPAN automatically.

All of the files dzil generates are put in a target directory and none of your source files are changed by dzil. This allows you to keep generated files strictly separate from the files you edit. It also enables consistent builds. dzil build will allways generate all boilerplate and keep the distribution of your package consistent.

Dist::Zilla supports both Module::Build and ExtUtils::MakeMaker as backends. So you continue to profit from the work that went into them and avoid the burden of updating all the boilerplate. It also allows you switch between ExtUtils::MakeMaker and Module::Build by substituting one plugin for the other. See, you can have it both ways :-)

For most typical projects Dist::Zilla is the ideal package maintenance tool. Once your project becomes more complex you may encounter some limitations. You can then either dig into the internals of ExtUtils::MakeMaker or write your own Dist::Zilla::Plugin::. Writing your own plugin may well turn out to be the better choice.