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 )