Remove unused Module::Build tests
[p5sagit/p5-mst-13.2.git] / lib / Module / Build / Base.pm
index c8d6275..0cc78e6 100644 (file)
@@ -17,6 +17,7 @@ use Text::ParseWords ();
 
 use Module::Build::ModuleInfo;
 use Module::Build::Notes;
+use Module::Build::Config;
 
 
 #################### Constructors ###########################
@@ -68,12 +69,16 @@ sub resume {
                    "   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;
@@ -90,7 +95,7 @@ sub new_from_context {
   # as it is during resume() (and thereafter).
   {
     local @ARGV = $package->unparse_args(\%args);
-    do 'Build.PL';
+    do './Build.PL';
     die $@ if $@;
   }
   return $package->resume;
@@ -110,7 +115,7 @@ sub _construct {
 
   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,
@@ -120,7 +125,7 @@ sub _construct {
                   }, $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, $_);
@@ -181,62 +186,62 @@ sub log_warn {
 
 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};
 
@@ -343,7 +348,7 @@ sub _backticks {
   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 {
@@ -391,7 +396,7 @@ sub _perl_is_same {
 # invoking the wrong perl.
 sub find_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);
@@ -430,7 +435,7 @@ sub find_perl_interpreter {
     # 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() );
@@ -439,13 +444,10 @@ sub find_perl_interpreter {
   # 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 and $proto->os_type ne 'VMS') {
       $thisperl .= $exe unless $thisperl =~ m/$exe$/i;
     }
 
@@ -465,9 +467,11 @@ sub _is_interactive {
   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 {
@@ -484,26 +488,30 @@ sub prompt {
   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
-    print "\n";
-  } elsif ( !length($ans) ) { # Default
-    print "$def\n";
-    $ans = $def;
+  if ( !defined($ans)        # Ctrl-D or unattended
+       or !length($ans) ) {  # User hit return
+    print "$dispdef[1]\n";
+    $ans = scalar(@def) ? $def[0] : '';
   }
 
   return $ans;
@@ -517,13 +525,6 @@ sub y_n {
   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(@_);
@@ -681,7 +682,7 @@ sub ACTION_config_data {
        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]) ) {
@@ -756,6 +757,8 @@ __PACKAGE__->add_property(metafile => 'META.yml');
 __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};
@@ -771,7 +774,6 @@ __PACKAGE__->add_property(create_packlist => 1);
 }
 
 __PACKAGE__->add_property($_ => {}) for qw(
-  config
   get_options
   install_base_relpaths
   install_path
@@ -821,6 +823,17 @@ __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.
@@ -993,11 +1006,13 @@ sub read_config {
   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;
 }
 
@@ -1023,7 +1038,7 @@ 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));
@@ -1090,7 +1105,7 @@ sub prereq_failures {
 
       } 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 {
@@ -1124,7 +1139,8 @@ sub check_prereq {
   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
@@ -1233,10 +1249,8 @@ sub check_installed_status {
 sub compare_versions {
   my $self = shift;
   my ($v1, $op, $v2) = @_;
-
-  # for alpha versions - this doesn't cover all cases, but should work for most:
-  $v1 =~ s/_(\d+)\z/$1/;
-  $v2 =~ s/_(\d+)\z/$1/;
+  $v1 = Module::Build::Version->new($v1) 
+    unless UNIVERSAL::isa($v1,'Module::Build::Version');
 
   my $eval_str = "\$v1 $op \$v2";
   my $result   = eval $eval_str;
@@ -1268,10 +1282,17 @@ sub make_executable {
   my $self = shift;
   foreach (@_) {
     my $current_mode = (stat $_)[2];
-    chmod $current_mode | 0111, $_;
+    chmod $current_mode | oct(111), $_;
   }
 }
 
+sub is_executable {
+  # We assume this does the right thing on generic platforms, though
+  # we do some other more specific stuff on Unixish platforms.
+  my ($self, $file) = @_;
+  return -x $file;
+}
+
 sub _startperl { shift()->config('startperl') }
 
 # Return any directories in @INC which are not in the default @INC for
@@ -1609,8 +1630,12 @@ sub read_args {
   }
   $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{$_} ||= [];
@@ -1653,12 +1678,12 @@ sub read_args {
 }
 
 
+# (bash shell won't expand tildes mid-word: "--foo=~/thing")
+# TODO: handle ~user/foo
 sub _detildefy {
     my $arg = shift;
 
-    my($new_arg) = glob($arg) if $arg =~ /^~/;
-
-    return defined($new_arg) ? $new_arg : $arg;
+    return $arg =~ /^~/ ? (glob $arg)[0] : $arg;
 }
 
 
@@ -1786,15 +1811,19 @@ sub merge_args {
   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;
+      }
     }
   }
 }
@@ -1832,41 +1861,61 @@ sub known_actions {
 }
 
 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;
       }
-      next unless $found;
-      push @docs, $_;
-      ++$inlist if /^=over/;
-      --$inlist if /^=back/;
-      ++$found  if /^\w/; # Found descriptive text
     }
+    $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/;
+      }
+    }
+    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) {
@@ -1942,8 +1991,8 @@ sub ACTION_help {
   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;
   }
 
@@ -1975,13 +2024,94 @@ sub _action_listing {
   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;
@@ -1997,15 +2127,6 @@ sub ACTION_test {
         $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) {
@@ -2035,8 +2156,12 @@ sub test_files {
 
 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 {
@@ -2063,7 +2188,8 @@ sub ACTION_testcover {
     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    = 
@@ -2163,7 +2289,7 @@ sub process_script_files {
   
   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);
   }
 }
@@ -2214,7 +2340,7 @@ sub find_script_files {
 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($_) : $_ }
@@ -2250,7 +2376,7 @@ sub _find_file_by_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 );
 }
 
@@ -2261,9 +2387,9 @@ sub localize_dir_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";
@@ -2276,14 +2402,14 @@ sub fix_shebang_line { # Adapted from fixin() in ExtUtils::MM_Unix 1.35
     
     $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";
@@ -2301,10 +2427,10 @@ eval 'exec $interpreter $arg -S \$0 \${1+"\$\@"}'
     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 ':';
   }
 }
 
@@ -2335,6 +2461,18 @@ sub ACTION_testpodcoverage {
     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();
 }
 
@@ -2390,7 +2528,7 @@ sub manify_bin_pods {
   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) {
@@ -2414,7 +2552,7 @@ sub manify_lib_pods {
   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) {
@@ -2437,6 +2575,7 @@ sub _find_pods {
   foreach my $spec (@$dirs) {
     my $dir = $self->localize_dir_path($spec);
     next unless -e $dir;
+
     FILE: foreach my $file ( @{ $self->rscan_dir( $dir ) } ) {
       foreach my $regexp ( @{ $args{exclude} } ) {
        next FILE if $file =~ $regexp;
@@ -2496,10 +2635,10 @@ sub htmlify_pods {
 
   my $pods = $self->_find_pods( $self->{properties}{"${type}doc_dirs"},
                                 exclude => [ qr/\.(?:bat|com|html)$/ ] );
-  next unless %$pods;  # nothing to do
+  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: $!";
   }
 
@@ -2526,7 +2665,7 @@ sub htmlify_pods {
     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: $!";
     }
 
@@ -2745,6 +2884,26 @@ sub ACTION_ppmdist {
   $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) = @_;
   
@@ -2784,7 +2943,7 @@ sub _add_to_manifest {
     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";
@@ -3206,6 +3365,7 @@ sub prepare_metadata {
     die "ERROR: Missing required field '$_' for META.yml\n"
       unless defined($node->{$name}) && length($node->{$name});
   }
+  $node->{version} = '' . $node->{version}; # Stringify version objects
 
   if (defined( $self->license ) &&
       defined( my $url = $self->valid_licenses->{ $self->license } )) {
@@ -3223,7 +3383,7 @@ sub prepare_metadata {
   }
   my $pkgs = eval { $self->find_dist_packages };
   if ($@) {
-    $self->log_warn("WARNING: Possible missing or corrupt 'MANIFEST' file.\n" .
+    $self->log_warn("$@\nWARNING: Possible missing or corrupt 'MANIFEST' file.\n" .
                    "Nothing to enter for 'provides' field in META.yml\n");
   } else {
     $node->{provides} = $pkgs if %$pkgs;
@@ -3374,6 +3534,11 @@ sub find_dist_packages {
     }
   }
 
+  # Stringify versions.  Can't use exists() here because of bug in YAML::Node.
+  for (grep defined $_->{version}, values %prime) {
+    $_->{version} = '' . $_->{version};
+  }
+
   return \%prime;
 }
 
@@ -3787,10 +3952,10 @@ sub compile_xs {
     }
     @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");
@@ -3824,12 +3989,10 @@ sub run_perl_command {
   # 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);
 }
@@ -3865,20 +4028,19 @@ sub _infer_xs_spec {
   $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);
 
@@ -3898,7 +4060,7 @@ sub process_xs {
                   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});
@@ -3917,7 +4079,24 @@ sub process_xs {
 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 {
@@ -3947,15 +4126,25 @@ 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("Copying $file -> $to_path\n") if $args{verbose};
   
-  $self->log_info("$file -> $to_path\n") if $args{verbose};
-  File::Copy::copy($file, $to_path) or die "Can't copy('$file', '$to_path'): $!";
+  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 | ( -x $file ? 0111 : 0 );
+  my $mode = oct(444) | ( $self->is_executable($file) ? oct(111) : 0 );
   chmod( $mode, $to_path );
 
   return $to_path;
@@ -4031,11 +4220,11 @@ Please see the C<Module::Build> documentation for more details.
 
 =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.
@@ -4045,3 +4234,5 @@ modify it under the same terms as Perl itself.
 perl(1), Module::Build(3)
 
 =cut
+
+# vim:ts=8:sw=2:et:sta:sts=2