1 # -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*-
2 # vim:ts=8:sw=2:et:sta:sts=2
3 package Module::Metadata;
5 # stolen from Module::Build::Version and ::Base - this is perl licensed code,
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).
13 use vars qw($VERSION);
15 $VERSION = eval $VERSION;
19 use Module::Metadata::Version;
20 use Log::Contextual qw(:log :dlog);
21 use File::Find qw(find);
23 my $V_NUM_REGEXP = qr{v?[0-9._]+}; # crudely, a v-string or decimal
25 my $PKG_REGEXP = qr{ # match a package declaration
26 ^[\s\{;]* # intro chars on a line
27 package # the word 'package'
29 ([\w:]+) # a package name
30 \s* # optional whitespace
31 ($V_NUM_REGEXP)? # optional version number
32 \s* # optional whitesapce
33 ; # semicolon line terminator
36 my $VARNAME_REGEXP = qr{ # match fully-qualified VERSION name
37 ([\$*]) # sigil - $ or *
39 ( # optional leading package name
40 (?:::|\')? # possibly starting like just :: (Ì la $::VERSION)
41 (?:\w+(?:::|\'))* # Foo::Bar:: ...
47 my $VERS_REGEXP = qr{ # match a VERSION definition
49 \(\s*$VARNAME_REGEXP\s*\) # with parens
51 $VARNAME_REGEXP # without parens
54 =[^=~] # = but not ==, nor =~
60 my $filename = File::Spec->rel2abs( shift );
62 return undef unless defined( $filename ) && -f $filename;
63 return $class->_init(undef, $filename, @_);
71 $props{inc} ||= \@INC;
72 my $filename = $class->find_module_by_name( $module, $props{inc} );
73 return undef unless defined( $filename ) && -f $filename;
74 return $class->_init($module, $filename, %props);
79 my $compare_versions = sub {
80 my ($v1, $op, $v2) = @_;
81 $v1 = Module::Metadata::Version->new($v1)
82 unless UNIVERSAL::isa($v1,'Module::Metadata::Version');
84 my $eval_str = "\$v1 $op \$v2";
85 my $result = eval $eval_str;
86 log_info { "error comparing versions: '$eval_str' $@" } if $@;
91 my $normalize_version = sub {
93 if ( $version =~ /[=<>!,]/ ) { # logic, not just version
94 # take as is without modification
96 elsif ( ref $version eq 'version' ||
97 ref $version eq 'Module::Metadata::Version' ) { # version objects
98 $version = $version->is_qv ? $version->normal : $version->stringify;
100 elsif ( $version =~ /^[^v][^.]*\.[^.]+\./ ) { # no leading v, multiple dots
101 # normalize string tuples without "v": "1.2.3" -> "v1.2.3"
102 $version = "v$version";
110 # separate out some of the conflict resolution logic
112 my $resolve_module_versions = sub {
113 my $packages = shift;
115 my( $file, $version );
117 foreach my $p ( @$packages ) {
118 if ( defined( $p->{version} ) ) {
119 if ( defined( $version ) ) {
120 if ( $compare_versions->( $version, '!=', $p->{version} ) ) {
121 $err .= " $p->{file} ($p->{version})\n";
123 # same version declared multiple times, ignore
127 $version = $p->{version};
130 $file ||= $p->{file} if defined( $p->{file} );
134 $err = " $file ($version)\n" . $err;
146 sub package_versions_from_directory {
147 my ( $class, $dir, $files ) = @_;
156 push @files, $_ if -f $_ && /\.pm$/;
162 # First, we enumerate all packages & versions,
163 # separating into primary & alternative candidates
165 foreach my $file (@files) {
166 my $mapped_filename = File::Spec->abs2rel( $file, $dir );
167 my @path = split( /\//, $mapped_filename );
168 (my $prime_package = join( '::', @path )) =~ s/\.pm$//;
170 my $pm_info = $class->new_from_file( $file );
172 foreach my $package ( $pm_info->packages_inside ) {
173 next if $package eq 'main'; # main can appear numerous times, ignore
174 next if $package eq 'DB'; # special debugging package, ignore
175 next if grep /^_/, split( /::/, $package ); # private package, ignore
177 my $version = $pm_info->version( $package );
179 if ( $package eq $prime_package ) {
180 if ( exists( $prime{$package} ) ) {
181 # M::B::ModuleInfo will handle this conflict
182 die "Unexpected conflict in '$package'; multiple versions found.\n";
184 $prime{$package}{file} = $mapped_filename;
185 $prime{$package}{version} = $version if defined( $version );
188 push( @{$alt{$package}}, {
189 file => $mapped_filename,
196 # Then we iterate over all the packages found above, identifying conflicts
197 # and selecting the "best" candidate for recording the file & version
199 foreach my $package ( keys( %alt ) ) {
200 my $result = $resolve_module_versions->( $alt{$package} );
202 if ( exists( $prime{$package} ) ) { # primary package selected
204 if ( $result->{err} ) {
205 # Use the selected primary package, but there are conflicting
206 # errors among multiple alternative packages that need to be
209 "Found conflicting versions for package '$package'\n" .
210 " $prime{$package}{file} ($prime{$package}{version})\n" .
214 } elsif ( defined( $result->{version} ) ) {
215 # There is a primary package selected, and exactly one
216 # alternative package
218 if ( exists( $prime{$package}{version} ) &&
219 defined( $prime{$package}{version} ) ) {
220 # Unless the version of the primary package agrees with the
221 # version of the alternative package, report a conflict
222 if ( $compare_versions->(
223 $prime{$package}{version}, '!=', $result->{version}
228 "Found conflicting versions for package '$package'\n" .
229 " $prime{$package}{file} ($prime{$package}{version})\n" .
230 " $result->{file} ($result->{version})\n"
235 # The prime package selected has no version so, we choose to
236 # use any alternative package that does have a version
237 $prime{$package}{file} = $result->{file};
238 $prime{$package}{version} = $result->{version};
242 # no alt package found with a version, but we have a prime
243 # package so we use it whether it has a version or not
246 } else { # No primary package was selected, use the best alternative
248 if ( $result->{err} ) {
250 "Found conflicting versions for package '$package'\n" .
255 # Despite possible conflicting versions, we choose to record
256 # something rather than nothing
257 $prime{$package}{file} = $result->{file};
258 $prime{$package}{version} = $result->{version}
259 if defined( $result->{version} );
263 # Normalize versions. Can't use exists() here because of bug in YAML::Node.
264 # XXX "bug in YAML::Node" comment seems irrelvant -- dagolden, 2009-05-18
265 for (grep defined $_->{version}, values %prime) {
266 $_->{version} = $normalize_version->( $_->{version} );
277 my $filename = shift;
280 my( %valid_props, @valid_props );
281 @valid_props = qw( collect_pod inc );
282 @valid_props{@valid_props} = delete( @props{@valid_props} );
283 warn "Unknown properties: @{[keys %props]}\n" if scalar( %props );
287 filename => $filename,
298 my $self = bless(\%data, $class);
300 $self->_parse_file();
302 unless($self->{module} and length($self->{module})) {
303 my ($v, $d, $f) = File::Spec->splitpath($self->{filename});
306 my @candidates = grep /$f$/, @{$self->{packages}};
307 $self->{module} = shift(@candidates); # punt
310 if(grep /main/, @{$self->{packages}}) {
311 $self->{module} = 'main';
314 $self->{module} = $self->{packages}[0] || '';
319 $self->{version} = $self->{versions}{$self->{module}}
320 if defined( $self->{module} );
326 sub _do_find_module {
328 my $module = shift || die 'find_module_by_name() requires a package name';
329 my $dirs = shift || \@INC;
331 my $file = File::Spec->catfile(split( /::/, $module));
332 foreach my $dir ( @$dirs ) {
333 my $testfile = File::Spec->catfile($dir, $file);
334 return [ File::Spec->rel2abs( $testfile ), $dir ]
335 if -e $testfile and !-d _; # For stuff like ExtUtils::xsubpp
336 return [ File::Spec->rel2abs( "$testfile.pm" ), $dir ]
337 if -e "$testfile.pm";
343 sub find_module_by_name {
344 my $found = shift()->_do_find_module(@_) or return;
349 sub find_module_dir_by_name {
350 my $found = shift()->_do_find_module(@_) or return;
355 # given a line of perl code, attempt to parse it if it looks like a
356 # $VERSION assignment, returning sigil, full name, & package name
357 sub _parse_version_expression {
361 my( $sig, $var, $pkg );
362 if ( $line =~ $VERS_REGEXP ) {
363 ( $sig, $var, $pkg ) = $2 ? ( $1, $2, $3 ) : ( $4, $5, $6 );
365 $pkg = ($pkg eq '::') ? 'main' : $pkg;
370 return ( $sig, $var, $pkg );
376 my $filename = $self->{filename};
377 my $fh = IO::File->new( $filename )
378 or die( "Can't open '$filename': $!" );
380 $self->_parse_fh($fh);
384 my ($self, $fh) = @_;
386 my( $in_pod, $seen_end, $need_vers ) = ( 0, 0, 0 );
387 my( @pkgs, %vers, %pod, @pod );
392 while (defined( my $line = <$fh> )) {
396 next if $line =~ /^\s*#/;
398 $in_pod = ($line =~ /^=(?!cut)/) ? 1 : ($line =~ /^=cut/) ? 0 : $in_pod;
400 # Would be nice if we could also check $in_string or something too
401 last if !$in_pod && $line =~ /^__(?:DATA|END)__$/;
403 if ( $in_pod || $line =~ /^=cut/ ) {
405 if ( $line =~ /^=head\d\s+(.+)\s*$/ ) {
407 if ( $self->{collect_pod} && length( $pod_data ) ) {
408 $pod{$pod_sect} = $pod_data;
414 } elsif ( $self->{collect_pod} ) {
415 $pod_data .= "$line\n";
424 # parse $line to see if it's a $VERSION declaration
425 my( $vers_sig, $vers_fullname, $vers_pkg ) =
426 $self->_parse_version_expression( $line );
428 if ( $line =~ $PKG_REGEXP ) {
430 push( @pkgs, $pkg ) unless grep( $pkg eq $_, @pkgs );
431 $vers{$pkg} = (defined $2 ? $2 : undef) unless exists( $vers{$pkg} );
432 $need_vers = defined $2 ? 0 : 1;
434 # VERSION defined with full package spec, i.e. $Module::VERSION
435 } elsif ( $vers_fullname && $vers_pkg ) {
436 push( @pkgs, $vers_pkg ) unless grep( $vers_pkg eq $_, @pkgs );
437 $need_vers = 0 if $vers_pkg eq $pkg;
439 unless ( defined $vers{$vers_pkg} && length $vers{$vers_pkg} ) {
441 $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
443 # Warn unless the user is using the "$VERSION = eval
444 # $VERSION" idiom (though there are probably other idioms
445 # that we should watch out for...)
446 warn <<"EOM" unless $line =~ /=\s*eval/;
447 Package '$vers_pkg' already declared with version '$vers{$vers_pkg}',
448 ignoring subsequent declaration on line $line_num.
452 # first non-comment line in undeclared package main is VERSION
453 } elsif ( !exists($vers{main}) && $pkg eq 'main' && $vers_fullname ) {
456 $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
458 push( @pkgs, 'main' );
460 # first non-comment line in undeclared package defines package main
461 } elsif ( !exists($vers{main}) && $pkg eq 'main' && $line =~ /\w+/ ) {
464 push( @pkgs, 'main' );
466 # only keep if this is the first $VERSION seen
467 } elsif ( $vers_fullname && $need_vers ) {
470 $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
473 unless ( defined $vers{$pkg} && length $vers{$pkg} ) {
477 Package '$pkg' already declared with version '$vers{$pkg}'
478 ignoring new version '$v' on line $line_num.
488 if ( $self->{collect_pod} && length($pod_data) ) {
489 $pod{$pod_sect} = $pod_data;
492 $self->{versions} = \%vers;
493 $self->{packages} = \@pkgs;
494 $self->{pod} = \%pod;
495 $self->{pod_headings} = \@pod;
500 sub _evaluate_version_line {
502 my( $sigil, $var, $line ) = @_;
504 # Some of this code came from the ExtUtils:: hierarchy.
506 # We compile into $vsub because 'use version' would cause
507 # compiletime/runtime issues with local()
509 $pn++; # everybody gets their own package
510 my $eval = qq{BEGIN { q# Hide from _packages_inside()
511 #; package Module::Metadata::_version::p$pn;
512 use Module::Metadata::Version;
524 # Try to get the $VERSION
526 # some modules say $VERSION = $Foo::Bar::VERSION, but Foo::Bar isn't
527 # installed, so we need to hunt in ./lib for it
528 if ( $@ =~ /Can't locate/ && -d 'lib' ) {
529 local @INC = ('lib',@INC);
532 warn "Error evaling version line '$eval' in $self->{filename}: $@\n"
534 (ref($vsub) eq 'CODE') or
535 die "failed to build version sub for $self->{filename}";
536 my $result = eval { $vsub->() };
537 die "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n"
540 # Activestate apparently creates custom versions like '1.23_45_01', which
541 # cause M::B::Version to think it's an invalid alpha. So check for that
543 my $num_dots = () = $result =~ m{\.}g;
544 my $num_unders = () = $result =~ m{_}g;
545 if ( substr($result,0,1) ne 'v' && $num_dots < 2 && $num_unders > 1 ) {
549 # Bless it into our own version class
550 eval { $result = Module::Metadata::Version->new($result) };
551 die "Version '$result' from $self->{filename} does not appear to be valid:\n$eval\n\nThe fatal error was: $@\n"
559 ############################################################
562 sub name { $_[0]->{module} }
564 sub filename { $_[0]->{filename} }
565 sub packages_inside { @{$_[0]->{packages}} }
566 sub pod_inside { @{$_[0]->{pod_headings}} }
567 sub contains_pod { $#{$_[0]->{pod_headings}} }
571 my $mod = shift || $self->{module};
573 if ( defined( $mod ) && length( $mod ) &&
574 exists( $self->{versions}{$mod} ) ) {
575 return $self->{versions}{$mod};
584 if ( defined( $sect ) && length( $sect ) &&
585 exists( $self->{pod}{$sect} ) ) {
586 return $self->{pod}{$sect};
596 =for :stopwords ModuleInfo
600 ModuleInfo - Gather package and POD information from a perl module file
607 =item new_from_file($filename, collect_pod => 1)
609 Construct a C<ModuleInfo> object given the path to a file. Takes an optional
610 argument C<collect_pod> which is a boolean that determines whether
611 POD data is collected and stored for reference. POD data is not
612 collected by default. POD headings are always collected.
614 =item new_from_module($module, collect_pod => 1, inc => \@dirs)
616 Construct a C<ModuleInfo> object given a module or package name. In addition
617 to accepting the C<collect_pod> argument as described above, this
618 method accepts a C<inc> argument which is a reference to an array of
619 of directories to search for the module. If none are given, the
624 Returns the name of the package represented by this module. If there
625 are more than one packages, it makes a best guess based on the
626 filename. If it's a script (i.e. not a *.pm) the package name is
629 =item version($package)
631 Returns the version as defined by the $VERSION variable for the
632 package as returned by the C<name> method if no arguments are
633 given. If given the name of a package it will attempt to return the
634 version of that package if it is specified in the file.
638 Returns the absolute path to the file.
640 =item packages_inside()
642 Returns a list of packages.
646 Returns a list of POD sections.
650 Returns true if there is any POD in the file.
654 Returns the POD data in the given section.
656 =item find_module_by_name($module, \@dirs)
658 Returns the path to a module given the module or package name. A list
659 of directories can be passed in as an optional parameter, otherwise
662 Can be called as either an object or a class method.
664 =item find_module_dir_by_name($module, \@dirs)
666 Returns the entry in C<@dirs> (or C<@INC> by default) that contains
667 the module C<$module>. A list of directories can be passed in as an
668 optional parameter, otherwise @INC is searched.
670 Can be called as either an object or a class method.
677 Ken Williams <kwilliams@cpan.org>, Randy W. Sims <RandyS@ThePierianSpring.org>
682 Copyright (c) 2001-2006 Ken Williams. All rights reserved.
684 This library is free software; you can redistribute it and/or
685 modify it under the same terms as Perl itself.
690 perl(1), L<Module::Metadata>(3)