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