File Coverage

bin/report-prereqs
Criterion Covered Total %
statement 136 137 99.2
branch 39 42 92.8
condition 26 30 86.6
subroutine 14 14 100.0
pod n/a
total 215 223 96.4


line stmt bran cond sub pod time code
1             #!perl
2              
3 12     12   7814 use 5.006;
  12         39  
4 12     12   52 use strict;
  12         20  
  12         227  
5 12     12   49 use warnings;
  12         17  
  12         643  
6              
7             our $VERSION = '0.008';
8              
9             package App::ReportPrereqs;
10              
11 12     12   5679 use CPAN::Meta;
  12         342293  
  12         426  
12 12     12   12089 use ExtUtils::MakeMaker ();
  12         1168571  
  12         417  
13 12     12   92 use File::Basename qw(fileparse);
  12         21  
  12         720  
14 12     12   7752 use Getopt::Long qw(GetOptions);
  12         115099  
  12         72  
15 12     12   12793 use HTTP::Tiny 0.014 ();
  12         479639  
  12         470  
16 12     12   97 use List::Util qw(max);
  12         19  
  12         1168  
17 12     12   6032 use Module::CPANfile ();
  12         65525  
  12         434  
18 12     12   5185 use Module::Path qw(module_path);
  12         7354  
  12         745  
19 12     12   87 use version 0.77 ();
  12         195  
  12         12279  
20              
21             if ( !caller ) {
22             my $rc = _main();
23             exit 0 if !defined $rc;
24             exit 2 if $rc == 2;
25             exit 1;
26             }
27              
28             sub _main {
29 39     39   148672 my $cpanfile;
30             my $meta;
31 39         79 my $with_develop = 0;
32 39         60 my @features;
33 39         244 my $getoptions_ok = GetOptions(
34             'cpanfile:s' => \$cpanfile,
35             'meta:s' => \$meta,
36             'with-develop' => \$with_develop,
37             'with-feature=s@' => \@features,
38             );
39              
40 39         17363 my $url = shift @ARGV;
41              
42 39 100 100     626 if (
      100        
      100        
      100        
      100        
      100        
43             # Wrong options used
44             !$getoptions_ok
45              
46             # --cpanfile and --meta cannot be used together
47             || ( defined $cpanfile && defined $meta )
48              
49             # --cpanfile or --meta cannot be used together with a URL
50             || ( ( defined $cpanfile || defined $meta ) && defined $url )
51              
52             # Only at most one URL can be specified
53             || (@ARGV)
54             )
55             {
56 5         8 _usage();
57 5         21 return 2;
58             }
59              
60 34         71 my $prereqs;
61             my $source;
62 34 100       78 if ( defined $meta ) {
63 20 100       62 if ( $meta eq q{} ) {
64 11         25 $meta = 'META.json';
65             }
66 20         40 $source = $meta;
67              
68 20         28 local $@; ## no critic (Variables::RequireInitializationForLocalVars)
69 20 100       38 if ( !eval { $prereqs = CPAN::Meta->load_file($meta)->effective_prereqs( \@features ); 1 } ) {
  20         205  
  15         249599  
70 5         41341 my $error = $@;
71 5         11 print {*STDERR} "Cannot read meta file '$meta': $error\n";
  5         345  
72 5         71 return 1;
73             }
74             }
75             else {
76 14 100       44 if ( defined $url ) {
77 2         4 $source = $url;
78              
79 2         17 my $res = HTTP::Tiny->new->get($source);
80 2 100       241 if ( !$res->{success} ) {
81 1         2 print {*STDERR} $res->{content};
  1         89  
82 1         12 return 1;
83             }
84              
85 1         3 $cpanfile = \$res->{content};
86             }
87             else {
88 12 50 33     70 if ( !defined $cpanfile || $cpanfile eq q{} ) {
89 12         31 $cpanfile = 'cpanfile';
90             }
91 12         16 $source = $cpanfile;
92             }
93              
94 13         24 local $@; ## no critic (Variables::RequireInitializationForLocalVars)
95 13 100       22 if ( !eval { $prereqs = Module::CPANfile->load($cpanfile)->prereqs_with(@features); 1; } ) {
  13         109  
  10         26444  
96 3         852 my $error = $@;
97 3         4 print {*STDERR} "Cannot read cpanfile file '$cpanfile': $error\n";
  3         222  
98 3         32 return 1;
99             }
100             }
101              
102             # ---
103 25         93 my @full_reports;
104             my @dep_errors;
105              
106             PHASE:
107 25         76 for my $phase (qw(configure build test runtime develop)) {
108 125 100 100     366 next PHASE if ( $phase eq 'develop' ) and ( !$with_develop );
109              
110             TYPE:
111 104         152 for my $type (qw(requires recommends suggests conflicts)) {
112 416         766 my $req_ref = $prereqs->requirements_for( $phase, $type )->as_string_hash;
113 416         23019 my @modules = grep { $_ ne 'perl' } keys %{$req_ref};
  153         250  
  416         657  
114 416 100       980 next TYPE if !@modules;
115              
116 65         204 my $title = "\u$phase \u$type";
117 65         120 my @reports = ( [qw(Module Want Have)] );
118              
119             MODULE:
120 65         209 for my $module ( sort @modules ) {
121 129         206 my $want = $req_ref->{$module};
122 129 50       315 if ( !defined $want ) {
    100          
123 0         0 $want = 'undef';
124             }
125             elsif ( $want eq '0' ) {
126 120         175 $want = 'any';
127             }
128              
129 129 100       201 my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required";
130              
131 129         318 my $mod_path = module_path($module);
132              
133 129 100       45938 if ( defined $mod_path ) {
134 66         448 my $have = MM->parse_version($mod_path); ## no critic (Modules::RequireExplicitInclusion)
135              
136             # This validation was added in EUMM 7.47_01 in ExtUtils::MM_Unix
137             # We use the same validation to make the file testable - otherwise the
138             # result depends on the version of EUMM used.
139 66 100 66     19448 if ( ( !defined $have )
      66        
140             or ( $have !~ m{ ^ v? [0-9_\.\-]+ $ }xsm )
141 60         944 or ( !eval { version->parse($have) } ) )
142             {
143 6         17 $have = 'undef';
144             }
145              
146 66         324 push @reports, [ $module, $want, $have ];
147              
148 66 100       213 next MODULE if $type ne 'requires';
149              
150 48 100       91 if ( $have eq 'undef' ) {
151 6         20 push @dep_errors, "$module version unknown ($req_string)";
152 6         16 next MODULE;
153             }
154              
155 42 100       133 if ( !$prereqs->requirements_for( $phase, $type )->accepts_module( $module => $have ) ) {
156 3         308 push @dep_errors, "$module version '$have' is not in required range '$want'";
157 3         9 next MODULE;
158             }
159              
160 39         3483 next MODULE;
161             }
162              
163 63         199 push @reports, [ $module, $want, 'missing' ];
164              
165 63 100       153 next MODULE if $type ne 'requires';
166              
167 45         144 push @dep_errors, "$module is not installed ($req_string)";
168             }
169              
170 65         157 push @full_reports, "=== $title ===\n\n";
171              
172 65         114 my $ml = max( map { length $_->[0] } @reports );
  194         434  
173 65         101 my $wl = max( map { length $_->[1] } @reports );
  194         243  
174 65         94 my $hl = max( map { length $_->[2] } @reports );
  194         259  
175              
176 65         278 splice @reports, 1, 0, [ q{-} x $ml, q{-} x $wl, q{-} x $hl ];
177 65         105 push @full_reports, map { sprintf " %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2] } @reports;
  259         929  
178              
179 65         299 push @full_reports, "\n";
180             }
181             }
182              
183 25 50       69 if (@full_reports) {
184 25         1756 print "Versions for all modules listed in $source:\n\n", @full_reports;
185             }
186              
187 25 100       142 if (@dep_errors) {
188 22         246 print "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n\n";
189 22         198 print "The following REQUIRED prerequisites were not satisfied:\n\n";
190              
191 22         69 for my $error (@dep_errors) {
192 54         487 print $error, "\n";
193             }
194             }
195              
196 25         581 return;
197             }
198              
199             sub _usage {
200 5     5   132 my $basename = fileparse($0);
201              
202 5         9 print {*STDERR} "usage: $basename [--with-develop] [--with-feature ] [URL]\n";
  5         201  
203 5         19 print {*STDERR} " $basename [--with-develop] [--with-feature ] [--cpanfile []]\n";
  5         62  
204 5         15 print {*STDERR} " $basename [--with-develop] [--with-feature ] [--meta [|]]\n";
  5         50  
205              
206 5         16 return;
207             }
208              
209             1;
210              
211             __END__