Sign In/My Account | View Cart  
advertisement


Listen Print

Module::Build

by Dave Rolsky
February 12, 2003

Dave Rolsky is a co-author of the recently released Embedding Perl in HTML with Mason.

This article was originally published in February of 2003, and was updated by Dave Rolsky and Michael Schwern in January, 2008.

If you've ever created a Perl module for distribution on CPAN, you've used the ExtUtils::MakeMaker module. This venerable module goes back to the dawn of modern Perl, which began with the release of Perl 5.000.

Recently, Ken Williams has created a potential replacement for ExtUtils::MakeMaker called Module::Build, which was first released in August of 2002. Hugo van der Sanden, the pumpking for the current development version of Perl, has expressed interest in replacing ExtUtils::MakeMaker with Module::Build for the 5.10 release of Perl, and Michael Schwern, the current ExtUtils::MakeMaker maintainer, agrees with him. ExtUtils::MakeMaker won't go away any time soon, but we can hope for a gradual transition to a new and improved build system.

Why ExtUtils::MakeMaker is Important

The ExtUtils::MakeMaker module, along with the h2xs script, has been a huge boon to the Perl community, as it makes it possible to have a standard way to distribute and install Perl modules. It automates many tasks that module authors would otherwise have to to implement by hand, such as turning XS into C, compiling that C code, generating man pages from POD, running a module's test suite, and of course, installing the module.

ExtUtils::MakeMaker is a huge part of what makes PAUSE and CPAN possible, and it is quite a programming feat. Python did not have a similar utility until the September, 1999 release of distutils, PHP's PEAR didn't begin until mid-2000, and Ruby is just beginning to work on a standard library distribution mechanism.

The Scary Guts of ExtUtils::MakeMaker

ExtUtils::MakeMaker works by generating a makefile that contains targets for the various tasks needed when maintaining or installing a module. Here's an example target:

  install :: all pure_install doc_install

If you're familiar with makefile syntax, you'll realize that all this target does is call three other targets, which are named "all", "pure_install", and "doc_install". These targets in turn may call other targets, or use system commands to do whatever action is needed, in this case installing the module and its associated documentation.

The makefiles generated by ExtUtils::MakeMaker are fairly complex. For example, using version 6.05 of ExtUtils::MakeMaker to install my Exception::Class module, a pure Perl distribution containing just one module, creates a makefile with about 390 lines of makefile code. Figuring out what this makefile actually does is no simple feat, because it consists of a maze of twisty macros, all alike, many of which simply call Perl one-liners from the command line to perform some task.

The ExtUtils::MakeMaker code itself is extremely complicated, as it works on many operating systems (almost as many as Perl itself), and needs to accommodate their file systems, command line shells, different versions of make, and so on. And this is all done with an extra layer of indirection in place, because it is generating a makefile which does all the work.

If you want to customize the module build or installation process, good luck. To do this, you must subclass ExtUtils::MakeMaker, override methods that generate specific makefile targets, and then tweak the generated text to include your custom behavior, all the while preserving the basic behavior of the target. Considering that there is no documentation describing what to expect from these targets, and that the actual target text may change between releases of ExtUtils::MakeMaker or between different OS's, this can be quite painful to implement and maintain.

And by the way, you can't really subclass ExtUtils::MakeMaker, instead you are subclassing the MY package. This is a deeply strange hack, but the end result is that you can only override certain pre-defined methods in ExtUtils::MakeMaker.

For example, the HTML::Mason module includes this snippet:

  package MY;
  sub test {
      my $self = shift;
      my $test = $self->SUPER::test(@_);
      # %MY::APACHE is set in makeconfig.pl.
      # If we are not going to test with Apache there is no harm in
      # setting this anyway.
      # The PORT env var is used by Apache::test.  Don't delete it!
      my $port = $MY::APACHE{port} || 8228;
      $MY::APACHE{apache_dir} ||= ";
      my $is_maintainer = main::is_maintainer();
      # This works for older MakeMakers
      $test =~ s/(runtests \@ARGV;)/\$\$ENV{MASON_VERBOSE} == 
	  \$(TEST_VERBOSE) ? \$(TEST_VERBOSE) : \$\$ENV{MASON_VERBOSE}; 
	  \$\$ENV{PORT}=$port; 
	  \$\$ENV{APACHE_DIR}=q^$MY::APACHE{apache_dir}^; 
	  \$\$ENV{MASON_MAINTAINER}=$is_maintainer; $1/;
      my $bs = $^O =~ /Win32/i ? " : '\\';
      # This works for newer MakeMakers (5.48_01 +)
	  $test =~ s/("-MExtUtils::Command::MM" "-e" ")
	  (test_harness\(\$\(TEST_VERBOSE\).*?\)"
	  \$\(TEST_FILES\))/$1 $bs\$\$ENV{MASON_VERBOSE} == \$(TEST_VERBOSE) ?
	  \$(TEST_VERBOSE) : $bs\$\$ENV{MASON_VERBOSE}; $bs\$\$ENV{PORT}=$port;
	  $bs\$\$ENV{APACHE_DIR}=q^$MY::APACHE{apache_dir}^;
	  $bs\$\$ENV{MASON_MAINTAINER}=$is_maintainer; $2/;
      return $test;
  }

The goal of all this code is to pass some additional environment information to the test scripts when they are run, so we can do tests with a live Apache server. It accommodates several versions of ExtUtils::MakeMaker, and attempts to work properly on multiple operating systems (at least Win32 and *nix), and it has to be careful about escaping things properly so that it executes properly from the shell.

Why not Perl?

All of this prompts the question of "why not just use Perl itself for all of this?" That's exactly the question that Ken Williams answered with Module::Build. The goal of Module::Build is to do everything useful that ExtUtils::MakeMaker does, but to do this all with pure Perl wherever possible.

This greatly simplifies the build system code, and Module::Build works on systems which don't normally include make, like Win32 and Mac OS. Of course, if a module installation requires the compilation of C code, you'll still need an external C compiler.

Additionally, customizing Module::Build's behavior is often quite trivial, and only requires that you know Perl, as opposed to knowing make syntax and possibly having to learn about multiple command line environments.

Module::Build also aims to improve on some of the features provided by ExtUtils::MakeMaker. One example is its prerequisite-checking system, which provides much more flexibility than what ExtUtils::MakeMaker allows. While these features could be added to ExtUtils::MakeMaker, it's risky to make major changes to such an important module, especially one with such complex internals.

Using Module::Build

From an end-user perspective, a module that uses Module::Build looks quite a bit like one using ExtUtils::MakeMaker, and intentionally so. So to install a module using Module::Build you'd type the following lines from the command line:

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

The Build.PL script tells Module::Build to create a Build script. During this process, Module::Build also writes some files to a _build/ directory. These files are used to store the build system's state between invocations of the Build script. This script, when invoked, simply loads up Module::Build again, and tells it to perform the specified action. An action is the Module::Build version of a makefile target, and actions are implemented in pure Perl whenever possible.

A bare bones Build.PL script might look like this:

  use Module::Build;
  Module::Build->new
      ( module_name => 'My::Module',
        license => 'perl',
      )->create_build_script;

The "module_name" parameter is like the ExtUtils::MakeMaker "NAME" parameter.

The "license" parameter is new with Module::Build, and its intended use it allow automated tools to determine under which license your module is distributed.

To determine your module's version, Module::Build looks in the module specified by the "module_name" parameter, though this can be overridden either by specifying a different module to look in, or by providing the version number directly.

Of course, there are more options than those. For example, Module::Build implements a prerequisite feature similar to that implemented by ExtUtils::MakeMaker, so we can write:

  Module::Build->new
      ( module_name => 'My::Module',
        license => 'perl',
        requires => { 'CGI' => 0,
                      'DBD::mysql' => 2.1013,
                    },
      )->create_build_script;

If you have any experience with ExtUtils::MakeMaker, you can probably figure out that this says that our module requires any version of CGI, and version 2.1013 or greater of DBD::mysql. So far, this looks just like what ExtUtils::MakeMaker provides, but Module::Build goes further.

Pages: 1, 2, 3

Next Pagearrow