treedeps/lib/Module.pm
2017-07-25 13:18:11 -07:00

178 lines
3.7 KiB
Perl
Executable file

package Module;
use 5.22.0;
use Moose;
use Dist;
use Module::CoreList;
use Storable;
use IPC::Run qw/run/;
no warnings 'experimental';
use Carp qw/croak carp cluck confess/;
has 'name' => (is => 'ro');
has 'dist' => (is => 'ro',
builder => '_get_dist',
lazy => 1);
has 'depends' => (
builder => 'get_deps',
is => 'ro',
isa => 'ArrayRef[Module]',
lazy => 1,
);
sub _get_dist {
my $self = shift;
return $Dist::mod_to_dist{$self->name} // $self->name;
}
our %cache;
our %rev_depcache;
if (-e 'modcache.stor') {
eval {
my $cache_href=retrieve('modcache.stor');
%cache = $cache_href->%*;
};
if ($@) {
print STDERR "Couldn't load cache $@\n";
}
}
if (-e 'revdepcache.stor') {
eval {
my $cache_href=retrieve('revdepcache.stor');
%rev_depcache = $cache_href->%*;
};
if ($@) {
print STDERR "Couldn't load cache $@\n";
}
}
sub __save_cache {
store \%cache, 'modcache.stor';
store \%rev_depcache, 'revdepcache.stor';
};
END {__save_cache};
sub new_module {
my $class = shift;
my $module = shift;
confess "\n\nNo module name given\n" unless $module;
my ($name, $version) = split (/[\-~]/, $module);
my $dist = $Dist::mod_to_dist{$name} // $name;
return $cache{$dist} if exists $cache{$dist};
$cache{$dist} = Module->new(name => $name);
}
{
my @banned = do {open (my $fh, "<banned.lst"); map {chomp; $Dist::mod_to_dist{$_} // $_} <$fh>};
use Data::Dumper;
sub _is_banned {
my $module = shift;
my $dist = $Dist::mod_to_dist{$module} // $module;
return _is_core($module) || ($dist ~~ @banned);
}
}
sub _is_core {
my $module = shift;
my ($name, $version) = split (/[\-~]/, $module);
return 0 unless $name;
my $ret = ($name eq 'perl' || Module::CoreList->first_release($name)) // 0;
return $ret;
}
sub get_deps {
my $self=shift;
my $module = $self->name;
# skip perl, or core modules
return [] if _is_banned($module);
print "Getting deps for $module\n";
my @cmd = (qw|cpanm --quiet --showdeps|, $module);
$SIG{TERM}="ignore";
my $out;
my $ret = run \@cmd, '>&', \$out;
die "Failed to get deps for $module: $?" unless $ret;
my $deps = [map {Module->new_module($_)} grep {!_is_core($_)} grep {defined $_ && $_ !~ /^\s*$/} split($/, $out)];
__save_cache;
return $deps;
}
sub print_deps {
my $self = shift;
$self->_print_deps(0, []);
}
sub _print_deps {
my ($self, $level, $v) = @_;
for my $dep ($self->depends->@*) {
my $name = $dep->name;
print ((" " x $level), $name, "\n");
$dep->_print_deps($level+1, [@$v, $name]) unless ($name ~~ @$v);
}
}
sub depends_on {
my ($self, $module) = @_;
my $dist = $module->_get_dist();
for my $dep ($self->depends->@*) {
return 1 if $dep->_get_dist eq $dist;
}
return 0;
}
# Look at all failed modules, see if they might have depended on us.
sub get_failed_descendants {
my $self = shift;
my $candidates = shift;
my $level = shift || 0;
my $loop = shift || [];
my $sdist = $self->_get_dist;
if (grep {$_->_get_dist eq $sdist} @$loop) {
print STDERR "Loop detected with ", $self->name, ". Short circuiting\n";
return();
}
if (ref($rev_depcache{$self->name}) eq 'ARRAY') {
return $rev_depcache{$self->name}->@*;
}
my @total;
my $c=1;
for my $mod_name (@$candidates) {
print STDERR sprintf "%03d %05d\r", $level, $c++;
my $module = Module->new_module($mod_name);
if ($module->depends_on($self)) {
push @total, $module, $module->get_failed_descendants($candidates, $level+1, [@$loop, $self]);
}
}
print STDERR "\n";
$rev_depcache{$self->name} = [@total];
__save_cache;
return @total;
}
1;