2024 π Daylatest newsbuy art
Thoughts rearrange, familiar now strange.Holly Golightly & The Greenhornes break flowersmore quotes
very clickable
data + munging

The Perl Journal

Volumes 1–6 (1996–2002)

Code tarballs available for issues 1–21.

I reformatted the CD-ROM contents. Some things may still be a little wonky — oh, why hello there <FONT> tag. Syntax highlighting is iffy. Please report any glaring issues.

The Perl Journal
#7
Fall 1997
vol 2
num 3
Just the FAQs: Short Circuits
&& and || or and and or, and chomp() and LABELs.
Win32 Perl
Perl for Windows.
Infinite Lists
A new construct that can manipulate endless data streams.
Perl/Tk: Binding Basics
Associating actions with events.
Perl News
What's new in the Perl community.
Perfect Programming
A collection of tips for the paranoid programmer.
A Perl in the Oil Patch
Of salt and sysread().
WebPluck
Amassing a personalized newspaper from the web.
MakeMaker: Doing More While Doing Less
How to prepare your modules for maximum portability.
Obfuscated Perl Contest - The Winners
A frightening display of cryptic virtuosity.
The Perl Journal One-Liners
Randy J. Ray (1997) MakeMaker: Doing More While Doing Less. The Perl Journal, vol 2(3), issue #7, Fall 1997.

MakeMaker: Doing More While Doing Less

How to prepare your modules for maximum portability.

Randy J. Ray


M akeMaker 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.

AttributeWhat to passWhat it does
CONFIGURECode referenceThe code reference is executed, and is expected to return a hash reference with other attributes for Makefile generation.
INSTALLSCRIPTScalarThe installation directory for scripts.
INSTALLSITELIBScalarThe 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_FILESHash referenceMaps .PL files to their full filenames.
PREREQ_PMHash referenceA 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:

AttributeWhat to passWhat it does
cleanHash referenceThe key FILES should map to an anonymous list of files to be removed during make clean.
realcleanHash referenceAs above, for make realclean.
distHash referenceContains 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:

MethodWhat Does It Do?
constantsProduces 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.
distProduces the macros for making the dist targets.
macroProduces macros derived from the macro attribute passed into MakeMaker.
post_constantsLets the user place override constants immediately after MakeMaker executes the constants method.
postambleAllows 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:

  • Your module consists of several .pm files set up for autoloading. You don't want to manually split them, so you have MakeMaker do it for you.

  • You want your package to be installed into the standard area—wherever that might be. These paths are available to MakeMaker using Perl's configuration data.

  • Your module requires the presence of other Perl modules, and you want to ensure they're available.

  • You've got some computation to perform when your module is being configured.

  • Users are used to installing CPAN modules with perl Makefile.PL and a make. Why surprise them?

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:

DirectoryWhat's Stored There
INSTALLSITESEARCHArchitecture-dependent files (such as dynamic libraries)
INSTALLSITELIBOther library files (such as .pm files)
INSTALLBINBinary executables (used if an extension provides the option of creating a new perl with the extension built in.)
INSTALLSCRIPTRunnable scripts
INSTALLMAN1DIRSection 1 manual pages (from scripts or standalone pods)
INSTALLMAN3DIRSection 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.


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.

listing 1

Makefile.PL from X11::Fvwm Amplitudes
Randy J. Ray (1997) MakeMaker: Doing More While Doing Less. The Perl Journal, vol 2(3), issue #7, Fall 1997.
Makefile.PL from X11::Fvwm Amplitudes

# $Id: Makefile.PL,v 1.2 1997/04/30 18:04:34 rjray Exp rjray $ 
require 5.002; 
use ExtUtils::MakeMaker; 
# This is borrowed almost verbatim from Graham Barr’s MailTools package 
sub chk_version { 
    my ($pkg, $wanted) = @_; 
    $| = 1; 
    print "Checking for $pkg..."; 
    eval { my $p; ($p = $pkg . ".pm") =~ s!::!/!g; require $p; }; 
    my $vstr = ${"${pkg}::VERSION"} ? "found v" . ${"${pkg}::VERSION"} 
                                    : "not found";
    my $vnum = ${"${pkg}::VERSION"} || 0; 
    print $vnum >= $wanted ? "ok\n" : " " . $vstr . "\n"; 
    $vnum >= $wanted; 
} 
chk_version(Tk => 400.200) or 
  warn "\n\tThe Tk extension (400.200 or newer) was not found. You will\n" . 
  "\tnot be able to use Tk as a GUI (via X11::Fvwm::Tk) without it.\n\n"; 
chk_version(X11::Xforms => 0.7) or 
  warn "\n\tThe X11::Xforms extension (0.7 or newer) was not found. You\n" 
  "\twill not be able to use X11::Xforms as a GUI (via X11::Fvwm::Xforms)\n". 
  "\twithout it.\n\n"; 
@DEMO_SCRIPTS = qw(scripts/PerlTkWL scripts/PerlWinList scripts/TkPerlConsole 
                  scripts/pDebug); 
%PL_SCRIPTS = map { sprintf("%s.PL", $_) => "$_" } @DEMO_SCRIPTS;
WriteMakefile( 
              NAME          => ’X11::Fvwm’, 
              VERSION_FROM  => ’Fvwm.pm’, 
              LIBS          => [’’], 
              DEFINE        => ’’, 
              INSTALLSCRIPT => ’/usr/local/lib/X11/fvwm2’, 
              INC           => ’-I$(FVWMSRCDIR)’, 
              macro         => { ’FVWMSRCDIR’ => ’/usr/local/src/fvwm’ }, 
              dist          => { COMPRESS => ’gzip -9f’, SUFFIX => ’gz’ }, 
              EXE_FILES     => [@DEMO_SCRIPTS], 
              PL_FILES      => \%PL_SCRIPTS, 
              PMLIBDIRS     => [’Fvwm’], 
              clean => { FILES => join(’ ’, @DEMO_SCRIPTS) }, 
              realclean => { FILES => join(’ ’, @DEMO_SCRIPTS) } 
);
Martin Krzywinski | contact | Canada's Michael Smith Genome Sciences CentreBC Cancer Research CenterBC CancerPHSA
Google whack “vicissitudinal corporealization”
{ 10.9.234.151 }