Alien-Base-ModuleBuild
view release on metacpan or search on metacpan
lib/Alien/Base/ModuleBuild.pm view on Meta::CPAN
# |-- (non-api) connection: holder for Net::FTP-like object (needs cwd, ls, and get methods)
__PACKAGE__->add_property( 'alien_repository' => [] );
__PACKAGE__->add_property( 'alien_repository_default' => {} );
__PACKAGE__->add_property( 'alien_repository_class' => {} );
# alien_isolate_dynamic
__PACKAGE__->add_property( 'alien_isolate_dynamic' => 0 );
__PACKAGE__->add_property( 'alien_autoconf_with_pic' => 1 );
# alien_inline_auto_include
__PACKAGE__->add_property( 'alien_inline_auto_include' => [] );
# use MSYS even if %c isn't found
__PACKAGE__->add_property( 'alien_msys' => 0 );
# Alien packages that provide build dependencies
__PACKAGE__->add_property( 'alien_bin_requires' => {} );
# Do a staged install to blib instead of trying to install to the final location.
__PACKAGE__->add_property( 'alien_stage_install' => 1 );
# Should modules be installed into arch specific directory?
# Most alien dists will have arch specific files in their share so it makes sense to install
# the module in the arch specific location. If you are alienizing something that isn't arch
# specific, like javascript source or java byte code, then you'd want to set this to 0.
# For now this will default to off. See gh#119 for a discussion.
__PACKAGE__->add_property( 'alien_arch' => defined $ENV{ALIEN_ARCH} ? $ENV{ALIEN_ARCH} : 0 );
__PACKAGE__->add_property( 'alien_helper' => {} );
__PACKAGE__->add_property( 'alien_env' => {} );
# Extra enviroment variable to effect to "configure"
__PACKAGE__->add_property( 'alien_extra_site_config' => {} );
################
# ConfigData #
################
# working_directory: full path to the extracted source or binary of the library
# pkgconfig: hashref of A::B::PkgConfig objects created from .pc file found in working_directory
# install_type: either system or share
# version: version number installed or available
# Cflags: holder for cflags if manually specified
# Libs: same but libs
# name: holder for name as needed by pkg-config
# finished_installing: set to true once ACTION_install is finished, this helps testing now and real checks later
############################
# Initialization Methods #
############################
sub new {
my $class = shift;
my %args = @_;
# merge default and user-defined repository classes
$args{alien_repository_class}{$_} ||= $default_repository_class{$_}
for keys %default_repository_class;
my $self = $class->SUPER::new(%args);
## Recheck Force System
if(!defined($ENV{ALIEN_INSTALL_TYPE}) && !defined($ENV{ALIEN_FORCE}) && defined($self->alien_install_type)) {
if ($self->alien_install_type eq 'share' ) { $Force = 1; $ForceSystem = 0; }
elsif($self->alien_install_type eq 'system') { $Force = 0; $ForceSystem = 1; }
}
$self->config_data("Force" => $Force);
$self->config_data("ForceSystem" => $ForceSystem);
$self->alien_helper->{pkg_config} = 'Alien::Base::PkgConfig->pkg_config_command'
unless defined $self->alien_helper->{pkg_config};
my $ab_version = eval {
require Alien::Base;
Alien::Base->VERSION;
};
$ab_version ||= 0;
# setup additional temporary directories, and yes we have to add File::ShareDir manually
if($ab_version < 0.77) {
$self->_add_prereq( 'requires', 'File::ShareDir', '1.00' );
}
# this just gets passed from the Build.PL to the config so that it can
# be used by the auto_include method
$self->config_data( 'inline_auto_include' => $self->alien_inline_auto_include );
if($Force || !$self->alien_check_installed_version) {
if (any { /(?<!\%)\%c/ } map { ref($_) ? @{$_} : $_ } @{ $self->alien_build_commands }) {
$self->config_data( 'autoconf' => 1 );
}
if ($^O eq 'MSWin32' && ($self->config_data( 'autoconf') || $self->alien_msys)) {
$self->_add_prereq( 'build_requires', 'Alien::MSYS', '0' );
$self->config_data( 'msys' => 1 );
} else {
$self->config_data( 'msys' => 0 );
}
foreach my $tool (keys %{ $self->alien_bin_requires }) {
my $version = $self->alien_bin_requires->{$tool};
if($tool eq 'Alien::CMake' && $version < 0.07) {
$version = '0.07';
}
$self->_add_prereq( 'build_requires', $tool, $version );
}
my @repos = ref $args{alien_repository} eq 'ARRAY'
? @{ $args{alien_repository} }
: ( $args{alien_repository} );
foreach my $repo (@repos)
{
next unless defined $repo;
if(($repo->{protocol}||'') eq 'https')
{
$self->_add_prereq( 'build_requires', 'IO::Socket::SSL', '1.56' );
$self->_add_prereq( 'build_requires', 'Net::SSLeay', '1.49' );
}
lib/Alien/Base/ModuleBuild.pm view on Meta::CPAN
my $self = shift;
my $temp_dir = $self->alien_temp_dir;
my $share_dir = $self->alien_share_dir;
# make sure we are in base_dir
local $CWD = $self->base_dir;
unless ( -d $temp_dir ) {
mkdir $temp_dir or croak "Could not create temporary directory $temp_dir";
}
$self->add_to_cleanup( $temp_dir );
unless ( -d $share_dir ) {
mkdir $share_dir or croak "Could not create temporary directory $share_dir";
}
$self->add_to_cleanup( $share_dir );
# add share dir to share dir list
my $share_dirs = $self->share_dir;
unshift @{ $share_dirs->{dist} }, $share_dir;
$self->share_dir( $share_dirs );
{
local $CWD = $share_dir;
open my $fh, '>', 'README' or die "Could not open README for writing (in directory $share_dir)\n";
print $fh <<'END';
This README file is autogenerated by Alien::Base.
Currently it exists for testing purposes, but it might eventually contain information about the file(s) installed.
END
}
}
####################
# ACTION methods #
####################
sub ACTION_alien_fakebuild {
my $self = shift;
# needed for any helpers provided by alien_bin_requires
$self->_alien_bin_require($_) for keys %{ $self->alien_bin_requires };
print "# Build\n";
foreach my $cmd (@{ $self->alien_build_commands })
{
my @cmd = map { $self->alien_interpolate($_) } ref($cmd) ? @$cmd : ($cmd);
print "+ @cmd\n";
}
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
unless ($self->alien_do_commands('build')) {
print "Failed\n";
croak "Build not completed";
}
}
print "Done\n";
}
$self->config_data( install_type => 'share' );
$self->config_data( original_prefix => $self->alien_library_destination );
my $pc = $self->alien_load_pkgconfig;
my $pc_version = (
$pc->{$self->alien_name} || $pc->{_manual}
)->keyword('Version');
unless (defined $version) {
local $CWD = $self->config_data( 'working_directory' );
$version = $self->alien_check_built_version
}
if (! $version && ! $pc_version) {
print STDERR "If you are the author of this Alien dist, you may need to provide a an\n";
print STDERR "alien_check_built_version method for your Alien::Base::ModuleBuild\n";
print STDERR "class. See:\n";
print STDERR "https://metacpan.org/pod/Alien::Base::ModuleBuild#alien_check_built_version\n";
carp "Library looks like it installed, but no version was determined";
$self->config_data( version => 0 );
return
}
if ( $version and $pc_version and versioncmp($version, $pc_version)) {
carp "Version information extracted from the file name and pkgconfig data disagree";
}
$self->config_data( version => $pc_version || $version );
# prevent building multiple times (on M::B::dispatch)
$self->notes( 'ACTION_alien_completed' => 1 );
return;
}
sub ACTION_alien_test {
my $self = shift;
print "Testing library (if applicable) ... ";
if ($self->config_data( 'install_type' ) eq 'share') {
if (defined (my $wdir = $self->config_data( 'working_directory' ))) {
local $CWD = $wdir;
$self->alien_do_commands('test') or die "Failed\n";
}
}
print "Done\n";
}
sub ACTION_test {
my $self = shift;
$self->depends_on('alien_test');
$self->SUPER::ACTION_test;
}
sub ACTION_install {
my $self = shift;
$self->SUPER::ACTION_install;
if($self->alien_stage_install) {
$self->alien_relocation_fixup;
} else {
$self->depends_on('alien_install');
}
}
sub ACTION_alien_install {
my $self = shift;
local $| = 1; # don't buffer stdout
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 ) {
#merge defaults into spec
foreach my $key ( keys %$repo_default ) {
next if defined $repo->{$key};
$repo->{$key} = $repo_default->{$key};
}
$repo->{platform} = 'src' unless defined $repo->{platform};
lib/Alien/Base/ModuleBuild.pm view on Meta::CPAN
$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;
# correct incantation for configure on platform
$string =~ s/(?<!\%)\%c/$configure/g;
# library name (ph: %n)
$string =~ s/(?<!\%)\%n/$name/g;
# current interpreter ($^X) (ph: %x)
my $perl = $self->perl;
$string =~ s/(?<!\%)\%x/$perl/g;
$perl =~ s{\\}{/}g if $^O eq 'MSWin32';
$string =~ s/(?<!\%)\%X/$perl/g;
# Version, but only if needed. Complain if needed and not yet
# stored.
if ($string =~ /(?<!\%)\%v/) {
( run in 0.409 second using v1.01-cache-2.11-cpan-119454b85a5 )