Module::Build
by Dave Rolsky
|
Pages: 1, 2, 3
There are a couple workarounds for this problem. The simplest is to just include a Build.PL script and document this in the README or INSTALL files included with your distribution. This has the appeal of requiring of very little work to implement, but the downside is that people who expect things to just work with a CPAN shell will give up when your distribution doesn't install properly.
Another possibility is to create functionally equivalent Build.PL
and Makefile.PL scripts. If you're using Module::Build because
you need to customize installation behavior in a way that is difficult
to do with ExtUtils::MakeMaker, this pretty much defeats the
purpose of using Module::Build at all, and in any case having two
separate pieces of code that do the same thing is always unappealing.
Then there's the approach which involves using a
Makefile.PL script that simply installs Module::Build if needed,
and then generates a Makefile that passes everything through to the
./Build script. This is known as the "passthrough" method.
I think this approach gives the best result for the effort involved,
and is the method I prefer. The Module::Build distribution
includes a Module::Build::Compat module, which does the dirty work needed for this approach.
Simply add create_makefile_pl => 'passthrough' to the Build.PL parameters and a Makefile.PL will be created as part of the Build dist process.
Here's an example of such a Makefile.PL script:
# Note: this file was auto-generated by Module::Build::Compat version 0.03
unless (eval "use Module::Build::Compat 0.02; 1" ) {
print "This module requires Module::Build to install itself.\n";
require ExtUtils::MakeMaker;
my $yn = ExtUtils::MakeMaker::prompt
(' Install Module::Build now from CPAN?', 'y');
unless ($yn =~ /^y/i) {
die " *** Cannot install without Module::Build. Exiting ...\n";
}
require Cwd;
require File::Spec;
require CPAN;
# Save this 'cause CPAN will chdir all over the place.
my $cwd = Cwd::cwd();
CPAN::Shell->install('Module::Build::Compat');
CPAN::Shell->expand("Module", "Module::Build::Compat")->uptodate
or die "Couldn't install Module::Build, giving up.\n";
chdir $cwd or die "Cannot chdir() back to $cwd: $!";
}
eval "use Module::Build::Compat 0.02; 1" or die $@;
Module::Build::Compat->run_build_pl(args => \@ARGV);
require Module::Build;
Module::Build::Compat->write_makefile(build_class => 'Module::Build');
So what exactly is going on here? A good question indeed. Let's walk through some of the code.
unless (eval "use Module::Build::Compat 0.02; 1" ) {
print "This module requires Module::Build to install itself.\n";
require ExtUtils::MakeMaker;
my $yn = ExtUtils::MakeMaker::prompt
(' Install Module::Build now from CPAN?', 'y');
unless ($yn =~ /^y/i) {
die " *** Cannot install without Module::Build. Exiting ...\n";
}
This first attempts to load version 0.02 or greater of the
Module::Build::Compat module. If it isn't installed we know we
need to install Module::Build. Because we're polite, we ask the
user if they would like to install Module::Build before going
further. Some people dislike interactive installations, but fortunately the promp() command is pretty smart about detecting if there's a user at the end of the line.
Assuming that the user agrees to install Module::Build (if they don't the installer has to give up) this is what comes next:
# Save this 'cause CPAN will chdir all over the place.
my $cwd = Cwd::cwd();
CPAN::Shell->install('Module::Build::Compat');
CPAN::Shell->expand("Module", "Module::Build::Compat")->uptodate
or die "Couldn't install Module::Build, giving up.\n";
chdir $cwd or die "Cannot chdir() back to $cwd: $!";
We want to use CPAN.pm to actually install Module::Build, but we
need to first save our current directory, because CPAN.pm calls
chdir() quite a bit, and we'll need to be in the same directory as
we started in after installing Module::Build.
Then we load CPAN.pm and tell it to install Module::Build.
After that, we chdir() back to our original directory.
eval "use Module::Build::Compat 0.02; 1" or die $@;
Module::Build::Compat->run_build_pl(args => \@ARGV);
require Module::Build;
Module::Build::Compat->write_makefile(build_class => 'Module::Build');
First it checks that the Module::Build install worked. Then it simply tells Module::Build::Compat to run the Build.PL
script, and to write out a "passthrough" Makefile.
Module::Build::Compat will attempt to convert
ExtUtils::MakeMaker style arguments, like "PREFIX", to arguments
that Module::Build can understand, like "--prefix".
The "passthrough" Makefile that Module::Build::Compat generates
looks something like this:
all :
./Build
realclean :
./Build realclean
rm -f \$(THISFILE)
.DEFAULT :
./Build \$@
.PHONY : install manifest
The ".DEFAULT" target is called when there is no matching make target for the one given on the command line. It uses the "$@" make variable, which will contain the name of the target that was passed to make. So if "make install" is called, then "$@" contains "install", and it ends up running "./Build install".
The generated Makefile also contains a comment which specifies the
module's prerequisites, because this is how CPAN.pm figures out
what a module's prerequisites are (scary but true).
This approach is the most elegant of all, but the code that translates
ExtUtils::MakeMaker arguments to something Module::Build
understands is quite minimal and won't handle all possibilities.
I have used this approach for one CPAN module, Thesaurus.pm, and in
my limited testing it did work. If you are inclined to try installing
this module, please send bug reports to me or the Module::Build
users list, module-build-general@lists.sf.net.
Recently, Autrijus Tang submitted a more complex Makefile.PL script
which implements several pieces of additional functionality. First of
all, it makes sure that the script is running as a user that can
actually install Module::Build. Second, it prefers CPANPLUS.pm
to CPAN.pm.
Autrijus' script looks promising, but since it hasn't yet been tested,
I've chosen not to include it here. It's quite likely that some
version of his script will be documented in future versions of
Module::Build
Custom Behavior
As was hinted at earlier, you can directly subclass Module::Build
in order to implement custom behavior. This is a big topic unto
itself, and will be the topic of a future article here on perl.com.
The Future
There is plenty of work left to be done on Module::Build. Off the
top of my head, here are some things that still need to be done:
The installation phase does not yet create man pages based on POD included in the distribution.
Module::Build needs to implement a "local install" feature like the
one provided by the ExtUtils::MakeMaker "PREFIX" argument. The
logic that implements this in ExtUtils::MakeMaker is Byzantine, but
only because doing this correctly is quite complicated. This logic
needs to be implemented for Module::Build as well.
Module::Build needs better backwards compatibility with
ExtUtils::MakeMaker. The argument translation in
Module::Build::Compat is currently just a placeholder. Things like
"PREFIX", "LIB", and "UNINST=1" all need to be translated by
Module::Build::Compat, and the equivalent functionality needs to be
implemented for Module::Build
CPANPLUS.pm could take advantage of more Module::Build features.
For example, it currently ignores the "conflict" information that
Module::Build makes available, and it doesn't attempt to
distinguish between "build_requires", "requires", or "recommends".
Some of what Module::Build provides is intended for use by external
tools, such as the meta-data provided by the META.yaml file.
CPANPLUS.pm could use this to avoid having to run the Build.PL
and Build scripts, thus avoiding the need to install any of the
"build_requires" modules. Package managers like rpm or the Debian
tools could also use it to construct installable packages for Perl
modules more easily.
Adding at least basic support for Module::Build to CPAN.pm would
be nice. If anyone is interested in pursuing this, I have an old
patch that may provide a decent start on this. Contact me if you're
interested.
More Information
If you want to learn more about Module::Build, the first thing you
should do is install it (it will install itself just fine under
CPAN.pm) and read the docs for the Module::Build and
Module::Build::Compat modules. There is a project on SourceForge
for Module::Build at
http://www.sourceforge.net/projects/module-build. The source is in
CVS on SourceForge, and is accessible via anonymous CVS and and
online.
Finally, if you're interested in using or helping to develop
Module::Build, please sign up on the
module-build-general@lists.sf.net email list. See
http://lists.sourceforge.net/lists/listinfo/module-build-general for
more details.
Thanks
Thanks to Ken Williams for reviewing this article before publication,
and for writing Module::Build.

