File Coverage

blib/lib/B/PerlReq.pm
Criterion Covered Total %
statement 25 221 11.3
branch 2 148 1.3
condition 0 60 0.0
subroutine 9 28 32.1
pod 0 18 0.0
total 36 475 7.5


line stmt bran cond sub pod time code
1             # From `The UNIX-HATERS Handbook', p.55:
2             #
3             # Anyone who had both access to the source code and the
4             # inclination to read it soon found themselves in for a rude
5             # surprise:
6             #
7             # /* You are not expected to understand this */
8             #
9             # Although this comment originally appeared in the Unix V6 kernel
10             # source code, it could easily have applied to any of the original
11             # AT&T code, which was a nightmare of in-line hand-optimizations
12             # and micro hacks.
13              
14             package B::PerlReq;
15             our $VERSION = '0.82';
16              
17 1     1   25478 use 5.006;
  1         3  
  1         40  
18 1     1   4 use strict;
  1         1  
  1         26  
19 1     1   440 use PerlReq::Utils qw(mod2path path2dep verf verf_perl sv_version);
  1         2  
  1         277  
20              
21             our @Skip = (
22             qr(^Makefile\b),
23             # OS-specific
24             qr(^machine/ansi\b), # gcc 3.3 stddef.h (FreeBSD 4)
25             qr(^sys/_types\b), # gcc 3.3 stddef.h (FreeBSD 5)
26             qr(^sys/systeminfo\b), # solaris
27             qr(^Convert/EBCDIC\b), # os390
28             qr(^ExtUtils/XSSymSet\b), # VMS
29             qr(\bOS2|OS2\b),
30             qr(\bMacPerl|\bMac\b),
31             qr(\bMacOS|MacOS\b),
32             qr(\bMacOSX|MacOSX\b),
33             qr(\bvmsish\b),
34             qr(\bVMS|VMS\b),
35             qr(\bWin32|Win32\b),
36             qr(\bCygwin|Cygwin\b),
37             # most common
38             qr(^Carp\.pm$),
39             qr(^Exporter\.pm$),
40             qr(^strict\.pm$),
41             qr(^vars\.pm$),
42             qr(^warnings\.pm$),
43             );
44              
45             our ($Strict, $Relaxed, $Verbose, $Debug);
46              
47 1     1   449 use B::Walker qw(const_sv);
  1         3  
  1         2721  
48              
49             sub RequiresPerl ($) {
50 0     0 0   my $v = shift;
51 0           my $dep = "perl-base >= " . verf_perl($v);
52 0           my $msg = "$dep at line $B::Walker::Line (depth $B::Walker::Level)";
53 0 0 0       if (not $Strict and $v < 5.010) {
54 0 0         print STDERR "# $msg old perl SKIP\n" if $Verbose;
55 0           return;
56             }
57 0 0         print STDERR "# $msg REQ\n" if $Verbose;
58 0           print "$dep\n";
59             }
60              
61             # XXX prevDepF is a hack to please t/01-B-PerlReq.t
62             my $prevDepF;
63              
64             sub Requires ($;$) {
65 0     0 0   my ($f, $v) = @_;
66 0 0         my $dep = path2dep($f) . ($v ? " >= " . verf($v) : "");
67 0           my $msg = "$dep at line $B::Walker::Line (depth $B::Walker::Level)";
68 0 0         if ($f !~ m#^\w+(?:[/-]\w+)*[.]p[lmh]$#) { # bits/ioctl-types.ph
69 0           print STDERR "# $msg invalid SKIP\n";
70 0           return;
71             }
72 0 0 0       if ($B::Walker::Sub eq "BEGIN" and not $INC{$f} and $B::Walker::Opname ne "autouse") {
      0        
73 0           print STDERR "# $msg not loaded at BEGIN SKIP\n";
74 0           return;
75             }
76 0 0 0       if (not $Strict and grep { $f =~ $_ } @Skip) {
  0            
77 0 0         print STDERR "# $msg builtin SKIP\n" if $Verbose;
78 0           return;
79             }
80 0 0 0       if ($B::Walker::Sub eq "BEGIN" and $INC{$f}) {
81 0           goto req;
82             }
83 0 0 0       if (not $Strict and $B::Walker::BlockData{Eval}) {
84 0           print STDERR "# $msg inside eval SKIP\n";
85 0           return;
86             }
87 0 0 0       if ($Relaxed and $B::Walker::Level > 4) {
88 0           print STDERR "# $msg deep SKIP\n";
89 0           return;
90             }
91 0 0         req: print STDERR "# $msg REQ\n" if $Verbose;
92 0 0 0       if ($prevDepF and $prevDepF ne $f) {
93 0           print path2dep($prevDepF) . "\n";
94             }
95 0           undef $prevDepF;
96 0 0         if ($v) {
97 0           print "$dep\n";
98             } else {
99 0           $prevDepF = $f;
100             }
101             }
102             sub finalize {
103 0 0   0 0   print path2dep($prevDepF) . "\n"
104             if $prevDepF;
105             }
106              
107             sub check_encoding ($) {
108 0     0 0   my $enc = shift;
109 0 0         eval { local $SIG{__DIE__}; require Encode; } or do {
  0            
  0            
110 0           print STDERR "Encode.pm not available at $0 line $B::Walker::Line\n";
111 0           return;
112             };
113 0 0         my $e = Encode::resolve_alias($enc) or do {
114 0           print STDERR "invalid encoding $enc at $0 line $B::Walker::Line\n";
115 0           return;
116             };
117 0 0 0       my $mod = $Encode::ExtModule{$e} || $Encode::ExtModule{lc($e)} or do {
118 0           print STDERR "no module for encoding $enc at $0 line $B::Walker::Line\n";
119 0           return;
120             };
121 0           Requires(mod2path($mod));
122             }
123              
124             sub check_perlio_string ($) {
125 0     0 0   local $_ = shift;
126 0           while (s/\b(\w+)[(](\S+?)[)]//g) {
127 0           Requires("PerlIO.pm");
128 0           Requires("PerlIO/$1.pm");
129 0 0         if ($1 eq "encoding") {
130 0           Requires("Encode.pm");
131 0           check_encoding($2);
132             }
133             }
134             }
135              
136             sub grok_perlio ($) {
137 0     0 0   my $op = shift;
138 0           my $opname = $op->name;
139 0 0         $op = $op->first; return unless $$op; # pushmark
  0            
140 0 0         $op = $op->sibling; return unless $$op; # gv[*FH] -- arg1
  0            
141 0 0 0       $op = $op->sibling; return unless $$op and $op->name eq "const";
  0            
142 0 0         my $sv = const_sv($op); return unless $sv->can("PV");
  0            
143 0           local $B::Walker::Opname = $opname;
144 0           my $arg2 = $sv->PV; $arg2 =~ s/\s//g;
  0            
145 0 0         if ($opname eq "open") {
146 0 0         return unless $arg2 =~ s/^[+]?[<>]+//; # validate arg2
147 0 0         $op = $op->sibling; return unless $$op; # arg3 required
  0            
148 0 0         if ($op->name eq "srefgen") { # check arg3
149 0           Requires("PerlIO.pm");
150 0           Requires("PerlIO/scalar.pm");
151             }
152             }
153 0           check_perlio_string($arg2);
154             }
155              
156             sub grok_require ($) {
157 0     0 0   my $op = shift;
158 0 0         return unless $op->first->name eq "const";
159 0           my $sv = const_sv($op->first);
160 0           my $v = sv_version($sv);
161 0 0         defined($v)
162             ? RequiresPerl($v)
163             : Requires($sv->PV)
164             ;
165             }
166              
167             sub grok_args ($) {
168 0     0 0   my $op = shift;
169 0           my @args;
170 0   0       while ($$op and $op->name eq "const") {
171 0           my $sv = const_sv($op);
172 0           my $arg;
173 0 0         if (ref($sv) eq "B::SPECIAL") {
174 0 0         if ($$sv == ${B::sv_yes()}) {
  0 0          
  0            
175 0           $arg = (1 == 1);
176             }
177             elsif ($$sv == ${B::sv_no()}) {
178 0           $arg = (1 == 0);
179             }
180             }
181             else {
182 0           $arg = ${$sv->object_2svref};
  0            
183             }
184 0           push @args, $arg;
185 0           $op = $op->sibling;
186             }
187 0           return @args;
188             }
189              
190             sub grok_import ($$$) {
191 0     0 0   my ($class, undef, $op) = @_;
192 0 0         my @args = grok_args($op) or return;
193 0           local $B::Walker::Opname = $class;
194 0 0 0       if ($class eq "base" or $class eq "parent") {
    0          
    0          
    0          
    0          
    0          
195 0           foreach my $m (@args) {
196 0           my $f = mod2path($m);
197             # XXX Requires($f) if $INC{$f};
198 0           foreach (@INC) {
199 0 0         if (-f "$_/$f") {
200 0           Requires($f);
201 0           last;
202             }
203             }
204             }
205             }
206             elsif ($class eq "autouse") {
207 0           my $f = mod2path($args[0]);
208 0           Requires($f);
209             }
210             elsif ($class eq "encoding") {
211 0           require Config;
212 0 0         Requires("PerlIO/encoding.pm") if $Config::Config{useperlio};
213 0 0         check_encoding($args[0]) if $args[0] =~ /^[^:]/;
214 0 0         Requires("Filter/Util/Call.pm") if grep { $_ eq "Filter" } @args;
  0            
215             }
216             elsif ($class eq "overload") {
217             # avoid version check for << use overload "0+" => ... >>
218             }
219             elsif ($class eq "if") {
220 0           my $f = mod2path($args[1]);
221 0 0         Requires($f) if $args[0];
222             }
223             elsif ($args[0] =~ /^\d/) {
224             # the first import arg is possibly a version, see Exporter/Heavy.pm
225 0           my $sv = const_sv($op);
226 0           my $v = sv_version($sv);
227 0           my $f = mod2path($class);
228 0 0         Requires($f, $v) if $v;
229             }
230             }
231              
232             sub grok_version ($$$) {
233 0     0 0   my ($class, undef, $op) = @_;
234 0 0         return unless $op->name eq "const";
235 0           my $sv = const_sv($op);
236 0           my $version = sv_version($sv);
237 0 0         return unless $version;
238 0           my $f = mod2path($class);
239 0           local $B::Walker::Opname = "version";
240 0           Requires($f, $version);
241             }
242              
243             sub grok_new {
244 0     0 0   my ($class, undef, $op) = @_;
245 0 0         if ($class eq "IO::File") {
246 0 0         if ($op->name eq "srefgen") {
247 0           Requires("PerlIO.pm");
248 0           Requires("PerlIO/scalar.pm");
249             }
250             }
251             }
252              
253             our %methods = (
254             'import' => \&grok_import,
255             'VERSION' => \&grok_version,
256             'require_version' => \&grok_version,
257             'new' => \&grok_new,
258             );
259              
260             sub grok_with {
261 0 0   0 0   return unless $INC{"Moose.pm"};
262 0           my (undef, $op) = @_;
263 0           my @args = grok_args($op);
264 0           for my $m (@args) {
265 0 0         next unless $m =~ /^\w+(?:::\w+)+\z/;
266 0           my $f = mod2path($m);
267 0           Requires($f);
268             }
269             }
270              
271             my %TryCV;
272              
273             sub grok_try {
274 0 0   0 0   return unless $INC{"Try/Tiny.pm"};
275 0           my (undef, $op) = @_;
276 0 0         return unless $op->name eq "refgen";
277 0           $op = $op->first->first->sibling;
278 0 0         return unless $op->name eq "anoncode";
279 0           my $cv = padval($op->targ);
280 0           $TryCV{$$cv} = 1;
281             }
282              
283             sub grok_catch {
284             # suppress nested catch/finally deps
285 0 0   0 0   &grok_try if $TryCV{$$B::Walker::CV};
286             }
287              
288             our %funcs = (
289             'with' => \&grok_with,
290             'try' => \&grok_try,
291             'catch' => \&grok_catch,
292             'finally' => \&grok_catch,
293             );
294              
295             sub grok_entersub ($) {
296 0     0 0   my $op = shift;
297 0           $op = $op->first;
298 0 0         $op = $op->first unless ${$op->sibling};
  0            
299             # die "not pushmark" unless $op->name eq "pushmark";
300 0           my $args = $op = $op->sibling;
301 0           while (${$op->sibling}) {
  0            
302 0 0 0       last if $op->name eq "method" or
303             $op->name eq "method_named";
304 0           $op = $op->sibling;
305             }
306 0 0         if ($op->name eq "method_named") {
    0          
307 0           my $method = const_sv($op)->PV;
308 0 0         return unless $methods{$method};
309 0 0         return unless $args->name eq "const";
310 0           my $sv = const_sv($args);
311 0 0         return unless $sv->can("PV");
312 0           my $class = $sv->PV;
313 0           $args = $args->sibling;
314 0           $methods{$method}->($class, $method, $args);
315             }
316             elsif ($op->first->name eq "gv") {
317 0           $op = $op->first;
318 1     1   8 use B::Walker qw(padval);
  1         3  
  1         137  
319 0           my $func = padval($op->padix)->NAME;
320 0 0         return unless $funcs{$func};
321 0           $funcs{$func}->($func, $args);
322             }
323             }
324              
325             sub grok_padsv {
326 0     0 0   my $op = shift;
327 1     1   4 use B qw(OPpLVAL_INTRO);
  1         2  
  1         75  
328 0 0         return unless $op->private & OPpLVAL_INTRO;
329 1     1   5 use B::Walker qw(padname);
  1         2  
  1         116  
330 0           my $padsv = padname($op->targ);
331 0 0         return unless $padsv->can('PV');
332 0 0         RequiresPerl(5.010) if $padsv->PV eq '$_';
333 1 50       942 use constant OPpPAD_STATE =>
334 1     1   5 defined &B::OPpPAD_STATE ? &B::OPpPAD_STATE : 0;
  1         2  
335 0 0         RequiresPerl(5.010) if $op->private & OPpPAD_STATE;
336             }
337              
338             my %filetests = map { $_ => 1 }
339             qw(ftrread ftrwrite ftrexec fteread ftewrite fteexec ftis ftsize
340             ftmtime ftatime ftctime ftrowned fteowned ftzero ftsock ftchr ftblk
341             ftfile ftdir ftpipe ftsuid ftsgid ftsvtx ftlink fttty fttext ftbinary);
342              
343             sub grok_filetest {
344 0     0 0   my $op = shift;
345 0 0         return unless $filetests{$op->next->name};
346 0 0         return if $filetests{$op->first->name};
347 0           RequiresPerl(5.010);
348             }
349              
350             %B::Walker::Ops = (
351             'require' => \&grok_require,
352             'dofile' => \&grok_require,
353             'entersub' => \&grok_entersub,
354             'open' => \&grok_perlio,
355             'binmode' => \&grok_perlio,
356             'dbmopen' => sub { Requires("AnyDBM_File.pm") },
357             'leavetry' => sub { $B::Walker::BlockData{Eval} = $B::Walker::Level },
358             'leavesub' => sub { $B::Walker::BlockData{Eval} = $B::Walker::Level if $TryCV{$$B::Walker::CV} },
359             'leave' => sub { $B::Walker::BlockData{Eval} = $B::Walker::Level if $TryCV{$$B::Walker::CV} },
360             'dor' => sub { RequiresPerl(5.010) },
361             'dorassign' => sub { RequiresPerl(5.010) },
362             'leavegiven' => sub { RequiresPerl(5.010) },
363             'leavewhen' => sub { RequiresPerl(5.010) },
364             'smartmatch' => sub { RequiresPerl(5.010) },
365             'say' => sub { RequiresPerl(5.010) },
366             'padsv' => \&grok_padsv,
367              
368             map { $_ => \&grok_filetest } keys %filetests,
369             );
370              
371             sub compile {
372 0     0 0   my $pkg = __PACKAGE__;
373 0           for my $opt (@_) {
374 0 0 0       $opt =~ /^-(?:s|-?strict)$/ and $Strict = 1 or
      0        
      0        
      0        
      0        
      0        
      0        
375             $opt =~ /^-(?:r|-?relaxed)$/ and $Relaxed = 1 or
376             $opt =~ /^-(?:v|-?verbose)$/ and $Verbose = 1 or
377             $opt =~ /^-(?:d|-?debug)$/ and $Verbose = $Debug = 1 or
378             die "$pkg: unknown option: $opt\n";
379             }
380 0 0 0       die "$pkg: options -strict and -relaxed are mutually exclusive\n"
381             if $Strict and $Relaxed;
382             return sub {
383 0     0     $| = 1;
384             local $SIG{__DIE__} = sub {
385             # checking $^S is unreliable because O.pm uses eval
386 0           print STDERR "dying at $0 line $B::Walker::Line\n";
387 0           require Carp;
388 0           Carp::cluck();
389 0           };
390 0           B::Walker::walk_blocks();
391 0           B::Walker::walk_main();
392 0 0         B::Walker::walk_subs() if not $Relaxed;
393 0           finalize();
394 0           };
395             }
396              
397             END {
398 1 50   1   6434559 print STDERR "# Eval=$B::Walker::BlockData{Eval}\n" if $B::Walker::BlockData{Eval};
399             }
400              
401             1;
402              
403             __END__