File Coverage

blib/lib/App/findeps.pm
Criterion Covered Total %
statement 31 111 27.9
branch 0 76 0.0
condition 0 53 0.0
subroutine 9 16 56.2
pod 0 4 0.0
total 40 260 15.3


line stmt bran cond sub pod time code
1             package App::findeps;
2              
3 1     1   824 use 5.012005;
  1         4  
4 1     1   5 use strict;
  1         2  
  1         19  
5 1     1   5 use warnings;
  1         1  
  1         42  
6              
7             our $VERSION = "0.11";
8              
9 1     1   5 use Carp qw(carp croak);
  1         2  
  1         68  
10 1     1   559 use ExtUtils::Installed;
  1         115153  
  1         46  
11 1     1   8 use List::Util qw(first);
  1         3  
  1         67  
12 1     1   7 use FastGlob qw(glob);
  1         2  
  1         45  
13 1     1   3450 use Module::CoreList;
  1         136024  
  1         11  
14              
15             our $Upgrade = 0;
16             our $myLib = 'lib';
17             our $toCpanfile = 0;
18             my $RE = qr/\w+\.((?i:p[ml]|t|cgi|psgi))$/;
19             my $qr4name = qr/[a-zA-Z][a-zA-Z\d]+(?:::[a-zA-Z\d]+){0,}/;
20              
21             sub scan {
22 1     1 0 1333 my %args = @_;
23 1         2 my %pairs;
24 1         3 while ( my $file = shift @{ $args{files} } ) {
  1         7  
25 0         0 $file =~ $RE;
26 0   0     0 my $ext = $1 || croak 'Unvalid extension was set';
27 0 0       0 open my $fh, '<', $file or die "Can't open < $file: $!";
28 0         0 while (<$fh>) {
29 0         0 chomp;
30 0 0       0 next unless length $_;
31 0 0       0 last if /^__(?:END|DATA)__$/;
32 0 0       0 next if /^\s*#.*$/;
33 0         0 state( $pod, $here, $eval );
34 0 0 0     0 if ( !$pod and /^=(\w+)/ ) {
    0 0        
35 0         0 $pod = $1;
36             } elsif ( $pod and /^=cut$/ ) {
37 0         0 undef $pod;
38 0         0 next;
39             }
40 0 0 0     0 if ( !$here and my @catch = /(?:<<(['"])?(\w+)\1?){1,}/g ) {
    0 0        
41 0         0 $here = $catch[-1];
42             } elsif ( $here and /^$here$/ ) {
43 0         0 undef $here;
44 0         0 next;
45             }
46 0         0 s/\s+#.*$//g;
47 0 0 0     0 if ( !$eval and /eval\s*(['"{])$/ ) {
    0 0        
    0 0        
48 0 0       0 $eval = $1 eq '{' ? '}' : $1;
49             } elsif ( $eval and /$eval(?:.*)?;$/ ) {
50 0         0 undef $eval;
51 0         0 next;
52             } elsif ( $eval and /(require|use)\s+($qr4name)/ ) {
53 0         0 warnIgnored( $2, $1, 'eval' );
54             }
55 0         0 state $if = 0;
56 0 0 0     0 if (/^\s*(?:if|unless)\s*\(.*\)\s*{$/) {
    0 0        
    0          
57 0         0 $if++;
58             } elsif ( $if > 0 and /^\s*}$/ ) {
59 0         0 $if--;
60 0         0 next;
61             } elsif ( $if > 0 and /^\s*(require|use)\s+($qr4name)/ ) {
62 0         0 warnIgnored( $2, $1, 'if' );
63             }
64 0 0 0     0 next if $pod or $here or $eval or $if;
      0        
      0        
65 0         0 scan_line( \%pairs, $_ );
66             }
67 0         0 close $fh;
68             }
69 1         2 my $deps = {};
70 1         4 my @local = &glob("$myLib/*.p[lm]");
71 1         329 while ( my ( $name, $version ) = each %pairs ) {
72 0 0       0 next if !defined $name;
73 0 0       0 next if exists $deps->{$name};
74 0 0   0   0 next if first { $_ =~ /$name\.p[lm]$/ } @local;
  0         0  
75 0 0 0     0 $deps->{$name} = $version if !defined $version or $Upgrade or $toCpanfile;
      0        
76             }
77 1         5 return $deps;
78             }
79              
80             # subroutines #----#----#----#----#----#----#----#----#----#----#----#----#
81             my @pragmas = qw(
82             attributes autodie autouse
83             base bigint bignum bigrat blib bytes
84             charnames constant diagnostics encoding
85             feature fields filetest if integer less lib locale mro
86             open ops overload overloading parent re
87             sigtrap sort strict subs
88             threads threads::shared utf8 vars vmsish
89             warnings warnings::register
90             );
91              
92             sub scan_line {
93 0     0 0   my $pairs = shift;
94 0           local $_ = shift;
95 0           s/#.*$//;
96 0           my @names = ();
97 0 0         return if /^\s*(?:require|use)\s+5\.\d{3}_?\d{3};$/;
98 0 0 0       if (/use\s+(?:base|parent)\s+qw[\("']\s*((?:$qr4name\s*){1,})[\)"']/) {
    0          
    0          
    0          
    0          
    0          
    0          
99 0           push @names, split /\s+/, $1;
100             } elsif (/use\s+(?:base|parent|autouse)\s+(['"])?($qr4name)\1?/) {
101 0           $names[0] = $2;
102             } elsif (/eval\s*(['"{])\s*(require|use)\s+($qr4name).*(?:\1|})/) {
103 0           warnIgnored( $3, $2, 'eval' );
104             } elsif ( /(?:if|unless)\s+\(.*\)\s*\{.*require\s+($qr4name).*\}/
105             or /require\s+($qr4name)\s+(?:if|unless)\s+\(?.*\)?/ )
106             {
107 0           warnIgnored( $1, 'require', 'if' );
108             } elsif (/^\s*(?:require|use)\s+($qr4name)/) {
109 0           $names[0] = $1;
110              
111             } elsif (m!^\s*require\s*(["'])((?:\./)?(?:\w+/){0,}$qr4name\.pm)\1!) {
112 0           $names[0] = _name($2);
113             } elsif (/^\s*(require|use)\s+(['"]?)(.*)\2/) {
114 0           my $name = $3;
115 0 0         my $exists = ( -e "$myLib/$name" ) ? 'exists' : "does not exist in $myLib";
116 0           warn "just detected but not listed: $name($exists) $1d\n";
117             }
118 0           for my $name (@names) {
119 0 0         next unless length $name;
120 0 0         next if exists $pairs->{$name};
121 0 0         next if $name eq 'Plack::Builder';
122 0 0 0       next if $Upgrade and Module::CoreList->is_core($name);
123 0 0   0     next if first { $name eq $_ } @pragmas;
  0            
124 0           $pairs->{$name} = get_version($name);
125             }
126 0           return %$pairs;
127             }
128              
129             sub get_version {
130 0     0 0   my $name = shift;
131 0           my $installed = ExtUtils::Installed->new( skip_cwd => 1 );
132 0     0     my $module = first { $_ eq $name } $installed->modules();
  0            
133 0           my $version = eval { $installed->version($module) };
  0            
134 0 0         return "$version" if $version;
135 0 0         eval "use lib '$myLib'; require $name" or return undef;
136 0   0       return eval "no strict 'subs';\$${name}::VERSION" || 0;
137             }
138              
139             sub warnIgnored {
140 0     0 0   my $name = shift;
141 0           my $func = shift;
142 0           my $cmd = shift;
143 0 0         warn "$name is ${func}d inside of '$cmd'\n" unless Module::CoreList->is_core($name);
144             }
145              
146             sub _name {
147 0     0     my $str = shift;
148 0 0         $str =~ s!/!::!g if $str =~ /\.pm$/;
149 0           $str =~ s!^lib::!!;
150 0           $str =~ s!.pm$!!i;
151 0           $str =~ s!^auto::(.+)::.*!$1!;
152 0           return $str;
153             }
154              
155             1;
156              
157             __END__