Alien-Base-ModuleBuild

 view release on metacpan or  search on metacpan

lib/Alien/Base/ModuleBuild.pm  view on Meta::CPAN

  }
  print "# Build install\n";
  foreach my $cmd (@{ $self->alien_install_commands })
  {
    my @cmd = map { $self->alien_interpolate($_) } ref($cmd) ? @$cmd : ($cmd);
    print "+ @cmd\n";
  }
}

sub ACTION_code {
  my $self = shift;
  $self->notes( 'alien_blib_scheme' => $self->alien_detect_blib_scheme );

  # PLEASE NOTE, BOTH BRANCHES CALL SUPER::ACTION_code !!!!!!!
  if ( $self->notes('ACTION_alien_completed') ) {

    $self->SUPER::ACTION_code;

  } else {

    $self->depends_on('alien_code');
    $self->SUPER::ACTION_code;
  }

  my $module = $self->module_name;
  my $file   = File::Spec->catfile($self->blib, 'lib', split /::/, "$module\::Install::Files.pm");
  unless (-e $file) {
    mkpath(File::Spec->catdir($self->blib, 'lib', split /::/, "$module\::Install"), { verbose => 0 });
    open my $fh, '>', $file;
    print $fh <<EOF;
package $module\::Install::Files;
use strict;
use warnings;
require $module;
sub Inline { shift; $module->Inline(\@_) }
1;

=begin Pod::Coverage

  Inline

=end Pod::Coverage

=cut
EOF
    close $fh;
  }

  if($self->alien_arch) {
    my @parts = split /::/, $module;
    my $arch_dir = File::Spec->catdir($self->blib, 'arch', 'auto', @parts);
    File::Path::mkpath($arch_dir, 0, oct(777)) unless -d $arch_dir;
    open my $fh, '>', File::Spec->catfile($arch_dir, $parts[-1].".txt");
    print $fh "Alien based distribution with architecture specific file in share\n";
    close $fh;
  }

  $self->depends_on('alien_install') if $self->alien_stage_install;
}

sub process_share_dir_files {
  my $self = shift;
  $self->SUPER::process_share_dir_files(@_);

  # copy the compiled files into blib if running under blib scheme
  $self->depends_on('alien_install') if $self->notes('alien_blib_scheme') || $self->alien_stage_install;
}

sub alien_extract_archive {
  my ($self, $archive) = @_;
  print "Extracting Archive ... ";
  my $ae = Archive::Extract->new( archive => $archive );
  $ae->extract or croak "Archive extraction failed!";
  print "Done\n";
  return $ae->extract_path;
}

sub ACTION_alien_code {
  my $self = shift;
  local $| = 1; # don't buffer stdout

  $self->alien_init_temp_dir;

  $self->config_data( name => $self->alien_name );
  $self->config_data( ffi_name => $self->alien_ffi_name );

  my $version;
  $version = $self->alien_check_installed_version
    unless $self->config_data('Force');

  if ($version) {
    $self->config_data( install_type => 'system' );
    $self->config_data( version => $version );
    my %system_provides;
    $system_provides{Cflags} = $self->alien_provides_cflags
      if defined $self->alien_provides_cflags;
    $system_provides{Libs}   = $self->alien_provides_libs
      if defined $self->alien_provides_libs;
    $self->config_data( system_provides => \%system_provides );
    return;
  }

  if ($self->config_data('ForceSystem')) {
    die "Requested system install, but system package not detected."
  }

  my @repos = $self->alien_create_repositories;

  my $cabinet = Alien::Base::ModuleBuild::Cabinet->new;

  foreach my $repo (@repos) {
    $cabinet->add_files( $repo->probe );
  }

  $cabinet->sort_files;

  {
    local $CWD = $self->alien_temp_dir;

    my $file = $cabinet->files->[0];

    unless (defined $file) {
      die "no files found in repository";

lib/Alien/Base/ModuleBuild.pm  view on Meta::CPAN

  return if $self->config_data( 'install_type' ) eq 'system';

  my $destdir = $self->destdir;
  my $target = $self->alien_library_destination;

  if(defined $destdir && !$self->alien_stage_install)
  {
    # Note: no longer necessary when doing a staged install
    # prefix the target directory with $destdir so that package builds
    # can install into a fake root
    $target = File::Spec->catdir($destdir, $target);
  }

  {
    local $CWD = $target;

    # The only form of introspection that exists is to see that the README file
    # which was placed in the share_dir (default _share) exists where we expect
    # after installation.
    unless ( -e 'README' ) {
      die "share_dir mismatch detected ($target)\n"
    }
  }

  if(!$self->config_data( 'finished_installing' ))
  {
    local $CWD = $self->config_data( 'working_directory' );
    local $ENV{DESTDIR} = $ENV{DESTDIR};
    $ENV{DESTDIR} = $destdir if defined $destdir && !$self->alien_stage_install;
    print "Installing library to $CWD ... ";
    $self->alien_do_commands('install') or die "Failed\n";
    print "Done\n";
  }

  if ( $self->alien_isolate_dynamic ) {
    local $CWD = $target;
    print "Isolating dynamic libraries ... ";
    mkdir 'dynamic' unless -d 'dynamic';
    foreach my $dir (qw( bin lib )) {
      next unless -d $dir;
      opendir(my $dh, $dir);
      my @dlls = grep { /\.so/ || /\.(dylib|bundle|la|dll|dll\.a)$/ } grep !/^\./, readdir $dh;
      closedir $dh;
      foreach my $dll (@dlls) {
        my $from = File::Spec->catfile($dir, $dll);
        my $to   = File::Spec->catfile('dynamic', $dll);
        unlink $to if -e $to;
        move($from, $to);
      }
    }
    print "Done\n";
  }

  # refresh metadata after library installation
  $self->alien_refresh_manual_pkgconfig( $self->alien_library_destination );
  $self->config_data( 'finished_installing' => 1 );

  if ( $self->notes( 'alien_blib_scheme') || $self->alien_stage_install) {

    ### TODO: empty if should be claned up before 0.017.
    ### we used to call process_files_by_extension('pm')
    ### here, but with gh#121 it is unecessary
    ## reinstall config_data to blib
    #$self->process_files_by_extension('pm');

  } else {

    # to refresh config_data
    $self->SUPER::ACTION_config_data;

    # reinstall config_data
    $self->SUPER::ACTION_install;

    # refresh the packlist
    $self->alien_refresh_packlist( $self->alien_library_destination );
  }
}

#######################
#  Pre-build Methods  #
#######################

sub alien_check_installed_version {
  my $self = shift;
  my $command = $self->alien_version_check;

  my %result = $self->do_system($command, {verbose => 0});
  my $version = ($result{success} && $result{stdout}) || 0;

  return $version;
}

sub alien_create_repositories {
  my $self = shift;

  ## get repository specs
  my $repo_default = $self->alien_repository_default;
  my $repo_specs = $self->alien_repository;

  # upconvert to arrayref if a single hashref is passed
  if (ref $repo_specs eq 'HASH') {
    $repo_specs = [ $repo_specs ];
  }

  my $module_env_part = uc $self->module_name;
  $module_env_part =~ s/^ALIEN:://;
  $module_env_part =~ s/::/_/g;
  my $env_prefix = 'ALIEN_'.$module_env_part.'_REPO_';

  # Catch e.g. 'ALIEN_MYMODULE_REPO_HTTP_HOST' variables to override repo config
  my @env_overrides =
    grep { index($_, $env_prefix) == 0 }
    keys %ENV;

  unless(@$repo_specs)
  {
    print STDERR "If you are the author of this Alien dist, you need to provide at least\n";
    print STDERR "one repository in your Build.PL.  See:\n";
    print STDERR "https://metacpan.org/pod/Alien::Base::ModuleBuild::API#alien_repository\n";
    croak "No repositories specified.";
  }

  my @repos;
  foreach my $repo ( @$repo_specs ) {

lib/Alien/Base/ModuleBuild.pm  view on Meta::CPAN

    grep { $self->alien_validate_repo($_) }
    @repos;

  unless (@repos) {
    croak "No valid repositories available";
  }

  return @repos;

}

sub alien_validate_repo {
  my $self = shift;
  my ($repo) = @_;
  my $platform = $repo->{platform};

  # return true if platform is undefined
  return 1 unless defined $platform;

  # if $valid is src, check for c compiler
  if ($platform eq 'src') {
    return !$repo->{c_compiler_required} || $self->have_c_compiler;
  }

  # $valid is a string (OS) to match against
  return $self->os_type eq $platform;
}

sub alien_library_destination {
  my $self = shift;

  # send the library into the blib if running under the blib scheme
  my $lib_dir =
    $self->notes('alien_blib_scheme') || $self->alien_stage_install
    ? File::Spec->catdir( $self->base_dir, $self->blib, 'lib' )
    : $self->install_destination($self->alien_arch ? 'arch' : 'lib');

  my $dist_name = $self->dist_name;
  my $dest = _catdir( $lib_dir, qw/auto share dist/, $dist_name );
  return $dest;
}

# CPAN testers often run tests without installing modules, but rather add
# blib dirs to @INC, this is a problem, so here we try to deal with it
sub alien_detect_blib_scheme {
  my $self = shift;

  return 0 if $self->alien_stage_install;
  return $ENV{ALIEN_BLIB} if defined $ENV{ALIEN_BLIB};

  # check to see if Alien::Base::ModuleBuild is running from blib.
  # if so it is likely that this is the blib scheme

  (undef, my $dir, undef) = File::Spec->splitpath( __FILE__ );
  my @dirs = File::Spec->splitdir($dir);

  shift @dirs while @dirs && $dirs[0] ne 'blib';
  return unless @dirs;

  if ( $dirs[1] && $dirs[1] eq 'lib' ) {
    print qq{'blib' scheme is detected. Setting ALIEN_BLIB=1. If this has been done in error, please set ALIEN_BLIB and restart build process to disambiguate.\n};
    return 1;
  }

  carp q{'blib' scheme is suspected, but uncertain. Please set ALIEN_BLIB and restart build process to disambiguate. Setting ALIEN_BLIB=1 for now.};
  return 1;
}

###################
#  Build Methods  #
###################

sub _shell_config_generate
{
  my $scg = Shell::Config::Generate->new;
  $scg->comment(
    'this script sets the environment needed to build this package.',
    'generated by: ' . __FILE__,
  );
  $scg;
}

sub _filter_defines
{
  join ' ', grep !/^-D/, shellwords($_[0]);
}

sub _alien_bin_require {
  my($self, $mod) = @_;

  my $version = $self->alien_bin_requires->{$mod};

  unless(eval { $mod->can('new') })
  {
    my $pm = "$mod.pm";
    $pm =~ s/::/\//g;
    require $pm;
    $mod->VERSION($version) if $version;
  }

  if($mod->can('alien_helper')) {
    my $helpers = $mod->alien_helper;
    foreach my $k (keys %$helpers) {
      my $v = $helpers->{$k};
      next if defined $self->alien_helper->{$k};
      $self->alien_helper->{$k} = $v;
    }
  }
}

sub alien_do_system {
  my $self = shift;
  my $command = shift;

  local %ENV = %ENV;

  if ($self->config_data( 'msys' )) {
    $self->alien_bin_requires->{'Alien::MSYS'} ||= 0
  }

  my $config;

  foreach my $mod (keys %{ $self->alien_bin_requires }) {
    $self->_alien_bin_require($mod);

lib/Alien/Base/ModuleBuild.pm  view on Meta::CPAN

    my $value = $self->alien_interpolate($self->alien_env->{$key});
    defined $value ? $ENV{$key} = $value : delete $ENV{$key};
    $config ||= _shell_config_generate();
    $config->set( $key => $value );
  }

  if($config) {
    if($^O eq 'MSWin32') {
      local $CWD;
      pop @CWD;
      $config->generate_file( Shell::Guess->command_shell, 'env.bat' );
      $config->generate_file( Shell::Guess->cmd_shell,     'env.cmd' );
      $config->generate_file( Shell::Guess->power_shell,   'env.ps1' );
    } else {
      local $CWD;
      pop @CWD;
      $config->generate_file( Shell::Guess->bourne_shell,  'env.sh'  );
      $config->generate_file( Shell::Guess->c_shell,       'env.csh' );
    }
  }

  $self->do_system( ref($command) ? @$command : $command );
}

# alias for backwards compatibility
*_env_do_system = \&alien_do_system;

sub alien_check_built_version {
  return;
}

sub alien_do_commands {
  my $self = shift;
  my $phase = shift;

  my $attr = "alien_${phase}_commands";
  my $commands = $self->$attr();

  print "\n+ cd $CWD\n";

  foreach my $command (@$commands) {

    my %result = $self->alien_do_system( $command );
    unless ($result{success}) {
      carp "External command ($result{command}) failed! Error: $?\n";
      return 0;
    }
  }

  return 1;
}

# wrapper for M::B::do_system which interpolates alien_ vars first
# futher it either captures or tees depending on the value of $Verbose
sub do_system {
  my $self = shift;
  my $opts = ref $_[-1] ? pop : { verbose => 1 };

  my $verbose = $Verbose || $opts->{verbose};

  # prevent build process from cwd-ing from underneath us
  local $CWD;
  my $initial_cwd = $CWD;

  my @args = map { $self->alien_interpolate($_) } @_;

  print "+ @args\n";

  my $old_super_verbose = $self->verbose;
  $self->verbose(0);
  my ($out, $err, $success) =
    $verbose
    ? tee     { $self->SUPER::do_system(@args) }
    : capture { $self->SUPER::do_system(@args) }
  ;
  $self->verbose($old_super_verbose);

  my %return = (
    stdout => $out,
    stderr => $err,
    success => $success,
    command => join(' ', @args),
  );

  # restore wd
  $CWD = $initial_cwd;

  return wantarray ? %return : $return{success};  ## no critic (Policy::Community::Wantarray)
}

sub _alien_execute_helper {
  my($self, $helper) = @_;
  my $code = $self->alien_helper->{$helper};
  die "no such helper: $helper" unless defined $code;

  if(ref($code) ne 'CODE') {
    my $perl = $code;
    $code = sub {
      my $value = eval $perl; ## no critic (Policy::BuiltinFunctions::ProhibitStringyEval)
      die $@ if $@;
      $value;
    };
  }

  $code->();
}

sub alien_interpolate {
  my $self = shift;
  my ($string) = @_;

  my $prefix = $self->alien_exec_prefix;
  my $configure = $self->alien_configure;
  my $share  = $self->alien_library_destination;
  my $name   = $self->alien_name || '';

  # substitute:
  #   install location share_dir (placeholder: %s)
  $string =~ s/(?<!\%)\%s/$share/g;
  #   local exec prefix (ph: %p)
  $string =~ s/(?<!\%)\%p/$prefix/g;

lib/Alien/Base/ModuleBuild.pm  view on Meta::CPAN

C<success> and C<command>.

=head2 alien_do_commands

 $amb->alien_do_commands($phase);

Executes the commands for the given phase.

=head2 alien_interpolate

 my $string = $amb->alien_interpolate($string);

Takes the input string and interpolates the results.

=head2 alien_install_network

[version 1.16]

 my $bool = $amb->alien_install_network;

Returns true if downloading source from the internet is allowed.  This
is true unless C<ALIEN_INSTALL_NETWORK> is defined and false.

=head2 alien_download_rule

[version 1.16]

 my $rule = $amb->alien_download_rule;

This will return one of C<warn>, C<digest>, C<encrypt>, C<digest_or_encrypt>
or C<digest_and_encrypt>.  This is based on the C<ALIEN_DOWNLOAD_RULE>
environment variable.

=head1 GUIDE TO DOCUMENTATION

The documentation for C<Module::Build> is broken up into sections:

=over

=item General Usage (L<Module::Build>)

This is the landing document for L<Alien::Base::ModuleBuild>'s parent class.
It describes basic usage and background information.
Its main purpose is to assist the user who wants to learn how to invoke
and control C<Module::Build> scripts at the command line.

It also lists the extra documentation for its use. Users and authors of Alien::
modules should familiarize themselves with these documents. L<Module::Build::API>
is of particular importance to authors.

=item Alien-Specific Usage (L<Alien::Base::ModuleBuild>)

This is the document you are currently reading.

=item Authoring Reference (L<Alien::Base::Authoring>)

This document describes the structure and organization of
C<Alien::Base> based projects, beyond that contained in
C<Module::Build::Authoring>, and the relevant concepts needed by authors who are
writing F<Build.PL> scripts for a distribution or controlling
C<Alien::Base::ModuleBuild> processes programmatically.

Note that as it contains information both for the build and use phases of
L<Alien::Base> projects, it is located in the upper namespace.

=item API Reference (L<Alien::Base::ModuleBuild::API>)

This is a reference to the C<Alien::Base::ModuleBuild> API beyond that contained
in C<Module::Build::API>.

=item Using the resulting L<Alien> (L<Alien::Build::Manual::AlienUser>)

Once you have an L<Alien> you or your users can review this manual for how to use
it.  Generally speaking you should have some useful usage information in your
L<Alien>'s POD, but some authors choose to direct their users to this manual
instead.

=item Using L<Alien::Build> instead (L<Alien::Build::Manual>)

As mentioned at the top, you are encouraged to use the L<Alien::Build> and
L<alienfile> system instead.  This manual is a starting point for the other
L<Alien::Build> documentation.

=back

=head1 ENVIRONMENT

=over 4

=item B<ALIEN_ARCH>

Set to a true value to install to an arch-specific directory.

=item B<ALIEN_DOWNLOAD_RULE>

This controls security options for fetching alienized packages over the internet.
The legal values are:

=over 4

=item C<warn>

Warn if the package is either unencrypted or lacks a digest.  This is currently
the default, but will change in the near future.

=item C<digest>

Fetch will not happen unless there is a digest for the alienized package.

=item C<encrypt>

Fetch will not happen unless via an encrypted protocol like C<https>, or if the
package is bundled with the L<Alien>.

=item C<digest_or_encrypt>

Fetch will only happen if the alienized package has a cryptographic signature digest,
or if an encrypted protocol like C<https> is used, or if the package is bundled with
the L<Alien>.  This will be the default in the near future.

=item C<digest_and_encrypt>



( run in 0.794 second using v1.01-cache-2.11-cpan-99c4e6809bf )