File Coverage

blib/lib/Module/Metadata.pm
Criterion Covered Total %
statement 341 345 98.8
branch 145 188 77.1
condition 79 111 71.1
subroutine 53 53 100.0
pod 15 15 100.0
total 633 712 88.9


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