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   34037 use strict;
  35         39  
  35         1562  
2             package CPAN::Reporter::PrereqCheck;
3              
4             our $VERSION = '1.2018';
5              
6 35     35   1476 use ExtUtils::MakeMaker 6.36;
  35         154396  
  35         2893  
7 35     35   139 use File::Spec;
  35         40  
  35         594  
8 35     35   859 use CPAN::Version;
  35         2350  
  35         9961  
9              
10             _run() if ! caller();
11              
12             sub _run {
13 2     2   3099 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         85 while ( <> ) {
18 5         27 push @modules, $_;
19             }
20 2         6 local *DEVNULL;
21 2         52 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         4 for (@modules) {
25 5         16 m/^(\S+)\s+([^\n]*)/;
26 5         12 my ($mod, $need) = ($1, $2);
27 5 50       14 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         15 my($have, $inst_file, $dir, @packpath);
35 4 100       8 if ( $mod eq "perl" ) {
36 1         1 $have = $];
37             }
38             else {
39 3         8 @packpath = split( /::/, $mod );
40 3         5 $packpath[-1] .= ".pm";
41 3 50 33     10 if (@packpath == 1 && $packpath[0] eq "readline.pm") {
42 0         0 unshift @packpath, "Term", "ReadLine"; # historical reasons
43             }
44             INCDIR:
45 3         5 foreach my $dir (@INC) {
46 31         137 my $pmfile = File::Spec->catfile($dir,@packpath);
47 31 100       471 if (-f $pmfile){
48 2         4 $inst_file = $pmfile;
49 2         5 last INCDIR;
50             }
51             }
52              
53             # get version from file or else report missing
54 3 100       7 if ( defined $inst_file ) {
55 2         31 $have = my $preliminary_version = MM->parse_version($inst_file);
56 2 100 66     507 $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         5 select DEVNULL; ## no critic
60 2 50       7 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     13 if (! defined $have || $have eq 'undef') {
69 35     35   143 no strict 'refs';
  35         41  
  35         19285  
70 1         1 my $mod_version = ${$mod.'::VERSION'};
  1         4  
71 1 50       2 if (defined $mod_version) {
72 1         2 $have = $mod_version;
73             } else {
74 0         0 $have = 0; # fallback
75             }
76             }
77 2         6 select STDOUT; ## no critic
78             }
79             else {
80 1         4 print "$mod 0 n/a\n";
81 1         2 next;
82             }
83             }
84              
85             # complex requirements are comma separated
86 3         14 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       36 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       1 if (CPAN::Version->vcmp($have,$rq)) {
100 1         18 $passes++; # didn't match
101             }
102 1         1 next RQ;
103             } elsif ($rq =~ s|<=\s*||) {
104 1 50       3 if (! CPAN::Version->vgt($have,$rq)){
105 1         19 $passes++;
106             }
107 1         1 next RQ;
108             } elsif ($rq =~ s|<\s*||) {
109 1 50       3 if (CPAN::Version->vlt($have,$rq)){
110 1         18 $passes++;
111             }
112 1         2 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         95 $passes++;
117             }
118             }
119 3 100       8 my $ok = $passes == @requirements ? 1 : 0;
120 3         12 print "$mod $ok $have\n"
121             }
122 2         16 return;
123             }
124              
125             sub _try_load {
126 8     8   1645 my ($module, $have) = @_;
127              
128 8         39 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         91 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         68 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     58 if ( $module eq 'Module::Install' && $have < 0.95 ) {
    100 66        
    100          
    100          
186 1         12 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         11 return 1;
192             }
193 120         142 elsif ( grep { $_ eq $module } @do_not_load ) {
194 1         11 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         11 return 1;
200             }
201              
202 4 100       12 if ( exists $loading_conflicts{$module} ) {
203 1         2 foreach my $mod1 ( @{ $loading_conflicts{$module} } ) {
  1         4  
204 1         3 my $file = "$mod1.pm";
205 1         3 $file =~ s{::}{/}g;
206 1 50       4 if (exists $INC{$file}) {
207 1         11 return 1;
208             }
209             }
210             }
211              
212 3 100       8 if (exists $load_before{$module}) {
213 1         67 eval "require $load_before{$module};1;";
214             }
215              
216 3         8 my $file = "$module.pm";
217 3         9 $file =~ s{::}{/}g;
218              
219 3         4 return eval {require $file; 1}; ## no critic
  3         643  
  2         423  
220             }
221              
222             1;
223              
224             # ABSTRACT: Modulino for prerequisite tests
225              
226             __END__