File Coverage

blib/lib/CPAN/Reporter/PrereqCheck.pm
Criterion Covered Total %
statement 100 102 98.0
branch 53 58 91.3
condition 10 15 66.6
subroutine 7 7 100.0
pod n/a
total 170 182 93.4


line stmt bran cond sub pod time code
1 59     47   3311757 use strict;
  59         230  
  47         3956  
2             package CPAN::Reporter::PrereqCheck;
3              
4             our $VERSION = '1.2020';
5              
6 47     47   13483 use ExtUtils::MakeMaker 6.36;
  47         2110926  
  47         7608  
7 47     47   444 use File::Spec;
  47         121  
  47         1264  
8 47     47   7502 use CPAN::Version;
  47         23290  
  47         25276  
9              
10             _run() if ! caller();
11              
12             sub _run {
13 14     14   252483 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 14         3172 while ( <> ) {
18 158         680 push @modules, $_;
19             }
20 14         77 local *DEVNULL;
21 14         1067 open DEVNULL, ">" . File::Spec->devnull; ## no critic
22             # ensure actually installed, not ./inc/... or ./t/..., etc.
23 14         102 local @INC = grep { $_ ne '.' } @INC;
  121         364  
24 14         60 for (@modules) {
25 158         1208 m/^(\S+)\s+([^\n]*)/;
26 158         865 my ($mod, $need) = ($1, $2);
27 158 100       439 die "Couldn't read module for '$_'" unless $mod;
28 158 50       424 $need = 0 if not defined $need;
29              
30             # only evaluate a module once
31 158 100       906 next if $saw_mod{$mod}++;
32              
33             # get installed version from file with EU::MM
34 157         323 my($have, $inst_file, $dir, @packpath);
35 157 100       423 if ( $mod eq "perl" ) {
36 5         18 $have = $];
37             }
38             else {
39 152         602 @packpath = split( /::/, $mod );
40 152         323 $packpath[-1] .= ".pm";
41 152 50 66     624 if (@packpath == 1 && $packpath[0] eq "readline.pm") {
42 0         0 unshift @packpath, "Term", "ReadLine"; # historical reasons
43             }
44             INCDIR:
45 152         348 foreach my $dir (@INC) {
46 874         7248 my $pmfile = File::Spec->catfile($dir,@packpath);
47 874 100       13590 if (-f $pmfile){
48 130         293 $inst_file = $pmfile;
49 130         410 last INCDIR;
50             }
51             }
52              
53             # get version from file or else report missing
54 152 100       411 if ( defined $inst_file ) {
55 130         1388 $have = my $preliminary_version = MM->parse_version($inst_file);
56 130 100 66     73187 $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 130         519 select DEVNULL; ## no critic
60 130 100       528 if ( ! _try_load( $mod, $preliminary_version ) ) {
61 4         1482 select STDOUT; ## no critic
62 4         28 print "$mod 0 broken\n";
63 4         26 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 126 100 66     963 if (! defined $have || $have eq 'undef') {
69 47     47   425 no strict 'refs';
  47         120  
  47         74331  
70 5         12 my $mod_version = ${$mod.'::VERSION'};
  5         48  
71 5 100       24 if (defined $mod_version) {
72 1         3 $have = $mod_version;
73             } else {
74 4         8 $have = 0; # fallback
75             }
76             }
77 126         657 select STDOUT; ## no critic
78             }
79             else {
80 22         198 print "$mod 0 n/a\n";
81 22         228 next;
82             }
83             }
84              
85             # complex requirements are comma separated
86 131         676 my ( @requirements ) = split /\s*,\s*/, $need;
87              
88 131         273 my $passes = 0;
89             RQ:
90 131         383 for my $rq (@requirements) {
91 143 100       1079 if ($rq =~ s|>=\s*||) {
    100          
    100          
    100          
    100          
92             # no-op -- just trimmed string
93             } elsif ($rq =~ s|>\s*||) {
94 5 50       39 if (CPAN::Version->vgt($have,$rq)){
95 0         0 $passes++;
96             }
97 5         143 next RQ;
98             } elsif ($rq =~ s|!=\s*||) {
99 9 100       45 if (CPAN::Version->vcmp($have,$rq)) {
100 1         38 $passes++; # didn't match
101             }
102 9         134 next RQ;
103             } elsif ($rq =~ s|<=\s*||) {
104 5 50       41 if (! CPAN::Version->vgt($have,$rq)){
105 5         324 $passes++;
106             }
107 5         19 next RQ;
108             } elsif ($rq =~ s|<\s*||) {
109 9 100       44 if (CPAN::Version->vlt($have,$rq)){
110 5         203 $passes++;
111             }
112 9         161 next RQ;
113             }
114             # if made it here, then it's a normal >= comparison
115 115 100       1027 if (! CPAN::Version->vlt($have, $rq)){
116 111         6708 $passes++;
117             }
118             }
119 131 100       624 my $ok = $passes == @requirements ? 1 : 0;
120 131         2320 print "$mod $ok $have\n"
121             }
122 14         568 return;
123             }
124              
125             sub _try_load {
126 136     136   3840 my ($module, $have) = @_;
127              
128 136         1426 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 136         2144 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 136         2407 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 136 100 66     840 if ( $module eq 'Module::Install' && $have < 0.95 ) {
    100 66        
    100          
    100          
203 1         10 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         10 return 1;
209             }
210 4020         14950 elsif ( grep { $_ eq $module } @do_not_load ) {
211 6         82 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         16 return 1;
217             }
218              
219 127 100       372 if ( exists $loading_conflicts{$module} ) {
220 1         3 foreach my $mod1 ( @{ $loading_conflicts{$module} } ) {
  1         3  
221 1         3 my $file = "$mod1.pm";
222 1         3 $file =~ s{::}{/}g;
223 1 50       5 if (exists $INC{$file}) {
224 1         17 return 1;
225             }
226             }
227             }
228              
229 126 100       390 if (exists $load_before{$module}) {
230 1         78 eval "require $load_before{$module};1;";
231             }
232              
233 126         298 my $file = "$module.pm";
234 126         588 $file =~ s{::}{/}g;
235              
236 126         261 return eval {require $file; 1}; ## no critic
  126         54102  
  121         2926557  
237             }
238              
239             1;
240              
241             # ABSTRACT: Modulino for prerequisite tests
242              
243             __END__