Acme-Sort-Sleep

 view release on metacpan or  search on metacpan

local/lib/perl5/Module/Build/Base.pm  view on Meta::CPAN

    }
  }

  # record for later use in resume;
  $self->{properties}{_added_to_INC} = [ $self->_added_to_INC ];

  $self->set_bundle_inc;

  $self->dist_name;
  $self->dist_version;
  $self->release_status;
  $self->_guess_module_name unless $self->module_name;

  $self->_find_nested_builds;

  return $self;
}

sub resume {
  my $package = shift;
  my $self = $package->_construct(@_);
  $self->read_config;

  my @added_earlier = @{ $self->{properties}{_added_to_INC} || [] };

  @INC = ($self->_added_to_INC, @added_earlier, $self->_default_INC);

  # If someone called Module::Build->current() or
  # Module::Build->new_from_context() and the correct class to use is
  # actually a *subclass* of Module::Build, we may need to load that
  # subclass here and re-delegate the resume() method to it.
  unless ( $package->isa($self->build_class) ) {
    my $build_class = $self->build_class;
    my $config_dir = $self->config_dir || '_build';
    my $build_lib = File::Spec->catdir( $config_dir, 'lib' );
    unshift( @INC, $build_lib );
    unless ( $build_class->can('new') ) {
      eval "require $build_class; 1" or die "Failed to re-load '$build_class': $@";
    }
    return $build_class->resume(@_);
  }

  unless ($self->_perl_is_same($self->{properties}{perl})) {
    my $perl = $self->find_perl_interpreter;
    die(<<"DIEFATAL");
* FATAL ERROR: Perl interpreter mismatch. Configuration was initially
  created with '$self->{properties}{perl}'
  but we are now using '$perl'.  You must
  run 'Build realclean' or 'make realclean' and re-configure.
DIEFATAL
  }

  $self->cull_args(@ARGV);

  unless ($self->allow_mb_mismatch) {
    my $mb_version = $Module::Build::VERSION;
    if ( $mb_version ne $self->{properties}{mb_version} ) {
      $self->log_warn(<<"MISMATCH");
* WARNING: Configuration was initially created with Module::Build
  version '$self->{properties}{mb_version}' but we are now using version '$mb_version'.
  If errors occur, you must re-run the Build.PL or Makefile.PL script.
MISMATCH
    }
  }

  $self->{invoked_action} = $self->{action} ||= 'build';

  return $self;
}

sub new_from_context {
  my ($package, %args) = @_;

  $package->run_perl_script('Build.PL',[],[$package->unparse_args(\%args)]);
  return $package->resume;
}

sub current {
  # hmm, wonder what the right thing to do here is
  local @ARGV;
  return shift()->resume;
}

sub _construct {
  my ($package, %input) = @_;

  my $args   = delete $input{args}   || {};
  my $config = delete $input{config} || {};

  my $self = bless {
      args => {%$args},
      config => Module::Build::Config->new(values => $config),
      properties => {
          base_dir        => $package->cwd,
          mb_version      => $Module::Build::VERSION,
          %input,
      },
      phash => {},
      stash => {}, # temporary caching, not stored in _build
  }, $package;

  $self->_set_defaults;
  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, $_);
    $ph->{$_} = Module::Build::Notes->new(file => $file);
    $ph->{$_}->restore if -e $file;
    if (exists $p->{$_}) {
      my $vals = delete $p->{$_};
      foreach my $k (sort keys %$vals) {
        $self->$_($k, $vals->{$k});
      }
    }
  }

  # The following warning could be unnecessary if the user is running
  # an embedded perl, but there aren't too many of those around, and
  # embedded perls aren't usually used to install modules, and the
  # installation process sometimes needs to run external scripts
  # (e.g. to run tests).

local/lib/perl5/Module/Build/Base.pm  view on Meta::CPAN


sub _backticks {
  my ($self, @cmd) = @_;
  if ($self->have_forkpipe) {
    local *FH;
    my $pid = open *FH, "-|";
    if ($pid) {
      return wantarray ? <FH> : join '', <FH>;
    } else {
      die "Can't execute @cmd: $!\n" unless defined $pid;
      exec { $cmd[0] } @cmd;
    }
  } else {
    my $cmd = $self->_quote_args(@cmd);
    return `$cmd`;
  }
}

# Tells us whether the construct open($fh, '-|', @command) is
# supported.  It would probably be better to dynamically sense this.
sub have_forkpipe { 1 }

# Determine whether a given binary is the same as the perl
# (configuration) that started this process.
sub _perl_is_same {
  my ($self, $perl) = @_;

  my @cmd = ($perl);

  # When run from the perl core, @INC will include the directories
  # where perl is yet to be installed. We need to reference the
  # absolute path within the source distribution where it can find
  # it's Config.pm This also prevents us from picking up a Config.pm
  # from a different configuration that happens to be already
  # installed in @INC.
  if ($ENV{PERL_CORE}) {
    push @cmd, '-I' . File::Spec->catdir(File::Basename::dirname($perl), 'lib');
  }

  push @cmd, qw(-MConfig=myconfig -e print -e myconfig);
  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 interpreter 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
# interpreter, on other it may contain a relative path, or simply
# 'perl'. This can also vary depending on whether a path was supplied
# when perl was invoked. Additionally, the value in $^X may omit 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 _discover_perl_interpreter {
  my $proto = shift;
  my $c     = ref($proto) ? $proto->{config} : 'Module::Build::Config';

  my $perl  = $^X;
  my $perl_basename = File::Basename::basename($perl);

  my @potential_perls;

  # Try 1, Check $^X for absolute path
  push( @potential_perls, $perl )
      if File::Spec->file_name_is_absolute($perl);

  # Try 2, Check $^X for a valid relative path
  my $abs_perl = File::Spec->rel2abs($perl);
  push( @potential_perls, $abs_perl );

  # Try 3, Last ditch effort: These two option use hackery to try to locate
  # a suitable perl. The hack varies depending on whether we are running
  # from an installed perl or an uninstalled perl in the perl source dist.
  if ($ENV{PERL_CORE}) {

    # Try 3.A, If we are in a perl source tree, running an uninstalled
    # perl, we can keep moving up the directory tree until we find our
    # binary. We wouldn't do this under any other circumstances.

    # CBuilder is also in the core, so it should be available here
    require ExtUtils::CBuilder;
    my $perl_src = Cwd::realpath( ExtUtils::CBuilder->perl_src );
    if ( defined($perl_src) && length($perl_src) ) {
      my $uninstperl =
        File::Spec->rel2abs(File::Spec->catfile( $perl_src, $perl_basename ));
      push( @potential_perls, $uninstperl );
    }

  } else {

    # 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->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->get('exe_ext');
  foreach my $thisperl ( @potential_perls ) {

    if (defined $exe) {
      $thisperl .= $exe unless $thisperl =~ m/$exe$/i;
    }

    if ( -f $thisperl && $proto->_perl_is_same($thisperl) ) {
      return $thisperl;

local/lib/perl5/Module/Build/Base.pm  view on Meta::CPAN

  sub _mb_classes {
    my $class = ref($_[0]) || $_[0];
    return ($class, $class->mb_parents);
  }

  sub valid_property {
    my ($class, $prop) = @_;
    return grep exists( $valid_properties{$_}{$prop} ), $class->_mb_classes;
  }

  sub valid_properties {
    return keys %{ shift->valid_properties_defaults() };
  }

  sub valid_properties_defaults {
    my %out;
    for my $class (reverse shift->_mb_classes) {
      @out{ keys %{ $valid_properties{$class} } } = map {
        $_->()
      } values %{ $valid_properties{$class} };
    }
    return \%out;
  }

  sub array_properties {
    map { exists $additive_properties{$_}->{ARRAY} ? @{$additive_properties{$_}->{ARRAY}} : () } shift->_mb_classes;
  }

  sub hash_properties {
    map { exists $additive_properties{$_}->{HASH} ? @{$additive_properties{$_}->{HASH}} : () } shift->_mb_classes;
  }

  sub add_property {
    my ($class, $property) = (shift, shift);
    die "Property '$property' already exists"
      if $class->valid_property($property);
    my %p = @_ == 1 ? ( default => shift ) : @_;

    my $type = ref $p{default};
    $valid_properties{$class}{$property} =
      $type eq 'CODE' ? $p{default}                           :
      $type eq 'HASH' ? sub { return { %{ $p{default} } }   } :
      $type eq 'ARRAY'? sub { return [ @{ $p{default} } ]   } :
                        sub { return $p{default}            } ;

    push @{$additive_properties{$class}->{$type}}, $property
      if $type;

    unless ($class->can($property)) {
      # TODO probably should put these in a util package
      my $sub = $type eq 'HASH'
        ? _make_hash_accessor($property, \%p)
        : _make_accessor($property, \%p);
      no strict 'refs';
      *{"$class\::$property"} = $sub;
    }

    return $class;
  }

  sub property_error {
    my $self = shift;
    die 'ERROR: ', @_;
  }

  sub _set_defaults {
    my $self = shift;

    # Set the build class.
    $self->{properties}{build_class} ||= ref $self;

    # If there was no orig_dir, set to the same as base_dir
    $self->{properties}{orig_dir} ||= $self->{properties}{base_dir};

    my $defaults = $self->valid_properties_defaults;

    foreach my $prop (keys %$defaults) {
      $self->{properties}{$prop} = $defaults->{$prop}
        unless exists $self->{properties}{$prop};
    }

    # Copy defaults for arrays any arrays.
    for my $prop ($self->array_properties) {
      $self->{properties}{$prop} = [@{$defaults->{$prop}}]
        unless exists $self->{properties}{$prop};
    }
    # Copy defaults for arrays any hashes.
    for my $prop ($self->hash_properties) {
      $self->{properties}{$prop} = {%{$defaults->{$prop}}}
        unless exists $self->{properties}{$prop};
    }
  }

} # end enclosure
########################################################################
sub _make_hash_accessor {
  my ($property, $p) = @_;
  my $check = $p->{check} || sub { 1 };

  return sub {
    my $self = shift;

    # This is only here to deprecate the historic accident of calling
    # properties as class methods - I suspect it only happens in our
    # test suite.
    unless(ref($self)) {
      carp("\n$property not a class method (@_)");
      return;
    }

    my $x = $self->{properties};
    return $x->{$property} unless @_;

    my $prop = $x->{$property};
    if ( defined $_[0] && !ref $_[0] ) {
      if ( @_ == 1 ) {
        return exists $prop->{$_[0]} ? $prop->{$_[0]} : undef;
      } elsif ( @_ % 2 == 0 ) {
        my %new = (%{ $prop }, @_);
        local $_ = \%new;
        $x->{$property} = \%new if $check->($self);

local/lib/perl5/Module/Build/Base.pm  view on Meta::CPAN

  };
}
########################################################################
sub _make_accessor {
  my ($property, $p) = @_;
  my $check = $p->{check} || sub { 1 };

  return sub {
    my $self = shift;

    # This is only here to deprecate the historic accident of calling
    # properties as class methods - I suspect it only happens in our
    # test suite.
    unless(ref($self)) {
      carp("\n$property not a class method (@_)");
      return;
    }

    my $x = $self->{properties};
    return $x->{$property} unless @_;
    local $_ = $_[0];
    $x->{$property} = shift if $check->($self);
    return $x->{$property};
  };
}
########################################################################

# Add the default properties.
__PACKAGE__->add_property(auto_configure_requires => 1);
__PACKAGE__->add_property(blib => 'blib');
__PACKAGE__->add_property(build_class => 'Module::Build');
__PACKAGE__->add_property(build_elements => [qw(PL support pm xs share_dir pod script)]);
__PACKAGE__->add_property(build_script => 'Build');
__PACKAGE__->add_property(build_bat => 0);
__PACKAGE__->add_property(bundle_inc => []);
__PACKAGE__->add_property(bundle_inc_preload => []);
__PACKAGE__->add_property(config_dir => '_build');
__PACKAGE__->add_property(dynamic_config => 1);
__PACKAGE__->add_property(include_dirs => []);
__PACKAGE__->add_property(license => 'unknown');
__PACKAGE__->add_property(metafile => 'META.yml');
__PACKAGE__->add_property(mymetafile => 'MYMETA.yml');
__PACKAGE__->add_property(metafile2 => 'META.json');
__PACKAGE__->add_property(mymetafile2 => 'MYMETA.json');
__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);
__PACKAGE__->add_property(test_file_exts => ['.t']);
__PACKAGE__->add_property(use_tap_harness => 0);
__PACKAGE__->add_property(cpan_client => 'cpan');
__PACKAGE__->add_property(tap_harness_args => {});
__PACKAGE__->add_property(pureperl_only => 0);
__PACKAGE__->add_property(allow_pureperl => 0);
__PACKAGE__->add_property(
  'installdirs',
  default => 'site',
  check   => sub {
    return 1 if /^(core|site|vendor)$/;
    return shift->property_error(
      $_ eq 'perl'
      ? 'Perhaps you meant installdirs to be "core" rather than "perl"?'
      : 'installdirs must be one of "core", "site", or "vendor"'
    );
    return shift->property_error("Perhaps you meant 'core'?") if $_ eq 'perl';
    return 0;
  },
);

{
  __PACKAGE__->add_property(html_css => '');
}

{
  my @prereq_action_types = qw(requires build_requires test_requires conflicts recommends);
  foreach my $type (@prereq_action_types) {
    __PACKAGE__->add_property($type => {});
  }
  __PACKAGE__->add_property(prereq_action_types => \@prereq_action_types);
}

__PACKAGE__->add_property($_ => {}) for qw(
  get_options
  install_base_relpaths
  install_path
  install_sets
  meta_add
  meta_merge
  original_prefix
  prefix_relpaths
  configure_requires
);

__PACKAGE__->add_property($_) for qw(
  PL_files
  autosplit
  base_dir
  bindoc_dirs
  c_source
  cover
  create_license
  create_makefile_pl
  create_readme
  debugger
  destdir
  dist_abstract
  dist_author
  dist_name
  dist_suffix
  dist_version
  dist_version_from
  extra_compiler_flags
  extra_linker_flags
  has_config_data
  install_base
  libdoc_dirs
  magic_number
  mb_version
  module_name
  needs_compiler
  orig_dir
  perl
  pm_files
  pod_files
  pollute

local/lib/perl5/Module/Build/Base.pm  view on Meta::CPAN

  }

  print {$fh} Module::Build::Dumper->_data_dump($data);
  close $fh;
}

sub write_config {
  my ($self) = @_;

  File::Path::mkpath($self->{properties}{config_dir});
  -d $self->{properties}{config_dir} or die "Can't mkdir $self->{properties}{config_dir}: $!";

  my @items = @{ $self->prereq_action_types };
  $self->_write_data('prereqs', { map { $_, $self->$_() } @items });
  $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));

  $self->{phash}{$_}->write() foreach qw(notes cleanup features auto_features config_data runtime_params);
}

{
  # packfile map -- keys are guts of regular expressions;  If they match,
  # values are module names corresponding to the packlist
  my %packlist_map = (
    '^File::Spec'         => 'Cwd',
    '^Devel::AssertOS'    => 'Devel::CheckOS',
  );

  sub _find_packlist {
    my ($self, $inst, $mod) = @_;
    my $lookup = $mod;
    my $packlist = eval { $inst->packlist($lookup) };
    if ( ! $packlist ) {
      # try from packlist_map
      while ( my ($re, $new_mod) = each %packlist_map ) {
        if ( $mod =~ qr/$re/ ) {
          $lookup = $new_mod;
          $packlist = eval { $inst->packlist($lookup) };
          last;
        }
      }
    }
    return $packlist ? $lookup : undef;
  }

  sub set_bundle_inc {
    my $self = shift;

    my $bundle_inc = $self->{properties}{bundle_inc};
    my $bundle_inc_preload = $self->{properties}{bundle_inc_preload};
    # We're in author mode if inc::latest is loaded, but not from cwd
    return unless inc::latest->can('loaded_modules');
    require ExtUtils::Installed;
    # ExtUtils::Installed is buggy about finding additions to default @INC
    my $inst = eval { ExtUtils::Installed->new(extra_libs => [@INC]) };
    if ($@) {
      $self->log_warn( << "EUI_ERROR" );
Bundling in inc/ is disabled because ExtUtils::Installed could not
create a list of your installed modules.  Here is the error:
$@
EUI_ERROR
      return;
    }
    my @bundle_list = map { [ $_, 0 ] } inc::latest->loaded_modules;

    # XXX TODO: Need to get ordering of prerequisites correct so they are
    # are loaded in the right order. Use an actual tree?!

    while( @bundle_list ) {
      my ($mod, $prereq) = @{ shift @bundle_list };

      # XXX TODO: Append prereqs to list
      # skip if core or already in bundle or preload lists
      # push @bundle_list, [$_, 1] for prereqs()

      # Locate packlist for bundling
      my $lookup = $self->_find_packlist($inst,$mod);
      if ( ! $lookup ) {
        # XXX Really needs a more helpful error message here
        die << "NO_PACKLIST";
Could not find a packlist for '$mod'.  If it's a core module, try
force installing it from CPAN.
NO_PACKLIST
      }
      else {
        push @{ $prereq ? $bundle_inc_preload : $bundle_inc }, $lookup;
      }
    }
  } # sub check_bundling
}

sub check_autofeatures {
  my ($self) = @_;
  my $features = $self->auto_features;

  return 1 unless %$features;

  # TODO refactor into ::Util
  my $longest = sub {
    my @str = @_ or croak("no strings given");

    my @len = map({length($_)} @str);
    my $max = 0;
    my $longest;
    for my $i (0..$#len) {
      ($max, $longest) = ($len[$i], $str[$i]) if($len[$i] > $max);
    }
    return($longest);
  };
  my $max_name_len = length($longest->(keys %$features));

  my ($num_disabled, $log_text) = (0, "\nChecking optional features...\n");
  for my $name ( sort keys %$features ) {
    $log_text .= $self->_feature_deps_msg($name, $max_name_len);
  }

  $num_disabled = () = $log_text =~ /disabled/g;

  # warn user if features disabled
  if ( $num_disabled ) {
    $self->log_warn( $log_text );
    return 0;
  }
  else {
    $self->log_verbose( $log_text );
    return 1;
  }
}

sub _feature_deps_msg {
  my ($self, $name, $max_name_len) = @_;
    $max_name_len ||= length $name;
    my $features = $self->auto_features;
    my $info = $features->{$name};
    my $feature_text = "$name" . '.' x ($max_name_len - length($name) + 4);

    my ($log_text, $disabled) = ('','');
    if ( my $failures = $self->prereq_failures($info) ) {
      $disabled = grep( /^(?:\w+_)?(?:requires|conflicts)$/,

local/lib/perl5/Module/Build/Base.pm  view on Meta::CPAN

    return;
  }
  else {
    return eval "require $modname";
  }
}

sub check_installed_status {
  my ($self, $modname, $spec) = @_;
  my %status = (need => $spec);

  if ($modname eq 'perl') {
    $status{have} = $self->perl_version;

  } elsif (eval { no strict; $status{have} = ${"${modname}::VERSION"} }) {
    # Don't try to load if it's already loaded

  } else {
    my $pm_info = Module::Metadata->new_from_module( $modname );
    unless (defined( $pm_info )) {
      @status{ qw(have message) } = ('<none>', "$modname is not installed");
      return \%status;
    }

    $status{have} = eval { $pm_info->version() };
    if ($spec and !defined($status{have})) {
      @status{ qw(have message) } = (undef, "Couldn't find a \$VERSION in prerequisite $modname");
      return \%status;
    }
  }

  my @conditions = $self->_parse_conditions($spec);

  foreach (@conditions) {
    my ($op, $version) = /^\s*  (<=?|>=?|==|!=)  \s*  ([\w.]+)  \s*$/x
      or die "Invalid prerequisite condition '$_' for $modname";

    $version = $self->perl_version_to_float($version)
      if $modname eq 'perl';

    next if $op eq '>=' and !$version;  # Module doesn't have to actually define a $VERSION

    unless ($self->compare_versions( $status{have}, $op, $version )) {
      $status{message} = "$modname ($status{have}) is installed, but we need version $op $version";
      return \%status;
    }
  }

  $status{ok} = 1;
  return \%status;
}

sub compare_versions {
  my $self = shift;
  my ($v1, $op, $v2) = @_;
  $v1 = version->new($v1)
    unless eval { $v1->isa('version') };

  my $eval_str = "\$v1 $op \$v2";
  my $result   = eval $eval_str;
  $self->log_warn("error comparing versions: '$eval_str' $@") if $@;

  return $result;
}

# I wish I could set $! to a string, but I can't, so I use $@
sub check_installed_version {
  my ($self, $modname, $spec) = @_;

  my $status = $self->check_installed_status($modname, $spec);

  if ($status->{ok}) {
    return $status->{have} if $status->{have} and "$status->{have}" ne '<none>';
    return '0 but true';
  }

  $@ = $status->{message};
  return 0;
}

sub make_executable {
  # Perl's chmod() is mapped to useful things on various non-Unix
  # platforms, so we use it in the base class even though it looks
  # Unixish.

  my $self = shift;
  foreach (@_) {
    my $current_mode = (stat $_)[2];
    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
# this perl.  For example, stuff passed in with -I or loaded with "use lib".
sub _added_to_INC {
  my $self = shift;

  my %seen;
  $seen{$_}++ foreach $self->_default_INC;
  return grep !$seen{$_}++, @INC;
}

# Determine the default @INC for this Perl
{
  my @default_inc; # Memoize
  sub _default_INC {
    my $self = shift;
    return @default_inc if @default_inc;

    local $ENV{PERL5LIB};  # this is not considered part of the default.

    my $perl = ref($self) ? $self->perl : $self->find_perl_interpreter;

local/lib/perl5/Module/Build/Base.pm  view on Meta::CPAN


sub dispatch {
  my $self = shift;
  local $self->{_completed_actions} = {};

  if (@_) {
    my ($action, %p) = @_;
    my $args = $p{args} ? delete($p{args}) : {};

    local $self->{invoked_action} = $action;
    local $self->{args} = {%{$self->{args}}, %$args};
    local $self->{properties} = {%{$self->{properties}}, %p};
    return $self->_call_action($action);
  }

  die "No build action specified" unless $self->{action};
  local $self->{invoked_action} = $self->{action};
  $self->_call_action($self->{action});
}

sub _call_action {
  my ($self, $action) = @_;

  return if $self->{_completed_actions}{$action}++;

  local $self->{action} = $action;
  my $method = $self->can_action( $action );
  die "No action '$action' defined, try running the 'help' action.\n" unless $method;
  $self->log_debug("Starting ACTION_$action\n");
  my $rc = $self->$method();
  $self->log_debug("Finished ACTION_$action\n");
  return $rc;
}

sub can_action {
  my ($self, $action) = @_;
  return $self->can( "ACTION_$action" );
}

# cuts the user-specified options out of the command-line args
sub cull_options {
    my $self = shift;
    my (@argv) = @_;

    # XXX is it even valid to call this as a class method?
    return({}, @argv) unless(ref($self)); # no object

    my $specs = $self->get_options;
    return({}, @argv) unless($specs and %$specs); # no user options

    require Getopt::Long;
    # XXX Should we let Getopt::Long handle M::B's options? That would
    # be easy-ish to add to @specs right here, but wouldn't handle options
    # passed without "--" as M::B currently allows. We might be able to
    # get around this by setting the "prefix_pattern" Configure option.
    my @specs;
    my $args = {};
    # Construct the specifications for GetOptions.
    foreach my $k (sort keys %$specs) {
        my $v = $specs->{$k};
        # Throw an error if specs conflict with our own.
        die "Option specification '$k' conflicts with a " . ref $self
          . " option of the same name"
          if $self->valid_property($k);
        push @specs, $k . (defined $v->{type} ? $v->{type} : '');
        push @specs, $v->{store} if exists $v->{store};
        $args->{$k} = $v->{default} if exists $v->{default};
    }

    local @ARGV = @argv; # No other way to dupe Getopt::Long

    # Get the options values and return them.
    # XXX Add option to allow users to set options?
    if ( @specs ) {
      Getopt::Long::Configure('pass_through');
      Getopt::Long::GetOptions($args, @specs);
    }

    return $args, @ARGV;
}

sub unparse_args {
  my ($self, $args) = @_;
  my @out;
  foreach my $k (sort keys %$args) {
    my $v = $args->{$k};
    push @out, (ref $v eq 'HASH'  ? map {+"--$k", "$_=$v->{$_}"} sort keys %$v :
                ref $v eq 'ARRAY' ? map {+"--$k", $_} @$v :
                ("--$k", $v));
  }
  return @out;
}

sub args {
    my $self = shift;
    return wantarray ? %{ $self->{args} } : $self->{args} unless @_;
    my $key = shift;
    $self->{args}{$key} = shift if @_;
    return $self->{args}{$key};
}

# allows select parameters (with underscores) to be spoken with dashes
# when used as command-line options
sub _translate_option {
  my $self = shift;
  my $opt  = shift;

  (my $tr_opt = $opt) =~ tr/-/_/;

  return $tr_opt if grep $tr_opt =~ /^(?:no_?)?$_$/, qw(
    create_license
    create_makefile_pl
    create_readme
    extra_compiler_flags
    extra_linker_flags
    install_base
    install_path
    meta_add
    meta_merge
    test_files
    use_rcfile

local/lib/perl5/Module/Build/Base.pm  view on Meta::CPAN


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};

  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 => $p->{test_file_exts},
    (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} = [ map { ref $_ ? @$_ : $_ } @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;
}

# Test::Harness dies on failure but TAP::Harness does not, so we must
# die if running under TAP::Harness
sub do_tests {
  my $self = shift;

  my $tests = $self->find_test_files;

  local $ENV{PERL_DL_NONLAZY} = 1;

  if(@$tests) {
    my $args = $self->tap_harness_args;
    if($self->use_tap_harness or ($args and %$args)) {
      my $aggregate = $self->run_tap_harness($tests);
      if ( $aggregate->has_errors ) {
        die "Errors in testing.  Cannot continue.\n";
      }
    }
    else {
      $self->run_test_harness($tests);
    }
  }
  else {
    $self->log_info("No tests defined.\n");
  }

  $self->run_visual_script;
}

sub run_tap_harness {
  my ($self, $tests) = @_;

  require TAP::Harness::Env;

  # TODO allow the test @INC to be set via our API?

  my $aggregate = TAP::Harness::Env->create({
    lib => [@INC],
    verbosity => $self->{properties}{verbose},
    switches  => [ $self->harness_switches ],
    %{ $self->tap_harness_args },
  })->runtests(@$tests);

  return $aggregate;
}

sub run_test_harness {
    my ($self, $tests) = @_;
    require Test::Harness;

    local $Test::Harness::verbose = $self->verbose || 0;
    local $Test::Harness::switches = join ' ', $self->harness_switches;

    Test::Harness::runtests(@$tests);
}

sub run_visual_script {
    my $self = shift;
    # This will get run and the user will see the output.  It doesn't
    # emit Test::Harness-style output.
    $self->run_perl_script('visual.pl', '-Mblib='.$self->blib)
        if -e 'visual.pl';
}

sub harness_switches {
    my $self = shift;
    my @res;
    push @res, qw(-w -d) if $self->{properties}{debugger};
    push @res, '-MDevel::Cover' if $self->{properties}{cover};
    return @res;
}

sub test_files {
  my $self = shift;
  my $p = $self->{properties};

local/lib/perl5/Module/Build/Base.pm  view on Meta::CPAN


# 1) If it's an ActiveState perl install, we need to run
#    ActivePerl::DocTools->UpdateTOC;
# 2) Links to other modules are not being generated
sub htmlify_pods {
  my $self = shift;
  my $type = shift;
  my $htmldir = shift || File::Spec->catdir($self->blib, "${type}html");

  $self->add_to_cleanup('pod2htm*');

  my $pods = $self->_find_pods( $self->{properties}{"${type}doc_dirs"},
                                exclude => [ $self->file_qr('\.(?:bat|com|html)$') ] );
  return unless %$pods;  # nothing to do

  unless ( -d $htmldir ) {
    File::Path::mkpath($htmldir, 0, oct(755))
      or die "Couldn't mkdir $htmldir: $!";
  }

  my @rootdirs = ($type eq 'bin') ? qw(bin) :
      $self->installdirs eq 'core' ? qw(lib) : qw(site lib);
  my $podroot = $ENV{PERL_CORE}
              ? File::Basename::dirname($ENV{PERL_CORE})
              : $self->original_prefix('core');

  my $htmlroot = $self->install_sets('core')->{libhtml};
  my $podpath;
  unless (defined $self->args('html_links') and !$self->args('html_links')) {
    my @podpath = ( (map { File::Spec->abs2rel($_ ,$podroot) } grep { -d  }
                     ( $self->install_sets('core', 'lib'), # lib
                       $self->install_sets('core', 'bin'), # bin
                       $self->install_sets('site', 'lib'), # site/lib
                     ) ), File::Spec->rel2abs($self->blib) );

    $podpath = $ENV{PERL_CORE}
      ? File::Spec->catdir($podroot, 'lib')
        : join(":", map { tr,:\\,|/,; $_ } @podpath);
  }

  my $blibdir = join('/', File::Spec->splitdir(
    (File::Spec->splitpath(File::Spec->rel2abs($htmldir),1))[1]),''
  );

  my ($with_ActiveState, $htmltool);

  if ( $with_ActiveState = $self->_is_ActivePerl
    && eval { require ActivePerl::DocTools::Pod; 1 }
  ) {
    my $tool_v = ActiveState::DocTools::Pod->VERSION;
    $htmltool = "ActiveState::DocTools::Pod";
    $htmltool .= " $tool_v" if $tool_v && length $tool_v;
  }
  else {
      require Module::Build::PodParser;
      require Pod::Html;
    $htmltool = "Pod::Html " .  Pod::Html->VERSION;
  }
  $self->log_verbose("Converting Pod to HTML with $htmltool\n");

  my $errors = 0;

  POD:
  foreach my $pod ( sort keys %$pods ) {

    my ($name, $path) = File::Basename::fileparse($pods->{$pod},
      $self->file_qr('\.(?:pm|plx?|pod)$')
    );
    my @dirs = File::Spec->splitdir( File::Spec->canonpath( $path ) );
    pop( @dirs ) if scalar(@dirs) && $dirs[-1] eq File::Spec->curdir;

    my $fulldir = File::Spec->catdir($htmldir, @rootdirs, @dirs);
    my $tmpfile = File::Spec->catfile($fulldir, "${name}.tmp");
    my $outfile = File::Spec->catfile($fulldir, "${name}.html");
    my $infile  = File::Spec->abs2rel($pod);

    next if $self->up_to_date($infile, $outfile);

    unless ( -d $fulldir ){
      File::Path::mkpath($fulldir, 0, oct(755))
        or die "Couldn't mkdir $fulldir: $!";
    }

    $self->log_verbose("HTMLifying $infile -> $outfile\n");
    if ( $with_ActiveState ) {
      my $depth = @rootdirs + @dirs;
      my %opts = ( infile => $infile,
        outfile => $tmpfile,
        ( defined($podpath) ? (podpath => $podpath) : ()),
        podroot => $podroot,
        index => 1,
        depth => $depth,
      );
      eval {
        ActivePerl::DocTools::Pod::pod2html(map { ($_, $opts{$_}) } sort keys %opts);
        1;
      } or $self->log_warn("[$htmltool] pod2html (" .
        join(", ", map { "q{$_} => q{$opts{$_}}" } (sort keys %opts)) . ") failed: $@");
    } else {
      my $path2root = File::Spec->catdir((File::Spec->updir) x @dirs);
      open(my $fh, '<', $infile) or die "Can't read $infile: $!";
      my $abstract = Module::Build::PodParser->new(fh => $fh)->get_abstract();

      my $title = join( '::', (@dirs, $name) );
      $title .= " - $abstract" if $abstract;

      my @opts = (
        "--title=$title",
        ( defined($podpath) ? "--podpath=$podpath" : ()),
        "--infile=$infile",
        "--outfile=$tmpfile",
        "--podroot=$podroot",
        ($path2root ? "--htmlroot=$path2root" : ()),
      );

      unless ( eval{Pod::Html->VERSION(1.12)} ) {
        push( @opts, ('--flush') ); # caching removed in 1.12
      }

      if ( eval{Pod::Html->VERSION(1.12)} ) {
        push( @opts, ('--header', '--backlink') );
      } elsif ( eval{Pod::Html->VERSION(1.03)} ) {
        push( @opts, ('--header', '--backlink=Back to Top') );
      }

      $self->log_verbose("P::H::pod2html @opts\n");
      {
        my $orig = Cwd::getcwd();
        eval { Pod::Html::pod2html(@opts); 1 }
          or $self->log_warn("[$htmltool] pod2html( " .
          join(", ", map { "q{$_}" } @opts) . ") failed: $@");
        chdir($orig);
      }
    }
    # We now have to cleanup the resulting html file
    if ( ! -r $tmpfile ) {
      $errors++;
      next POD;
    }
    open(my $fh, '<', $tmpfile) or die "Can't read $tmpfile: $!";
    my $html = join('',<$fh>);
    close $fh;
    if (!$self->_is_ActivePerl) {
      # These fixups are already done by AP::DT:P:pod2html
      # The output from pod2html is NOT XHTML!
      # IE6+ will display content that is not valid for DOCTYPE
      $html =~ s#^<!DOCTYPE .*?>#<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">#im;
      $html =~ s#<html xmlns="http://www.w3.org/1999/xhtml">#<html>#i;

      # IE6+ will not display local HTML files with strict
      # security without this comment
      $html =~ s#<head>#<head>\n<!-- saved from url=(0017)http://localhost/ -->#i;
    }
    # Fixup links that point to our temp blib
    $html =~ s/\Q$blibdir\E//g;

    open($fh, '>', $outfile) or die "Can't write $outfile: $!";
    print $fh $html;
    close $fh;
    unlink($tmpfile);
  }

  return ! $errors;

}

# Adapted from ExtUtils::MM_Unix
sub man1page_name {
  my $self = shift;
  return File::Basename::basename( shift );
}

# Adapted from ExtUtils::MM_Unix and Pod::Man
# Depending on M::B's dependency policy, it might make more sense to refactor
# Pod::Man::begin_pod() to extract a name() methods, and use them...
#    -spurkis
sub man3page_name {
  my $self = shift;
  my ($vol, $dirs, $file) = File::Spec->splitpath( shift );
  my @dirs = File::Spec->splitdir( File::Spec->canonpath($dirs) );

  # Remove known exts from the base name
  $file =~ s/\.p(?:od|m|l)\z//i;

  return join( $self->manpage_separator, @dirs, $file );
}

sub manpage_separator {
  return '::';
}

# For systems that don't have 'diff' executable, should use Algorithm::Diff
sub ACTION_diff {
  my $self = shift;
  $self->depends_on('build');
  my $local_lib = File::Spec->rel2abs('lib');
  my @myINC = grep {$_ ne $local_lib} @INC;

  # The actual install destination might not be in @INC, so check there too.
  push @myINC, map $self->install_destination($_), qw(lib arch);

  my @flags = @{$self->{args}{ARGV}};
  @flags = $self->split_like_shell($self->{args}{flags} || '') unless @flags;

  my $installmap = $self->install_map;
  delete $installmap->{read};
  delete $installmap->{write};

  my $text_suffix = $self->file_qr('\.(pm|pod)$');

  foreach my $localdir (sort keys %$installmap) {
    my @localparts = File::Spec->splitdir($localdir);
    my $files = $self->rscan_dir($localdir, sub {-f});

    foreach my $file (@$files) {
      my @parts = File::Spec->splitdir($file);
      @parts = @parts[@localparts .. $#parts]; # Get rid of blib/lib or similar

      my $installed = Module::Metadata->find_module_by_name(
                        join('::', @parts), \@myINC );
      if (not $installed) {
        print "Only in lib: $file\n";
        next;

local/lib/perl5/Module/Build/Base.pm  view on Meta::CPAN

  # Localize
  my %dist_files = map { $self->localize_file_path($_) => $_ }
                       keys %$manifest;

  my @pm_files = sort grep { $_ !~ m{^t} } # skip things in t/
                   grep {exists $dist_files{$_}}
                     keys %{ $self->find_pm_files };

  return $self->find_packages_in_files(\@pm_files, \%dist_files);
}

# XXX Do not document this function; mst wrote it and now says the API is
# stupid and needs to be fixed and it shouldn't become a public API until then
sub find_packages_in_files {
  my ($self, $file_list, $filename_map) = @_;

  # First, we enumerate all packages & versions,
  # separating into primary & alternative candidates
  my( %prime, %alt );
  foreach my $file (@{$file_list}) {
    my $mapped_filename = $filename_map->{$file};
    my @path = split( /\//, $mapped_filename );
    (my $prime_package = join( '::', @path[1..$#path] )) =~ s/\.pm$//;

    my $pm_info = Module::Metadata->new_from_file( $file );

    foreach my $package ( $pm_info->packages_inside ) {
      next if $package eq 'main';  # main can appear numerous times, ignore
      next if $package eq 'DB';    # special debugging package, ignore
      next if grep /^_/, split( /::/, $package ); # private package, ignore

      my $version = $pm_info->version( $package );

      if ( $package eq $prime_package ) {
        if ( exists( $prime{$package} ) ) {
          # Module::Metadata will handle this conflict
          die "Unexpected conflict in '$package'; multiple versions found.\n";
        } else {
          $prime{$package}{file} = $mapped_filename;
          $prime{$package}{version} = $version if defined( $version );
        }
      } else {
        push( @{$alt{$package}}, {
                                  file    => $mapped_filename,
                                  version => $version,
                                 } );
      }
    }
  }

  # Then we iterate over all the packages found above, identifying conflicts
  # and selecting the "best" candidate for recording the file & version
  # for each package.
  foreach my $package ( sort keys( %alt ) ) {
    my $result = $self->_resolve_module_versions( $alt{$package} );

    if ( exists( $prime{$package} ) ) { # primary package selected

      if ( $result->{err} ) {
        # Use the selected primary package, but there are conflicting
        # errors among multiple alternative packages that need to be
        # reported
        $self->log_warn(
          "Found conflicting versions for package '$package'\n" .
          "  $prime{$package}{file} ($prime{$package}{version})\n" .
          $result->{err}
        );

      } elsif ( defined( $result->{version} ) ) {
        # There is a primary package selected, and exactly one
        # alternative package

        if ( exists( $prime{$package}{version} ) &&
             defined( $prime{$package}{version} ) ) {
          # Unless the version of the primary package agrees with the
          # version of the alternative package, report a conflict
          if ( $self->compare_versions( $prime{$package}{version}, '!=',
                                        $result->{version} ) ) {
            $self->log_warn(
              "Found conflicting versions for package '$package'\n" .
              "  $prime{$package}{file} ($prime{$package}{version})\n" .
              "  $result->{file} ($result->{version})\n"
            );
          }

        } else {
          # The prime package selected has no version so, we choose to
          # use any alternative package that does have a version
          $prime{$package}{file}    = $result->{file};
          $prime{$package}{version} = $result->{version};
        }

      } else {
        # no alt package found with a version, but we have a prime
        # package so we use it whether it has a version or not
      }

    } else { # No primary package was selected, use the best alternative

      if ( $result->{err} ) {
        $self->log_warn(
          "Found conflicting versions for package '$package'\n" .
          $result->{err}
        );
      }

      # Despite possible conflicting versions, we choose to record
      # something rather than nothing
      $prime{$package}{file}    = $result->{file};
      $prime{$package}{version} = $result->{version}
          if defined( $result->{version} );
    }
  }

  # Normalize versions or delete them if undef/0
  for my $provides ( values %prime ) {
    if ( $provides->{version} ) {
      $provides->{version} = $self->normalize_version( $provides->{version} )
    }
    else {
      delete $provides->{version};



( run in 0.773 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )