File Coverage

blib/lib/CPAN/Reporter/PrereqCheck.pm
Criterion Covered Total %
statement 96 102 94.1
branch 47 58 81.0
condition 9 15 60.0
subroutine 7 7 100.0
pod n/a
total 159 182 87.3


line stmt bran cond sub pod time code
1 35     35   94425 use strict;
  35         85  
  35         2093  
2             package CPAN::Reporter::PrereqCheck;
3              
4             our $VERSION = '1.2019';
5              
6 35     35   1955 use ExtUtils::MakeMaker 6.36;
  35         222136  
  35         3814  
7 35     35   322 use File::Spec;
  35         104  
  35         815  
8 35     35   1118 use CPAN::Version;
  35         2490  
  35         14846  
9              
10             _run() if ! caller();
11              
12             sub _run {
13 2     2   3759 my %saw_mod;
14             # read module and prereq string from STDIN
15             # do this as early as possible: https://github.com/cpan-testers/CPAN-Reporter/issues/20
16             my @modules;
17 2         134 while ( <> ) {
18 5         51 push @modules, $_;
19             }
20 2         25 local *DEVNULL;
21 2         106 open DEVNULL, ">" . File::Spec->devnull; ## no critic
22             # ensure actually installed, not ./inc/... or ./t/..., etc.
23 2         15 local @INC = grep { $_ ne '.' } @INC;
  23         65  
24 2         5 for (@modules) {
25 5         23 m/^(\S+)\s+([^\n]*)/;
26 5         20 my ($mod, $need) = ($1, $2);
27 5 50       14 die "Couldn't read module for '$_'" unless $mod;
28 5 50       12 $need = 0 if not defined $need;
29              
30             # only evaluate a module once
31 5 100       22 next if $saw_mod{$mod}++;
32              
33             # get installed version from file with EU::MM
34 4         9 my($have, $inst_file, $dir, @packpath);
35 4 100       11 if ( $mod eq "perl" ) {
36 1         9 $have = $];
37             }
38             else {
39 3         9 @packpath = split( /::/, $mod );
40 3         9 $packpath[-1] .= ".pm";
41 3 50 33     12 if (@packpath == 1 && $packpath[0] eq "readline.pm") {
42 0         0 unshift @packpath, "Term", "ReadLine"; # historical reasons
43             }
44             INCDIR:
45 3         19 foreach my $dir (@INC) {
46 31         255 my $pmfile = File::Spec->catfile($dir,@packpath);
47 31 100       815 if (-f $pmfile){
48 2         8 $inst_file = $pmfile;
49 2         6 last INCDIR;
50             }
51             }
52              
53             # get version from file or else report missing
54 3 100       12 if ( defined $inst_file ) {
55 2         24 $have = my $preliminary_version = MM->parse_version($inst_file);
56 2 100 66     893 $preliminary_version = "0" if ! defined $preliminary_version || $preliminary_version eq 'undef';
57             # report broken if it can't be loaded
58             # "select" to try to suppress spurious newlines
59 2         11 select DEVNULL; ## no critic
60 2 50       9 if ( ! _try_load( $mod, $preliminary_version ) ) {
61 0         0 select STDOUT; ## no critic
62 0         0 print "$mod 0 broken\n";
63 0         0 next;
64             }
65             # Now the module is loaded: if MM->parse_version previously failed to
66             # get the version, then we can now look at the value of the $VERSION
67             # variable.
68 2 100 66     19 if (! defined $have || $have eq 'undef') {
69 35     35   301 no strict 'refs';
  35         100  
  35         29745  
70 1         2 my $mod_version = ${$mod.'::VERSION'};
  1         6  
71 1 50       5 if (defined $mod_version) {
72 1         6 $have = $mod_version;
73             } else {
74 0         0 $have = 0; # fallback
75             }
76             }
77 2         8 select STDOUT; ## no critic
78             }
79             else {
80 1         7 print "$mod 0 n/a\n";
81 1         4 next;
82             }
83             }
84              
85             # complex requirements are comma separated
86 3         23 my ( @requirements ) = split /\s*,\s*/, $need;
87              
88 3         7 my $passes = 0;
89             RQ:
90 3         9 for my $rq (@requirements) {
91 7 100       44 if ($rq =~ s|>=\s*||) {
    100          
    100          
    100          
    100          
92             # no-op -- just trimmed string
93             } elsif ($rq =~ s|>\s*||) {
94 1 50       3 if (CPAN::Version->vgt($have,$rq)){
95 0         0 $passes++;
96             }
97 1         35 next RQ;
98             } elsif ($rq =~ s|!=\s*||) {
99 1 50       2 if (CPAN::Version->vcmp($have,$rq)) {
100 1         69 $passes++; # didn't match
101             }
102 1         9 next RQ;
103             } elsif ($rq =~ s|<=\s*||) {
104 1 50       3 if (! CPAN::Version->vgt($have,$rq)){
105 1         35 $passes++;
106             }
107 1         3 next RQ;
108             } elsif ($rq =~ s|<\s*||) {
109 1 50       3 if (CPAN::Version->vlt($have,$rq)){
110 1         33 $passes++;
111             }
112 1         2 next RQ;
113             }
114             # if made it here, then it's a normal >= comparison
115 3 50       17 if (! CPAN::Version->vlt($have, $rq)){
116 3         154 $passes++;
117             }
118             }
119 3 100       9 my $ok = $passes == @requirements ? 1 : 0;
120 3         19 print "$mod $ok $have\n"
121             }
122 2         32 return;
123             }
124              
125             sub _try_load {
126 8     8   2743 my ($module, $have) = @_;
127              
128 8         49 my @do_not_load = (
129             # should not be loaded directly
130             qw/Term::ReadLine::Perl Term::ReadLine::Gnu MooseX::HasDefaults Readonly::XS
131             POE::Loop::Event SOAP::Constants
132             Moose::Meta::TypeConstraint::Parameterizable Moose::Meta::TypeConstraint::Parameterized/,
133             'Devel::Trepan', #"require Enbugger; require Devel::Trepan;" starts debugging session
134             'Test::BDD::Cucumber::Loader', #by doing something to Test::Builder breaks Test::SharedFork and any module that uses it
135              
136             #removed modules, they still exist but die
137             qw/Pegex::Mo YAML::LibYAML Params::CheckCompiler/,
138              
139             #have additional prereqs
140             qw/Log::Dispatch::Email::MailSender RDF::NS::Trine Plack::Handler::FCGI Web::Scraper::LibXML
141             DBIx::Class::EncodedColumn::Crypt::Eksblowfish::Bcrypt/,
142              
143             #modify @INC. 'lib' appearing in @INC will prevent correct
144             #checking of modules with XS part, for ex. List::Util
145             qw/ExtUtils::ParseXS ExtUtils::ParseXS::Utilities/,
146            
147             #require special conditions to run
148             qw/mylib Test::YAML Cache::Reddit Dist::Zilla::Plugin::TestMLIncluder/,
149              
150             #print text to STDOUT which C::R::PC cannot intercept
151             qw/Test::Sys::Info Test::Subs/,
152              
153             #do not return true value
154             qw/perlsecret Alt::Crypt::RSA::BigInt/,
155              
156             #Try::Tiny::Tiny and modules that use it conflict with several modules
157             'Try::Tiny::Tiny',
158             'Date::Lectionary::Time',
159             );
160              
161 8         81 my %loading_conflicts = (
162             'signatures' => ['Catalyst'],
163             'Dancer::Plugin::FlashMessage' => ['Dancer::Plugin::FlashNote'],
164             'Dancer::Plugin::FlashNote' => ['Dancer::Plugin::FlashMessage'],
165             'Dancer::Plugin::Mongoose' => ['Dancer::Plugin::DBIC'],
166             'Dancer::Plugin::DBIC' => ['Dancer::Plugin::Mongoose'],
167             'Test::Mock::LWP::UserAgent' => ['HTTP::Response'],
168             'Test::BDD::Cucumber::Loader' => ['Test::Exception', 'Test::MockObject', 'Test::SharedFork'], #works in different order
169             'Test::SharedFork' => ['threads'], #dies if $INC{'threads.pm'}
170             'Test::TCP' => ['threads'], #loads Test::SharedFork
171             'Test::Fake::HTTPD' => ['threads'], #loads Test::SharedFork
172             'Plack::Test::Suite' => ['threads'], #loads Test::SharedFork
173             #Note: Test::Perl::Critic and other modules load threads, so reordering will not help
174             'App::Sqitch' => ['Moose'], #has "no Moo::sification"
175             ); #modules that conflict with each other
176            
177 8         102 my %load_before = (
178             'Tk::Font' => 'Tk',
179             'Tk::Widget' => 'Tk',
180             'Tk::Label' => 'Tk',
181             'Tk::Menubutton' => 'Tk',
182             'Tk::Entry' => 'Tk',
183             'Class::MOP::Class' => 'Class::MOP',
184             'Moose::Meta::TypeConstraint::Role' => 'Moose',
185             'Moose::Meta::TypeConstraint::Union' => 'Moose',
186             'Moose::Meta::Attribute::Native' => 'Class::MOP',
187             'Moose::Meta::Role::Attribute' => 'Class::MOP',
188             'Moose::Util::TypeConstraints' => 'Moose',
189             'Test::More::Hooks' => 'Test::More',
190             'Net::HTTP::Spore::Middleware::DefaultParams' => 'Net::HTTP::Spore::Meta::Method',
191             'Log::Log4perl::Filter' => 'Log::Log4perl',
192             'RDF::DOAP::Project' => 'RDF::Trine', #or other modules that use RDF::Trine will fail
193             'Win32::API::Type' => 'Win32::API',
194             'Dancer2::Plugin::DBIC' => 'Dancer2', #or later Strehler::API will fail
195             'Wx::AUI' => 'Wx',
196             'Dist::Zilla::Role::Tempdir' => 'Dist::Zilla', # or it would not be possible to check Dist::Zilla
197             'Pod::Perldoc::ToMan' => 'Pod::Perldoc',
198             );
199              
200             # M::I < 0.95 dies in require, so we can't check if it loads
201             # Instead we just pretend that it works
202 8 100 66     54 if ( $module eq 'Module::Install' && $have < 0.95 ) {
    100 66        
    100          
    100          
203 1         13 return 1;
204             }
205             # circular dependency with Catalyst::Runtime, so this module
206             # does not depends on it, but still does not work without it.
207             elsif ( $module eq 'Catalyst::DispatchType::Regex' && $have <= 5.90032 ) {
208 1         11 return 1;
209             }
210 180         295 elsif ( grep { $_ eq $module } @do_not_load ) {
211 1         11 return 1;
212             }
213             # loading Acme modules like Acme::Bleach can do bad things,
214             # so never try to load them; just pretend that they work
215             elsif( $module =~ /^Acme::/ ) {
216 1         10 return 1;
217             }
218              
219 4 100       12 if ( exists $loading_conflicts{$module} ) {
220 1         1 foreach my $mod1 ( @{ $loading_conflicts{$module} } ) {
  1         3  
221 1         4 my $file = "$mod1.pm";
222 1         2 $file =~ s{::}{/}g;
223 1 50       3 if (exists $INC{$file}) {
224 1         15 return 1;
225             }
226             }
227             }
228              
229 3 100       31 if (exists $load_before{$module}) {
230 1         67 eval "require $load_before{$module};1;";
231             }
232              
233 3         12 my $file = "$module.pm";
234 3         11 $file =~ s{::}{/}g;
235              
236 3         7 return eval {require $file; 1}; ## no critic
  3         823  
  2         499  
237             }
238              
239             1;
240              
241             # ABSTRACT: Modulino for prerequisite tests
242              
243             __END__