File Coverage

blib/lib/Module/Metadata.pm
Criterion Covered Total %
statement 335 339 98.8
branch 141 184 76.6
condition 71 102 69.6
subroutine 52 52 100.0
pod 15 15 100.0
total 614 692 88.7


line stmt bran cond sub pod time code
1             # -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*-
2             # vim:ts=8:sw=2:et:sta:sts=2:tw=78
3             package Module::Metadata; # git description: v1.000035-3-gaa51be1
4             # ABSTRACT: Gather package and POD information from perl module files
5              
6             # Adapted from Perl-licensed code originally distributed with
7             # Module-Build by Ken Williams
8              
9             # This module provides routines to gather information about
10             # perl modules (assuming this may be expanded in the distant
11             # parrot future to look at other types of modules).
12              
13 111     111   10473 sub __clean_eval { eval $_[0] }
  6     7   48  
  6     5   12  
  6     5   41  
  4     5   29  
  4     5   19  
  4     4   19  
  4     4   33  
  4     4   19  
  4     4   18  
  4     1   29  
  4     1   9  
  4     1   21  
  4     1   31  
  4         10  
  4         19  
  3         22  
  3         7  
  3         23  
  3         21  
  3         6  
  3         27  
  3         22  
  3         7  
  3         14  
  3         30  
  3         7  
  3         16  
14 10     10   481229 use strict;
  10         97  
  10         240  
15 10     10   54 use warnings;
  10         27  
  10         378  
16              
17             our $VERSION = '1.000036';
18              
19 10     10   63 use Carp qw/croak/;
  10         40  
  10         403  
20 10     10   66 use File::Spec;
  10         27  
  10         657  
21             BEGIN {
22             # Try really hard to not depend ony any DynaLoaded module, such as IO::File or Fcntl
23             eval {
24 10         56 require Fcntl; Fcntl->import('SEEK_SET'); 1;
  10         273  
  10         282  
25 2         4 } or *SEEK_SET = sub { 0 }
26 10 50   10   40 }
27 10     10   3629 use version 0.87;
  10         15609  
  10         68  
28             BEGIN {
29 10 50   10   1207 if ($INC{'Log/Contextual.pm'}) {
30 2         19 require "Log/Contextual/WarnLogger.pm"; # Hide from AutoPrereqs
31 2         5 Log::Contextual->import('log_info',
32             '-default_logger' => Log::Contextual::WarnLogger->new({ env_prefix => 'MODULE_METADATA', }),
33             );
34             }
35             else {
36 10     2   196 *log_info = sub (&) { warn $_[0]->() };
  2         15  
37             }
38             }
39 10     10   426 use File::Find qw(find);
  10         41  
  10         36166  
40              
41             my $V_NUM_REGEXP = qr{v?[0-9._]+}; # crudely, a v-string or decimal
42              
43             my $PKG_FIRST_WORD_REGEXP = qr{ # the FIRST word in a package name
44             [a-zA-Z_] # the first word CANNOT start with a digit
45             (?:
46             [\w']? # can contain letters, digits, _, or ticks
47             \w # But, NO multi-ticks or trailing ticks
48             )*
49             }x;
50              
51             my $PKG_ADDL_WORD_REGEXP = qr{ # the 2nd+ word in a package name
52             \w # the 2nd+ word CAN start with digits
53             (?:
54             [\w']? # and can contain letters or ticks
55             \w # But, NO multi-ticks or trailing ticks
56             )*
57             }x;
58              
59             my $PKG_NAME_REGEXP = qr{ # match a package name
60             (?: :: )? # a pkg name can start with arisdottle
61             $PKG_FIRST_WORD_REGEXP # a package word
62             (?:
63             (?: :: )+ ### arisdottle (allow one or many times)
64             $PKG_ADDL_WORD_REGEXP ### a package word
65             )* # ^ zero, one or many times
66             (?:
67             :: # allow trailing arisdottle
68             )?
69             }x;
70              
71             my $PKG_REGEXP = qr{ # match a package declaration
72             ^[\s\{;]* # intro chars on a line
73             package # the word 'package'
74             \s+ # whitespace
75             ($PKG_NAME_REGEXP) # a package name
76             \s* # optional whitespace
77             ($V_NUM_REGEXP)? # optional version number
78             \s* # optional whitesapce
79             [;\{] # semicolon line terminator or block start (since 5.16)
80             }x;
81              
82             my $VARNAME_REGEXP = qr{ # match fully-qualified VERSION name
83             ([\$*]) # sigil - $ or *
84             (
85             ( # optional leading package name
86             (?:::|\')? # possibly starting like just :: (a la $::VERSION)
87             (?:\w+(?:::|\'))* # Foo::Bar:: ...
88             )?
89             VERSION
90             )\b
91             }x;
92              
93             my $VERS_REGEXP = qr{ # match a VERSION definition
94             (?:
95             \(\s*$VARNAME_REGEXP\s*\) # with parens
96             |
97             $VARNAME_REGEXP # without parens
98             )
99             \s*
100             =[^=~>] # = but not ==, nor =~, nor =>
101             }x;
102              
103             sub new_from_file {
104 103     103 1 259345 my $class = shift;
105 103         1721 my $filename = File::Spec->rel2abs( shift );
106              
107 103 100 66     1997 return undef unless defined( $filename ) && -f $filename;
108 102         472 return $class->_init(undef, $filename, @_);
109             }
110              
111             sub new_from_handle {
112 7     7 1 2661 my $class = shift;
113 7         18 my $handle = shift;
114 7         30 my $filename = shift;
115 7 100 66     47 return undef unless defined($handle) && defined($filename);
116 6         134 $filename = File::Spec->rel2abs( $filename );
117              
118 6         40 return $class->_init(undef, $filename, @_, handle => $handle);
119              
120             }
121              
122              
123             sub new_from_module {
124 8     8 1 3584 my $class = shift;
125 8         17 my $module = shift;
126 8         28 my %props = @_;
127              
128 8   100     44 $props{inc} ||= \@INC;
129 8         27 my $filename = $class->find_module_by_name( $module, $props{inc} );
130 8 100 66     114 return undef unless defined( $filename ) && -f $filename;
131 7         49 return $class->_init($module, $filename, %props);
132             }
133              
134             {
135              
136             my $compare_versions = sub {
137             my ($v1, $op, $v2) = @_;
138             $v1 = version->new($v1)
139             unless UNIVERSAL::isa($v1,'version');
140              
141             my $eval_str = "\$v1 $op \$v2";
142             my $result = eval $eval_str;
143             log_info { "error comparing versions: '$eval_str' $@" } if $@;
144              
145             return $result;
146             };
147              
148             my $normalize_version = sub {
149             my ($version) = @_;
150             if ( $version =~ /[=<>!,]/ ) { # logic, not just version
151             # take as is without modification
152             }
153             elsif ( ref $version eq 'version' ) { # version objects
154             $version = $version->is_qv ? $version->normal : $version->stringify;
155             }
156             elsif ( $version =~ /^[^v][^.]*\.[^.]+\./ ) { # no leading v, multiple dots
157             # normalize string tuples without "v": "1.2.3" -> "v1.2.3"
158             $version = "v$version";
159             }
160             else {
161             # leave alone
162             }
163             return $version;
164             };
165              
166             # separate out some of the conflict resolution logic
167              
168             my $resolve_module_versions = sub {
169             my $packages = shift;
170              
171             my( $file, $version );
172             my $err = '';
173             foreach my $p ( @$packages ) {
174             if ( defined( $p->{version} ) ) {
175             if ( defined( $version ) ) {
176             if ( $compare_versions->( $version, '!=', $p->{version} ) ) {
177             $err .= " $p->{file} ($p->{version})\n";
178             }
179             else {
180             # same version declared multiple times, ignore
181             }
182             }
183             else {
184             $file = $p->{file};
185             $version = $p->{version};
186             }
187             }
188             $file ||= $p->{file} if defined( $p->{file} );
189             }
190              
191             if ( $err ) {
192             $err = " $file ($version)\n" . $err;
193             }
194              
195             my %result = (
196             file => $file,
197             version => $version,
198             err => $err
199             );
200              
201             return \%result;
202             };
203              
204             sub provides {
205 4     4 1 1916 my $class = shift;
206              
207 4 50       23 croak "provides() requires key/value pairs \n" if @_ % 2;
208 4         33 my %args = @_;
209              
210             croak "provides() takes only one of 'dir' or 'files'\n"
211 4 50 33     18 if $args{dir} && $args{files};
212              
213             croak "provides() requires a 'version' argument"
214 4 50       27 unless defined $args{version};
215              
216             croak "provides() does not support version '$args{version}' metadata"
217 4 50       36 unless grep $args{version} eq $_, qw/1.4 2/;
218              
219 4 100       14 $args{prefix} = 'lib' unless defined $args{prefix};
220              
221 4         16 my $p;
222 4 50       125 if ( $args{dir} ) {
223 4         9 $p = $class->package_versions_from_directory($args{dir});
224             }
225             else {
226             croak "provides() requires 'files' to be an array reference\n"
227 2 0       10 unless ref $args{files} eq 'ARRAY';
228 2         25 $p = $class->package_versions_from_directory($args{files});
229             }
230              
231             # Now, fix up files with prefix
232 4 50       12 if ( length $args{prefix} ) { # check in case disabled with q{}
233 4         15 $args{prefix} =~ s{/$}{};
234 4         105 for my $v ( values %$p ) {
235 6         29 $v->{file} = "$args{prefix}/$v->{file}";
236             }
237             }
238              
239 4         22 return $p
240             }
241              
242             sub package_versions_from_directory {
243 6     6 1 1228 my ( $class, $dir, $files ) = @_;
244              
245 6         11 my @files;
246              
247 6 100       24 if ( $files ) {
248 3         18 @files = @$files;
249             }
250             else {
251             find( {
252             wanted => sub {
253 8 100 66 8   418 push @files, $_ if -f $_ && /\.pm$/;
254             },
255 5         281 no_chdir => 1,
256             }, $dir );
257             }
258              
259             # First, we enumerate all packages & versions,
260             # separating into primary & alternative candidates
261 6         40 my( %prime, %alt );
262 6         17 foreach my $file (@files) {
263 6         322 my $mapped_filename = File::Spec->abs2rel( $file, $dir );
264 6         47 my @path = File::Spec->splitdir( $mapped_filename );
265 6         37 (my $prime_package = join( '::', @path )) =~ s/\.pm$//;
266              
267 6         34 my $pm_info = $class->new_from_file( $file );
268              
269 6         36 foreach my $package ( $pm_info->packages_inside ) {
270 22 100       49 next if $package eq 'main'; # main can appear numerous times, ignore
271 18 100       45 next if $package eq 'DB'; # special debugging package, ignore
272 14 100       88 next if grep /^_/, split( /::/, $package ); # private package, ignore
273              
274 10         30 my $version = $pm_info->version( $package );
275              
276 10 100       35 $prime_package = $package if lc($prime_package) eq lc($package);
277 9 100       29 if ( $package eq $prime_package ) {
278 5 50       13 if ( exists( $prime{$package} ) ) {
279 1         7 croak "Unexpected conflict in '$package'; multiple versions found.\n";
280             }
281             else {
282 5 50       22 $mapped_filename = "$package.pm" if lc("$package.pm") eq lc($mapped_filename);
283 5         21 $prime{$package}{file} = $mapped_filename;
284 5 50       20 $prime{$package}{version} = $version if defined( $version );
285             }
286             }
287             else {
288 5         14 push( @{$alt{$package}}, {
  5         26  
289             file => $mapped_filename,
290             version => $version,
291             } );
292             }
293             }
294             }
295              
296             # Then we iterate over all the packages found above, identifying conflicts
297             # and selecting the "best" candidate for recording the file & version
298             # for each package.
299 5         20 foreach my $package ( keys( %alt ) ) {
300 5         19 my $result = $resolve_module_versions->( $alt{$package} );
301              
302 5 50       13 if ( exists( $prime{$package} ) ) { # primary package selected
303              
304 1 0       8 if ( $result->{err} ) {
    0          
305             # Use the selected primary package, but there are conflicting
306             # errors among multiple alternative packages that need to be
307             # reported
308             log_info {
309             "Found conflicting versions for package '$package'\n" .
310             " $prime{$package}{file} ($prime{$package}{version})\n" .
311             $result->{err}
312 1     2   7 };
  1         15  
313              
314             }
315             elsif ( defined( $result->{version} ) ) {
316             # There is a primary package selected, and exactly one
317             # alternative package
318              
319 1 0 0     6 if ( exists( $prime{$package}{version} ) &&
320             defined( $prime{$package}{version} ) ) {
321             # Unless the version of the primary package agrees with the
322             # version of the alternative package, report a conflict
323 1 0       20 if ( $compare_versions->(
324             $prime{$package}{version}, '!=', $result->{version}
325             )
326             ) {
327              
328             log_info {
329 1     2   13 "Found conflicting versions for package '$package'\n" .
330             " $prime{$package}{file} ($prime{$package}{version})\n" .
331             " $result->{file} ($result->{version})\n"
332 1         2 };
333             }
334              
335             }
336             else {
337             # The prime package selected has no version so, we choose to
338             # use any alternative package that does have a version
339 1         7 $prime{$package}{file} = $result->{file};
340 1         14 $prime{$package}{version} = $result->{version};
341             }
342              
343             }
344             else {
345             # no alt package found with a version, but we have a prime
346             # package so we use it whether it has a version or not
347             }
348              
349             }
350             else { # No primary package was selected, use the best alternative
351              
352 5 50       27 if ( $result->{err} ) {
353             log_info {
354             "Found conflicting versions for package '$package'\n" .
355             $result->{err}
356 1     2   7 };
  1         11  
357             }
358              
359             # Despite possible conflicting versions, we choose to record
360             # something rather than nothing
361 5         19 $prime{$package}{file} = $result->{file};
362             $prime{$package}{version} = $result->{version}
363 5 50       25 if defined( $result->{version} );
364             }
365             }
366              
367             # Normalize versions. Can't use exists() here because of bug in YAML::Node.
368             # XXX "bug in YAML::Node" comment seems irrelevant -- dagolden, 2009-05-18
369 5         21 for (grep defined $_->{version}, values %prime) {
370 9         22 $_->{version} = $normalize_version->( $_->{version} );
371             }
372              
373 5         27 return \%prime;
374             }
375             }
376              
377              
378             sub _init {
379 110     111   261 my $class = shift;
380 110         171 my $module = shift;
381 110         186 my $filename = shift;
382 110         226 my %props = @_;
383              
384 110         223 my $handle = delete $props{handle};
385 110         189 my( %valid_props, @valid_props );
386 110         277 @valid_props = qw( collect_pod inc );
387 110         384 @valid_props{@valid_props} = delete( @props{@valid_props} );
388 110 50       260 warn "Unknown properties: @{[keys %props]}\n" if scalar( %props );
  1         2  
389              
390 110         941 my %data = (
391             module => $module,
392             filename => $filename,
393             version => undef,
394             packages => [],
395             versions => {},
396             pod => {},
397             pod_headings => [],
398             collect_pod => 0,
399              
400             %valid_props,
401             );
402              
403 110         303 my $self = bless(\%data, $class);
404              
405 110 100       257 if ( not $handle ) {
406 106         215 my $filename = $self->{filename};
407 106 50       3439 open $handle, '<', $filename
408             or croak( "Can't open '$filename': $!" );
409              
410 106         473 $self->_handle_bom($handle, $filename);
411             }
412 110         345 $self->_parse_fh($handle);
413              
414 110         323 @{$self->{packages}} = __uniq(@{$self->{packages}});
  110         311  
  110         401  
415              
416 110 100 66     354 unless($self->{module} and length($self->{module})) {
417             # CAVEAT (possible TODO): .pmc files not treated the same as .pm
418 105 100       652 if ($self->{filename} =~ /\.pm$/) {
419 96         1372 my ($v, $d, $f) = File::Spec->splitpath($self->{filename});
420 96         398 $f =~ s/\..+$//;
421 96         159 my @candidates = grep /(^|::)$f$/, @{$self->{packages}};
  96         784  
422 96         331 $self->{module} = shift(@candidates); # this may be undef
423             }
424             else {
425             # this seems like an atrocious heuristic, albeit marginally better than
426             # what was here before. It should be rewritten entirely to be more like
427             # "if it's not a .pm file, it's not require()able as a name, therefore
428             # name() should be undef."
429 10 100 100     24 if ((grep /main/, @{$self->{packages}})
  10         54  
430 4         20 or (grep /main/, keys %{$self->{versions}})) {
431 9         24 $self->{module} = 'main';
432             }
433             else {
434             # TODO: this should maybe default to undef instead
435 2   50     15 $self->{module} = $self->{packages}[0] || '';
436             }
437             }
438             }
439              
440             $self->{version} = $self->{versions}{$self->{module}}
441 110 100       355 if defined( $self->{module} );
442              
443 110         1824 return $self;
444             }
445              
446             # class method
447             sub _do_find_module {
448 8     9   20 my $class = shift;
449 8   33     29 my $module = shift || croak 'find_module_by_name() requires a package name';
450 8   100     34 my $dirs = shift || \@INC;
451              
452 8         105 my $file = File::Spec->catfile(split( /::/, $module));
453 8         28 foreach my $dir ( @$dirs ) {
454 10         75 my $testfile = File::Spec->catfile($dir, $file);
455 9 50 33     158 return [ File::Spec->rel2abs( $testfile ), $dir ]
456             if -e $testfile and !-d _; # For stuff like ExtUtils::xsubpp
457             # CAVEAT (possible TODO): .pmc files are not discoverable here
458 9         24 $testfile .= '.pm';
459 9 100       276 return [ File::Spec->rel2abs( $testfile ), $dir ]
460             if -e $testfile;
461             }
462 1         4 return;
463             }
464              
465             # class method
466             sub find_module_by_name {
467 7 100   9 1 487 my $found = shift()->_do_find_module(@_) or return;
468 6         26 return $found->[0];
469             }
470              
471             # class method
472             sub find_module_dir_by_name {
473 0 0   2 1 0 my $found = shift()->_do_find_module(@_) or return;
474 0         0 return $found->[1];
475             }
476              
477              
478             # given a line of perl code, attempt to parse it if it looks like a
479             # $VERSION assignment, returning sigil, full name, & package name
480             sub _parse_version_expression {
481 133     135   234 my $self = shift;
482 133         248 my $line = shift;
483              
484 133         218 my( $sigil, $variable_name, $package);
485 133 100       935 if ( $line =~ /$VERS_REGEXP/o ) {
486 114 100       530 ( $sigil, $variable_name, $package) = $2 ? ( $1, $2, $3 ) : ( $4, $5, $6 );
487 114 100       262 if ( $package ) {
488 20 100       58 $package = ($package eq '::') ? 'main' : $package;
489 20         85 $package =~ s/::$//;
490             }
491             }
492              
493 133         449 return ( $sigil, $variable_name, $package );
494             }
495              
496             # Look for a UTF-8/UTF-16BE/UTF-16LE BOM at the beginning of the stream.
497             # If there's one, then skip it and set the :encoding layer appropriately.
498             sub _handle_bom {
499 105     107   284 my ($self, $fh, $filename) = @_;
500              
501 105         267 my $pos = tell $fh;
502 105 50       218 return unless defined $pos;
503              
504 105         181 my $buf = ' ' x 2;
505 105         1237 my $count = read $fh, $buf, length $buf;
506 105 50 33     569 return unless defined $count and $count >= 2;
507              
508 105         156 my $encoding;
509 105 100       389 if ( $buf eq "\x{FE}\x{FF}" ) {
    100          
    100          
510 1         2 $encoding = 'UTF-16BE';
511             }
512             elsif ( $buf eq "\x{FF}\x{FE}" ) {
513 1         2 $encoding = 'UTF-16LE';
514             }
515             elsif ( $buf eq "\x{EF}\x{BB}" ) {
516 1         3 $buf = ' ';
517 1         3 $count = read $fh, $buf, length $buf;
518 1 50 33     9 if ( defined $count and $count >= 1 and $buf eq "\x{BF}" ) {
      33        
519 1         3 $encoding = 'UTF-8';
520             }
521             }
522              
523 105 100       222 if ( defined $encoding ) {
524 3 50       15 if ( "$]" >= 5.008 ) {
525 3     2   51 binmode( $fh, ":encoding($encoding)" );
  1         7  
  1         1  
  1         6  
526             }
527             }
528             else {
529 102 50       1098 seek $fh, $pos, SEEK_SET
530             or croak( sprintf "Can't reset position to the top of '$filename'" );
531             }
532              
533 105         15512 return $encoding;
534             }
535              
536             sub _parse_fh {
537 109     111   275 my ($self, $fh) = @_;
538              
539 109         221 my( $in_pod, $seen_end, $need_vers ) = ( 0, 0, 0 );
540 109         168 my( @packages, %vers, %pod, @pod );
541 109         179 my $package = 'main';
542 109         150 my $pod_sect = '';
543 109         171 my $pod_data = '';
544 109         169 my $in_end = 0;
545              
546 109         1015 while (defined( my $line = <$fh> )) {
547 1651         3059 my $line_num = $.;
548              
549 1651         2531 chomp( $line );
550              
551             # From toke.c : any line that begins by "=X", where X is an alphabetic
552             # character, introduces a POD segment.
553 1651         2171 my $is_cut;
554 1651 100       3131 if ( $line =~ /^=([a-zA-Z].*)/ ) {
555 89         177 my $cmd = $1;
556             # Then it goes back to Perl code for "=cutX" where X is a non-alphabetic
557             # character (which includes the newline, but here we chomped it away).
558 89         172 $is_cut = $cmd =~ /^cut(?:[^a-zA-Z]|$)/;
559 89         139 $in_pod = !$is_cut;
560             }
561              
562 1651 100       3124 if ( $in_pod ) {
    100          
563              
564 430 100       915 if ( $line =~ /^=head[1-4]\s+(.+)\s*$/ ) {
    100          
565 42         99 push( @pod, $1 );
566 42 100 100     106 if ( $self->{collect_pod} && length( $pod_data ) ) {
567 1         4 $pod{$pod_sect} = $pod_data;
568 1         33 $pod_data = '';
569             }
570 42         82 $pod_sect = $1;
571             }
572             elsif ( $self->{collect_pod} ) {
573 12         76 $pod_data .= "$line\n";
574             }
575 430         1227 next;
576             }
577             elsif ( $is_cut ) {
578 9 100 66     36 if ( $self->{collect_pod} && length( $pod_data ) ) {
579 1         6 $pod{$pod_sect} = $pod_data;
580 1         2 $pod_data = '';
581             }
582 9         17 $pod_sect = '';
583 9         95 next;
584             }
585              
586             # Skip after __END__
587 1212 100       1931 next if $in_end;
588              
589             # Skip comments in code
590 1210 100       2722 next if $line =~ /^\s*#/;
591              
592             # Would be nice if we could also check $in_string or something too
593 1112 100       2217 if ($line eq '__END__') {
594 2         7 $in_end++;
595 2         9 next;
596             }
597              
598 1110 100       2056 last if $line eq '__DATA__';
599              
600             # parse $line to see if it's a $VERSION declaration
601 1109 100       3168 my( $version_sigil, $version_fullname, $version_package ) =
602             index($line, 'VERSION') >= 1
603             ? $self->_parse_version_expression( $line )
604             : ();
605              
606 1109 100 100     9095 if ( $line =~ /$PKG_REGEXP/o ) {
    100 100        
    100 100        
    100 100        
    100 100        
      100        
607 134         385 $package = $1;
608 134         245 my $version = $2;
609 134 100       503 push( @packages, $package ) unless grep( $package eq $_, @packages );
610 134 100       314 $need_vers = defined $version ? 0 : 1;
611              
612 134 100 100     956 if ( not exists $vers{$package} and defined $version ){
613             # Upgrade to a version object.
614 6         13 my $dwim_version = eval { _dwim_version($version) };
  6         18  
615 6 50       17 croak "Version '$version' from $self->{filename} does not appear to be valid:\n$line\n\nThe fatal error was: $@\n"
616             unless defined $dwim_version; # "0" is OK!
617 6         55 $vers{$package} = $dwim_version;
618             }
619             }
620              
621             # VERSION defined with full package spec, i.e. $Module::VERSION
622             elsif ( $version_fullname && $version_package ) {
623             # we do NOT save this package in found @packages
624 20 100       66 $need_vers = 0 if $version_package eq $package;
625              
626 20 100 66     140 unless ( defined $vers{$version_package} && length $vers{$version_package} ) {
627 18         49 $vers{$version_package} = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line );
628             }
629             }
630              
631             # first non-comment line in undeclared package main is VERSION
632             elsif ( $package eq 'main' && $version_fullname && !exists($vers{main}) ) {
633 8         15 $need_vers = 0;
634 8         24 my $v = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line );
635 8         22 $vers{$package} = $v;
636 8         90 push( @packages, 'main' );
637             }
638              
639             # first non-comment line in undeclared package defines package main
640             elsif ( $package eq 'main' && !exists($vers{main}) && $line =~ /\w/ ) {
641 8         23 $need_vers = 1;
642 8         21 $vers{main} = '';
643 8         78 push( @packages, 'main' );
644             }
645              
646             # only keep if this is the first $VERSION seen
647             elsif ( $version_fullname && $need_vers ) {
648 83         133 $need_vers = 0;
649 83         195 my $v = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line );
650              
651 83 50 66     286 unless ( defined $vers{$package} && length $vers{$package} ) {
652 83         712 $vers{$package} = $v;
653             }
654             }
655             } # end loop over each line
656              
657 109 100 100     445 if ( $self->{collect_pod} && length($pod_data) ) {
658 1         3 $pod{$pod_sect} = $pod_data;
659             }
660              
661 109         350 $self->{versions} = \%vers;
662 109         248 $self->{packages} = \@packages;
663 109         191 $self->{pod} = \%pod;
664 109         342 $self->{pod_headings} = \@pod;
665             }
666              
667             sub __uniq (@)
668             {
669 109     111   188 my (%seen, $key);
670 109         614 grep !$seen{ $key = $_ }++, @_;
671             }
672              
673             {
674             my $pn = 0;
675             sub _evaluate_version_line {
676 109     110   169 my $self = shift;
677 109         255 my( $sigil, $variable_name, $line ) = @_;
678              
679             # We compile into a local sub because 'use version' would cause
680             # compiletime/runtime issues with local()
681 109         181 $pn++; # everybody gets their own package
682 109         516 my $eval = qq{ my \$dummy = q# Hide from _packages_inside()
683             #; package Module::Metadata::_version::p${pn};
684             use version;
685             sub {
686             local $sigil$variable_name;
687             $line;
688             return \$$variable_name if defined \$$variable_name;
689             return \$Module::Metadata::_version::p${pn}::$variable_name;
690             };
691             };
692              
693 109 50       557 $eval = $1 if $eval =~ m{^(.+)}s;
694              
695 109         384 local $^W;
696             # Try to get the $VERSION
697 109         244 my $vsub = __clean_eval($eval);
698             # some modules say $VERSION $Foo::Bar::VERSION, but Foo::Bar isn't
699             # installed, so we need to hunt in ./lib for it
700 109 50 33     429 if ( $@ =~ /Can't locate/ && -d 'lib' ) {
701 0         0 local @INC = ('lib',@INC);
702 0         0 $vsub = __clean_eval($eval);
703             }
704 109 50       241 warn "Error evaling version line '$eval' in $self->{filename}: $@\n"
705             if $@;
706              
707 109 50       310 (ref($vsub) eq 'CODE') or
708             croak "failed to build version sub for $self->{filename}";
709              
710 109         177 my $result = eval { $vsub->() };
  109         2271  
711             # FIXME: $eval is not the right thing to print here
712 109 50       283 croak "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n"
713             if $@;
714              
715             # Upgrade it into a version object
716 109         160 my $version = eval { _dwim_version($result) };
  109         264  
717              
718             # FIXME: $eval is not the right thing to print here
719 109 50       241 croak "Version '$result' from $self->{filename} does not appear to be valid:\n$eval\n\nThe fatal error was: $@\n"
720             unless defined $version; # "0" is OK!
721              
722 109         1078 return $version;
723             }
724             }
725              
726             # Try to DWIM when things fail the lax version test in obvious ways
727             {
728             my @version_prep = (
729             # Best case, it just works
730             sub { return shift },
731              
732             # If we still don't have a version, try stripping any
733             # trailing junk that is prohibited by lax rules
734             sub {
735             my $v = shift;
736             $v =~ s{([0-9])[a-z-].*$}{$1}i; # 1.23-alpha or 1.23b
737             return $v;
738             },
739              
740             # Activestate apparently creates custom versions like '1.23_45_01', which
741             # cause version.pm to think it's an invalid alpha. So check for that
742             # and strip them
743             sub {
744             my $v = shift;
745             my $num_dots = () = $v =~ m{(\.)}g;
746             my $num_unders = () = $v =~ m{(_)}g;
747             my $leading_v = substr($v,0,1) eq 'v';
748             if ( ! $leading_v && $num_dots < 2 && $num_unders > 1 ) {
749             $v =~ s{_}{}g;
750             $num_unders = () = $v =~ m{(_)}g;
751             }
752             return $v;
753             },
754              
755             # Worst case, try numifying it like we would have before version objects
756             sub {
757             my $v = shift;
758 10     10   86 no warnings 'numeric';
  10         45  
  10         4316  
759             return 0 + $v;
760             },
761              
762             );
763              
764             sub _dwim_version {
765 115     116   217 my ($result) = shift;
766              
767 115 100       250 return $result if ref($result) eq 'version';
768              
769 110         183 my ($version, $error);
770 110         240 for my $f (@version_prep) {
771 122         285 $result = $f->($result);
772 122         203 $version = eval { version->new($result) };
  122         1010  
773 122 100 66     341 $error ||= $@ if $@; # capture first failure
774 122 100       302 last if defined $version;
775             }
776              
777 110 50       226 croak $error unless defined $version;
778              
779 110         236 return $version;
780             }
781             }
782              
783             ############################################################
784              
785             # accessors
786 21     22 1 210 sub name { $_[0]->{module} }
787              
788 1     2 1 3 sub filename { $_[0]->{filename} }
789 31     32 1 461 sub packages_inside { @{$_[0]->{packages}} }
  31         126  
790 2     3 1 5 sub pod_inside { @{$_[0]->{pod_headings}} }
  2         12  
791 4     5 1 1085 sub contains_pod { 0+@{$_[0]->{pod_headings}} }
  4         26  
792              
793             sub version {
794 100     101 1 1073 my $self = shift;
795 100   100     481 my $mod = shift || $self->{module};
796 100         148 my $vers;
797 100 100 66     537 if ( defined( $mod ) && length( $mod ) &&
      100        
798             exists( $self->{versions}{$mod} ) ) {
799 85         350 return $self->{versions}{$mod};
800             }
801             else {
802 15         49 return undef;
803             }
804             }
805              
806             sub pod {
807 4     5 1 689 my $self = shift;
808 4         8 my $sect = shift;
809 4 100 33     26 if ( defined( $sect ) && length( $sect ) &&
      66        
810             exists( $self->{pod}{$sect} ) ) {
811 2         6 return $self->{pod}{$sect};
812             }
813             else {
814 2         10 return undef;
815             }
816             }
817              
818             sub is_indexable {
819 8     9 1 22 my ($self, $package) = @_;
820              
821 8         21 my @indexable_packages = grep $_ ne 'main', $self->packages_inside;
822              
823             # check for specific package, if provided
824 8 100       60 return !! grep $_ eq $package, @indexable_packages if $package;
825              
826             # otherwise, check for any indexable packages at all
827 2         12 return !! @indexable_packages;
828             }
829              
830             1;
831              
832             __END__