Skip to content

Instantly share code, notes, and snippets.

@joelrebel
Created July 1, 2015 23:12
Show Gist options
  • Save joelrebel/9211baca6428bc94d9e5 to your computer and use it in GitHub Desktop.
Save joelrebel/9211baca6428bc94d9e5 to your computer and use it in GitHub Desktop.
sub find_prereqs {
my($self, $dist) = @_;
my @deps = $self->extract_meta_prereqs($dist);
if ($dist->{module} =~ /^Bundle::/i) {
push @deps, $self->bundle_deps($dist);
}
if ($self->{cpanfile_requirements} && !$dist->{cpanfile}) {
for my $dep (@deps) {
$dep->merge_with($self->{cpanfile_requirements});
}
}
return @deps;
}
sub extract_meta_prereqs {
my($self, $dist) = @_;
if ($dist->{cpanfile}) {
my @features = $self->configure_features($dist, $dist->{cpanfile}->features);
my $prereqs = $dist->{cpanfile}->prereqs_with(@features);
# TODO: creating requirements is useful even without cpanfile to detect conflicting prereqs
$self->{cpanfile_requirements} = $prereqs->merged_requirements($dist->{want_phases}, ['requires']);
return App::cpanminus::Dependency->from_prereqs($prereqs, $dist->{want_phases}, $self->{install_types});
}
require CPAN::Meta;
my @deps;
my($meta_file) = grep -f, qw(MYMETA.json MYMETA.yml);
if ($meta_file) {
$self->chat("Checking dependencies from $meta_file ...\n");
my $mymeta = eval { CPAN::Meta->load_file($meta_file, { lazy_validation => 1 }) };
if ($mymeta) {
$dist->{meta}{name} = $mymeta->name;
$dist->{meta}{version} = $mymeta->version;
return $self->extract_prereqs($mymeta, $dist);
}
}
if (-e '_build/prereqs') {
$self->chat("Checking dependencies from _build/prereqs ...\n");
my $prereqs = do { open my $in, "_build/prereqs"; $self->safe_eval(join "", <$in>) };
my $meta = CPAN::Meta->new(
{ name => $dist->{meta}{name}, version => $dist->{meta}{version}, %$prereqs },
{ lazy_validation => 1 },
);
@deps = $self->extract_prereqs($meta, $dist);
} elsif (-e 'Makefile') {
$self->chat("Finding PREREQ from Makefile ...\n");
open my $mf, "Makefile";
while (<$mf>) {
if (/^\#\s+PREREQ_PM => \{\s*(.*?)\s*\}/) {
my @all;
my @pairs = split ', ', $1;
for (@pairs) {
my ($pkg, $v) = split '=>', $_;
push @all, [ $pkg, $v ];
}
my $list = join ", ", map { "'$_->[0]' => $_->[1]" } @all;
my $prereq = $self->safe_eval("no strict; +{ $list }");
push @deps, App::cpanminus::Dependency->from_versions($prereq) if $prereq;
last;
}
}
}
return @deps;
}
sub bundle_deps {
my($self, $dist) = @_;
my @files;
File::Find::find({
wanted => sub { push @files, File::Spec->rel2abs($_) if /\.pm/i },
no_chdir => 1,
}, '.');
my @deps;
for my $file (@files) {
open my $pod, "<", $file or next;
my $in_contents;
while (<$pod>) {
if (/^=head\d\s+CONTENTS/) {
$in_contents = 1;
} elsif (/^=/) {
$in_contents = 0;
} elsif ($in_contents) {
/^(\S+)\s*(\S+)?/
and push @deps, App::cpanminus::Dependency->new($1, $self->maybe_version($2));
}
}
}
return @deps;
}
sub maybe_version {
my($self, $string) = @_;
return $string && $string =~ /^\.?\d/ ? $string : undef;
}
sub extract_prereqs {
my($self, $meta, $dist) = @_;
my @features = $self->configure_features($dist, $meta->features);
my $prereqs = $self->soften_makemaker_prereqs($meta->effective_prereqs(\@features)->clone);
return App::cpanminus::Dependency->from_prereqs($prereqs, $dist->{want_phases}, $self->{install_types});
}
# Workaround for Module::Install 1.04 creating a bogus (higher) MakeMaker requirement that it needs in build_requires
# Assuming MakeMaker requirement is already satisfied in configure_requires, there's no need to have higher version of
# MakeMaker in build/test anyway. https://github.com/miyagawa/cpanminus/issues/463
sub soften_makemaker_prereqs {
my($self, $prereqs) = @_;
return $prereqs unless -e "inc/Module/Install.pm";
for my $phase (qw( build test runtime )) {
my $reqs = $prereqs->requirements_for($phase, 'requires');
if ($reqs->requirements_for_module('ExtUtils::MakeMaker')) {
$reqs->clear_requirement('ExtUtils::MakeMaker');
$reqs->add_minimum('ExtUtils::MakeMaker' => 0);
}
}
$prereqs;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment