whitespace fixes
[p5sagit/Module-Metadata.git] / lib / Module / Metadata.pm
CommitLineData
5ac756c6 1# -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*-
2# vim:ts=8:sw=2:et:sta:sts=2
3package Module::Metadata;
4
cd41f0db 5# Adapted from Perl-licensed code originally distributed with
6# Module-Build by Ken Williams
5ac756c6 7
8# This module provides routines to gather information about
9# perl modules (assuming this may be expanded in the distant
10# parrot future to look at other types of modules).
11
12use strict;
eed8b6fa 13use warnings;
14
f2a5537b 15our $VERSION = '1.000019';
5ac756c6 16$VERSION = eval $VERSION;
17
6f3c7f28 18use Carp qw/croak/;
5ac756c6 19use File::Spec;
20use IO::File;
4850170c 21use version 0.87;
3db27017 22BEGIN {
23 if ($INC{'Log/Contextual.pm'}) {
24 Log::Contextual->import('log_info');
25 } else {
e6ddd765 26 *log_info = sub (&) { warn $_[0]->() };
3db27017 27 }
28}
5ac756c6 29use File::Find qw(find);
30
31my $V_NUM_REGEXP = qr{v?[0-9._]+}; # crudely, a v-string or decimal
32
8e4bef01 33my $PKG_FIRST_WORD_REGEXP = qr{ # the FIRST word in a package name
34 [a-zA-Z_] # the first word CANNOT start with a digit
35 (?:
36 [\w']? # can contain letters, digits, _, or ticks
37 \w # But, NO multi-ticks or trailing ticks
38 )*
39}x;
40
41my $PKG_ADDL_WORD_REGEXP = qr{ # the 2nd+ word in a package name
42 \w # the 2nd+ word CAN start with digits
43 (?:
44 [\w']? # and can contain letters or ticks
45 \w # But, NO multi-ticks or trailing ticks
46 )*
47}x;
48
49my $PKG_NAME_REGEXP = qr{ # match a package name
50 (?: :: )? # a pkg name can start with aristotle
51 $PKG_FIRST_WORD_REGEXP # a package word
52 (?:
53 (?: :: )+ ### aristotle (allow one or many times)
54 $PKG_ADDL_WORD_REGEXP ### a package word
55 )* # ^ zero, one or many times
56 (?:
57 :: # allow trailing aristotle
58 )?
59}x;
60
5ac756c6 61my $PKG_REGEXP = qr{ # match a package declaration
62 ^[\s\{;]* # intro chars on a line
63 package # the word 'package'
64 \s+ # whitespace
8e4bef01 65 ($PKG_NAME_REGEXP) # a package name
5ac756c6 66 \s* # optional whitespace
67 ($V_NUM_REGEXP)? # optional version number
68 \s* # optional whitesapce
710f253f 69 [;\{] # semicolon line terminator or block start (since 5.16)
5ac756c6 70}x;
71
72my $VARNAME_REGEXP = qr{ # match fully-qualified VERSION name
73 ([\$*]) # sigil - $ or *
74 (
75 ( # optional leading package name
76 (?:::|\')? # possibly starting like just :: (Ì la $::VERSION)
77 (?:\w+(?:::|\'))* # Foo::Bar:: ...
78 )?
79 VERSION
80 )\b
81}x;
82
83my $VERS_REGEXP = qr{ # match a VERSION definition
84 (?:
85 \(\s*$VARNAME_REGEXP\s*\) # with parens
86 |
87 $VARNAME_REGEXP # without parens
88 )
89 \s*
629a0930 90 =[^=~>] # = but not ==, nor =~, nor =>
5ac756c6 91}x;
92
5ac756c6 93sub new_from_file {
94 my $class = shift;
95 my $filename = File::Spec->rel2abs( shift );
96
97 return undef unless defined( $filename ) && -f $filename;
98 return $class->_init(undef, $filename, @_);
99}
100
f33c0a6c 101sub new_from_handle {
102 my $class = shift;
103 my $handle = shift;
104 my $filename = shift;
105 return undef unless defined($handle) && defined($filename);
106 $filename = File::Spec->rel2abs( $filename );
107
108 return $class->_init(undef, $filename, @_, handle => $handle);
109
110}
111
112
5ac756c6 113sub new_from_module {
114 my $class = shift;
115 my $module = shift;
116 my %props = @_;
117
118 $props{inc} ||= \@INC;
119 my $filename = $class->find_module_by_name( $module, $props{inc} );
120 return undef unless defined( $filename ) && -f $filename;
121 return $class->_init($module, $filename, %props);
122}
123
124{
dd5a4b10 125
5ac756c6 126 my $compare_versions = sub {
127 my ($v1, $op, $v2) = @_;
4850170c 128 $v1 = version->new($v1)
129 unless UNIVERSAL::isa($v1,'version');
dd5a4b10 130
5ac756c6 131 my $eval_str = "\$v1 $op \$v2";
132 my $result = eval $eval_str;
133 log_info { "error comparing versions: '$eval_str' $@" } if $@;
dd5a4b10 134
5ac756c6 135 return $result;
136 };
137
138 my $normalize_version = sub {
139 my ($version) = @_;
140 if ( $version =~ /[=<>!,]/ ) { # logic, not just version
141 # take as is without modification
142 }
4850170c 143 elsif ( ref $version eq 'version' ) { # version objects
5ac756c6 144 $version = $version->is_qv ? $version->normal : $version->stringify;
145 }
146 elsif ( $version =~ /^[^v][^.]*\.[^.]+\./ ) { # no leading v, multiple dots
147 # normalize string tuples without "v": "1.2.3" -> "v1.2.3"
148 $version = "v$version";
149 }
150 else {
151 # leave alone
152 }
153 return $version;
154 };
155
156 # separate out some of the conflict resolution logic
157
158 my $resolve_module_versions = sub {
159 my $packages = shift;
dd5a4b10 160
5ac756c6 161 my( $file, $version );
162 my $err = '';
163 foreach my $p ( @$packages ) {
164 if ( defined( $p->{version} ) ) {
141ef4bd 165 if ( defined( $version ) ) {
166 if ( $compare_versions->( $version, '!=', $p->{version} ) ) {
167 $err .= " $p->{file} ($p->{version})\n";
168 } else {
169 # same version declared multiple times, ignore
170 }
171 } else {
172 $file = $p->{file};
173 $version = $p->{version};
174 }
5ac756c6 175 }
141ef4bd 176 $file ||= $p->{file} if defined( $p->{file} );
177 }
dd5a4b10 178
5ac756c6 179 if ( $err ) {
180 $err = " $file ($version)\n" . $err;
181 }
dd5a4b10 182
5ac756c6 183 my %result = (
184 file => $file,
185 version => $version,
186 err => $err
187 );
dd5a4b10 188
5ac756c6 189 return \%result;
190 };
191
ca33f3bd 192 sub provides {
193 my $class = shift;
194
6f3c7f28 195 croak "provides() requires key/value pairs \n" if @_ % 2;
ca33f3bd 196 my %args = @_;
197
6f3c7f28 198 croak "provides() takes only one of 'dir' or 'files'\n"
ca33f3bd 199 if $args{dir} && $args{files};
200
6f3c7f28 201 croak "provides() requires a 'version' argument"
c06d0187 202 unless defined $args{version};
203
6f3c7f28 204 croak "provides() does not support version '$args{version}' metadata"
c06d0187 205 unless grep { $args{version} eq $_ } qw/1.4 2/;
206
ca33f3bd 207 $args{prefix} = 'lib' unless defined $args{prefix};
208
209 my $p;
210 if ( $args{dir} ) {
211 $p = $class->package_versions_from_directory($args{dir});
212 }
213 else {
6f3c7f28 214 croak "provides() requires 'files' to be an array reference\n"
ca33f3bd 215 unless ref $args{files} eq 'ARRAY';
216 $p = $class->package_versions_from_directory($args{files});
217 }
218
219 # Now, fix up files with prefix
220 if ( length $args{prefix} ) { # check in case disabled with q{}
221 $args{prefix} =~ s{/$}{};
222 for my $v ( values %$p ) {
223 $v->{file} = "$args{prefix}/$v->{file}";
224 }
225 }
226
227 return $p
228 }
229
5ac756c6 230 sub package_versions_from_directory {
231 my ( $class, $dir, $files ) = @_;
232
233 my @files;
234
235 if ( $files ) {
236 @files = @$files;
237 } else {
238 find( {
239 wanted => sub {
240 push @files, $_ if -f $_ && /\.pm$/;
241 },
242 no_chdir => 1,
243 }, $dir );
244 }
245
246 # First, we enumerate all packages & versions,
247 # separating into primary & alternative candidates
248 my( %prime, %alt );
249 foreach my $file (@files) {
713aab0e 250 my $mapped_filename = File::Spec::Unix->abs2rel( $file, $dir );
5ac756c6 251 my @path = split( /\//, $mapped_filename );
252 (my $prime_package = join( '::', @path )) =~ s/\.pm$//;
dd5a4b10 253
5ac756c6 254 my $pm_info = $class->new_from_file( $file );
dd5a4b10 255
5ac756c6 256 foreach my $package ( $pm_info->packages_inside ) {
257 next if $package eq 'main'; # main can appear numerous times, ignore
258 next if $package eq 'DB'; # special debugging package, ignore
259 next if grep /^_/, split( /::/, $package ); # private package, ignore
dd5a4b10 260
5ac756c6 261 my $version = $pm_info->version( $package );
dd5a4b10 262
713aab0e 263 $prime_package = $package if lc($prime_package) eq lc($package);
5ac756c6 264 if ( $package eq $prime_package ) {
265 if ( exists( $prime{$package} ) ) {
6f3c7f28 266 croak "Unexpected conflict in '$package'; multiple versions found.\n";
5ac756c6 267 } else {
713aab0e 268 $mapped_filename = "$package.pm" if lc("$package.pm") eq lc($mapped_filename);
5ac756c6 269 $prime{$package}{file} = $mapped_filename;
270 $prime{$package}{version} = $version if defined( $version );
271 }
272 } else {
273 push( @{$alt{$package}}, {
274 file => $mapped_filename,
275 version => $version,
276 } );
277 }
278 }
279 }
dd5a4b10 280
5ac756c6 281 # Then we iterate over all the packages found above, identifying conflicts
282 # and selecting the "best" candidate for recording the file & version
283 # for each package.
284 foreach my $package ( keys( %alt ) ) {
285 my $result = $resolve_module_versions->( $alt{$package} );
dd5a4b10 286
5ac756c6 287 if ( exists( $prime{$package} ) ) { # primary package selected
dd5a4b10 288
5ac756c6 289 if ( $result->{err} ) {
141ef4bd 290 # Use the selected primary package, but there are conflicting
291 # errors among multiple alternative packages that need to be
292 # reported
5ac756c6 293 log_info {
141ef4bd 294 "Found conflicting versions for package '$package'\n" .
295 " $prime{$package}{file} ($prime{$package}{version})\n" .
296 $result->{err}
5ac756c6 297 };
dd5a4b10 298
5ac756c6 299 } elsif ( defined( $result->{version} ) ) {
141ef4bd 300 # There is a primary package selected, and exactly one
301 # alternative package
302
303 if ( exists( $prime{$package}{version} ) &&
304 defined( $prime{$package}{version} ) ) {
305 # Unless the version of the primary package agrees with the
306 # version of the alternative package, report a conflict
307 if ( $compare_versions->(
5ac756c6 308 $prime{$package}{version}, '!=', $result->{version}
309 )
310 ) {
311
312 log_info {
313 "Found conflicting versions for package '$package'\n" .
141ef4bd 314 " $prime{$package}{file} ($prime{$package}{version})\n" .
315 " $result->{file} ($result->{version})\n"
5ac756c6 316 };
141ef4bd 317 }
dd5a4b10 318
141ef4bd 319 } else {
320 # The prime package selected has no version so, we choose to
321 # use any alternative package that does have a version
322 $prime{$package}{file} = $result->{file};
323 $prime{$package}{version} = $result->{version};
324 }
dd5a4b10 325
5ac756c6 326 } else {
141ef4bd 327 # no alt package found with a version, but we have a prime
328 # package so we use it whether it has a version or not
5ac756c6 329 }
dd5a4b10 330
5ac756c6 331 } else { # No primary package was selected, use the best alternative
dd5a4b10 332
5ac756c6 333 if ( $result->{err} ) {
334 log_info {
335 "Found conflicting versions for package '$package'\n" .
141ef4bd 336 $result->{err}
5ac756c6 337 };
338 }
dd5a4b10 339
5ac756c6 340 # Despite possible conflicting versions, we choose to record
341 # something rather than nothing
342 $prime{$package}{file} = $result->{file};
343 $prime{$package}{version} = $result->{version}
141ef4bd 344 if defined( $result->{version} );
5ac756c6 345 }
346 }
dd5a4b10 347
5ac756c6 348 # Normalize versions. Can't use exists() here because of bug in YAML::Node.
868feb8a 349 # XXX "bug in YAML::Node" comment seems irrelevant -- dagolden, 2009-05-18
5ac756c6 350 for (grep defined $_->{version}, values %prime) {
351 $_->{version} = $normalize_version->( $_->{version} );
352 }
dd5a4b10 353
5ac756c6 354 return \%prime;
355 }
dd5a4b10 356}
357
5ac756c6 358
359sub _init {
360 my $class = shift;
361 my $module = shift;
362 my $filename = shift;
363 my %props = @_;
364
f33c0a6c 365 my $handle = delete $props{handle};
5ac756c6 366 my( %valid_props, @valid_props );
367 @valid_props = qw( collect_pod inc );
368 @valid_props{@valid_props} = delete( @props{@valid_props} );
369 warn "Unknown properties: @{[keys %props]}\n" if scalar( %props );
370
371 my %data = (
372 module => $module,
373 filename => $filename,
374 version => undef,
375 packages => [],
376 versions => {},
377 pod => {},
378 pod_headings => [],
379 collect_pod => 0,
380
381 %valid_props,
382 );
383
384 my $self = bless(\%data, $class);
385
f33c0a6c 386 if ( $handle ) {
387 $self->_parse_fh($handle);
388 }
389 else {
390 $self->_parse_file();
391 }
5ac756c6 392
393 unless($self->{module} and length($self->{module})) {
394 my ($v, $d, $f) = File::Spec->splitpath($self->{filename});
395 if($f =~ /\.pm$/) {
396 $f =~ s/\..+$//;
397 my @candidates = grep /$f$/, @{$self->{packages}};
398 $self->{module} = shift(@candidates); # punt
399 }
400 else {
401 if(grep /main/, @{$self->{packages}}) {
402 $self->{module} = 'main';
403 }
404 else {
405 $self->{module} = $self->{packages}[0] || '';
406 }
407 }
408 }
409
410 $self->{version} = $self->{versions}{$self->{module}}
411 if defined( $self->{module} );
412
413 return $self;
414}
415
416# class method
417sub _do_find_module {
418 my $class = shift;
6f3c7f28 419 my $module = shift || croak 'find_module_by_name() requires a package name';
5ac756c6 420 my $dirs = shift || \@INC;
421
422 my $file = File::Spec->catfile(split( /::/, $module));
423 foreach my $dir ( @$dirs ) {
424 my $testfile = File::Spec->catfile($dir, $file);
425 return [ File::Spec->rel2abs( $testfile ), $dir ]
141ef4bd 426 if -e $testfile and !-d _; # For stuff like ExtUtils::xsubpp
5ac756c6 427 return [ File::Spec->rel2abs( "$testfile.pm" ), $dir ]
141ef4bd 428 if -e "$testfile.pm";
5ac756c6 429 }
430 return;
431}
432
433# class method
434sub find_module_by_name {
435 my $found = shift()->_do_find_module(@_) or return;
436 return $found->[0];
437}
438
439# class method
440sub find_module_dir_by_name {
441 my $found = shift()->_do_find_module(@_) or return;
442 return $found->[1];
443}
444
445
446# given a line of perl code, attempt to parse it if it looks like a
447# $VERSION assignment, returning sigil, full name, & package name
448sub _parse_version_expression {
449 my $self = shift;
450 my $line = shift;
451
452 my( $sig, $var, $pkg );
69859aa0 453 if ( $line =~ /$VERS_REGEXP/o ) {
5ac756c6 454 ( $sig, $var, $pkg ) = $2 ? ( $1, $2, $3 ) : ( $4, $5, $6 );
455 if ( $pkg ) {
456 $pkg = ($pkg eq '::') ? 'main' : $pkg;
457 $pkg =~ s/::$//;
458 }
459 }
460
461 return ( $sig, $var, $pkg );
462}
463
464sub _parse_file {
465 my $self = shift;
466
467 my $filename = $self->{filename};
468 my $fh = IO::File->new( $filename )
6f3c7f28 469 or croak( "Can't open '$filename': $!" );
5ac756c6 470
f77c3f08 471 $self->_handle_bom($fh, $filename);
472
5ac756c6 473 $self->_parse_fh($fh);
474}
475
f77c3f08 476# Look for a UTF-8/UTF-16BE/UTF-16LE BOM at the beginning of the stream.
477# If there's one, then skip it and set the :encoding layer appropriately.
478sub _handle_bom {
479 my ($self, $fh, $filename) = @_;
480
481 my $pos = $fh->getpos;
482 return unless defined $pos;
483
484 my $buf = ' ' x 2;
485 my $count = $fh->read( $buf, length $buf );
486 return unless defined $count and $count >= 2;
487
488 my $encoding;
489 if ( $buf eq "\x{FE}\x{FF}" ) {
490 $encoding = 'UTF-16BE';
491 } elsif ( $buf eq "\x{FF}\x{FE}" ) {
492 $encoding = 'UTF-16LE';
493 } elsif ( $buf eq "\x{EF}\x{BB}" ) {
494 $buf = ' ';
495 $count = $fh->read( $buf, length $buf );
496 if ( defined $count and $count >= 1 and $buf eq "\x{BF}" ) {
497 $encoding = 'UTF-8';
498 }
499 }
500
501 if ( defined $encoding ) {
502 if ( "$]" >= 5.008 ) {
503 # $fh->binmode requires perl 5.10
504 binmode( $fh, ":encoding($encoding)" );
505 }
506 } else {
507 $fh->setpos($pos)
508 or croak( sprintf "Can't reset position to the top of '$filename'" );
509 }
510
511 return $encoding;
512}
513
5ac756c6 514sub _parse_fh {
515 my ($self, $fh) = @_;
516
517 my( $in_pod, $seen_end, $need_vers ) = ( 0, 0, 0 );
518 my( @pkgs, %vers, %pod, @pod );
519 my $pkg = 'main';
520 my $pod_sect = '';
521 my $pod_data = '';
30890a12 522 my $in_end = 0;
5ac756c6 523
524 while (defined( my $line = <$fh> )) {
525 my $line_num = $.;
526
527 chomp( $line );
5ac756c6 528
cfedad89 529 # From toke.c : any line that begins by "=X", where X is an alphabetic
530 # character, introduces a POD segment.
921abefc 531 my $is_cut;
cfedad89 532 if ( $line =~ /^=([a-zA-Z].*)/ ) {
533 my $cmd = $1;
534 # Then it goes back to Perl code for "=cutX" where X is a non-alphabetic
535 # character (which includes the newline, but here we chomped it away).
536 $is_cut = $cmd =~ /^cut(?:[^a-zA-Z]|$)/;
921abefc 537 $in_pod = !$is_cut;
538 }
5ac756c6 539
617f8754 540 if ( $in_pod ) {
5ac756c6 541
a4aafbc2 542 if ( $line =~ /^=head[1-4]\s+(.+)\s*$/ ) {
141ef4bd 543 push( @pod, $1 );
544 if ( $self->{collect_pod} && length( $pod_data ) ) {
5ac756c6 545 $pod{$pod_sect} = $pod_data;
546 $pod_data = '';
547 }
141ef4bd 548 $pod_sect = $1;
5ac756c6 549
5ac756c6 550 } elsif ( $self->{collect_pod} ) {
141ef4bd 551 $pod_data .= "$line\n";
5ac756c6 552
553 }
554
617f8754 555 } elsif ( $is_cut ) {
5ac756c6 556
617f8754 557 if ( $self->{collect_pod} && length( $pod_data ) ) {
558 $pod{$pod_sect} = $pod_data;
559 $pod_data = '';
560 }
5ac756c6 561 $pod_sect = '';
617f8754 562
563 } else {
5ac756c6 564
30890a12 565 # Skip after __END__
566 next if $in_end;
567
81ce8c82 568 # Skip comments in code
569 next if $line =~ /^\s*#/;
570
b48c592a 571 # Would be nice if we could also check $in_string or something too
30890a12 572 if ($line eq '__END__') {
573 $in_end++;
574 next;
575 }
576 last if $line eq '__DATA__';
b48c592a 577
5ac756c6 578 # parse $line to see if it's a $VERSION declaration
579 my( $vers_sig, $vers_fullname, $vers_pkg ) =
9922478c 580 ($line =~ /VERSION/)
581 ? $self->_parse_version_expression( $line )
582 : ();
5ac756c6 583
69859aa0 584 if ( $line =~ /$PKG_REGEXP/o ) {
5ac756c6 585 $pkg = $1;
586 push( @pkgs, $pkg ) unless grep( $pkg eq $_, @pkgs );
a88210e4 587 $vers{$pkg} = $2 unless exists( $vers{$pkg} );
5ac756c6 588 $need_vers = defined $2 ? 0 : 1;
589
590 # VERSION defined with full package spec, i.e. $Module::VERSION
591 } elsif ( $vers_fullname && $vers_pkg ) {
141ef4bd 592 push( @pkgs, $vers_pkg ) unless grep( $vers_pkg eq $_, @pkgs );
593 $need_vers = 0 if $vers_pkg eq $pkg;
5ac756c6 594
141ef4bd 595 unless ( defined $vers{$vers_pkg} && length $vers{$vers_pkg} ) {
596 $vers{$vers_pkg} = $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
597 }
5ac756c6 598
599 # first non-comment line in undeclared package main is VERSION
600 } elsif ( !exists($vers{main}) && $pkg eq 'main' && $vers_fullname ) {
141ef4bd 601 $need_vers = 0;
602 my $v = $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
603 $vers{$pkg} = $v;
604 push( @pkgs, 'main' );
5ac756c6 605
606 # first non-comment line in undeclared package defines package main
607 } elsif ( !exists($vers{main}) && $pkg eq 'main' && $line =~ /\w+/ ) {
141ef4bd 608 $need_vers = 1;
609 $vers{main} = '';
610 push( @pkgs, 'main' );
5ac756c6 611
612 # only keep if this is the first $VERSION seen
613 } elsif ( $vers_fullname && $need_vers ) {
141ef4bd 614 $need_vers = 0;
615 my $v = $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
5ac756c6 616
141ef4bd 617 unless ( defined $vers{$pkg} && length $vers{$pkg} ) {
618 $vers{$pkg} = $v;
619 }
5ac756c6 620 }
5ac756c6 621 }
5ac756c6 622 }
623
624 if ( $self->{collect_pod} && length($pod_data) ) {
625 $pod{$pod_sect} = $pod_data;
626 }
627
628 $self->{versions} = \%vers;
629 $self->{packages} = \@pkgs;
630 $self->{pod} = \%pod;
631 $self->{pod_headings} = \@pod;
632}
633
634{
635my $pn = 0;
636sub _evaluate_version_line {
637 my $self = shift;
638 my( $sigil, $var, $line ) = @_;
639
640 # Some of this code came from the ExtUtils:: hierarchy.
641
642 # We compile into $vsub because 'use version' would cause
643 # compiletime/runtime issues with local()
644 my $vsub;
645 $pn++; # everybody gets their own package
a8a9baef 646 my $eval = qq{BEGIN { my \$dummy = q# Hide from _packages_inside()
5ac756c6 647 #; package Module::Metadata::_version::p$pn;
4850170c 648 use version;
5ac756c6 649 no strict;
72a35bb9 650 no warnings;
5ac756c6 651
5ac756c6 652 \$vsub = sub {
398fe5a2 653 local $sigil$var;
654 \$$var=undef;
5ac756c6 655 $line;
656 \$$var
657 };
658 }};
659
5ae49e26 660 $eval = $1 if $eval =~ m{^(.+)}s;
661
5ac756c6 662 local $^W;
663 # Try to get the $VERSION
664 eval $eval;
665 # some modules say $VERSION = $Foo::Bar::VERSION, but Foo::Bar isn't
666 # installed, so we need to hunt in ./lib for it
667 if ( $@ =~ /Can't locate/ && -d 'lib' ) {
668 local @INC = ('lib',@INC);
669 eval $eval;
670 }
671 warn "Error evaling version line '$eval' in $self->{filename}: $@\n"
672 if $@;
673 (ref($vsub) eq 'CODE') or
6f3c7f28 674 croak "failed to build version sub for $self->{filename}";
5ac756c6 675 my $result = eval { $vsub->() };
6f3c7f28 676 croak "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n"
5ac756c6 677 if $@;
678
d880ef1f 679 # Upgrade it into a version object
92ad06ed 680 my $version = eval { _dwim_version($result) };
681
6f3c7f28 682 croak "Version '$result' from $self->{filename} does not appear to be valid:\n$eval\n\nThe fatal error was: $@\n"
92ad06ed 683 unless defined $version; # "0" is OK!
5ac756c6 684
92ad06ed 685 return $version;
5ac756c6 686}
687}
688
92ad06ed 689# Try to DWIM when things fail the lax version test in obvious ways
690{
691 my @version_prep = (
692 # Best case, it just works
693 sub { return shift },
694
695 # If we still don't have a version, try stripping any
696 # trailing junk that is prohibited by lax rules
697 sub {
698 my $v = shift;
699 $v =~ s{([0-9])[a-z-].*$}{$1}i; # 1.23-alpha or 1.23b
700 return $v;
701 },
702
703 # Activestate apparently creates custom versions like '1.23_45_01', which
704 # cause version.pm to think it's an invalid alpha. So check for that
705 # and strip them
706 sub {
707 my $v = shift;
708 my $num_dots = () = $v =~ m{(\.)}g;
709 my $num_unders = () = $v =~ m{(_)}g;
710 my $leading_v = substr($v,0,1) eq 'v';
711 if ( ! $leading_v && $num_dots < 2 && $num_unders > 1 ) {
712 $v =~ s{_}{}g;
713 $num_unders = () = $v =~ m{(_)}g;
714 }
715 return $v;
716 },
717
718 # Worst case, try numifying it like we would have before version objects
719 sub {
720 my $v = shift;
721 no warnings 'numeric';
722 return 0 + $v;
723 },
724
725 );
726
727 sub _dwim_version {
728 my ($result) = shift;
729
730 return $result if ref($result) eq 'version';
731
732 my ($version, $error);
733 for my $f (@version_prep) {
734 $result = $f->($result);
735 $version = eval { version->new($result) };
736 $error ||= $@ if $@; # capture first failure
737 last if defined $version;
738 }
739
6f3c7f28 740 croak $error unless defined $version;
92ad06ed 741
742 return $version;
743 }
744}
5ac756c6 745
746############################################################
747
748# accessors
b50a9801 749sub name { $_[0]->{module} }
5ac756c6 750
b50a9801 751sub filename { $_[0]->{filename} }
752sub packages_inside { @{$_[0]->{packages}} }
753sub pod_inside { @{$_[0]->{pod_headings}} }
754sub contains_pod { 0+@{$_[0]->{pod_headings}} }
5ac756c6 755
756sub version {
757 my $self = shift;
758 my $mod = shift || $self->{module};
759 my $vers;
760 if ( defined( $mod ) && length( $mod ) &&
141ef4bd 761 exists( $self->{versions}{$mod} ) ) {
762 return $self->{versions}{$mod};
5ac756c6 763 } else {
141ef4bd 764 return undef;
5ac756c6 765 }
766}
767
768sub pod {
769 my $self = shift;
770 my $sect = shift;
771 if ( defined( $sect ) && length( $sect ) &&
141ef4bd 772 exists( $self->{pod}{$sect} ) ) {
773 return $self->{pod}{$sect};
5ac756c6 774 } else {
141ef4bd 775 return undef;
5ac756c6 776 }
777}
778
22553051 779sub is_indexable {
780 my ($self, $package) = @_;
781
782 my @indexable_packages = grep { $_ ne 'main' } $self->packages_inside;
783
784 # check for specific package, if provided
785 return !! grep { $_ eq $package } @indexable_packages if $package;
786
787 # otherwise, check for any indexable packages at all
788 return !! @indexable_packages;
789}
790
5ac756c6 7911;
792
5ac756c6 793=head1 NAME
794
2c11e51d 795Module::Metadata - Gather package and POD information from perl module files
5ac756c6 796
6290f67c 797=head1 SYNOPSIS
798
799 use Module::Metadata;
800
801 # information about a .pm file
802 my $info = Module::Metadata->new_from_file( $file );
803 my $version = $info->version;
804
ca33f3bd 805 # CPAN META 'provides' field for .pm files in a directory
c06d0187 806 my $provides = Module::Metadata->provides(
807 dir => 'lib', version => 2
808 );
6290f67c 809
5ac756c6 810=head1 DESCRIPTION
811
c0278e58 812This module provides a standard way to gather metadata about a .pm file through
813(mostly) static analysis and (some) code execution. When determining the
814version of a module, the C<$VERSION> assignment is C<eval>ed, as is traditional
815in the CPAN toolchain.
6290f67c 816
817=head1 USAGE
818
819=head2 Class methods
820
5ac756c6 821=over 4
822
6290f67c 823=item C<< new_from_file($filename, collect_pod => 1) >>
5ac756c6 824
4c401e3f 825Constructs a C<Module::Metadata> object given the path to a file. Returns
826undef if the filename does not exist.
827
828C<collect_pod> is a optional boolean argument that determines whether POD
829data is collected and stored for reference. POD data is not collected by
830default. POD headings are always collected.
831
832If the file begins by an UTF-8, UTF-16BE or UTF-16LE byte-order mark, then
833it is skipped before processing, and the content of the file is also decoded
834appropriately starting from perl 5.8.
5ac756c6 835
6290f67c 836=item C<< new_from_handle($handle, $filename, collect_pod => 1) >>
f33c0a6c 837
838This works just like C<new_from_file>, except that a handle can be provided
4c401e3f 839as the first argument.
840
841Note that there is no validation to confirm that the handle is a handle or
842something that can act like one. Passing something that isn't a handle will
843cause a exception when trying to read from it. The C<filename> argument is
844mandatory or undef will be returned.
845
846You are responsible for setting the decoding layers on C<$handle> if
847required.
f33c0a6c 848
6290f67c 849=item C<< new_from_module($module, collect_pod => 1, inc => \@dirs) >>
5ac756c6 850
4c401e3f 851Constructs a C<Module::Metadata> object given a module or package name.
852Returns undef if the module cannot be found.
853
854In addition to accepting the C<collect_pod> argument as described above,
855this method accepts a C<inc> argument which is a reference to an array of
856directories to search for the module. If none are given, the default is
857@INC.
858
859If the file that contains the module begins by an UTF-8, UTF-16BE or
860UTF-16LE byte-order mark, then it is skipped before processing, and the
861content of the file is also decoded appropriately starting from perl 5.8.
5ac756c6 862
6290f67c 863=item C<< find_module_by_name($module, \@dirs) >>
5ac756c6 864
865Returns the path to a module given the module or package name. A list
866of directories can be passed in as an optional parameter, otherwise
867@INC is searched.
868
869Can be called as either an object or a class method.
870
6290f67c 871=item C<< find_module_dir_by_name($module, \@dirs) >>
5ac756c6 872
873Returns the entry in C<@dirs> (or C<@INC> by default) that contains
874the module C<$module>. A list of directories can be passed in as an
875optional parameter, otherwise @INC is searched.
876
877Can be called as either an object or a class method.
878
ca33f3bd 879=item C<< provides( %options ) >>
880
881This is a convenience wrapper around C<package_versions_from_directory>
882to generate a CPAN META C<provides> data structure. It takes key/value
883pairs. Valid option keys include:
884
885=over
886
c06d0187 887=item version B<(required)>
888
889Specifies which version of the L<CPAN::Meta::Spec> should be used as
890the format of the C<provides> output. Currently only '1.4' and '2'
891are supported (and their format is identical). This may change in
892the future as the definition of C<provides> changes.
893
894The C<version> option is required. If it is omitted or if
895an unsupported version is given, then C<provides> will throw an error.
896
ca33f3bd 897=item dir
898
899Directory to search recursively for F<.pm> files. May not be specified with
900C<files>.
901
902=item files
903
904Array reference of files to examine. May not be specified with C<dir>.
905
906=item prefix
907
908String to prepend to the C<file> field of the resulting output. This defaults
909to F<lib>, which is the common case for most CPAN distributions with their
910F<.pm> files in F<lib>. This option ensures the META information has the
911correct relative path even when the C<dir> or C<files> arguments are
912absolute or have relative paths from a location other than the distribution
913root.
914
915=back
916
917For example, given C<dir> of 'lib' and C<prefix> of 'lib', the return value
918is a hashref of the form:
919
920 {
921 'Package::Name' => {
922 version => '0.123',
923 file => 'lib/Package/Name.pm'
924 },
925 'OtherPackage::Name' => ...
926 }
927
6290f67c 928=item C<< package_versions_from_directory($dir, \@files?) >>
2c11e51d 929
930Scans C<$dir> for .pm files (unless C<@files> is given, in which case looks
931for those files in C<$dir> - and reads each file for packages and versions,
932returning a hashref of the form:
5ac756c6 933
2c11e51d 934 {
935 'Package::Name' => {
936 version => '0.123',
937 file => 'Package/Name.pm'
938 },
939 'OtherPackage::Name' => ...
940 }
941
ca33f3bd 942The C<DB> and C<main> packages are always omitted, as are any "private"
943packages that have leading underscores in the namespace (e.g.
944C<Foo::_private>)
945
946Note that the file path is relative to C<$dir> if that is specified.
947This B<must not> be used directly for CPAN META C<provides>. See
948the C<provides> method instead.
949
6290f67c 950=item C<< log_info (internal) >>
2c11e51d 951
952Used internally to perform logging; imported from Log::Contextual if
953Log::Contextual has already been loaded, otherwise simply calls warn.
954
955=back
5ac756c6 956
6290f67c 957=head2 Object methods
958
959=over 4
960
961=item C<< name() >>
962
963Returns the name of the package represented by this module. If there
ff547532 964is more than one package, it makes a best guess based on the
6290f67c 965filename. If it's a script (i.e. not a *.pm) the package name is
966'main'.
967
968=item C<< version($package) >>
969
970Returns the version as defined by the $VERSION variable for the
971package as returned by the C<name> method if no arguments are
972given. If given the name of a package it will attempt to return the
973version of that package if it is specified in the file.
974
975=item C<< filename() >>
976
977Returns the absolute path to the file.
978
979=item C<< packages_inside() >>
980
1f67ffd7 981Returns a list of packages. Note: this is a raw list of packages
982discovered (or assumed, in the case of C<main>). It is not
983filtered for C<DB>, C<main> or private packages the way the
8e4bef01 984C<provides> method does. Invalid package names are not returned,
985for example "Foo:Bar". Strange but valid package names are
986returned, for example "Foo::Bar::", and are left up to the caller
987on how to handle.
6290f67c 988
989=item C<< pod_inside() >>
990
991Returns a list of POD sections.
992
993=item C<< contains_pod() >>
994
995Returns true if there is any POD in the file.
996
997=item C<< pod($section) >>
998
999Returns the POD data in the given section.
1000
22553051 1001=item C<< is_indexable($package) >> or C<< is_indexable() >>
1002
1003Returns a boolean indicating whether the package (if provided) or any package
1004(otherwise) is eligible for indexing by PAUSE, the Perl Authors Upload Server.
1005Note This only checks for valid C<package> declarations, and does not take any
1006ownership information into account.
1007
6290f67c 1008=back
1009
5ac756c6 1010=head1 AUTHOR
1011
6290f67c 1012Original code from Module::Build::ModuleInfo by Ken Williams
1013<kwilliams@cpan.org>, Randy W. Sims <RandyS@ThePierianSpring.org>
5ac756c6 1014
2c11e51d 1015Released as Module::Metadata by Matt S Trout (mst) <mst@shadowcat.co.uk> with
6290f67c 1016assistance from David Golden (xdg) <dagolden@cpan.org>.
5ac756c6 1017
4c401e3f 1018=head1 COPYRIGHT & LICENSE
5ac756c6 1019
6290f67c 1020Original code Copyright (c) 2001-2011 Ken Williams.
1021Additional code Copyright (c) 2010-2011 Matt Trout and David Golden.
1022All rights reserved.
5ac756c6 1023
1024This library is free software; you can redistribute it and/or
1025modify it under the same terms as Perl itself.
1026
5ac756c6 1027=cut
1028