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 54 54 100.0
pod 15 15 100.0
total 634 713 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.000038-5-g7f7baae
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 115     115   12982 sub __clean_eval { eval $_[0] }
  6     7   53  
  6     5   19  
  6     5   57  
  4     5   33  
  4     5   8  
  4     4   28  
  4     4   36  
  4     4   8  
  4     4   24  
  4     1   38  
  4     1   10  
  4     1   35  
  4     1   33  
  4     1   7  
  4     1   24  
  3         27  
  3         8  
  3         20  
  3         25  
  3         6  
  3         17  
  3         23  
  3         7  
  3         17  
  3         26  
  3         7  
  3         19  
14 10     10   1296958 use strict;
  10         19  
  10         322  
15 10     10   56 use warnings;
  10         28  
  10         760  
16              
17             our $VERSION = '1.000039';
18              
19 10     10   70 use Carp qw/croak/;
  10         27  
  10         515  
20 10     10   67 use File::Spec;
  10         46  
  10         747  
21             BEGIN {
22             # Try really hard to not depend ony any DynaLoaded module, such as IO::File or Fcntl
23             eval {
24 10         63 require Fcntl; Fcntl->import('SEEK_SET'); 1;
  10         369  
  10         318  
25 2         23 } or *SEEK_SET = sub { 0 }
26 10 50   10   55 }
27 10     10   3400 use version 0.87;
  10         15201  
  10         66  
28             BEGIN {
29 10 50   10   1230 if ($INC{'Log/Contextual.pm'}) {
30 2         15 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   218 *log_info = sub (&) { warn $_[0]->() };
  2         14  
37             }
38             }
39 10     10   66 use File::Find qw(find);
  10         28  
  10         46617  
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             [:;\{] # attribute start, 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 122     122 1 2359292 my $class = shift;
116 122         2314 my $filename = File::Spec->rel2abs( shift );
117              
118 122 100 66     3035 return undef unless defined( $filename ) && -f $filename;
119 121         677 return $class->_init(undef, $filename, @_);
120             }
121              
122             sub new_from_handle {
123 7     7 1 502184 my $class = shift;
124 7         15 my $handle = shift;
125 7         31 my $filename = shift;
126 7 100 66     78 return undef unless defined($handle) && defined($filename);
127 6         134 $filename = File::Spec->rel2abs( $filename );
128              
129 6         32 return $class->_init(undef, $filename, @_, handle => $handle);
130              
131             }
132              
133              
134             sub new_from_module {
135 9     9 1 1068754 my $class = shift;
136 9         17 my $module = shift;
137 9         41 my %props = @_;
138              
139 9   100     44 $props{inc} ||= \@INC;
140 9         29 my $filename = $class->find_module_by_name( $module, $props{inc} );
141 9 100 66     111 return undef unless defined( $filename ) && -f $filename;
142 8         47 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 1698 my $class = shift;
217              
218 4 50       27 croak "provides() requires key/value pairs \n" if @_ % 2;
219 4         52 my %args = @_;
220              
221             croak "provides() takes only one of 'dir' or 'files'\n"
222 4 50 33     17 if $args{dir} && $args{files};
223              
224             croak "provides() requires a 'version' argument"
225 4 50       22 unless defined $args{version};
226              
227             croak "provides() does not support version '$args{version}' metadata"
228 4 50       27 unless grep $args{version} eq $_, qw/1.4 2/;
229              
230 4 100       10 $args{prefix} = 'lib' unless defined $args{prefix};
231              
232 4         14 my $p;
233 4 50       143 if ( $args{dir} ) {
234 4         12 $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       8 unless ref $args{files} eq 'ARRAY';
239 2         11 $p = $class->package_versions_from_directory($args{files});
240             }
241              
242             # Now, fix up files with prefix
243 4 50       9 if ( length $args{prefix} ) { # check in case disabled with q{}
244 4         14 $args{prefix} =~ s{/$}{};
245 4         96 for my $v ( values %$p ) {
246 6         10 $v->{file} = "$args{prefix}/$v->{file}";
247             }
248             }
249              
250 4         17 return $p
251             }
252              
253             sub package_versions_from_directory {
254 6     6 1 1201 my ( $class, $dir, $files ) = @_;
255              
256 6         11 my @files;
257              
258 6 100       19 if ( $files ) {
259 3         14 @files = @$files;
260             }
261             else {
262             find( {
263             wanted => sub {
264 8 100 66 8   553 push @files, $_ if -f $_ && /\.pm$/;
265             },
266 5         354 no_chdir => 1,
267             }, $dir );
268             }
269              
270             # First, we enumerate all packages & versions,
271             # separating into primary & alternative candidates
272 6         31 my( %prime, %alt );
273 6         14 foreach my $file (@files) {
274 6         297 my $mapped_filename = File::Spec->abs2rel( $file, $dir );
275 6         34 my @path = File::Spec->splitdir( $mapped_filename );
276 6         27 (my $prime_package = join( '::', @path )) =~ s/\.pm$//;
277              
278 6         35 my $pm_info = $class->new_from_file( $file );
279              
280 6         33 foreach my $package ( $pm_info->packages_inside ) {
281 22 100       55 next if $package eq 'main'; # main can appear numerous times, ignore
282 18 100       40 next if $package eq 'DB'; # special debugging package, ignore
283 14 100       90 next if grep /^_/, split( /::/, $package ); # private package, ignore
284              
285 10         42 my $version = $pm_info->version( $package );
286              
287 10 100       117 $prime_package = $package if lc($prime_package) eq lc($package);
288 10 100       33 if ( $package eq $prime_package ) {
289 6 50       11 if ( exists( $prime{$package} ) ) {
290 2         11 croak "Unexpected conflict in '$package'; multiple versions found.\n";
291             }
292             else {
293 6 50       34 $mapped_filename = "$package.pm" if lc("$package.pm") eq lc($mapped_filename);
294 6         20 $prime{$package}{file} = $mapped_filename;
295 6 50       53 $prime{$package}{version} = $version if defined( $version );
296             }
297             }
298             else {
299 5         14 push( @{$alt{$package}}, {
  5         39  
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         19 foreach my $package ( keys( %alt ) ) {
311 5         23 my $result = $resolve_module_versions->( $alt{$package} );
312              
313 5 50       13 if ( exists( $prime{$package} ) ) { # primary package selected
314              
315 1 0       10 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         1  
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     7 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         7 $prime{$package}{file} = $result->{file};
351 1         1 $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       18 if ( $result->{err} ) {
364             log_info {
365             "Found conflicting versions for package '$package'\n" .
366             $result->{err}
367 1     2   8 };
  1         1  
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       24 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         22 $_->{version} = $normalize_version->( $_->{version} );
382             }
383              
384 5         25 return \%prime;
385             }
386             }
387              
388              
389             sub _init {
390 130     131   252 my $class = shift;
391 130         216 my $module = shift;
392 130         270 my $filename = shift;
393 130         294 my %props = @_;
394              
395 130         304 my $handle = delete $props{handle};
396 130         222 my( %valid_props, @valid_props );
397 130         431 @valid_props = qw( collect_pod inc decode_pod );
398 130         618 @valid_props{@valid_props} = delete( @props{@valid_props} );
399 130 50       343 warn "Unknown properties: @{[keys %props]}\n" if scalar( %props );
  1         2  
400              
401 130         1314 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 130         439 my $self = bless(\%data, $class);
415              
416 130 100       374 if ( not $handle ) {
417 126         281 my $filename = $self->{filename};
418 126 50       4450 open $handle, '<', $filename
419             or croak( "Can't open '$filename': $!" );
420              
421 126         651 $self->_handle_bom($handle, $filename);
422             }
423 130         411 $self->_parse_fh($handle);
424              
425 130         205 @{$self->{packages}} = __uniq(@{$self->{packages}});
  130         315  
  130         445  
426              
427 130 100 66     426 unless($self->{module} and length($self->{module})) {
428             # CAVEAT (possible TODO): .pmc files not treated the same as .pm
429 124 100       598 if ($self->{filename} =~ /\.pm$/) {
430 115         2029 my ($v, $d, $f) = File::Spec->splitpath($self->{filename});
431 115         827 $f =~ s/\..+$//;
432 115         203 my @candidates = grep /(^|::)$f$/, @{$self->{packages}};
  115         1216  
433 115         340 $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     142 if ((grep /main/, @{$self->{packages}})
  10         41  
441 4         17 or (grep /main/, keys %{$self->{versions}})) {
442 9         18 $self->{module} = 'main';
443             }
444             else {
445             # TODO: this should maybe default to undef instead
446 2   50     13 $self->{module} = $self->{packages}[0] || '';
447             }
448             }
449             }
450              
451             $self->{version} = $self->{versions}{$self->{module}}
452 130 100       501 if defined( $self->{module} );
453              
454 130         2647 return $self;
455             }
456              
457             # class method
458             sub _do_find_module {
459 9     10   16 my $class = shift;
460 9   33     24 my $module = shift || croak 'find_module_by_name() requires a package name';
461 9   100     51 my $dirs = shift || \@INC;
462              
463 9         103 my $file = File::Spec->catfile(split( /::/, $module));
464 9         23 foreach my $dir ( @$dirs ) {
465 11         63 my $testfile = File::Spec->catfile($dir, $file);
466 11 50 33     253 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         20 $testfile .= '.pm';
470 11 100       247 return [ File::Spec->rel2abs( $testfile ), $dir ]
471             if -e $testfile;
472             }
473 2         28 return;
474             }
475              
476             # class method
477             sub find_module_by_name {
478 9 100   10 1 591 my $found = shift()->_do_find_module(@_) or return;
479 8         24 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 139     141   237 my $self = shift;
493 139         269 my $line = shift;
494              
495 139         253 my( $sigil, $variable_name, $package);
496 139 100       1249 if ( $line =~ /$VERS_REGEXP/o ) {
497 118 100       746 ( $sigil, $variable_name, $package) = $2 ? ( $1, $2, $3 ) : ( $4, $5, $6 );
498 118 100       274 if ( $package ) {
499 20 100       58 $package = ($package eq '::') ? 'main' : $package;
500 20         91 $package =~ s/::$//;
501             }
502             }
503              
504 139         506 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 125     127   308 my ($self, $fh, $filename) = @_;
511              
512 125         357 my $pos = tell $fh;
513 125 50       308 return unless defined $pos;
514              
515 125         250 my $buf = ' ' x 2;
516 125         1898 my $count = read $fh, $buf, length $buf;
517 125 50 33     659 return unless defined $count and $count >= 2;
518              
519 125         186 my $encoding;
520 125 100       599 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         3 $encoding = 'UTF-16LE';
525             }
526             elsif ( $buf eq "\x{EF}\x{BB}" ) {
527 1         4 $buf = ' ';
528 1         4 $count = read $fh, $buf, length $buf;
529 1 50 33     12 if ( defined $count and $count >= 1 and $buf eq "\x{BF}" ) {
      33        
530 1         4 $encoding = 'UTF-8';
531             }
532             }
533              
534 125 100       336 if ( defined $encoding ) {
535 3 50       26 if ( "$]" >= 5.008 ) {
536 3     2   73 binmode( $fh, ":encoding($encoding)" );
  1         836  
  1         14  
  1         5  
537             }
538             }
539             else {
540 122 50       980 seek $fh, $pos, SEEK_SET
541             or croak( sprintf "Can't reset position to the top of '$filename'" );
542             }
543              
544 125         5055 return $encoding;
545             }
546              
547             sub _parse_fh {
548 129     131   259 my ($self, $fh) = @_;
549              
550 129         269 my( $in_pod, $seen_end, $need_vers ) = ( 0, 0, 0 );
551 129         227 my( @packages, %vers, %pod, @pod );
552 129         199 my $package = 'main';
553 129         193 my $pod_sect = '';
554 129         206 my $pod_data = '';
555 129         184 my $in_end = 0;
556 129         224 my $encoding = '';
557              
558 129         1169 while (defined( my $line = <$fh> )) {
559 1746         2538 my $line_num = $.;
560              
561 1746         2297 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 1746         1928 my $is_cut;
566 1746 100       3142 if ( $line =~ /^=([a-zA-Z].*)/ ) {
567 98         174 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 98         143 $is_cut = $cmd =~ /^cut(?:[^a-zA-Z]|$)/;
571 98         131 $in_pod = !$is_cut;
572             }
573              
574 1746 100       5879 if ( $in_pod ) {
    100          
575              
576 462 100       930 if ( $line =~ /^=head[1-4]\s+(.+)\s*$/ ) {
    100          
577 45         96 push( @pod, $1 );
578 45 100 100     123 if ( $self->{collect_pod} && length( $pod_data ) ) {
579 4         9 $pod{$pod_sect} = $pod_data;
580 4         5 $pod_data = '';
581             }
582 45         71 $pod_sect = $1;
583             }
584             elsif ( $self->{collect_pod} ) {
585 25 100 100     50 if ( $self->{decode_pod} && $line =~ /^=encoding ([\w-]+)/ ) {
586 1         2 $encoding = $1;
587             }
588 25         27 $pod_data .= "$line\n";
589             }
590 462         1212 next;
591             }
592             elsif ( $is_cut ) {
593 10 100 66     27 if ( $self->{collect_pod} && length( $pod_data ) ) {
594 2         4 $pod{$pod_sect} = $pod_data;
595 2         3 $pod_data = '';
596             }
597 10         13 $pod_sect = '';
598 10         129 next;
599             }
600              
601             # Skip after __END__
602 1274 100       1921 next if $in_end;
603              
604             # Skip comments in code
605 1272 100       2530 next if $line =~ /^\s*#/;
606              
607             # Would be nice if we could also check $in_string or something too
608 1174 100       1907 if ($line eq '__END__') {
609 2         3 $in_end++;
610 2         7 next;
611             }
612              
613 1172 100       1848 last if $line eq '__DATA__';
614              
615             # parse $line to see if it's a $VERSION declaration
616 1171 100       2959 my( $version_sigil, $version_fullname, $version_package ) =
617             index($line, 'VERSION') >= 1
618             ? $self->_parse_version_expression( $line )
619             : ();
620              
621 1171 100 100     12371 if ( $line =~ /$PKG_REGEXP/o or $line =~ /$CLASS_REGEXP/ ) {
    100 100        
    100 100        
    100 100        
    100 100        
      100        
      100        
622 156         435 $package = $1;
623 156         345 my $version = $2;
624 156 100       595 push( @packages, $package ) unless grep( $package eq $_, @packages );
625 156 100       332 $need_vers = defined $version ? 0 : 1;
626              
627 156 100 100     1128 if ( not exists $vers{$package} and defined $version ){
628             # Upgrade to a version object.
629 19         29 my $dwim_version = eval { _dwim_version($version) };
  19         51  
630 19 50       53 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 19         172 $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       68 $need_vers = 0 if $version_package eq $package;
640              
641 20 100 66     181 unless ( defined $vers{$version_package} && length $vers{$version_package} ) {
642 18         56 $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         12 $need_vers = 0;
649 8         19 my $v = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line );
650 8         18 $vers{$package} = $v;
651 8         124 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         19 $need_vers = 1;
657 9         28 $vers{main} = '';
658 9         94 push( @packages, 'main' );
659             }
660              
661             # only keep if this is the first $VERSION seen
662             elsif ( $version_fullname && $need_vers ) {
663 87         158 $need_vers = 0;
664 87         250 my $v = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line );
665              
666 87 50 66     301 unless ( defined $vers{$package} && length $vers{$package} ) {
667 87         1222 $vers{$package} = $v;
668             }
669             }
670             } # end loop over each line
671              
672 129 100 100     501 if ( $self->{collect_pod} && length($pod_data) ) {
673 1         3 $pod{$pod_sect} = $pod_data;
674             }
675              
676 129 100 66     376 if ( $self->{decode_pod} && $encoding ) {
677 1         5 require Encode;
678 1         24 $_ = Encode::decode( $encoding, $_ ) for values %pod;
679             }
680              
681 129         483 $self->{versions} = \%vers;
682 129         343 $self->{packages} = \@packages;
683 129         234 $self->{pod} = \%pod;
684 129         386 $self->{pod_headings} = \@pod;
685             }
686              
687             sub __uniq (@)
688             {
689 129     131   203 my (%seen, $key);
690 129         791 grep !$seen{ $key = $_ }++, @_;
691             }
692              
693             {
694             my $pn = 0;
695             sub _evaluate_version_line {
696 113     115   164 my $self = shift;
697 113         307 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 113         187 $pn++; # everybody gets their own package
702 113         377 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 113 50       626 $eval = $1 if $eval =~ m{^(.+)}s;
714              
715 113         417 local $^W;
716             # Try to get the $VERSION
717 113         316 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 113 50 33     518 if ( $@ =~ /Can't locate/ && -d 'lib' ) {
721 0         0 local @INC = ('lib',@INC);
722 0         0 $vsub = __clean_eval($eval);
723             }
724 113 50       252 warn "Error evaling version line '$eval' in $self->{filename}: $@\n"
725             if $@;
726              
727 113 50       319 (ref($vsub) eq 'CODE') or
728             croak "failed to build version sub for $self->{filename}";
729              
730 113         186 my $result = eval { $vsub->() };
  113         2485  
731             # FIXME: $eval is not the right thing to print here
732 113 50       303 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 113         169 my $version = eval { _dwim_version($result) };
  113         310  
737              
738             # FIXME: $eval is not the right thing to print here
739 113 50       234 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 113         2001 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   89 no warnings 'numeric';
  10         38  
  10         6368  
779             return 0 + $v;
780             },
781              
782             );
783              
784             sub _dwim_version {
785 132     134   277 my ($result) = shift;
786              
787 132 100       375 return $result if ref($result) eq 'version';
788              
789 127         216 my ($version, $error);
790 127         296 for my $f (@version_prep) {
791 139         368 $result = $f->($result);
792 139         204 $version = eval { version->new($result) };
  139         1273  
793 139 100 66     399 $error ||= $@ if $@; # capture first failure
794 139 100       383 last if defined $version;
795             }
796              
797 127 50       283 croak $error unless defined $version;
798              
799 127         320 return $version;
800             }
801             }
802              
803             ############################################################
804              
805             # accessors
806 21     22 1 185 sub name { $_[0]->{module} }
807              
808 1     2 1 2 sub filename { $_[0]->{filename} }
809 34     35 1 395 sub packages_inside { @{$_[0]->{packages}} }
  34         158  
810 2     3 1 4 sub pod_inside { @{$_[0]->{pod_headings}} }
  2         9  
811 4     5 1 1024 sub contains_pod { 0+@{$_[0]->{pod_headings}} }
  4         20  
812              
813             sub version {
814 116     117 1 1429 my $self = shift;
815 116   100     498 my $mod = shift || $self->{module};
816 116         169 my $vers;
817 116 100 66     700 if ( defined( $mod ) && length( $mod ) &&
      100        
818             exists( $self->{versions}{$mod} ) ) {
819 99         369 return $self->{versions}{$mod};
820             }
821             else {
822 17         46 return undef;
823             }
824             }
825              
826             sub pod {
827 5     6 1 479 my $self = shift;
828 5         6 my $sect = shift;
829 5 100 33     24 if ( defined( $sect ) && length( $sect ) &&
      66        
830             exists( $self->{pod}{$sect} ) ) {
831 3         7 return $self->{pod}{$sect};
832             }
833             else {
834 2         6 return undef;
835             }
836             }
837              
838             sub is_indexable {
839 8     9 1 19 my ($self, $package) = @_;
840              
841 8         23 my @indexable_packages = grep $_ ne 'main', $self->packages_inside;
842              
843             # check for specific package, if provided
844 8 100       53 return !! grep $_ eq $package, @indexable_packages if $package;
845              
846             # otherwise, check for any indexable packages at all
847 2         11 return !! @indexable_packages;
848             }
849              
850             1;
851              
852             __END__