| ExtUtils-AutoInstall documentation | Contained in the ExtUtils-AutoInstall distribution. |
ExtUtils::AutoInstall - Automatic install of dependencies via CPAN
This document describes version 0.63 of ExtUtils::AutoInstall, released September 12, 2005.
In Makefile.PL, with Module::Install available on the author's system:
use inc::Module::Install;
name ('Joe-Hacker');
abstract ('Perl Interface to Joe Hacker');
author ('Joe Hacker <joe@hacker.org>');
include ('ExtUtils::AutoInstall');
requires ('Module0'); # mandatory modules
features (
-config => {
make_args => '--hello', # option(s) for CPAN::Config
force => 1, # pseudo-option to force install
do_once => 1, # skip previously failed modules
},
'Feature1' => [
'Module2' => '0.1',
],
'Feature2' => [
'Module3' => '1.0',
],
);
auto_install();
&WriteAll;
Invoking the resulting Makefile.PL:
% perl Makefile.PL # interactive behaviour
% perl Makefile.PL --defaultdeps # accept default value on prompts
% perl Makefile.PL --checkdeps # check only, no Makefile produced
% perl Makefile.PL --skipdeps # ignores all dependencies
% perl Makefile.PL --testonly # don't write installation targets
Note that the trailing 'deps' of arguments may be omitted, too.
Using --defaultdeps will make Makefile.PL behave similarly to a regular
Makefile.PL file with PREREQ_PM dependencies.
One can use environment variables (see "ENVIRONMENT") below to set a default behavior instead of specifying it in the command line for every invocation of Makefile.PL.
Using make (or nmake):
% make [all|test|install] # install dependencies first
% make checkdeps # same as the --checkdeps above
% make installdeps # install dependencies only
ExtUtils::AutoInstall lets module writers to specify a more
sophisticated form of dependency information than the PREREQ_PM
option offered by ExtUtils::MakeMaker.
This module works best with the Module::Install framework, a drop-in replacement for MakeMaker. However, this module also supports Makefile.PL files based on MakeMaker; see EXAMPLES for instructions.
Prerequisites are grouped into features, and the user could choose
yes/no on each one's dependencies; the module writer may also supply a
boolean value via -default to specify the default choice.
The Core Features marked by the name -core will double-check with
the user, if the user chooses not to install the mandatory modules.
This differs from the pre-0.26 'silent install' behaviour.
Starting from version 0.27, if -core is set to the string all
(case-insensitive), every feature will be considered mandatory.
The dependencies are expressed as pairs of Module => version
inside an array reference. If the order does not matter, and there
are no -default, -tests or -skiptests directives for that
feature, you may also use a hash reference.
Once ExtUtils::AutoInstall has determined which module(s) are needed, it checks whether it's running under the CPAN shell and should therefore let CPAN handle the dependency.
Finally, the WriteMakefile() is overridden to perform some additional
checks, as well as skips tests associated with disabled features by the
-tests option.
The actual installation happens at the end of the make config target;
both make test and make install will trigger the installation of
required modules.
If it's not running under CPAN, the installer will probe for an
active connection by trying to resolve the domain cpan.org, and check
for the user's permission to use CPAN. If all went well, a separateCPAN instance is created to install the required modules.
If you have the CPANPLUS package installed in your system, it is
preferred by default over CPAN; it also accepts some extra options
(e.g. -target => 'skiptest', -skiptest => 1 to skip testing).
All modules scheduled to be installed will be deleted from %INC
first, so ExtUtils::MakeMaker will check the newly installed modules.
Additionally, you could use the make installdeps target to install
the modules, and the make checkdeps target to check dependencies
without actually installing them; the perl Makefile.PL --checkdeps
command has an equivalent effect.
If the Makefile.PL itself needs to use an independent module (e.g. Acme::KillarApp, v1.21 or greater), then use something like below:
BEGIN {
require ExtUtils::AutoInstall;
# the first argument is an arrayref of the -config flags
ExtUtils::AutoInstall->install([], 'Acme::KillerApp' => 1.21);
}
use Acme::KillerApp 1.21;
ExtUtils::AutoInstall->import(
# ... arguments as usual ...
);
Note the version test in the use clause; if you are so close to the cutting edge that Acme::KillerApp 1.20 is the latest version on CPAN, this will prevent your module from going awry.
User-defined pre-installation and post-installation hooks are
available via MY::preinstall and MY::postinstall subroutines,
as shown below:
# pre-install handler; takes $module_name and $version
sub MY::preinstall { return 1; } # return false to skip install
# post-install handler; takes $module_name, $version, $success
sub MY::postinstall { return; } # the return value doesn't matter
Note that since ExtUtils::AutoInstall performs installation at the
time of use (i.e. before perl parses the remainder of
Makefile.PL), you have to declare those two handlers before the
use statement for them to take effect.
If the user did not choose to install a module or it already exists on the system, neither of the handlers is invoked. Both handlers are invoked exactly once for each module when installation is attempted.
MY::preinstall takes two arguments, $module_name and $version;
if it returns a false value, installation for that module will be
skipped, and MY::postinstall won't be called at all.
MY::postinstall takes three arguments, $module_name, $version
and $success. The last one denotes whether the installation
succeeded or not: 1 means installation completed successfully, 0
means failure during install, and undef means that the installation
was not attempted at all, possibly due to connection problems, or that
module does not exist on CPAN at all.
MY::postambleStarting from version 0.43, ExtUtils::AutoInstall supports modules
that require a MY::postamble subroutine in their Makefile.PL.
The user-defined MY::postamble, if present, is responsible for
calling ExtUtils::AutoInstall::postamble and include the output in
its return value.
For example, the DBD::* (database driver) modules for the Perl DBI
are required to include the postamble generated by the function
dbd_postamble, so their Makefile.PL may contain lines like this:
sub MY::postamble {
return &ExtUtils::AutoInstall::postamble . &dbd_postamble;
}
Note that the ExtUtils::AutoInstall module does not export the
postamble function, so the name should always be fully qualified.
ExtUtils::AutoInstall will add UNINST=1 to your make install
flags if your effective uid is 0 (root), unless you explicitly disable
it by setting CPAN's make_install_arg configuration option (or the
makeflags option of CPANPLUS) to include UNINST=0. This may
cause dependency problems if you are using a fine-tuned directory
structure for your site. Please consult FAQ in CPAN for an explanation
in detail.
If either version or Sort::Versions is available, they will be used to compare the required version with the existing module's version and the CPAN module's. Otherwise it silently falls back to use cmp. This may cause inconsistent behaviours in pathetic situations.
Since this module is needed before writing Makefile, it makes little use as a CPAN module; hence each distribution must include it in full. The only alternative I'm aware of, namely prompting in Makefile.PL to force user install it (cf. the Template Toolkit's dependency on AppConfig) is not very desirable either.
The current compromise is to add the bootstrap code listed in the SYNOPSIS before every script, but that does not look pretty, and will not work without an Internet connection.
Since we do not want all future options of ExtUtils::AutoInstall to
be painfully detected manually like above, this module provides a
bootstrapping mechanism via the -version flag. If a newer version
is needed by the Makefile.PL, it will go ahead to fetch a new
version, reload it into memory, and pass the arguments forward.
If you have any suggestions, please let me know. Thanks.
ExtUtils::AutoInstall uses a single environment variable,
PERL_EXTUTILS_AUTOINSTALL. It is taken as the command line argument
passed to Makefile.PL; you could set it to either --defaultdeps or
--skipdeps to avoid interactive behaviour.
To use this module with ExtUtils::MakeMaker, first make a
inc/ExtUtils/ subdirectory in the directory containing your
Makefile.PL, and put a copy this module under it as
inc/ExtUtils/AutoInstall.pm. You can find out where this module
has been installed by typing perldoc -l ExtUtils::AutoInstall
in the command line.
Your Makefile.PL should look like this:
# pull in ExtUtils/AutoInstall.pm from 'inc'
use lib 'inc';
use ExtUtils::AutoInstall (
-core => [ # mandatory modules
'Module0' => '', # any version would suffice
],
'Feature1' => [
# do we want to install this feature by default?
-default => ( system('feature1 --version') == 0 ),
Module1 => '0.01',
],
'Feature2' => [
# associate tests to be disabled if this feature is missing
-tests => [ <t/feature2*.t> ],
# associate tests to be disabled if this feature is present
-skiptests => [ <t/nofeature2*.t> ],
Module2 => '0.02',
],
'Feature3' => { # hash reference works, too
# force installation even if tests fail
Module2 => '0.03',
}
);
WriteMakefile(
AUTHOR => 'Joe Hacker <joe@hacker.org>',
ABSTRACT => 'Perl Interface to Joe Hacker',
NAME => 'Joe::Hacker',
VERSION_FROM => 'Hacker.pm',
DISTNAME => 'Joe-Hacker',
);
If you do not wish to put a copy of ExtUtils::AutoInstall under
inc/, and are confident that users will have internet access, you
may replace the use lib 'inc'; line with this block of code:
# ExtUtils::AutoInstall Bootstrap Code, version 7.
BEGIN{my$p='ExtUtils::AutoInstall';my$v=0.45;$p->VERSION||0>=$v
or+eval"use $p $v;1"or+do{my$e=$ENV{PERL_EXTUTILS_AUTOINSTALL};
(!defined($e)||$e!~m/--(?:default|skip|testonly)/and-t STDIN or
eval"use ExtUtils::MakeMaker;WriteMakefile(PREREQ_PM=>{'$p',$v}
);1"and exit)and print"==> $p $v required. Install it from CP".
"AN? [Y/n] "and<STDIN>!~/^n/i and print"*** Installing $p\n"and
do{if (eval '$>' and lc(`sudo -V`) =~ /version/){system('sudo',
$^X,"-MCPANPLUS","-e","CPANPLUS::install $p");eval"use $p $v;1"
||system('sudo', $^X, "-MCPAN", "-e", "CPAN::install $p")}eval{
require CPANPLUS;CPANPLUS::install$p};eval"use $p $v;1"or eval{
require CPAN;CPAN::install$p};eval"use $p $v;1"||die"*** Please
manually install $p $v from cpan.org first...\n"}}}
If the user did not have ExtUtils::AutoInstall installed, the block of code above will automatically download and install it.
However, due to its space-compressed (and obfuscated) nature, you should think twice before employing this block of code; it is usually much more desirable to just use Module::Install instead.
perlmodlib, ExtUtils::MakeMaker, Sort::Versions, CPAN, CPANPLUS, Module::Install
The test script included in the ExtUtils::AutoInstall distribution contains code adapted from Michael Schwern's Test::More under the Perl License. Please consult to t/AutoInstall.t for details.
See the AUTHORS file in this module's source distribution for the list of contributors.
Autrijus Tang <autrijus@autrijus.org>
Copyright 2001, 2002, 2003, 2004 by Autrijus Tang <autrijus@autrijus.org>.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| ExtUtils-AutoInstall documentation | Contained in the ExtUtils-AutoInstall distribution. |
package ExtUtils::AutoInstall; $ExtUtils::AutoInstall::VERSION = '0.63'; use strict; use Cwd (); use ExtUtils::MakeMaker ();
# special map on pre-defined feature sets my %FeatureMap = ( '' => 'Core Features', # XXX: deprecated '-core' => 'Core Features', ); # various lexical flags my (@Missing, @Existing, %DisabledTests, $UnderCPAN, $HasCPANPLUS); my ($Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly); my ($PostambleActions, $PostambleUsed); _accept_default(!-t STDIN); # see if it's a non-interactive session _init(); sub _accept_default { $AcceptDefault = shift; } sub missing_modules { return @Missing; } sub do_install { __PACKAGE__->install( [ UNIVERSAL::isa($Config, 'HASH') ? %{$Config} : @{$Config}], @Missing, ); } # initialize various flags, and/or perform install sub _init { foreach my $arg (@ARGV, split(/[\s\t]+/, $ENV{PERL_EXTUTILS_AUTOINSTALL} || '')) { if ($arg =~ /^--config=(.*)$/) { $Config = [ split(',', $1) ]; } elsif ($arg =~ /^--installdeps=(.*)$/) { __PACKAGE__->install($Config, @Missing = split(/,/, $1)); exit 0; } elsif ($arg =~ /^--default(?:deps)?$/) { $AcceptDefault = 1; } elsif ($arg =~ /^--check(?:deps)?$/) { $CheckOnly = 1; } elsif ($arg =~ /^--skip(?:deps)?$/) { $SkipInstall = 1; } elsif ($arg =~ /^--test(?:only)?$/) { $TestOnly = 1; } } } # overrides MakeMaker's prompt() to automatically accept the default choice sub _prompt { goto &ExtUtils::MakeMaker::prompt unless $AcceptDefault; my ($prompt, $default) = @_; my $y = ($default =~ /^[Yy]/); print $prompt, ' [', ($y ? 'Y' : 'y'), '/', ($y ? 'n' : 'N'), '] '; print "$default\n"; return $default; } # the workhorse sub import { my $class = shift; my @args = @_ or return; my $core_all; print "*** $class version ".$class->VERSION."\n"; print "*** Checking for dependencies...\n"; my $cwd = Cwd::cwd(); $Config = []; my $maxlen = length((sort { length($b) <=> length($a) } grep { /^[^\-]/ } map { ref($_) ? keys %{ref($_) eq 'HASH' ? $_ : +{@{$_}}} : '' } map { +{@args}->{$_} } grep { /^[^\-]/ or /^-core$/i } keys %{+{@args}})[0]); while (my ($feature, $modules) = splice(@args, 0, 2)) { my (@required, @tests, @skiptests); my $default = 1; my $conflict = 0; if ($feature =~ m/^-(\w+)$/) { my $option = lc($1); # check for a newer version of myself _update_to($modules, @_) and return if $option eq 'version'; # sets CPAN configuration options $Config = $modules if $option eq 'config'; # promote every features to core status $core_all = ($modules =~ /^all$/i) and next if $option eq 'core'; next unless $option eq 'core'; } print "[".($FeatureMap{lc($feature)} || $feature)."]\n"; $modules = [ %{$modules} ] if UNIVERSAL::isa($modules, 'HASH'); unshift @$modules, -default => &{shift(@$modules)} if (ref($modules->[0]) eq 'CODE'); # XXX: bugward combatability while (my ($mod, $arg) = splice(@$modules, 0, 2)) { if ($mod =~ m/^-(\w+)$/) { my $option = lc($1); $default = $arg if ($option eq 'default'); $conflict = $arg if ($option eq 'conflict'); @tests = @{$arg} if ($option eq 'tests'); @skiptests = @{$arg} if ($option eq 'skiptests'); next; } printf("- %-${maxlen}s ...", $mod); # XXX: check for conflicts and uninstalls(!) them. if (defined(my $cur = _version_check(_load($mod), $arg ||= 0))) { print "loaded. ($cur".($arg ? " >= $arg" : '').")\n"; push @Existing, $mod => $arg; $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } else { print "missing." . ($arg ? " (would need $arg)" : '') . "\n"; push @required, $mod => $arg; } } next unless @required; my $mandatory = ($feature eq '-core' or $core_all); if (!$SkipInstall and ($CheckOnly or _prompt( qq{==> Auto-install the }. (@required / 2). ($mandatory ? ' mandatory' : ' optional'). qq{ module(s) from CPAN?}, $default ? 'y' : 'n', ) =~ /^[Yy]/)) { push (@Missing, @required); $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } elsif (!$SkipInstall and $default and $mandatory and _prompt( qq{==> The module(s) are mandatory! Really skip?}, 'n', ) =~ /^[Nn]/) { push (@Missing, @required); $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } else { $DisabledTests{$_} = 1 for map { glob($_) } @tests; } } _check_lock(); # check for $UnderCPAN if (@Missing and not ($CheckOnly or $UnderCPAN)) { require Config; print "*** Dependencies will be installed the next time you type '$Config::Config{make}'.\n"; # make an educated guess of whether we'll need root permission. print " (You may need to do that as the 'root' user.)\n" if eval '$>'; } print "*** $class configuration finished.\n"; chdir $cwd; # import to main:: no strict 'refs'; *{'main::WriteMakefile'} = \&Write if caller(0) eq 'main'; } # CPAN.pm is non-reentrant, so check if we're under it and have no CPANPLUS sub _check_lock { return unless @Missing; return if _has_cpanplus(); require CPAN; CPAN::Config->load; my $lock = MM->catfile($CPAN::Config->{cpan_home}, ".lock"); if (-f $lock and open(LOCK, $lock) and ($^O eq 'MSWin32' ? _under_cpan() : <LOCK> == getppid()) and ($CPAN::Config->{prerequisites_policy} || '') ne 'ignore' ) { print << '.'; *** Since we're running under CPAN, I'll just let it take care of the dependency's installation later. . $UnderCPAN = 1; } close LOCK; } sub install { my $class = shift; my $i; # used below to strip leading '-' from config keys my @config = (map { s/^-// if ++$i; $_ } @{+shift}); my (@modules, @installed); while (my ($pkg, $ver) = splice(@_, 0, 2)) { # grep out those already installed if (defined(_version_check(_load($pkg), $ver))) { push @installed, $pkg; } else { push @modules, $pkg, $ver; } } return @installed unless @modules; # nothing to do print "*** Installing dependencies...\n"; return unless _connected_to('cpan.org'); my %args = @config; my %failed; local *FAILED; if ($args{do_once} and open(FAILED, '.#autoinstall.failed')) { while (<FAILED>) { chomp; $failed{$_}++ } close FAILED; my @newmod; while (my ($k, $v) = splice(@modules, 0, 2)) { push @newmod, ($k => $v) unless $failed{$k}; } @modules = @newmod; } if (_has_cpanplus()) { _install_cpanplus(\@modules, \@config); } else { _install_cpan(\@modules, \@config); } print "*** $class installation finished.\n"; # see if we have successfully installed them while (my ($pkg, $ver) = splice(@modules, 0, 2)) { if (defined(_version_check(_load($pkg), $ver))) { push @installed, $pkg; } elsif ($args{do_once} and open(FAILED, '>> .#autoinstall.failed')) { print FAILED "$pkg\n"; } } close FAILED if $args{do_once}; return @installed; } sub _install_cpanplus { my @modules = @{+shift}; my @config = @{+shift}; my $installed = 0; require CPANPLUS::Backend; my $cp = CPANPLUS::Backend->new; my $conf = $cp->configure_object; return unless _can_write( $conf->can('conf') ? $conf->get_conf('base') # 0.05x+ : $conf->_get_build('base') # 0.04x ); # if we're root, set UNINST=1 to avoid trouble unless user asked for it. my $makeflags = $conf->get_conf('makeflags') || ''; if (UNIVERSAL::isa($makeflags, 'HASH')) { # 0.03+ uses a hashref here $makeflags->{UNINST} = 1 unless exists $makeflags->{UNINST}; } else { # 0.02 and below uses a scalar $makeflags = join(' ', split(' ', $makeflags), 'UNINST=1') if ($makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' }); } $conf->set_conf(makeflags => $makeflags); $conf->set_conf(prereqs => 1); while (my ($key, $val) = splice(@config, 0, 2)) { eval { $conf->set_conf($key, $val) }; } my $modtree = $cp->module_tree; while (my ($pkg, $ver) = splice(@modules, 0, 2)) { print "*** Installing $pkg...\n"; MY::preinstall($pkg, $ver) or next if defined &MY::preinstall; my $success; my $obj = $modtree->{$pkg}; if ($obj and defined(_version_check($obj->{version}, $ver))) { my $pathname = $pkg; $pathname =~ s/::/\\W/; foreach my $inc (grep { m/$pathname.pm/i } keys(%INC)) { delete $INC{$inc}; } my $rv = $cp->install( modules => [ $obj->{module} ]); if ($rv and ($rv->{$obj->{module}} or $rv->{ok})) { print "*** $pkg successfully installed.\n"; $success = 1; } else { print "*** $pkg installation cancelled.\n"; $success = 0; } $installed += $success; } else { print << "."; *** Could not find a version $ver or above for $pkg; skipping. . } MY::postinstall($pkg, $ver, $success) if defined &MY::postinstall; } return $installed; } sub _install_cpan { my @modules = @{+shift}; my @config = @{+shift}; my $installed = 0; my %args; require CPAN; CPAN::Config->load; require Config; return unless _can_write(MM->catfile($CPAN::Config->{cpan_home}, 'sources')) and _can_write($Config::Config{sitelib}); # if we're root, set UNINST=1 to avoid trouble unless user asked for it. my $makeflags = $CPAN::Config->{make_install_arg} || ''; $CPAN::Config->{make_install_arg} = join(' ', split(' ', $makeflags), 'UNINST=1') if ($makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' }); # don't show start-up info $CPAN::Config->{inhibit_startup_message} = 1; # set additional options while (my ($opt, $arg) = splice(@config, 0, 2)) { ($args{$opt} = $arg, next) if $opt =~ /^force$/; # pseudo-option $CPAN::Config->{$opt} = $arg; } local $CPAN::Config->{prerequisites_policy} = 'follow'; while (my ($pkg, $ver) = splice(@modules, 0, 2)) { MY::preinstall($pkg, $ver) or next if defined &MY::preinstall; print "*** Installing $pkg...\n"; my $obj = CPAN::Shell->expand(Module => $pkg); my $success = 0; if ($obj and defined(_version_check($obj->cpan_version, $ver))) { my $pathname = $pkg; $pathname =~ s/::/\\W/; foreach my $inc (grep { m/$pathname.pm/i } keys(%INC)) { delete $INC{$inc}; } $obj->force('install') if $args{force}; my $rv = $obj->install || eval { $CPAN::META->instance( 'CPAN::Distribution', $obj->cpan_file, )->{install} if $CPAN::META }; if ($rv eq 'YES') { print "*** $pkg successfully installed.\n"; $success = 1; } else { print "*** $pkg installation failed.\n"; $success = 0; } $installed += $success; } else { print << "."; *** Could not find a version $ver or above for $pkg; skipping. . } MY::postinstall($pkg, $ver, $success) if defined &MY::postinstall; } return $installed; } sub _has_cpanplus { return ( $HasCPANPLUS = ( $INC{'CPANPLUS/Config.pm'} or _load('CPANPLUS::Shell::Default') ) ); } # make guesses on whether we're under the CPAN installation directory sub _under_cpan { require Cwd; require File::Spec; my $cwd = File::Spec->canonpath(Cwd::cwd()); my $cpan = File::Spec->canonpath($CPAN::Config->{cpan_home}); return (index($cwd, $cpan) > -1); } sub _update_to { my $class = __PACKAGE__; my $ver = shift; return if defined(_version_check(_load($class), $ver)); # no need to upgrade if (_prompt( "==> A newer version of $class ($ver) is required. Install?", 'y' ) =~ /^[Nn]/) { die "*** Please install $class $ver manually.\n"; } print << "."; *** Trying to fetch it from CPAN... . # install ourselves _load($class) and return $class->import(@_) if $class->install([], $class, $ver); print << '.'; exit 1; *** Cannot bootstrap myself. :-( Installation terminated. . } # check if we're connected to some host, using inet_aton sub _connected_to { my $site = shift; return ( ( _load('Socket') and Socket::inet_aton($site) ) or _prompt(qq( *** Your host cannot resolve the domain name '$site', which probably means the Internet connections are unavailable. ==> Should we try to install the required module(s) anyway?), 'n' ) =~ /^[Yy]/ ); } # check if a directory is writable; may create it on demand sub _can_write { my $path = shift; mkdir ($path, 0755) unless -e $path; return 1 if -w $path; print << "."; *** You are not allowed to write to the directory '$path'; the installation may fail due to insufficient permissions. . if (eval '$>' and lc(`sudo -V`) =~ /version/ and _prompt(qq( ==> Should we try to re-execute the autoinstall process with 'sudo'?), 'y' ) =~ /^[Yy]/) { # try to bootstrap ourselves from sudo print << "."; *** Trying to re-execute the autoinstall process with 'sudo'... . my $missing = join(',', @Missing); my $config = join(',', UNIVERSAL::isa($Config, 'HASH') ? %{$Config} : @{$Config} ) if $Config; return unless system('sudo', $^X, $0, "--config=$config", "--installdeps=$missing"); print << "."; *** The 'sudo' command exited with error! Resuming... . } return _prompt(qq( ==> Should we try to install the required module(s) anyway?), 'n' ) =~ /^[Yy]/ } # load a module and return the version it reports sub _load { my $mod = pop; # class/instance doesn't matter my $file = $mod; $file =~ s|::|/|g; $file .= '.pm'; local $@; return eval { require $file; $mod->VERSION } || ($@ ? undef : 0); } # compare two versions, either use Sort::Versions or plain comparison sub _version_check { my ($cur, $min) = @_; return unless defined $cur; $cur =~ s/\s+$//; # check for version numbers that are not in decimal format if (ref($cur) or ref($min) or $cur =~ /v|\..*\./ or $min =~ /v|\..*\./) { if ($version::VERSION or defined(_load('version'))) { # use version.pm if it is installed. return ((version->new($cur) >= version->new($min)) ? $cur : undef); } elsif ($Sort::Versions::VERSION or defined(_load('Sort::Versions'))) { # use Sort::Versions as the sorting algorithm for a.b.c versions return ((Sort::Versions::versioncmp($cur, $min) != -1) ? $cur : undef); } warn "Cannot reliably compare non-decimal formatted versions.\n". "Please install version.pm or Sort::Versions.\n"; } # plain comparison local $^W = 0; # shuts off 'not numeric' bugs return ($cur >= $min ? $cur : undef); } # nothing; this usage is deprecated. sub main::PREREQ_PM { return {}; } sub _make_args { my %args = @_; $args{PREREQ_PM} = { %{$args{PREREQ_PM} || {} }, @Existing, @Missing } if $UnderCPAN or $TestOnly; if ($args{EXE_FILES}) { require ExtUtils::Manifest; my $manifest = ExtUtils::Manifest::maniread('MANIFEST'); $args{EXE_FILES} = [ grep { exists $manifest->{$_} } @{$args{EXE_FILES}} ]; } $args{test}{TESTS} ||= 't/*.t'; $args{test}{TESTS} = join(' ', grep { !exists($DisabledTests{$_}) } map { glob($_) } split(/\s+/, $args{test}{TESTS})); my $missing = join(',', @Missing); my $config = join(',', UNIVERSAL::isa($Config, 'HASH') ? %{$Config} : @{$Config} ) if $Config; $PostambleActions = ( $missing ? "\$(PERL) $0 --config=$config --installdeps=$missing" : "\@\$(NOOP)" ); return %args; } # a wrapper to ExtUtils::MakeMaker::WriteMakefile sub Write { require Carp; Carp::croak "WriteMakefile: Need even number of args" if @_ % 2; if ($CheckOnly) { print << "."; *** Makefile not written in check-only mode. . return; } my %args = _make_args(@_); no strict 'refs'; $PostambleUsed = 0; local *MY::postamble = \&postamble unless defined &MY::postamble; ExtUtils::MakeMaker::WriteMakefile(%args); print << "." unless $PostambleUsed; *** WARNING: Makefile written with customized MY::postamble() without including contents from ExtUtils::AutoInstall::postamble() -- auto installation features disabled. Please contact the author. . return 1; } sub postamble { $PostambleUsed = 1; return << "."; config :: installdeps \t\@\$(NOOP) checkdeps :: \t\$(PERL) $0 --checkdeps installdeps :: \t$PostambleActions . } 1; __END__