package Module::Build::Base;
use strict;
+use vars qw($VERSION);
+$VERSION = '0.2808_01';
+$VERSION = eval $VERSION;
BEGIN { require 5.00503 }
use Carp;
-use Config;
use File::Copy ();
use File::Find ();
use File::Path ();
use File::Basename ();
use File::Spec 0.82 ();
use File::Compare ();
-use Data::Dumper ();
+use Module::Build::Dumper ();
use IO::File ();
use Text::ParseWords ();
use Module::Build::ModuleInfo;
use Module::Build::Notes;
+use Module::Build::Config;
#################### Constructors ###########################
die "Too early to specify a build action '$self->{action}'. Do 'Build $self->{action}' instead.\n"
if $self->{action} && $self->{action} ne 'Build_PL';
- $self->dist_name;
- $self->dist_version;
-
$self->check_manifest;
$self->check_prereq;
$self->check_autofeatures;
+ $self->dist_name;
+ $self->dist_version;
+
$self->_set_install_paths;
$self->_find_nested_builds;
" but we are now using '$perl'.\n");
}
- my $mb_version = $Module::Build::VERSION;
- die(" * ERROR: Configuration was initially created with Module::Build version '$self->{properties}{mb_version}',\n".
- " but we are now using version '$mb_version'. Please re-run the Build.PL or Makefile.PL script.\n")
- unless $mb_version eq $self->{properties}{mb_version};
-
$self->cull_args(@ARGV);
+
+ unless ($self->allow_mb_mismatch) {
+ my $mb_version = $Module::Build::VERSION;
+ die(" * ERROR: Configuration was initially created with Module::Build version '$self->{properties}{mb_version}',\n".
+ " but we are now using version '$mb_version'. Please re-run the Build.PL or Makefile.PL script,\n".
+ " or use --allow_mb_mismatch 1 to skip this version check.\n")
+ if $mb_version ne $self->{properties}{mb_version};
+ }
+
$self->{invoked_action} = $self->{action} ||= 'build';
return $self;
# as it is during resume() (and thereafter).
{
local @ARGV = $package->unparse_args(\%args);
- do 'Build.PL';
+ do './Build.PL';
die $@ if $@;
}
return $package->resume;
my $self = bless {
args => {%$args},
- config => {%Config, %$config},
+ config => Module::Build::Config->new(values => $config),
properties => {
base_dir => $package->cwd,
mb_version => $Module::Build::VERSION,
}, $package;
$self->_set_defaults;
- my ($p, $c, $ph) = ($self->{properties}, $self->{config}, $self->{phash});
+ my ($p, $ph) = ($self->{properties}, $self->{phash});
foreach (qw(notes config_data features runtime_params cleanup auto_features)) {
my $file = File::Spec->catfile($self->config_dir, $_);
sub _set_install_paths {
my $self = shift;
- my $c = $self->config;
+ my $c = $self->{config};
my $p = $self->{properties};
- my @libstyle = $c->{installstyle} ?
- File::Spec->splitdir($c->{installstyle}) : qw(lib perl5);
- my $arch = $c->{archname};
- my $version = $c->{version};
+ my @libstyle = $c->get('installstyle') ?
+ File::Spec->splitdir($c->get('installstyle')) : qw(lib perl5);
+ my $arch = $c->get('archname');
+ my $version = $c->get('version');
- my $bindoc = $c->{installman1dir} || undef;
- my $libdoc = $c->{installman3dir} || undef;
+ my $bindoc = $c->get('installman1dir') || undef;
+ my $libdoc = $c->get('installman3dir') || undef;
- my $binhtml = $c->{installhtml1dir} || $c->{installhtmldir} || undef;
- my $libhtml = $c->{installhtml3dir} || $c->{installhtmldir} || undef;
+ my $binhtml = $c->get('installhtml1dir') || $c->get('installhtmldir') || undef;
+ my $libhtml = $c->get('installhtml3dir') || $c->get('installhtmldir') || undef;
$p->{install_sets} =
{
core => {
- lib => $c->{installprivlib},
- arch => $c->{installarchlib},
- bin => $c->{installbin},
- script => $c->{installscript},
+ lib => $c->get('installprivlib'),
+ arch => $c->get('installarchlib'),
+ bin => $c->get('installbin'),
+ script => $c->get('installscript'),
bindoc => $bindoc,
libdoc => $libdoc,
binhtml => $binhtml,
libhtml => $libhtml,
},
site => {
- lib => $c->{installsitelib},
- arch => $c->{installsitearch},
- bin => $c->{installsitebin} || $c->{installbin},
- script => $c->{installsitescript} ||
- $c->{installsitebin} || $c->{installscript},
- bindoc => $c->{installsiteman1dir} || $bindoc,
- libdoc => $c->{installsiteman3dir} || $libdoc,
- binhtml => $c->{installsitehtml1dir} || $binhtml,
- libhtml => $c->{installsitehtml3dir} || $libhtml,
+ lib => $c->get('installsitelib'),
+ arch => $c->get('installsitearch'),
+ bin => $c->get('installsitebin') || $c->get('installbin'),
+ script => $c->get('installsitescript') ||
+ $c->get('installsitebin') || $c->get('installscript'),
+ bindoc => $c->get('installsiteman1dir') || $bindoc,
+ libdoc => $c->get('installsiteman3dir') || $libdoc,
+ binhtml => $c->get('installsitehtml1dir') || $binhtml,
+ libhtml => $c->get('installsitehtml3dir') || $libhtml,
},
vendor => {
- lib => $c->{installvendorlib},
- arch => $c->{installvendorarch},
- bin => $c->{installvendorbin} || $c->{installbin},
- script => $c->{installvendorscript} ||
- $c->{installvendorbin} || $c->{installscript},
- bindoc => $c->{installvendorman1dir} || $bindoc,
- libdoc => $c->{installvendorman3dir} || $libdoc,
- binhtml => $c->{installvendorhtml1dir} || $binhtml,
- libhtml => $c->{installvendorhtml3dir} || $libhtml,
+ lib => $c->get('installvendorlib'),
+ arch => $c->get('installvendorarch'),
+ bin => $c->get('installvendorbin') || $c->get('installbin'),
+ script => $c->get('installvendorscript') ||
+ $c->get('installvendorbin') || $c->get('installscript'),
+ bindoc => $c->get('installvendorman1dir') || $bindoc,
+ libdoc => $c->get('installvendorman3dir') || $libdoc,
+ binhtml => $c->get('installvendorhtml1dir') || $binhtml,
+ libhtml => $c->get('installvendorhtml3dir') || $libhtml,
},
};
$p->{original_prefix} =
{
- core => $c->{installprefixexp} || $c->{installprefix} ||
- $c->{prefixexp} || $c->{prefix} || '',
- site => $c->{siteprefixexp},
- vendor => $c->{usevendorprefix} ? $c->{vendorprefixexp} : '',
+ core => $c->get('installprefixexp') || $c->get('installprefix') ||
+ $c->get('prefixexp') || $c->get('prefix') || '',
+ site => $c->get('siteprefixexp'),
+ vendor => $c->get('usevendorprefix') ? $c->get('vendorprefixexp') : '',
};
$p->{original_prefix}{site} ||= $p->{original_prefix}{core};
my ($self, @cmd) = @_;
if ($self->have_forkpipe) {
local *FH;
- my $pid = open FH, "-|";
+ my $pid = open *FH, "-|";
if ($pid) {
return wantarray ? <FH> : join '', <FH>;
} else {
return $self->_backticks(@cmd) eq Config->myconfig;
}
+# cache _discover_perl_interpreter() results
+{
+ my $known_perl;
+ sub find_perl_interpreter {
+ my $self = shift;
+
+ return $known_perl if defined($known_perl);
+ return $known_perl = $self->_discover_perl_interpreter;
+ }
+}
+
# Returns the absolute path of the perl interperter used to invoke
# this process. The path is derived from $^X or $Config{perlpath}. On
# some platforms $^X contains the complete absolute path of the
# executable extension on platforms that use one. It's a fatal error
# if the interpreter can't be found because it can result in undefined
# behavior by routines that depend on it (generating errors or
-# invoking the wrong perl.
-sub find_perl_interpreter {
+# invoking the wrong perl.)
+sub _discover_perl_interpreter {
my $proto = shift;
- my $c = ref($proto) ? $proto->config : \%Config::Config;
+ my $c = ref($proto) ? $proto->{config} : 'Module::Build::Config';
my $perl = $^X;
my $perl_basename = File::Basename::basename($perl);
} else {
- # Try 3.B, First look in $Config{perlpath}, then search the users
+ # Try 3.B, First look in $Config{perlpath}, then search the user's
# PATH. We do not want to do either if we are running from an
# uninstalled perl in a perl source tree.
- push( @potential_perls, $c->{perlpath} );
+ push( @potential_perls, $c->get('perlpath') );
push( @potential_perls,
map File::Spec->catfile($_, $perl_basename), File::Spec->path() );
# Now that we've enumerated the potential perls, it's time to test
# them to see if any of them match our configuration, returning the
# absolute path of the first successful match.
- my $exe = $c->{exe_ext};
+ my $exe = $c->get('exe_ext');
foreach my $thisperl ( @potential_perls ) {
- if ($proto->os_type eq 'VMS') {
- # VMS might have a file version at the end
- $thisperl .= $exe unless $thisperl =~ m/$exe(;\d+)?$/i;
- } elsif (defined $exe) {
+ if (defined $exe) {
$thisperl .= $exe unless $thisperl =~ m/$exe$/i;
}
return -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)) ; # Pipe?
}
+# NOTE this is a blocking operation if(-t STDIN)
sub _is_unattended {
my $self = shift;
- return $ENV{PERL_MM_USE_DEFAULT} || ( !$self->_is_interactive && eof STDIN );
+ return $ENV{PERL_MM_USE_DEFAULT} ||
+ ( !$self->_is_interactive && eof STDIN );
}
sub _readline {
my $mess = shift
or die "prompt() called without a prompt message";
- my $def;
- if ( $self->_is_unattended && !@_ ) {
+ # use a list to distinguish a default of undef() from no default
+ my @def;
+ @def = (shift) if @_;
+ # use dispdef for output
+ my @dispdef = scalar(@def) ?
+ ('[', (defined($def[0]) ? $def[0] . ' ' : ''), ']') :
+ (' ', '');
+
+ local $|=1;
+ print "$mess ", @dispdef;
+
+ if ( $self->_is_unattended && !@def ) {
die <<EOF;
ERROR: This build seems to be unattended, but there is no default value
for this question. Aborting.
EOF
}
- $def = shift if @_;
- ($def, my $dispdef) = defined $def ? ($def, "[$def] ") : ('', ' ');
-
- local $|=1;
- print "$mess $dispdef";
my $ans = $self->_readline();
if ( !defined($ans) # Ctrl-D or unattended
or !length($ans) ) { # User hit return
- print "$def\n";
- $ans = $def;
+ print "$dispdef[1]\n";
+ $ans = scalar(@def) ? $def[0] : '';
}
return $ans;
die "Invalid default value: y_n() default must be 'y' or 'n'"
if $def && $def !~ /^[yn]/i;
- if ( $self->_is_unattended && !$def ) {
- die <<EOF;
-ERROR: This build seems to be unattended, but there is no default value
-for this question. Aborting.
-EOF
- }
-
my $answer;
while (1) { # XXX Infinite or a large number followed by an exception ?
$answer = $self->prompt(@_);
return wantarray ? %features : \%features;
}
-BEGIN { *feature = \&features }
+BEGIN { *feature = \&features } # Alias
sub _mb_feature {
my $self = shift;
if ( $type eq 'HASH' ) {
*{"$class\::$property"} = sub {
my $self = shift;
- my $x = ( $property eq 'config' ) ? $self : $self->{properties};
+ my $x = $self->{properties};
return $x->{$property} unless @_;
if ( defined($_[0]) && !ref($_[0]) ) {
__PACKAGE__->add_property(recurse_into => []);
__PACKAGE__->add_property(use_rcfile => 1);
__PACKAGE__->add_property(create_packlist => 1);
+__PACKAGE__->add_property(allow_mb_mismatch => 0);
+__PACKAGE__->add_property(config => undef);
{
my $Is_ActivePerl = eval {require ActivePerl::DocTools};
}
__PACKAGE__->add_property($_ => {}) for qw(
- config
get_options
install_base_relpaths
install_path
meta_merge
original_prefix
prefix_relpaths
+ configure_requires
);
__PACKAGE__->add_property($_) for qw(
xs_files
);
+sub config {
+ my $self = shift;
+ my $c = ref($self) ? $self->{config} : 'Module::Build::Config';
+ return $c->all_config unless @_;
+
+ my $key = shift;
+ return $c->get($key) unless @_;
+
+ my $val = shift;
+ return $c->set($key => $val);
+}
sub mb_parents {
# Code borrowed from Class::ISA.
die ("Can't determine distribution version, must supply either 'dist_version',\n".
"'dist_version_from', or 'module_name' parameter")
- unless $p->{dist_version};
+ unless defined $p->{dist_version};
return $p->{dist_version};
}
my ($self) = @_;
my $file = $self->config_file('build_params')
- or die "No build_params?";
+ or die "Can't find 'build_params' in " . $self->config_dir;
my $fh = IO::File->new($file) or die "Can't read '$file': $!";
my $ref = eval do {local $/; <$fh>};
die if $@;
- ($self->{args}, $self->{config}, $self->{properties}) = @$ref;
+ my $c;
+ ($self->{args}, $c, $self->{properties}) = @$ref;
+ $self->{config} = Module::Build::Config->new(values => $c);
close $fh;
}
my $file = $self->config_file($filename);
my $fh = IO::File->new("> $file") or die "Can't create '$file': $!";
- local $Data::Dumper::Terse = 1;
- print $fh ref($data) ? Data::Dumper::Dumper($data) : $data;
+ unless (ref($data)) { # e.g. magicnum
+ print $fh $data;
+ return;
+ }
+
+ print {$fh} Module::Build::Dumper->_data_dump($data);
}
sub write_config {
my @items = @{ $self->prereq_action_types };
$self->_write_data('prereqs', { map { $_, $self->$_() } @items });
- $self->_write_data('build_params', [$self->{args}, $self->{config}, $self->{properties}]);
+ $self->_write_data('build_params', [$self->{args}, $self->{config}->values_set, $self->{properties}]);
# Set a new magic number and write it to a file
$self->_write_data('magicnum', $self->magic_number(int rand 1_000_000));
} elsif ($type =~ /^(?:\w+_)?recommends$/) {
next if $status->{ok};
- $status->{message} = ($status->{have} eq '<none>'
+ $status->{message} = (!ref($status->{have}) && $status->{have} eq '<none>'
? "Optional prerequisite $modname is not installed"
: "$modname ($status->{have}) is installed, but we prefer to have $spec");
} else {
my $xs_files = $self->find_xs_files;
if (keys %$xs_files && !$self->_mb_feature('C_support')) {
$self->log_warn("Warning: this distribution contains XS files, ".
- "but Module::Build is not configured with C_support");
+ "but Module::Build is not configured with C_support. ".
+ "Please install ExtUtils::CBuilder to enable C_support.\n");
}
# Check to see if there are any prereqs to check
sub perl_version_to_float {
my ($self, $version) = @_;
+ return $version if grep( /\./, $version ) < 2;
$version =~ s/\./../;
$version =~ s/\.(\d+)/sprintf '%03d', $1/eg;
return $version;
}
$status{have} = $pm_info->version();
- if ($spec and !$status{have}) {
+ if ($spec and !defined($status{have})) {
@status{ qw(have message) } = (undef, "Couldn't find a \$VERSION in prerequisite $modname");
return \%status;
}
my $self = shift;
foreach (@_) {
my $current_mode = (stat $_)[2];
- chmod $current_mode | 0111, $_;
+ chmod $current_mode | oct(111), $_;
}
}
my $case_tolerant = 0+(File::Spec->can('case_tolerant')
&& File::Spec->case_tolerant);
$q{base_dir} = uc $q{base_dir} if $case_tolerant;
- $q{base_dir} = Win32::GetShortPathName($q{base_dir}) if $^O eq 'MSWin32';
+ $q{base_dir} = Win32::GetShortPathName($q{base_dir}) if $self->is_windowsish;
$q{magic_numfile} = $self->config_file('magicnum');
}
$args{ARGV} = \@argv;
+ for ('extra_compiler_flags', 'extra_linker_flags') {
+ $args{$_} = [ $self->split_like_shell($args{$_}) ] if exists $args{$_};
+ }
+
# Hashify these parameters
- for ($self->hash_properties) {
+ for ($self->hash_properties, 'config') {
next unless exists $args{$_};
my %hash;
$args{$_} ||= [];
# De-tilde-ify any path parameters
for my $key (qw(prefix install_base destdir)) {
next if !defined $args{$key};
- $args{$key} = _detildefy($args{$key});
+ $args{$key} = $self->_detildefy($args{$key});
}
for my $key (qw(install_path)) {
for my $subkey (keys %{$args{$key}}) {
next if !defined $args{$key}{$subkey};
- my $subkey_ext = _detildefy($args{$key}{$subkey});
+ my $subkey_ext = $self->_detildefy($args{$key}{$subkey});
if ( $subkey eq 'html' ) { # translate for compatability
$args{$key}{binhtml} = $subkey_ext;
$args{$key}{libhtml} = $subkey_ext;
return \%args, $action;
}
-
-sub _detildefy {
- my $arg = shift;
-
- my($new_arg) = glob($arg) if $arg =~ /^~/;
-
- return defined($new_arg) ? $new_arg : $arg;
-}
+# Default: do nothing. Overridden for Unix & Windows.
+sub _detildefy {}
# merge Module::Build argument lists that have already been parsed
while (my ($key, $val) = each %args) {
$self->{phash}{runtime_params}->access( $key => $val )
if $self->valid_property($key);
- my $add_to = ( $key eq 'config' ? $self->{config}
- : $additive{$key} ? $self->{properties}{$key}
- : $self->valid_property($key) ? $self->{properties}
- : $self->{args});
- if ($additive{$key}) {
- $add_to->{$_} = $val->{$_} foreach keys %$val;
+ if ($key eq 'config') {
+ $self->config($_ => $val->{$_}) foreach keys %$val;
} else {
- $add_to->{$key} = $val;
+ my $add_to = ( $additive{$key} ? $self->{properties}{$key}
+ : $self->valid_property($key) ? $self->{properties}
+ : $self->{args});
+
+ if ($additive{$key}) {
+ $add_to->{$_} = $val->{$_} foreach keys %$val;
+ } else {
+ $add_to->{$key} = $val;
+ }
}
}
}
}
sub get_action_docs {
- my ($self, $action, $actions) = @_;
- $actions ||= $self->known_actions;
- $@ = '';
- ($@ = "No known action '$action'\n"), return
- unless $actions->{$action};
-
+ my ($self, $action) = @_;
+ my $actions = $self->known_actions;
+ die "No known action '$action'" unless $actions->{$action};
+
my ($files_found, @docs) = (0);
foreach my $class ($self->super_classes) {
(my $file = $class) =~ s{::}{/}g;
+ # NOTE: silently skipping relative paths if any chdir() happened
$file = $INC{$file . '.pm'} or next;
my $fh = IO::File->new("< $file") or next;
$files_found++;
-
+
# Code below modified from /usr/bin/perldoc
-
+
# Skip to ACTIONS section
local $_;
while (<$fh>) {
last if /^=head1 ACTIONS\s/;
}
-
- # Look for our action
- my ($found, $inlist) = (0, 0);
+
+ # Look for our action and determine the style
+ my $style;
while (<$fh>) {
- if (/^=item\s+\Q$action\E\b/) {
- $found = 1;
- } elsif (/^=(item|back)/) {
- last if $found > 1 and not $inlist;
+ last if /^=head1 /;
+
+ # only item and head2 are allowed (3&4 are not in 5.005)
+ if(/^=(item|head2)\s+\Q$action\E\b/) {
+ $style = $1;
+ push @docs, $_;
+ last;
+ }
+ }
+ $style or next; # not here
+
+ # and the content
+ if($style eq 'item') {
+ my ($found, $inlist) = (0, 0);
+ while (<$fh>) {
+ if (/^=(item|back)/) {
+ last unless $inlist;
+ }
+ push @docs, $_;
+ ++$inlist if /^=over/;
+ --$inlist if /^=back/;
}
- next unless $found;
- push @docs, $_;
- ++$inlist if /^=over/;
- --$inlist if /^=back/;
- ++$found if /^\w/; # Found descriptive text
}
+ else { # head2 style
+ # stop at anything equal or greater than the found level
+ while (<$fh>) {
+ last if(/^=(?:head[12]|cut)/);
+ push @docs, $_;
+ }
+ }
+ # TODO maybe disallow overriding just pod for an action
+ # TODO and possibly: @docs and last;
}
unless ($files_found) {
my $vspace = q{ } x ($ver_len - length $mod->{need});
my $f = $mod->{ok} ? ' ' : '!';
$output .=
- " $f $mod->{name} $space $mod->{need} $vspace $mod->{have}\n";
+ " $f $mod->{name} $space $mod->{need} $vspace ".
+ (defined($mod->{have}) ? $mod->{have} : "")."\n";
}
}
return $output;
my $actions = $self->known_actions;
if (@{$self->{args}{ARGV}}) {
- my $msg = $self->get_action_docs($self->{args}{ARGV}[0], $actions) || "$@\n";
- print $msg;
+ my $msg = eval {$self->get_action_docs($self->{args}{ARGV}[0], $actions)};
+ print $@ ? "$@\n" : $msg;
return;
}
return $out;
}
+sub ACTION_retest {
+ my ($self) = @_;
+
+ # Protect others against our @INC changes
+ local @INC = @INC;
+
+ # Filter out nonsensical @INC entries - some versions of
+ # Test::Harness will really explode the number of entries here
+ @INC = grep {ref() || -d} @INC if @INC > 100;
+
+ $self->do_tests;
+}
+
+sub ACTION_testall {
+ my ($self) = @_;
+
+ my @types;
+ for my $action (grep { $_ ne 'all' } $self->get_test_types) {
+ # XXX We can't just dispatch because we get multiple summaries but
+ # we'll need to dispatch to support custom setup/teardown in the
+ # action. To support that, we'll need to call something besides
+ # Harness::runtests() because we'll need to collect the results in
+ # parts, then run the summary.
+ push(@types, $action);
+ #$self->_call_action( "test$action" );
+ }
+ $self->generic_test(types => ['default', @types]);
+}
+
+sub get_test_types {
+ my ($self) = @_;
+
+ my $t = $self->{properties}->{test_types};
+ return ( defined $t ? ( keys %$t ) : () );
+}
+
+
sub ACTION_test {
my ($self) = @_;
+ $self->generic_test(type => 'default');
+}
+
+sub generic_test {
+ my $self = shift;
+ (@_ % 2) and croak('Odd number of elements in argument hash');
+ my %args = @_;
+
my $p = $self->{properties};
- require Test::Harness;
-
+
+ my @types = (
+ (exists($args{type}) ? $args{type} : ()),
+ (exists($args{types}) ? @{$args{types}} : ()),
+ );
+ @types or croak "need some types of tests to check";
+
+ my %test_types = (
+ default => '.t',
+ (defined($p->{test_types}) ? %{$p->{test_types}} : ()),
+ );
+
+ for my $type (@types) {
+ croak "$type not defined in test_types!"
+ unless defined $test_types{ $type };
+ }
+
+ # we use local here because it ends up two method calls deep
+ local $p->{test_file_exts} = [ @test_types{@types} ];
$self->depends_on('code');
-
+
+ # Protect others against our @INC changes
+ local @INC = @INC;
+
+ # Make sure we test the module in blib/
+ unshift @INC, (File::Spec->catdir($p->{base_dir}, $self->blib, 'lib'),
+ File::Spec->catdir($p->{base_dir}, $self->blib, 'arch'));
+
+ # Filter out nonsensical @INC entries - some versions of
+ # Test::Harness will really explode the number of entries here
+ @INC = grep {ref() || -d} @INC if @INC > 100;
+
+ $self->do_tests;
+}
+
+sub do_tests {
+ my $self = shift;
+ my $p = $self->{properties};
+ require Test::Harness;
+
# Do everything in our power to work with all versions of Test::Harness
my @harness_switches = $p->{debugger} ? qw(-w -d) : ();
local $Test::Harness::switches = join ' ', grep defined, $Test::Harness::switches, @harness_switches;
$ENV{TEST_VERBOSE},
$ENV{HARNESS_VERBOSE}) = ($p->{verbose} || 0) x 4;
- # Make sure we test the module in blib/
- local @INC = (File::Spec->catdir($p->{base_dir}, $self->blib, 'lib'),
- File::Spec->catdir($p->{base_dir}, $self->blib, 'arch'),
- @INC);
-
- # Filter out nonsensical @INC entries - some versions of
- # Test::Harness will really explode the number of entries here
- @INC = grep {ref() || -d} @INC if @INC > 100;
-
my $tests = $self->find_test_files;
if (@$tests) {
sub expand_test_dir {
my ($self, $dir) = @_;
- return sort @{$self->rscan_dir($dir, qr{^[^.].*\.t$})} if $self->recursive_test_files;
- return sort glob File::Spec->catfile($dir, "*.t");
+ my $exts = $self->{properties}{test_file_exts} || ['.t'];
+
+ return sort map { @{$self->rscan_dir($dir, qr{^[^.].*\Q$_\E$})} } @$exts
+ if $self->recursive_test_files;
+
+ return sort map { glob File::Spec->catfile($dir, "*$_") } @$exts;
}
sub ACTION_testdb {
# See whether any of the *.pm files have changed since last time
# testcover was run. If so, start over.
if (-e 'cover_db') {
- my $pm_files = $self->rscan_dir(File::Spec->catdir($self->blib, 'lib'), qr{\.pm$} );
+ my $pm_files = $self->rscan_dir
+ (File::Spec->catdir($self->blib, 'lib'), file_qr('\.pm$') );
my $cover_files = $self->rscan_dir('cover_db', sub {-f $_ and not /\.html$/});
$self->do_system(qw(cover -delete))
- unless $self->up_to_date($pm_files, $cover_files);
+ unless $self->up_to_date($pm_files, $cover_files)
+ && $self->up_to_date($self->test_files, $cover_files);
}
local $Test::Harness::switches =
push @{$p->{include_dirs}}, $p->{c_source};
- my $files = $self->rscan_dir($p->{c_source}, qr{\.c(pp)?$});
+ my $files = $self->rscan_dir($p->{c_source}, file_qr('\.c(pp)?$'));
foreach my $file (@$files) {
push @{$p->{objects}}, $self->compile_c($file);
}
while (my ($file, $to) = each %$files) {
unless ($self->up_to_date( $file, $to )) {
- $self->run_perl_script($file, [], [@$to]);
+ $self->run_perl_script($file, [], [@$to]) or die "$file failed";
$self->add_to_cleanup(@$to);
}
}
foreach my $file (keys %$files) {
my $result = $self->copy_if_modified($file, $script_dir, 'flatten') or next;
- $self->fix_shebang_line($result) unless $self->os_type eq 'VMS';
+ $self->fix_shebang_line($result) unless $self->is_vmsish;
$self->make_executable($result);
}
}
}
return unless -d 'lib';
- return { map {$_, [/^(.*)\.PL$/]} @{ $self->rscan_dir('lib', qr{\.PL$}) } };
+ return { map {$_, [/^(.*)\.PL$/i ]} @{ $self->rscan_dir('lib',
+ file_qr('\.PL$')) } };
}
sub find_pm_files { shift->_find_file_by_type('pm', 'lib') }
sub find_test_files {
my $self = shift;
my $p = $self->{properties};
-
+
if (my $files = $p->{test_files}) {
$files = [keys %$files] if UNIVERSAL::isa($files, 'HASH');
$files = [map { -d $_ ? $self->expand_test_dir($_) : $_ }
return { map {$_, $_}
map $self->localize_file_path($_),
grep !/\.\#/,
- @{ $self->rscan_dir($dir, qr{\.$type$}) } };
+ @{ $self->rscan_dir($dir, file_qr("\\.$type\$")) } };
}
sub localize_file_path {
my ($self, $path) = @_;
- $path =~ s/\.\z// if $self->os_type eq 'VMS';
+ $path =~ s/\.\z// if $self->is_vmsish;
return File::Spec->catfile( split m{/}, $path );
}
sub fix_shebang_line { # Adapted from fixin() in ExtUtils::MM_Unix 1.35
my ($self, @files) = @_;
- my $c = $self->config;
+ my $c = ref($self) ? $self->{config} : 'Module::Build::Config';
- my ($does_shbang) = $c->{sharpbang} =~ /^\s*\#\!/;
+ my ($does_shbang) = $c->get('sharpbang') =~ /^\s*\#\!/;
for my $file (@files) {
my $FIXIN = IO::File->new($file) or die "Can't process '$file': $!";
local $/ = "\n";
$self->log_verbose("Changing sharpbang in $file to $interpreter");
my $shb = '';
- $shb .= "$c->{sharpbang}$interpreter $arg\n" if $does_shbang;
+ $shb .= $c->get('sharpbang')."$interpreter $arg\n" if $does_shbang;
# I'm not smart enough to know the ramifications of changing the
# embedded newlines here to \n, so I leave 'em in.
$shb .= qq{
eval 'exec $interpreter $arg -S \$0 \${1+"\$\@"}'
if 0; # not running under some shell
-} unless $self->os_type eq 'Windows'; # this won't work on win32, so don't
+} unless $self->is_windowsish; # this won't work on win32, so don't
my $FIXOUT = IO::File->new(">$file.new")
or die "Can't create new $file: $!\n";
rename("$file.new", $file)
or die "Can't rename $file.new to $file: $!";
- unlink "$file.bak"
+ $self->delete_filetree("$file.bak")
or $self->log_warn("Couldn't clean up $file.bak, leaving it there");
- $self->do_system($c->{eunicefix}, $file) if $c->{eunicefix} ne ':';
+ $self->do_system($c->get('eunicefix'), $file) if $c->get('eunicefix') ne ':';
}
}
or die "The 'testpod' action requires Test::Pod version 0.95";
my @files = sort keys %{$self->_find_pods($self->libdoc_dirs)},
- keys %{$self->_find_pods($self->bindoc_dirs, exclude => [ qr/\.bat$/ ])}
+ keys %{$self->_find_pods
+ ($self->bindoc_dirs,
+ exclude => [ file_qr('\.bat$') ])}
or die "Couldn't find any POD files to test\n";
{ package Module::Build::PodTester; # Don't want to pollute the main namespace
or die "The 'testpodcoverage' action requires ",
"Test::Pod::Coverage version 1.00";
+ # TODO this needs test coverage!
+
+ # XXX work-around a bug in Test::Pod::Coverage previous to v1.09
+ # Make sure we test the module in blib/
+ local @INC = @INC;
+ my $p = $self->{properties};
+ unshift(@INC,
+ # XXX any reason to include arch?
+ File::Spec->catdir($p->{base_dir}, $self->blib, 'lib'),
+ #File::Spec->catdir($p->{base_dir}, $self->blib, 'arch')
+ );
+
all_pod_coverage_ok();
}
foreach my $type ( qw(bin lib) ) {
my $files = $self->_find_pods( $self->{properties}{"${type}doc_dirs"},
- exclude => [ qr/\.bat$/ ] );
+ exclude => [ file_qr('\.bat$') ] );
next unless %$files;
my $sub = $self->can("manify_${type}_pods");
my $self = shift;
my $files = $self->_find_pods( $self->{properties}{bindoc_dirs},
- exclude => [ qr/\.bat$/ ] );
+ exclude => [ file_qr('\.bat$') ] );
return unless keys %$files;
my $mandir = File::Spec->catdir( $self->blib, 'bindoc' );
- File::Path::mkpath( $mandir, 0, 0777 );
+ File::Path::mkpath( $mandir, 0, oct(777) );
require Pod::Man;
foreach my $file (keys %$files) {
return unless keys %$files;
my $mandir = File::Spec->catdir( $self->blib, 'libdoc' );
- File::Path::mkpath( $mandir, 0, 0777 );
+ File::Path::mkpath( $mandir, 0, oct(777) );
require Pod::Man;
while (my ($file, $relfile) = each %$files) {
foreach my $type ( qw(bin lib) ) {
my $files = $self->_find_pods( $self->{properties}{"${type}doc_dirs"},
- exclude => [ qr/\.(?:bat|com|html)$/ ] );
+ exclude =>
+ [ file_qr('\.(?:bat|com|html)$') ] );
next unless %$files;
if ( $self->invoked_action eq 'html' ) {
$self->add_to_cleanup('pod2htm*');
my $pods = $self->_find_pods( $self->{properties}{"${type}doc_dirs"},
- exclude => [ qr/\.(?:bat|com|html)$/ ] );
+ exclude => [ file_qr('\.(?:bat|com|html)$') ] );
return unless %$pods; # nothing to do
unless ( -d $htmldir ) {
- File::Path::mkpath($htmldir, 0, 0755)
+ File::Path::mkpath($htmldir, 0, oct(755))
or die "Couldn't mkdir $htmldir: $!";
}
foreach my $pod ( keys %$pods ) {
my ($name, $path) = File::Basename::fileparse($pods->{$pod},
- qr{\.(?:pm|plx?|pod)$});
+ file_qr('\.(?:pm|plx?|pod)$'));
my @dirs = File::Spec->splitdir( File::Spec->canonpath( $path ) );
pop( @dirs ) if $dirs[-1] eq File::Spec->curdir;
next if $self->up_to_date($infile, $outfile);
unless ( -d $fulldir ){
- File::Path::mkpath($fulldir, 0, 0755)
+ File::Path::mkpath($fulldir, 0, oct(755))
or die "Couldn't mkdir $fulldir: $!";
}
delete $installmap->{read};
delete $installmap->{write};
- my $text_suffix = qr{\.(pm|pod)$};
+ my $text_suffix = file_qr('\.(pm|pod)$');
while (my $localdir = each %$installmap) {
my @localparts = File::Spec->splitdir($localdir);
# create a tarball;
# the directory tar'ed must be blib so we need to do a chdir first
- my $start_wd = $self->cwd;
- chdir( $ppm ) or die "Can't chdir to $ppm";
- $self->make_tarball( 'blib', File::Spec->catfile( $start_wd, $ppm ) );
- chdir( $start_wd ) or die "Can't chdir to $start_wd";
+ my $target = File::Spec->catfile( File::Spec->updir, $ppm );
+ $self->_do_in_dir( $ppm, sub { $self->make_tarball( 'blib', $target ) } );
$self->depends_on( 'ppd' );
$self->delete_filetree( $ppm );
}
+sub ACTION_pardist {
+ my ($self) = @_;
+
+ # Need PAR::Dist
+ if ( not eval { require PAR::Dist; PAR::Dist->VERSION(0.17) } ) {
+ $self->log_warn(
+ "In order to create .par distributions, you need to\n"
+ . "install PAR::Dist first."
+ );
+ return();
+ }
+
+ $self->depends_on( 'build' );
+
+ return PAR::Dist::blib_to_par(
+ name => $self->dist_name,
+ version => $self->dist_version,
+ );
+}
+
sub ACTION_dist {
my ($self) = @_;
or return;
my $mode = (stat $manifest)[2];
- chmod($mode | 0222, $manifest) or die "Can't make $manifest writable: $!";
+ chmod($mode | oct(222), $manifest) or die "Can't make $manifest writable: $!";
my $fh = IO::File->new("< $manifest") or die "Can't read $manifest: $!";
my $last_line = (<$fh>)[-1] || "\n";
$self->_add_to_manifest($manifest, "SIGNATURE Added here by Module::Build");
}
- # We protect the signing with an eval{} to make sure we get back to
- # the right directory after a signature failure. Would be nice if
- # Module::Signature took a directory argument.
+ # Would be nice if Module::Signature took a directory argument.
+ $self->_do_in_dir($dir, sub {local $Module::Signature::Quiet = 1; Module::Signature::sign()});
+}
+
+sub _do_in_dir {
+ my ($self, $dir, $do) = @_;
+
my $start_dir = $self->cwd;
chdir $dir or die "Can't chdir() to $dir: $!";
- eval {local $Module::Signature::Quiet = 1; Module::Signature::sign()};
+ eval {$do->()};
my @err = $@ ? ($@) : ();
chdir $start_dir or push @err, "Can't chdir() back to $start_dir: $!";
die join "\n", @err if @err;
$self->depends_on('distdir');
- my $start_dir = $self->cwd;
- my $dist_dir = $self->dist_dir;
- chdir $dist_dir or die "Cannot chdir to $dist_dir: $!";
- # XXX could be different names for scripts
+ $self->_do_in_dir
+ ( $self->dist_dir,
+ sub {
+ # XXX could be different names for scripts
- $self->run_perl_script('Build.PL') # XXX Should this be run w/ --nouse-rcfile
- or die "Error executing 'Build.PL' in dist directory: $!";
- $self->run_perl_script('Build')
- or die "Error executing 'Build' in dist directory: $!";
- $self->run_perl_script('Build', [], ['test'])
- or die "Error executing 'Build test' in dist directory";
- chdir $start_dir;
+ $self->run_perl_script('Build.PL') # XXX Should this be run w/ --nouse-rcfile
+ or die "Error executing 'Build.PL' in dist directory: $!";
+ $self->run_perl_script('Build')
+ or die "Error executing 'Build' in dist directory: $!";
+ $self->run_perl_script('Build', [], ['test'])
+ or die "Error executing 'Build test' in dist directory";
+ });
}
sub _write_default_maniskip {
ExtUtils::Manifest::mkmanifest();
}
+# Case insenstive regex for files
+sub file_qr {
+ return File::Spec->case_tolerant ? qr($_[0])i : qr($_[0]);
+}
+
sub dist_dir {
my ($self) = @_;
return "$self->{properties}{dist_name}-$self->{properties}{dist_version}";
return $_ = {$_ => 1};
}
- return $_ = { map {$_,1} $self->_files_in( File::Spec->catdir( $self->base_dir, 'bin' ) ) };
+ return $_ = { map {$_,1} $self->_files_in('bin') };
}
BEGIN { *scripts = \&script_files; }
{
- my %licenses =
- (
- perl => 'http://dev.perl.org/licenses/',
- gpl => 'http://www.opensource.org/licenses/gpl-license.php',
- apache => 'http://apache.org/licenses/LICENSE-2.0',
- artistic => 'http://opensource.org/licenses/artistic-license.php',
- lgpl => 'http://opensource.org/licenses/artistic-license.php',
- bsd => 'http://www.opensource.org/licenses/bsd-license.php',
- gpl => 'http://www.opensource.org/licenses/gpl-license.php',
- mit => 'http://opensource.org/licenses/mit-license.php',
- mozilla => 'http://opensource.org/licenses/mozilla1.1.php',
- open_source => undef,
- unrestricted => undef,
- restrictive => undef,
- unknown => undef,
- );
+ my %licenses = (
+ perl => 'http://dev.perl.org/licenses/',
+ apache => 'http://apache.org/licenses/LICENSE-2.0',
+ artistic => 'http://opensource.org/licenses/artistic-license.php',
+ artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php',
+ lgpl => 'http://opensource.org/licenses/lgpl-license.php',
+ bsd => 'http://opensource.org/licenses/bsd-license.php',
+ gpl => 'http://opensource.org/licenses/gpl-license.php',
+ mit => 'http://opensource.org/licenses/mit-license.php',
+ mozilla => 'http://opensource.org/licenses/mozilla1.1.php',
+ open_source => undef,
+ unrestricted => undef,
+ restrictive => undef,
+ unknown => undef,
+ );
sub valid_licenses {
return \%licenses;
}
die "ERROR: Missing required field '$_' for META.yml\n"
unless defined($node->{$name}) && length($node->{$name});
}
- # Really don't understand why I need the "... if exists" here
- $node->{version} = $node->{version}->stringify if exists $node->{version};
+ $node->{version} = '' . $node->{version}; # Stringify version objects
if (defined( $self->license ) &&
defined( my $url = $self->valid_licenses->{ $self->license } )) {
$node->{resources}{license} = $url;
}
- foreach ( @{$self->prereq_action_types} ) {
+ if (exists $p->{configure_requires}) {
+ foreach my $spec (keys %{$p->{configure_requires}}) {
+ warn ("Warning: $spec is listed in 'configure_requires', but ".
+ "it is not found in any of the other prereq fields.\n")
+ unless grep exists $p->{$_}{$spec},
+ grep !/conflicts$/, @{$self->prereq_action_types};
+ }
+ }
+
+ foreach ( 'configure_requires', @{$self->prereq_action_types} ) {
if (exists $p->{$_} and keys %{ $p->{$_} }) {
$add_node->($_, $p->{$_});
}
}
}
- # Stringify versions
- for (grep exists $_->{version}, values %prime) {
- $_->{version} = $_->{version}->stringify;
+ # Stringify versions. Can't use exists() here because of bug in YAML::Node.
+ for (grep defined $_->{version}, values %prime) {
+ $_->{version} = '' . $_->{version};
}
return \%prime;
if ($self->create_packlist and my $module_name = $self->module_name) {
my $archdir = $self->install_destination('arch');
my @ext = split /::/, $module_name;
- $map{write} = File::Spec->catdir($archdir, 'auto', @ext, '.packlist');
+ $map{write} = File::Spec->catfile($archdir, 'auto', @ext, '.packlist');
}
# Handle destdir
foreach (keys %map) {
# Need to remove volume from $map{$_} using splitpath, or else
# we'll create something crazy like C:\Foo\Bar\E:\Baz\Quux
- my ($volume, $path) = File::Spec->splitpath( $map{$_}, 1 );
- $map{$_} = File::Spec->catdir($destdir, $path);
+ # VMS will always have the file separate than the path.
+ my ($volume, $path, $file) = File::Spec->splitpath( $map{$_}, 1 );
+
+ # catdir needs a list of directories, or it will create something
+ # crazy like volume:[Foo.Bar.volume.Baz.Quux]
+ my @dirs = File::Spec->splitdir($path);
+
+ # First merge the directories
+ $path = File::Spec->catdir($destdir, @dirs);
+
+ # Then put the file back on if there is one.
+ if ($file ne '') {
+ $map{$_} = File::Spec->catfile($path, $file)
+ } else {
+ $map{$_} = $path;
+ }
}
}
}
@typemaps = map {+'-typemap', $_} @typemaps;
- my $cf = $self->config;
+ my $cf = $self->{config};
my $perl = $self->{properties}{perl};
- my @command = ($perl, "-I$cf->{installarchlib}", "-I$cf->{installprivlib}", $xsubpp, '-noprototypes',
+ my @command = ($perl, "-I".$cf->get('installarchlib'), "-I".$cf->get('installprivlib'), $xsubpp, '-noprototypes',
@typemaps, $file);
$self->log_info("@command\n");
# this before documenting.
my ($self, $args) = @_;
$args = [ $self->split_like_shell($args) ] unless ref($args);
- $args = [ split(/\s+/, $self->_quote_args($args)) ] if $self->os_type eq 'VMS';
my $perl = ref($self) ? $self->perl : $self->find_perl_interpreter;
# Make sure our local additions to @INC are propagated to the subprocess
- my $c = ref $self ? $self->config : \%Config::Config;
- local $ENV{PERL5LIB} = join $c->{path_sep}, $self->_added_to_INC;
+ local $ENV{PERL5LIB} = join $self->config('path_sep'), $self->_added_to_INC;
return $self->do_system($perl, @$args);
}
$spec{bs_file} = File::Spec->catfile($spec{archdir}, "${file_base}.bs");
$spec{lib_file} = File::Spec->catfile($spec{archdir},
- "${file_base}.$cf->{dlext}");
+ "${file_base}.".$cf->get('dlext'));
$spec{c_file} = File::Spec->catfile( $spec{src_dir},
"${file_base}.c" );
$spec{obj_file} = File::Spec->catfile( $spec{src_dir},
- "${file_base}$cf->{obj_ext}" );
+ "${file_base}".$cf->get('obj_ext') );
return \%spec;
}
sub process_xs {
my ($self, $file) = @_;
- my $cf = $self->config; # For convenience
my $spec = $self->_infer_xs_spec($file);
defines => {VERSION => qq{"$v"}, XS_VERSION => qq{"$v"}});
# archdir
- File::Path::mkpath($spec->{archdir}, 0, 0777) unless -d $spec->{archdir};
+ File::Path::mkpath($spec->{archdir}, 0, oct(777)) unless -d $spec->{archdir};
# .xs -> .bs
$self->add_to_cleanup($spec->{bs_file});
sub do_system {
my ($self, @cmd) = @_;
$self->log_info("@cmd\n");
- return !system(@cmd);
+
+ # Some systems proliferate huge PERL5LIBs, try to ameliorate:
+ my %seen;
+ my $sep = $self->config('path_sep');
+ local $ENV{PERL5LIB} =
+ ( !exists($ENV{PERL5LIB}) ? '' :
+ length($ENV{PERL5LIB}) < 500
+ ? $ENV{PERL5LIB}
+ : join $sep, grep { ! $seen{$_}++ and -d $_ } split($sep, $ENV{PERL5LIB})
+ );
+
+ my $status = system(@cmd);
+ if ($status and $! =~ /Argument list too long/i) {
+ my $env_entries = '';
+ foreach (sort keys %ENV) { $env_entries .= "$_=>".length($ENV{$_})."; " }
+ warn "'Argument list' was 'too long', env lengths are $env_entries";
+ }
+ return !$status;
}
sub copy_if_modified {
return if $self->up_to_date($file, $to_path); # Already fresh
- $self->delete_filetree($to_path); # delete destination if exists
+ {
+ local $self->{properties}{quiet} = 1;
+ $self->delete_filetree($to_path); # delete destination if exists
+ }
# Create parent directories
- File::Path::mkpath(File::Basename::dirname($to_path), 0, 0777);
+ File::Path::mkpath(File::Basename::dirname($to_path), 0, oct(777));
- $self->log_info("$file -> $to_path\n") if $args{verbose};
- File::Copy::copy($file, $to_path) or die "Can't copy('$file', '$to_path'): $!";
+ $self->log_info("Copying $file -> $to_path\n") if $args{verbose};
+
+ if ($^O eq 'os2') {# copy will not overwrite; 0x1 = overwrite
+ chmod 0666, $to_path;
+ File::Copy::syscopy($file, $to_path, 0x1) or die "Can't copy('$file', '$to_path'): $!";
+ } else {
+ File::Copy::copy($file, $to_path) or die "Can't copy('$file', '$to_path'): $!";
+ }
+
# mode is read-only + (executable if source is executable)
- my $mode = 0444 | ( $self->is_executable($file) ? 0111 : 0 );
+ my $mode = oct(444) | ( $self->is_executable($file) ? oct(111) : 0 );
chmod( $mode, $to_path );
return $to_path;
=head1 AUTHOR
-Ken Williams <ken@cpan.org>
+Ken Williams <kwilliams@cpan.org>
=head1 COPYRIGHT
-Copyright (c) 2001-2005 Ken Williams. All rights reserved.
+Copyright (c) 2001-2006 Ken Williams. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
perl(1), Module::Build(3)
=cut
+
+# vim:ts=8:sw=2:et:sta:sts=2