| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #!perl | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 2 |  |  | 2 |  | 1974 | use 5.006; | 
|  | 2 |  |  |  |  | 7 |  | 
| 4 | 2 |  |  | 2 |  | 9 | use strict; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 45 |  | 
| 5 | 2 |  |  | 2 |  | 8 | use warnings; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 125 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | our $VERSION = '0.006'; | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | package App::ReportPrereqs; | 
| 10 |  |  |  |  |  |  |  | 
| 11 | 2 |  |  | 2 |  | 1798 | use ExtUtils::MakeMaker (); | 
|  | 2 |  |  |  |  | 229304 |  | 
|  | 2 |  |  |  |  | 77 |  | 
| 12 | 2 |  |  | 2 |  | 19 | use File::Basename qw(fileparse); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 119 |  | 
| 13 | 2 |  |  | 2 |  | 1552 | use Getopt::Long qw(GetOptions); | 
|  | 2 |  |  |  |  | 22136 |  | 
|  | 2 |  |  |  |  | 11 |  | 
| 14 | 2 |  |  | 2 |  | 2006 | use HTTP::Tiny 0.014 (); | 
|  | 2 |  |  |  |  | 100433 |  | 
|  | 2 |  |  |  |  | 80 |  | 
| 15 | 2 |  |  | 2 |  | 18 | use List::Util qw(max); | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 212 |  | 
| 16 | 2 |  |  | 2 |  | 1198 | use Module::CPANfile (); | 
|  | 2 |  |  |  |  | 29963 |  | 
|  | 2 |  |  |  |  | 63 |  | 
| 17 | 2 |  |  | 2 |  | 1035 | use Module::Path qw(module_path); | 
|  | 2 |  |  |  |  | 1342 |  | 
|  | 2 |  |  |  |  | 130 |  | 
| 18 | 2 |  |  | 2 |  | 14 | use version 0.77 (); | 
|  | 2 |  |  |  |  | 41 |  | 
|  | 2 |  |  |  |  | 1842 |  | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | if ( !caller ) { | 
| 21 |  |  |  |  |  |  | my $rc = _main(); | 
| 22 |  |  |  |  |  |  | exit 0 if !defined $rc; | 
| 23 |  |  |  |  |  |  | exit 2 if $rc == 2; | 
| 24 |  |  |  |  |  |  | exit 1; | 
| 25 |  |  |  |  |  |  | } | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | sub _main { | 
| 28 | 16 |  |  | 16 |  | 72875 | my $with_develop = 0; | 
| 29 | 16 |  |  |  |  | 48 | my @features; | 
| 30 | 16 | 100 |  |  |  | 77 | return _usage() if !GetOptions( 'with-develop' => \$with_develop, 'with-feature=s@' => \@features ); | 
| 31 |  |  |  |  |  |  |  | 
| 32 | 15 |  |  |  |  | 3261 | my $cpanfile     = 'cpanfile'; | 
| 33 | 15 |  |  |  |  | 24 | my $cpanfile_src = $cpanfile; | 
| 34 |  |  |  |  |  |  |  | 
| 35 | 15 | 100 |  |  |  | 41 | if ( @ARGV == 1 ) { | 
|  |  | 100 |  |  |  |  |  | 
| 36 | 2 |  |  |  |  | 4 | $cpanfile_src = $ARGV[0]; | 
| 37 |  |  |  |  |  |  |  | 
| 38 | 2 |  |  |  |  | 41 | my $res = HTTP::Tiny->new->get($cpanfile_src); | 
| 39 | 2 | 100 |  |  |  | 310 | if ( !$res->{success} ) { | 
| 40 | 1 |  |  |  |  | 3 | print {*STDERR} $res->{content}; | 
|  | 1 |  |  |  |  | 64 |  | 
| 41 | 1 |  |  |  |  | 12 | return 1; | 
| 42 |  |  |  |  |  |  | } | 
| 43 |  |  |  |  |  |  |  | 
| 44 | 1 |  |  |  |  | 3 | $cpanfile = \$res->{content}; | 
| 45 |  |  |  |  |  |  | } | 
| 46 |  |  |  |  |  |  | elsif (@ARGV) { | 
| 47 | 1 |  |  |  |  | 8 | return _usage(); | 
| 48 |  |  |  |  |  |  | } | 
| 49 |  |  |  |  |  |  |  | 
| 50 | 13 |  |  |  |  | 18 | my $prereqs; | 
| 51 | 13 | 100 |  |  |  | 15 | if ( !eval { $prereqs = Module::CPANfile->load($cpanfile)->prereqs_with(@features); 1; } ) { | 
|  | 13 |  |  |  |  | 99 |  | 
|  | 10 |  |  |  |  | 28623 |  | 
| 52 | 3 |  |  |  |  | 1232 | my $error = $@; | 
| 53 | 3 |  |  |  |  | 5 | print {*STDERR} "\n$error\n"; | 
|  | 3 |  |  |  |  | 102 |  | 
| 54 | 3 |  |  |  |  | 29 | return 1; | 
| 55 |  |  |  |  |  |  | } | 
| 56 |  |  |  |  |  |  |  | 
| 57 | 10 |  |  |  |  | 25 | my @full_reports; | 
| 58 |  |  |  |  |  |  | my @dep_errors; | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | PHASE: | 
| 61 | 10 |  |  |  |  | 21 | for my $phase (qw(configure build test runtime develop)) { | 
| 62 | 50 | 100 | 100 |  |  | 139 | next PHASE if ( $phase eq 'develop' ) and ( !$with_develop ); | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | TYPE: | 
| 65 | 42 |  |  |  |  | 57 | for my $type (qw(requires recommends suggests conflicts)) { | 
| 66 | 168 |  |  |  |  | 331 | my $req_ref = $prereqs->requirements_for( $phase, $type )->as_string_hash; | 
| 67 | 168 |  |  |  |  | 10469 | my @modules = grep { $_ ne 'perl' } keys %{$req_ref}; | 
|  | 74 |  |  |  |  | 132 |  | 
|  | 168 |  |  |  |  | 286 |  | 
| 68 | 168 | 100 |  |  |  | 425 | next TYPE if !@modules; | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 30 |  |  |  |  | 92 | my $title   = "\u$phase \u$type"; | 
| 71 | 30 |  |  |  |  | 59 | my @reports = ( [qw(Module Want Have)] ); | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | MODULE: | 
| 74 | 30 |  |  |  |  | 82 | for my $module ( sort @modules ) { | 
| 75 | 62 |  |  |  |  | 87 | my $want = $req_ref->{$module}; | 
| 76 | 62 | 50 |  |  |  | 158 | if ( !defined $want ) { | 
|  |  | 100 |  |  |  |  |  | 
| 77 | 0 |  |  |  |  | 0 | $want = 'undef'; | 
| 78 |  |  |  |  |  |  | } | 
| 79 |  |  |  |  |  |  | elsif ( $want eq '0' ) { | 
| 80 | 58 |  |  |  |  | 68 | $want = 'any'; | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 62 | 100 |  |  |  | 105 | my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 62 |  |  |  |  | 158 | my $mod_path = module_path($module); | 
| 86 |  |  |  |  |  |  |  | 
| 87 | 62 | 100 |  |  |  | 30679 | if ( defined $mod_path ) { | 
| 88 | 31 |  |  |  |  | 210 | my $have = MM->parse_version($mod_path);    ## no critic (Modules::RequireExplicitInclusion) | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | # This validation was added in EUMM 7.47_01 in ExtUtils::MM_Unix | 
| 91 |  |  |  |  |  |  | # We use the same validation to make the file testable - otherwise the | 
| 92 |  |  |  |  |  |  | # result depends on the version of EUMM used. | 
| 93 | 31 | 100 | 66 |  |  | 9731 | if (   ( !defined $have ) | 
|  |  |  | 66 |  |  |  |  | 
| 94 |  |  |  |  |  |  | or ( $have !~ m{ ^ v? [0-9_\.\-]+ $ }xsm ) | 
| 95 | 29 |  |  |  |  | 490 | or ( !eval { version->parse($have) } ) ) | 
| 96 |  |  |  |  |  |  | { | 
| 97 | 2 |  |  |  |  | 4 | $have = 'undef'; | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 31 |  |  |  |  | 162 | push @reports, [ $module, $want, $have ]; | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 31 | 100 |  |  |  | 87 | next MODULE if $type ne 'requires'; | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 22 | 100 |  |  |  | 37 | if ( $have eq 'undef' ) { | 
| 105 | 2 |  |  |  |  | 7 | push @dep_errors, "$module version unknown ($req_string)"; | 
| 106 | 2 |  |  |  |  | 6 | next MODULE; | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  |  | 
| 109 | 20 | 100 |  |  |  | 70 | if ( !$prereqs->requirements_for( $phase, $type )->accepts_module( $module => $have ) ) { | 
| 110 | 1 |  |  |  |  | 90 | push @dep_errors, "$module version '$have' is not in required range '$want'"; | 
| 111 | 1 |  |  |  |  | 4 | next MODULE; | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 19 |  |  |  |  | 1864 | next MODULE; | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 31 |  |  |  |  | 115 | push @reports, [ $module, $want, 'missing' ]; | 
| 118 |  |  |  |  |  |  |  | 
| 119 | 31 | 100 |  |  |  | 72 | next MODULE if $type ne 'requires'; | 
| 120 |  |  |  |  |  |  |  | 
| 121 | 22 |  |  |  |  | 75 | push @dep_errors, "$module is not installed ($req_string)"; | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 30 |  |  |  |  | 76 | push @full_reports, "=== $title ===\n\n"; | 
| 125 |  |  |  |  |  |  |  | 
| 126 | 30 |  |  |  |  | 50 | my $ml = max( map { length $_->[0] } @reports ); | 
|  | 92 |  |  |  |  | 207 |  | 
| 127 | 30 |  |  |  |  | 49 | my $wl = max( map { length $_->[1] } @reports ); | 
|  | 92 |  |  |  |  | 121 |  | 
| 128 | 30 |  |  |  |  | 37 | my $hl = max( map { length $_->[2] } @reports ); | 
|  | 92 |  |  |  |  | 119 |  | 
| 129 |  |  |  |  |  |  |  | 
| 130 | 30 |  |  |  |  | 102 | splice @reports, 1, 0, [ q{-} x $ml, q{-} x $wl, q{-} x $hl ]; | 
| 131 | 30 |  |  |  |  | 49 | push @full_reports, map { sprintf "    %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2] } @reports; | 
|  | 122 |  |  |  |  | 401 |  | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 30 |  |  |  |  | 144 | push @full_reports, "\n"; | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 | 10 | 50 |  |  |  | 25 | if (@full_reports) { | 
| 138 | 10 |  |  |  |  | 348 | print "Versions for all modules listed in $cpanfile_src:\n\n", @full_reports; | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 10 | 100 |  |  |  | 54 | if (@dep_errors) { | 
| 142 | 9 |  |  |  |  | 96 | print "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n\n"; | 
| 143 | 9 |  |  |  |  | 84 | print "The following REQUIRED prerequisites were not satisfied:\n\n"; | 
| 144 |  |  |  |  |  |  |  | 
| 145 | 9 |  |  |  |  | 26 | for my $error (@dep_errors) { | 
| 146 | 25 |  |  |  |  | 236 | print $error, "\n"; | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  |  | 
| 150 | 10 |  |  |  |  | 209 | return; | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | sub _usage { | 
| 154 | 2 |  |  | 2 |  | 789 | my $basename = fileparse($0); | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 2 |  |  |  |  | 6 | print {*STDERR} "usage: $basename [--with-{develop,feature=id}] [URL]\n"; | 
|  | 2 |  |  |  |  | 51 |  | 
| 157 | 2 |  |  |  |  | 27 | return 2; | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | 1; | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | __END__ |