PREVIOUS  TABLE OF CONTENTS  NEXT 

MakeMaker: Doing More While Doing Less

Randy J. Ray

MakeMaker might well be the most-used module in the core Perl distribution. It creates a Makefile for extensions you build, so that they can be compiled and installed by other people on other platforms. MakeMaker insulates you from the vagaries of different operating systems such as compiler flags or how dynamic libraries are constructed. It frees you to focus on the module itself rather than the installation process. If you're writing a module that will be distributed on the CPAN, or that needs to run on more than one architecture, MakeMaker is essential. This article assumes knowledge of basic configuration issues and the make command.

MakeMaker is the colloquial name for the ExtUtils::MakeMaker module. When loaded, it imports a set of core routines and a platform-specific library of routines. UNIX platforms are all alike to MakeMaker, since the Perl configuration process will already have identified the relevant differences between them Thanks to MakeMaker, packages as operating-system-dependent as Perl/Tk will automatically build on platforms as diverse as VMS, Windows 95, and O/S 2.

From The Beginning: h2xs

The h2xs program bundled with the Perl distribution, is indispensable for module developers. Although it was designed primarily for creating an extension framework from a C header file, it's flexible enough to serve the needs of most modules whether or not they use C.

h2xs creates a basic .pm file, a MANIFEST file, a Changes file, a simple test.pl script, and most importantly, a skeletal Makefile.PL. Here's the Makefile.PL generated by h2xs -n Test:

use ExtUtils::MakeMaker; 
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written. 
WriteMakefile( 
    'NAME'      => 'Test', 
    'VERSION_FROM' => 'Test.pm', # finds $VERSION 
    'LIBS'      => [''],   # e.g., '-lm' 
    'DEFINE'    => '',     # e.g., '-DHAVE_SOMETHING' 
    'INC'       => '',     # e.g., '-I/usr/include/other'
); 

There's not much here, but what little there is does a great deal. It contains the information necessary to have ExtUtils::MakeMaker obtain the release version from your .pm file, notice whether your module uses XS (to link in C or C++ code), build shared libraries from compiled code, create a distribution tar file, and more. (The above snippet affects both Test.pm and Test.xs. Invoking h2xs with the -X option would have suppressed the XS aspects and removed references to Test.xs from Test.pm.)

Consider using h2xs even if your module or extension makes no use at all of XS code; MakeMaker is easily tamed with it. Read the h2xs documentation for more information.

On to the components of the file itself.

Piece by Piece

MakeMaker allows people who download your module to prepare for compilation with one command: perl Makefile.PL.

Ideally, this results in a module (or extension) that compiles without problems and can be merged seamlessly with the user's existing Perl installation.

Makefile.PL is a Perl script, but it can't make any assumptions about, say, where your Perl executable is. Thus, it immediately imports the ExtUtils::MakeMaker module. The rest of the module is merely a call to that module's WriteMakefile() subroutine. WriteMakefile() is given some basic attributes of your extension: its name, the version number (if any), and whether extra libraries, compile-time definitions, or include paths are needed. Other attributes can be supplied; five of the most common are listed below. See the ExtUtils::MakeMaker documentation for the complete list.

Attribute What to pass What it does
CONFIGURE Code reference The code reference is executed, and is expected to return a hash reference with other attributes for Makefile generation.
INSTALLSCRIPT Scalar The installation directory for scripts.
INSTALLSITELIB Scalar The installation directory for libraries. This assumes that you're not building within the Perl distribution; it defaults to the site_lib set when Perl was built.
PL_FILES Hash reference Maps .PL files to their full filenames.
PREREQ_PM Hash reference A list of modules required for this module to work, such as Fcntl for SDBM_File. The hash should map module names to the version number you need.

If you provide the following attributes to WriteMakefile(), the appropriate Makefile commands will be generated:

Attribute What to pass What it does
clean Hash reference The key FILES should map to an anonymous list of files to be removed during make clean.
realclean Hash reference As above, for make realclean.
dist Hash reference Contains keys for bundling and unbundling the module. (More on this later.)

A Deeper Example

To see some of these items at work, consider the Makefile.PL from my X11::Fvwm module on CPAN:

The X11::Fvwm module provides access to Fvwm, a popular window manager for the X11 Window System. The current version of X11::Fvwm, 0.4, is based on Fvwm 2.0.45 and is available on the CPAN.

Makefile.PL from x11::Fvwm.

The require 5.002 ensures that users are running Perl 5.002 or newer. After the use ExtUtils::MakeMaker, the chk_version() subroutine (adopted from Graham Barr's MailTools package) is defined, which checks to see whether a package is present and sufficiently recent, printing "ok" if so, or a diagnostic message if not: either "not found or the version it found but couldn't accept. Unlike the PREREQ_PM attribute, it doesn't die() if the package can't be found; it only warns the user that some features might not be available. (The return code could also be used to configure some parts of the module, if the absence of the package makes this appealing.)

After Tk and X11::Forms are checked, the scripts provided by the module are declared, and the mapping from .PL to actual names is created. The hash is passed (as a reference) via PL_FILES. The original list will also be used in the definitions for clean and realclean, and is explicitly listed in EXE_FILES to ensure delivery into the appropriate install directory (INSTALLSCRIPT, since all four are scripts).

The WriteMakefile() subroutine provides the NAME of the module, and the version number for the distribution is gleaned from Fvwm.pm. There are no extra libs to be linked, nor defines for the C compiler lines. INSTALLSCRIPT is hard-coded to /usr/local/lib/X11/fvwm2 (the default for Fvwm 2, but I'm working on making this more flexible), and both INC and the attributes for macro utilize it. The macro attributes define a make macro called FVWMSRCDIR that the XS code needs in its include path for a few header files.

The two dist attributes fine-tune the results of make dist. COMPRESS identifies which compression utility to use, and SUFFIX identifies the suffix for the final filename. (If you specify COMPRESS, you must also specify SUFFIX). Other keys that can be used here include TAR to specify a tar utility, TARFLAGS to pass to tar, SHAR to define the shell archive utility, PREOP to define make commands to execute before creating the distribution, and POSTOP to define commands to be run after creating the distribution. There's also DIST_DEFAULT, which can be one of tardist, shdist, zipdist or uutardist. This defines which action is performed upon make dist; the default is tardist.

REALLY Hacking Your Makefile

Perhaps this still isn't enough flexibility for you. MakeMaker can oblige. Some of the methods used by MakeMaker (in the MM class) can be overridden if you provide a method of the same name in the MY class. This is the last resort for tailoring your Makefile; the documentation for ExtUtils::MM_Unix (the subclass of MM that provides UNIX-based methods) encourages those who need to write their own methods to mail the MakeMaker authors and let them know why MakeMaker wasn't up to snuff. I'll cover some of the more common methods that can be overloaded.

We'll explore a few methods that actually produce chunks of text for the resulting Makefile. Most of these take only the object reference as an argument and rely on other method calls for access to information:

Method What Does It Do?
constants Produces a code block that defines most of the make constants. Most of these pertain to system tools (ar, linkers, etc.) and Perl-related version strings and paths.
dist Produces the macros for making the dist targets.
macro Produces macros derived from the macro attribute passed into MakeMaker.
post_constants Lets the user place override constants immediately after MakeMaker executes the constants method.
postamble Allows the user to specify some text for the end of the Makefile.

There are many more than these five. In the documentation for both MakeMaker and the operating-system-specific variants (e.g. ExtUtils::MM_Unix.pm, ExtUtils::MM_Win32.pm), the methods that can be overridden are marked with an o. When overriding a method, you simply define it in Makefile.PL like so:

sub MY::post_constants {
   my $self = shift; 
   my @m; 

   push(@m, 
     "PURIFY_DIR = /usr/local/pure/purify/purify-4.0-hpux/\n",
     "PURIFY = $(PURIFY_DIR)/purify\n"); 

   join "", @m; 
} 

Or, if you intend to reference the original method (or any other methods from the MM package), use the MY package:

sub MY::constants {
   package MY;   # To help SUPER work right 
   my $self = shift; 
   my @m; 

   push(@m, $self->SUPER::constants(@_)); 
   push(@m, 
     "PURIFY_DIR = /usr/local/pure/purify/purify-4.0-hpux/\n",
     "PURIFY = $(PURIFY_DIR)/purify\n");

   join "", @m; 
} 

Simpler Uses

MakeMaker is absolutely necessary only when your module requires compilation of non-Perl code with XS. But there are several reasons why you might want to use MakeMaker even when your module is pure, portable Perl:

Some examples of Perl-only modules that use MakeMaker include my Image::Size (described in TPJ #6), Graham Barr's MailTools and libnet packages, and Gisle Aas' LWP package.

MakeMaker and Installation of Modules

As mentioned earlier, MakeMaker manages the rules associated with installing your module and related files. But where exactly are things placed, and can those locations be changed?

The destination directories for files are based on the file type and whether MakeMaker is operating within the Perl source tree. The important directories are:

Directory What's Stored There
INSTALLSITESEARCH Architecture-dependent files (such as dynamic libraries)
INSTALLSITELIB Other library files (such as .pm files)
INSTALLBIN Binary executables (used if an extension provides the option of creating a new perl with the extension built in.)
INSTALLSCRIPT Runnable scripts
INSTALLMAN1DIR Section 1 manual pages (from scripts or standalone pods)
INSTALLMAN3DIR Section 3 manual pages (from .pm files)

Two caveats: First, INSTALLSITEARCH and INSTALLSITELIB assume that you, the module author, want the end product to be installed in the site-local area where non-core modules are meant to go. If the user prefers to have their Perl installation keep all extensions and modules in the same place, they can force that by setting INSTALLDIRS to perl when they create the Makefile: perl Makefile.PL INSTALLDIRS=perl.

Second, if you explicitly set either INSTALLSITEARCH or INSTALLSITELIB, then INSTALLDIRS will take precedence. Installation directories are only defined for section 1 and section 3 manual pages. If you have man pages that belong in other sections, you'll have to encode those rules yourself, probably by overloading one of the MM methods such as postamble().

For users who want to build MakeMaker-based packages but don't have the necessary access to install them, MakeMaker supports the use of either LIB or PREFIX settings on the command line. Do not provide them to WriteMakefile(); it's LIB that should dictate where the .pm files go: perl Makefile.PL LIB=~/perl_lib. This causes ~/perl_lib to be used for all the non-architecture-specific files. Architecture-specific files will be deposited in ~/perl_lib/$arch, where $arch is the configured architecture name (PA-RISC1.1 on my HP/UX machine). This affects only the library code, however; it doesn't affect any scripts you provide in your package. If the user instead sets PREFIX, say, with perl Makefile.PL PREFIX=~, then all of the installation-related values will be set relative to ~. This overloads the configuration value prefix, which is often something like /usr/local or /opt/perl. My home directory is /home/tremere/rjray, and my Perl is installed below /usr/local.

For comparison, here are the six installation paths for my machine, with and without the PREFIX=~ override:

INSTALLSITEARCH
without PREFIX: /usr/local/lib/perl5/site_perl/PA-RISC1.1
with PREFIX: /home/tremere/rjray/lib/perl5/site_perl/PA-RISC1.1

INSTALLSITELIB
without PREFIX: /usr/local/lib/perl5/site_perl
with PREFIX: /home/tremere/rjray/lib/perl5/site_perl

INSTALLBIN
without PREFIX: /usr/local/bin
with PREFIX: /home/tremere/rjray/bin

INSTALLSCRIPT
without PREFIX: /usr/local/bin
with PREFIX: /home/tremere/rjray/bin

INSTALLMAN1DIR
without PREFIX: /usr/local/man/man1
with PREFIX: /home/tremere/rjray/man/man1

INSTALLMAN3DIR
without PREFIX: /usr/local/lib/perl5/man/man3
with PREFIX: /home/tremere/rjray/lib/perl5/man/man3

Any of these six could be overridden in the call to WriteMakefile(). For instance, the X11::Fvwm Makefile.PL shown earlier overrides INSTALLSCRIPT to force the scripts into the directory preferred by Fvwm. To look at the values from your configuration, try perl '-V:install.*'

perllocal.pod

You might not have known that Perl keeps a list of the packages installed with MakeMaker. Upon successful installation, several lines are appended to INSTALLARCHLIB/perllocal.pod, noting the package that was installed, a few details about it, and when the installation occurred. A tidy little cron job could give you a web-browsable record of what modules have been installed on your system. Take a look at perllocal.pod on your system and see what's there.

More detail can be found in the documentation for ExtUtils::MakeMaker, your system's MM methods (ExtUtils::MM_Unix for me) and h2xs.

_ _END_ _


Randy J. Ray has been with the Software Configuration Management team as U S WEST Communications since June 1992, where he is the lead developer and architect of an SCM system written entirely in Perl.
PREVIOUS  TABLE OF CONTENTS  NEXT