Alien-Build-Plugin-Build-Xrepo
view release on metacpan or search on metacpan
lib/Alien/Build/Plugin/Build/Xrepo.pm view on Meta::CPAN
package Alien::Build::Plugin::Build::Xrepo v1.0.0 {
use v5.40;
use Alien::Build::Plugin;
use Carp ();
use Cwd ();
use Path::Tiny ();
use File::Spec ();
#
has '+packages' => sub { Carp::croak 'packages is a required property' };
has name => undef;
has version => undef;
has kind => undef;
has root => undef;
has ffi => 0;
has verbose => 0;
has repo => undef;
has local_repos => undef;
has __build => undef;
sub init ( $self, $meta ) {
my $verbose = $self->verbose;
$meta->add_requires( 'configure', 'Alien::Build' => '2.84' );
$meta->add_requires( 'configure', 'Alien::Xrepo' => 'v1.0.0' );
require Alien::Xrepo;
require Alien::Xrepo::Build::Recipe;
require Alien::Xmake;
my $recipe = Alien::Xrepo::Build::Recipe->new(
( defined $self->name ? ( name => $self->name ) : () ),
( defined $self->local_repos ? ( local_repos => $self->local_repos ) : () ),
packages => $self->packages,
);
my %ambient;
$ambient{version} = $self->version if defined $self->version;
$ambient{kind} = $self->kind if defined $self->kind;
my $repo = $self->repo;
my $engine;
my $engine_for = sub {
return $engine if defined $engine;
if ( ref $repo eq 'CODE' ) {
$engine = $repo->();
}
elsif ( ref $repo ) {
$engine = $repo;
}
elsif ( defined $repo ) {
$engine = $repo->new();
}
else {
$engine = Alien::Xrepo->new( root => $self->root, verbose => $verbose );
}
$engine;
};
$meta->register_hook(
probe => sub ($build) {
my $r = eval { $engine_for->() };
if ( !$r ) {
my $err = $@ || 'unknown error';
$err =~ s/\s+/ /g;
$err = substr( $err, 0, 300 ) . '...' if length($err) > 300;
die "Alien::Build::Plugin::Build::Xrepo: could not construct the xrepo engine during probe: $err\n";
}
my %probed;
for my $name ( $recipe->packages ) {
my $version = $recipe->version_for($name);
$version = $ambient{version} unless defined $version;
my %opts = $recipe->opts_for( $name, %ambient );
my ( $found, $satisfied );
eval {
my $info = $r->info( $name, format => 'json', %opts );
if ( ref $info eq 'HASH' ) {
$found = $info->{version} // $info->{package}{version} // undef;
}
};
$satisfied = ( defined $found && defined $version ) ? ( $found eq $version ) : ( defined $found ) ? 1 : 0;
$probed{$name} = { version => $found, satisfied => $satisfied };
my $status = $satisfied ? 'satisfied' : 'missing';
say "[xrepo] probe: $name " . ( $found // 'not installed' ) . " ($status)\n" if $verbose;
}
$build->install_prop->{xrepo} ||= {};
$build->install_prop->{xrepo}{probed} = \%probed;
return 'share';
}
);
$meta->register_hook(
download => sub ($build) {
$self->__build($build);
unless ( ref $repo ) {
my $xrepo = eval { Alien::Xmake->new->xrepo } || 'xrepo';
my $ok = File::Spec->file_name_is_absolute($xrepo) ? ( -e $xrepo ) : _which_in_path($xrepo);
die "Alien::Build::Plugin::Build::Xrepo: could not locate xrepo ($xrepo). " .
'Install xmake, or inject an engine via the repo property.'
unless $ok;
}
my $r = $engine_for->();
my $src = Cwd::getcwd();
# Register any local repo trees (patched/private package recipes) with the engine
# before installing, mirroring the engine's configure stage.
for my $repo_def ( @{ $recipe->local_repos || [] } ) {
my $dir = Path::Tiny->new($repo_def)->absolute;
next unless $dir->child('packages')->is_dir;
my $nm = 'alien-' . $dir->basename;
say "[xrepo] no engine support for add_repo (local repo $nm ignored)" if !$r->can('add_repo');
eval { $r->add_repo( $nm, $dir->stringify ) };
}
my %packages;
my %errors;
for my $name ( $recipe->packages ) {
# a per-package recipe version wins; otherwise the ambient plugin version (the
# alienfile `version` property) is the install-time default.
my $version = $recipe->version_for($name);
$version = $ambient{version} unless defined $version;
my %opts = $recipe->opts_for( $name, %ambient );
my $info;
eval { $info = $r->install( $name, $version, %opts ) };
if ($@) {
$errors{$name} = "$@";
next;
lib/Alien/Build/Plugin/Build/Xrepo.pm view on Meta::CPAN
return $bins unless $bins;
my $root = $build->install_prop->{prefix};
my $rr = $build->runtime_prop->{prefix};
return $bins unless defined $root && defined $rr;
[ map {
my $rel = File::Spec->abs2rel( $_, $root );
File::Spec->catdir( $rr, $rel );
} @$bins
];
}
sub _reroot_dyn ( $build, $dyn ) {
return $dyn unless $dyn && @$dyn;
my $root = $build->install_prop->{prefix};
my $rr = $build->runtime_prop->{prefix};
return $dyn unless defined $root && defined $rr;
[ map {
my $rel = File::Spec->abs2rel( $_, $root );
File::Spec->catdir( $rr, $rel );
} @$dyn
];
}
sub _copy_tree ( $src, $dst ) {
$dst->mkpath;
for my $child ( $src->children ) {
my $out = $dst->child( $child->basename );
$child->is_dir ? _copy_tree( $child, $out ) : $child->copy($out);
}
1;
}
sub _locate_content_dir ($root) {
my $found;
my $depth = -1;
my @stack = ( [ $root, 0 ] );
while (@stack) {
my ( $dir, $d ) = @{ shift @stack };
my @kids = $dir->children;
my @with;
for my $k (@kids) {
next unless $k->is_dir;
next unless $k->basename =~ /^(?:include|lib|bin)$/;
next unless _has_entries($k);
push @with, $k;
}
if ( @with && $d > $depth ) {
$found = $dir;
$depth = $d;
}
push @stack, map { [ $_, $d + 1 ] } grep { $_->is_dir && $_->basename !~ /^(?:include|lib|bin)$/ } @kids;
}
return $found;
}
sub _has_entries ($dir) { scalar @{ [ $dir->children ] }; }
sub _which_in_path ($exec) {
my $sep = ( $^O eq 'MSWin32' ) ? ';' : ':';
my @exts = ( $^O eq 'MSWin32' ) ? qw(.exe .bat .cmd) : ('');
for my $dir ( split /\Q$sep\E/, $ENV{PATH} ) {
next if !length $dir;
for my $ext (@exts) {
my $cand = File::Spec->catfile( $dir, "$exec$ext" );
return $cand if -e $cand && !-d $cand;
}
}
return undef;
}
sub _pkg_props ( $info, $dir ) {
return undef unless $info && $dir;
my $t = Path::Tiny->new($dir);
my @inc = ();
my @ldirs = ();
my @bins = ();
push @inc, $t->child('include')->stringify if -d $t->child('include');
push @ldirs, $t->child('lib')->stringify if -d $t->child('lib');
push @bins, $t->child('bin')->stringify if -d $t->child('bin');
push @inc, $t->stringify if !@inc;
push @ldirs, $t->stringify if !@ldirs;
my $cflags = join ' ', map {"-I$_"} @inc;
my $libs = join ' ', map {"-L$_"} @ldirs;
$libs .= ' ' . join ' ', map {"-l$_"} @{ $info->{links} || [] };
{ cflags => $cflags, cflags_static => $cflags, libs => $libs, libs_static => $libs, version => $info->{version}, bin_dir => \@bins };
}
};
#
1;
( run in 4.622 seconds using v1.01-cache-2.11-cpan-54e63673c56 )