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   30794 use strict;
  35         41  
  35         1602  
2             package CPAN::Reporter::PrereqCheck;
3              
4             our $VERSION = '1.2017';
5              
6 35     35   1414 use ExtUtils::MakeMaker 6.36;
  35         146918  
  35         2943  
7 35     35   149 use File::Spec;
  35         35  
  35         577  
8 35     35   855 use CPAN::Version;
  35         2384  
  35         9816  
9              
10             _run() if ! caller();
11              
12             sub _run {
13 2     2   2695     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         59     while ( <> ) {
18 5         25         push @modules, $_;
19                 }
20 2         4     local *DEVNULL;
21 2         47     open DEVNULL, ">" . File::Spec->devnull; ## no critic
22             # ensure actually installed, not ./inc/... or ./t/..., etc.
23 2         5     local @INC = grep { $_ ne '.' } @INC;
  23         26  
24 2         5     for (@modules) {
25 5         16         m/^(\S+)\s+([^\n]*)/;
26 5         12         my ($mod, $need) = ($1, $2);
27 5 50       11         die "Couldn't read module for '$_'" unless $mod;
28 5 50       7         $need = 0 if not defined $need;
29              
30             # only evaluate a module once
31 5 100       13         next if $saw_mod{$mod}++;
32              
33             # get installed version from file with EU::MM
34 4         3         my($have, $inst_file, $dir, @packpath);
35 4 100       18         if ( $mod eq "perl" ) {
36 1         1             $have = $];
37                     }
38                     else {
39 3         9             @packpath = split( /::/, $mod );
40 3         3             $packpath[-1] .= ".pm";
41 3 50 33     8             if (@packpath == 1 && $packpath[0] eq "readline.pm") {
42 0         0                 unshift @packpath, "Term", "ReadLine"; # historical reasons
43                         }
44                         INCDIR:
45 3         6             foreach my $dir (@INC) {
46 31         130                 my $pmfile = File::Spec->catfile($dir,@packpath);
47 31 100       486                 if (-f $pmfile){
48 2         3                     $inst_file = $pmfile;
49 2         5                     last INCDIR;
50                             }
51                         }
52              
53             # get version from file or else report missing
54 3 100       5             if ( defined $inst_file ) {
55 2         22                 $have = my $preliminary_version = MM->parse_version($inst_file);
56 2 100 66     456                 $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         4                 select DEVNULL; ## no critic
60 2 50       6                 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     22                 if (! defined $have || $have eq 'undef') {
69 35     35   146                     no strict 'refs';
  35         37  
  35         18972  
70 1         1                     my $mod_version = ${$mod.'::VERSION'};
  1         3  
71 1 50       3                     if (defined $mod_version) {
72 1         1                         $have = $mod_version;
73                                 } else {
74 0         0                         $have = 0; # fallback
75                                 }
76                             }
77 2         5                 select STDOUT; ## no critic
78                         }
79                         else {
80 1         3                 print "$mod 0 n/a\n";
81 1         2                 next;
82                         }
83                     }
84              
85             # complex requirements are comma separated
86 3         13         my ( @requirements ) = split /\s*,\s*/, $need;
87              
88 3         3         my $passes = 0;
89                     RQ:
90 3         5         for my $rq (@requirements) {
91 7 100       34             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         20                 next RQ;
98                         } elsif ($rq =~ s|!=\s*||) {
99 1 50       2                 if (CPAN::Version->vcmp($have,$rq)) {
100 1         18                     $passes++; # didn't match
101                             }
102 1         2                 next RQ;
103                         } elsif ($rq =~ s|<=\s*||) {
104 1 50       1                 if (! CPAN::Version->vgt($have,$rq)){
105 1         19                     $passes++;
106                             }
107 1         2                 next RQ;
108                         } elsif ($rq =~ s|<\s*||) {
109 1 50       2                 if (CPAN::Version->vlt($have,$rq)){
110 1         19                     $passes++;
111                             }
112 1         1                 next RQ;
113                         }
114             # if made it here, then it's a normal >= comparison
115 3 50       14             if (! CPAN::Version->vlt($have, $rq)){
116 3         90                 $passes++;
117                         }
118                     }
119 3 100       4         my $ok = $passes == @requirements ? 1 : 0;
120 3         13         print "$mod $ok $have\n"
121                 }
122 2         15     return;
123             }
124              
125             sub _try_load {
126 8     8   1993   my ($module, $have) = @_;
127              
128 8         29   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              
135             #removed modules
136                 qw/Pegex::Mo YAML::LibYAML/,
137              
138             #have additional prereqs
139                 qw/Log::Dispatch::Email::MailSender RDF::NS::Trine Plack::Handler::FCGI Web::Scraper::LibXML/,
140              
141             #modify @INC. 'lib' appearing in @INC will prevent correct
142             #checking of modules with XS part, for ex. List::Util
143                 qw/ExtUtils::ParseXS ExtUtils::ParseXS::Utilities/,
144                 
145             #require special conditions to run
146                 qw/mylib/,
147              
148             #do not return true value
149                 qw/perlsecret Alt::Crypt::RSA::BigInt/,
150               );
151              
152 8         67   my %loading_conflicts = (
153                 'signatures' => ['Catalyst'],
154                 'Dancer::Plugin::FlashMessage' => ['Dancer::Plugin::FlashNote'],
155                 'Dancer::Plugin::FlashNote' => ['Dancer::Plugin::FlashMessage'],
156                 'Dancer::Plugin::Mongoose' => ['Dancer::Plugin::DBIC'],
157                 'Dancer::Plugin::DBIC' => ['Dancer::Plugin::Mongoose'],
158                 'Test::BDD::Cucumber::Loader' => ['Test::Exception', 'Test::MockObject'], #works in different order
159                 'Test::Mock::LWP::UserAgent' => ['HTTP::Response'],
160                 'Test::SharedFork' => ['threads'], #dies if $INC{'threads.pm'}
161                 'Test::TCP' => ['threads'], #loads Test::SharedFork
162                 'Test::Fake::HTTPD' => ['threads'], #loads Test::SharedFork
163             #Note: Test::Perl::Critic and other modules load threads, so reordering will not help
164               ); #modules that conflict with each other
165               
166 8         52   my %load_before = (
167                 'Tk::Font' => 'Tk',
168                 'Tk::Widget' => 'Tk',
169                 'Tk::Label' => 'Tk',
170                 'Tk::Menubutton' => 'Tk',
171                 'Tk::Entry' => 'Tk',
172                 'Class::MOP::Class' => 'Class::MOP',
173                 'Moose::Meta::TypeConstraint::Role' => 'Moose',
174                 'Moose::Meta::TypeConstraint::Union' => 'Moose',
175                 'Moose::Meta::Attribute::Native' => 'Class::MOP',
176                 'Moose::Meta::Role::Attribute' => 'Class::MOP',
177                 'Test::More::Hooks' => 'Test::More',
178                 'Net::HTTP::Spore::Middleware::DefaultParams' => 'Net::HTTP::Spore::Meta::Method',
179                 'Log::Log4perl::Filter' => 'Log::Log4perl',
180                 'RDF::DOAP::Project' => 'RDF::Trine', #or other modules that use RDF::Trine will fail
181               );
182              
183             # M::I < 0.95 dies in require, so we can't check if it loads
184             # Instead we just pretend that it works
185 8 100 66     43   if ( $module eq 'Module::Install' && $have < 0.95 ) {
    100 66        
    100          
    100          
186 1         8     return 1;
187               }
188             # circular dependency with Catalyst::Runtime, so this module
189             # does not depends on it, but still does not work without it.
190               elsif ( $module eq 'Catalyst::DispatchType::Regex' && $have <= 5.90032 ) {
191 1         7     return 1;
192               }
193 120         103   elsif ( grep { $_ eq $module } @do_not_load ) {
194 1         6     return 1;
195               }
196             # loading Acme modules like Acme::Bleach can do bad things,
197             # so never try to load them; just pretend that they work
198               elsif( $module =~ /^Acme::/ ) {
199 1         8     return 1;
200               }
201              
202 4 100       9   if ( exists $loading_conflicts{$module} ) {
203 1         1       foreach my $mod1 ( @{ $loading_conflicts{$module} } ) {
  1         2  
204 1         2          my $file = "$mod1.pm";
205 1         2          $file =~ s{::}{/}g;
206 1 50       2          if (exists $INC{$file}) {
207 1         7              return 1;
208                      }
209                   }
210               }
211              
212 3 100       6   if (exists $load_before{$module}) {
213 1         49       eval "require $load_before{$module};1;";
214               }
215              
216 3         6   my $file = "$module.pm";
217 3         8   $file =~ s{::}{/}g;
218              
219 3         5   return eval {require $file; 1}; ## no critic
  3         506  
  2         380  
220             }
221              
222             1;
223              
224             # ABSTRACT: Modulino for prerequisite tests
225              
226             __END__
227            
228             =pod
229            
230             =encoding UTF-8
231            
232             =head1 NAME
233            
234             CPAN::Reporter::PrereqCheck - Modulino for prerequisite tests
235            
236             =head1 VERSION
237            
238             version 1.2017
239            
240             =head1 SYNOPSIS
241            
242             require CPAN::Reporter::PrereqCheck;
243             my $prereq_check = $INC{'CPAN/Reporter/PrereqCheck.pm'};
244             my $result = qx/$perl $prereq_check < $prereq_file/;
245            
246             =head1 DESCRIPTION
247            
248             This modulino determines whether a list of prerequisite modules are
249             available and, if so, their version number. It is designed to be run
250             as a script in order to provide this information from the perspective of
251             a subprocess, just like CPAN::Reporter's invocation of C<<< perl Makefile.PL >>>
252             and so on.
253            
254             It reads a module name and prerequisite string pair from each line of input
255             and prints out the module name, 0 or 1 depending on whether the prerequisite
256             is satisfied, and the installed module version. If the module is not
257             available, it will print "nE<sol>a" for the version. If the module is available
258             but can't be loaded, it will print "broken" for the version. Modules
259             without a version will be treated as being of version "0".
260            
261             No user serviceable parts are inside. This modulino is packaged for
262             internal use by CPAN::Reporter.
263            
264             =head1 BUGS
265            
266             Please report any bugs or feature using the CPAN Request Tracker.
267             Bugs can be submitted through the web interface at
268             L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Reporter>
269            
270             When submitting a bug or request, please include a test-file or a patch to an
271             existing test-file that illustrates the bug or desired feature.
272            
273             =head1 SEE ALSO
274            
275             =over
276            
277             =item *
278            
279             L<CPAN::Reporter> -- main documentation
280            
281             =back
282            
283             =head1 AUTHOR
284            
285             David Golden <dagolden@cpan.org>
286            
287             =head1 COPYRIGHT AND LICENSE
288            
289             This software is Copyright (c) 2006 by David Golden.
290            
291             This is free software, licensed under:
292            
293             The Apache License, Version 2.0, January 2004
294            
295             =cut
296