App-cpm

 view release on metacpan or  search on metacpan

lib/App/cpm/Master.pm  view on Meta::CPAN

package App::cpm::Master;
use v5.24;
use warnings;
use experimental qw(lexical_subs signatures);

use App::cpm::CircularDependency;
use App::cpm::Distribution;
use App::cpm::Logger;
use App::cpm::Logger::Terminal;
use App::cpm::Task;
use App::cpm::version;
use CPAN::DistnameInfo;
use Module::Metadata;
use File::pushd 'pushd';

sub new ($class, %argv) {
    my $self = bless {
        %argv,
        installed_distributions => 0,
        tasks => +{},
        distributions => +{},
        dependency_ready => +{},
        _fail_resolve => +{},
        _fail_install => +{},
        _is_installed => +{},
    }, $class;
    if ($self->{target_perl}) {
        require Module::CoreList;
        if (!exists $Module::CoreList::version{$self->{target_perl}}) {
            die "Module::CoreList does not have target perl $self->{target_perl} entry, abort.\n";
        }
        if (!exists $Module::CoreList::version{$]}) {
            die "Module::CoreList does not have our perl $] entry, abort.\n";
        }
    }
    if (!$self->{global}) {
        if (eval { require Module::CoreList }) {
            if (!exists $Module::CoreList::version{$]}) {
                die "Module::CoreList does not have our perl $] entry, abort.\n";
            }
            $self->{_has_corelist} = 1;
        } else {
            my $msg = "You don't have Module::CoreList. "
                    . "The local-lib may result in incomplete self-contained directory.";
            App::cpm::Logger->log(result => "WARN", message => $msg);
        }
    }
    $self;
}

sub fail ($self, $ctx) {
    my @fail_resolve = sort keys $self->{_fail_resolve}->%*;
    my @fail_install = sort keys $self->{_fail_install}->%*;
    my @not_installed = grep { !$self->{_fail_install}{$_->distfile} && !$_->installed } $self->_final_install_distributions(1);
    return if !@fail_resolve && !@fail_install && !@not_installed;

    my $detector = App::cpm::CircularDependency->new;
    for my $dist (@not_installed) {
        my $req = $dist->requirements([qw(configure build test runtime)])->as_array;
        $detector->add($dist->distfile, $dist->provides, $req);
    }
    $detector->finalize;

    my $detected = $detector->detect;
    for my $distfile (sort keys $detected->%*) {
        my $distvname = $self->distribution($distfile)->distvname;
        my @circular = $detected->{$distfile}->@*;
        my $msg = join " -> ", map { $self->distribution($_)->distvname } @circular;
        local $ctx->{logger}{context} = $distvname;
        $ctx->log("Detected circular dependencies $msg");
        $ctx->log("Failed to install distribution");
    }
    for my $dist (sort { $a->distvname cmp $b->distvname } grep { !$detected->{$_->distfile} } @not_installed) {
        local $ctx->{logger}{context} = $dist->distvname;
        $ctx->log("Failed to install distribution, "
                            ."because of installing some dependencies failed");
    }

    my @fail_install_name = map { CPAN::DistnameInfo->new($_)->distvname || $_ } @fail_install;
    my @not_installed_name = map { $_->distvname } @not_installed;
    if (@fail_resolve || @fail_install_name) {
        $ctx->log("--");
        $ctx->log(
            "Installation failed. "
            . "The direct cause of the failure comes from the following packages/distributions; "
            . "you may want to grep this log file by them:"
        );
        $ctx->log(" * $_") for @fail_resolve, sort @fail_install_name;
    }
    { resolve => \@fail_resolve, install => [sort @fail_install_name, @not_installed_name] };
}

sub tasks ($self) { values $self->{tasks}->%* }

sub add_task ($self, $ctx, %task) {
    my $new = App::cpm::Task->new(%task);
    if (my ($existing) = grep { $_->equals($new) } $self->tasks) {
        $existing->{final_target} ||= $new->{final_target};
        return 0;
    } else {
        $self->{tasks}{$new->uid} = $new;
        return 1;
    }
}

sub get_task ($self, $ctx) {
    if (my @task = grep { !$_->in_charge } $self->tasks) {
        return @task;
    }
    $self->_add_tasks($ctx);
    return if !$self->tasks;
    if (my @task = grep { !$_->in_charge } $self->tasks) {
        return @task;
    }
    return;
}

sub register_result ($self, $ctx, $result) {
    my ($task) = grep { $_->uid eq $result->{uid} } $self->tasks;
    die "Missing task that has uid=$result->{uid}" if !$task;

    $task->%* = $result->%*; # XXX

    my $logged = $self->info($task);
    my $method = "_register_@{[$task->{type}]}_result";
    $self->$method($ctx, $task);
    $self->remove_task($ctx, $task);

    return 1;
}

sub info ($self, $task) {
    my $type = $task->type;
    if (!$App::cpm::Logger::VERBOSE && $task->{ok}) {
        return if !($self->{notest} && $type eq "fetch" && $task->{prebuilt})
            && !($self->{notest} && $type eq "build" && !$task->{prebuilt})
            && !(!$self->{notest} && $type eq "test");
    }
    my $name = $task->distvname;
    my ($message, $optional);
    if ($type eq "resolve") {
        $message = $task->{package};
        $message .= " -> $name" . ($task->{ref} ? "\@$task->{ref}" : "") if $task->{ok};
        $optional = "from $task->{from}" if $task->{ok} and $task->{from};
    } else {
        $message = $name;
        $optional = "using cache" if $type eq "fetch" and $task->{using_cache};
        $optional = "using prebuilt" if $task->{prebuilt};
    }
    my $elapsed = defined $task->{elapsed} ? sprintf "(%.3fsec) ", $task->{elapsed} : "";



( run in 0.854 second using v1.01-cache-2.11-cpan-39bf76dae61 )