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
3package Module::Metadata; # git description: v1.000036-4-g435a294
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
13sub __clean_eval { eval $_[0] }
14use strict;
15use warnings;
16
17our $VERSION = '1.000037';
18
19use Carp qw/croak/;
20use File::Spec;
21BEGIN {
22       # Try really hard to not depend ony any DynaLoaded module, such as IO::File or Fcntl
23       eval {
24               require Fcntl; Fcntl->import('SEEK_SET'); 1;
25       } or *SEEK_SET = sub { 0 }
26}
27use version 0.87;
28BEGIN {
29  if ($INC{'Log/Contextual.pm'}) {
30    require "Log/Contextual/WarnLogger.pm"; # Hide from AutoPrereqs
31    Log::Contextual->import('log_info',
32      '-default_logger' => Log::Contextual::WarnLogger->new({ env_prefix => 'MODULE_METADATA', }),
33    );
34  }
35  else {
36    *log_info = sub (&) { warn $_[0]->() };
37  }
38}
39use File::Find qw(find);
40
41my $V_NUM_REGEXP = qr{v?[0-9._]+};  # crudely, a v-string or decimal
42
43my $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
51my $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
59my $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
71my $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 whitesapce
79  [;\{]                 # semicolon line terminator or block start (since 5.16)
80}x;
81
82my $VARNAME_REGEXP = qr{ # match fully-qualified VERSION name
83  ([\$*])         # sigil - $ or *
84  (
85    (             # optional leading package name
86      (?:::|\')?  # possibly starting like just :: (a la $::VERSION)
87      (?:\w+(?:::|\'))*  # Foo::Bar:: ...
88    )?
89    VERSION
90  )\b
91}x;
92
93my $VERS_REGEXP = qr{ # match a VERSION definition
94  (?:
95    \(\s*$VARNAME_REGEXP\s*\) # with parens
96  |
97    $VARNAME_REGEXP           # without parens
98  )
99  \s*
100  =[^=~>]  # = but not ==, nor =~, nor =>
101}x;
102
103sub new_from_file {
104  my $class    = shift;
105  my $filename = File::Spec->rel2abs( shift );
106
107  return undef unless defined( $filename ) && -f $filename;
108  return $class->_init(undef, $filename, @_);
109}
110
111sub new_from_handle {
112  my $class    = shift;
113  my $handle   = shift;
114  my $filename = shift;
115  return undef unless defined($handle) && defined($filename);
116  $filename = File::Spec->rel2abs( $filename );
117
118  return $class->_init(undef, $filename, @_, handle => $handle);
119
120}
121
122
123sub new_from_module {
124  my $class   = shift;
125  my $module  = shift;
126  my %props   = @_;
127
128  $props{inc} ||= \@INC;
129  my $filename = $class->find_module_by_name( $module, $props{inc} );
130  return undef unless defined( $filename ) && -f $filename;
131  return $class->_init($module, $filename, %props);
132}
133
134{
135
136  my $compare_versions = sub {
137    my ($v1, $op, $v2) = @_;
138    $v1 = version->new($v1)
139      unless UNIVERSAL::isa($v1,'version');
140
141    my $eval_str = "\$v1 $op \$v2";
142    my $result   = eval $eval_str;
143    log_info { "error comparing versions: '$eval_str' $@" } if $@;
144
145    return $result;
146  };
147
148  my $normalize_version = sub {
149    my ($version) = @_;
150    if ( $version =~ /[=<>!,]/ ) { # logic, not just version
151      # take as is without modification
152    }
153    elsif ( ref $version eq 'version' ) { # version objects
154      $version = $version->is_qv ? $version->normal : $version->stringify;
155    }
156    elsif ( $version =~ /^[^v][^.]*\.[^.]+\./ ) { # no leading v, multiple dots
157      # normalize string tuples without "v": "1.2.3" -> "v1.2.3"
158      $version = "v$version";
159    }
160    else {
161      # leave alone
162    }
163    return $version;
164  };
165
166  # separate out some of the conflict resolution logic
167
168  my $resolve_module_versions = sub {
169    my $packages = shift;
170
171    my( $file, $version );
172    my $err = '';
173      foreach my $p ( @$packages ) {
174        if ( defined( $p->{version} ) ) {
175          if ( defined( $version ) ) {
176            if ( $compare_versions->( $version, '!=', $p->{version} ) ) {
177              $err .= "  $p->{file} ($p->{version})\n";
178            }
179            else {
180              # same version declared multiple times, ignore
181            }
182          }
183          else {
184            $file    = $p->{file};
185            $version = $p->{version};
186          }
187        }
188      $file ||= $p->{file} if defined( $p->{file} );
189    }
190
191    if ( $err ) {
192      $err = "  $file ($version)\n" . $err;
193    }
194
195    my %result = (
196      file    => $file,
197      version => $version,
198      err     => $err
199    );
200
201    return \%result;
202  };
203
204  sub provides {
205    my $class = shift;
206
207    croak "provides() requires key/value pairs \n" if @_ % 2;
208    my %args = @_;
209
210    croak "provides() takes only one of 'dir' or 'files'\n"
211      if $args{dir} && $args{files};
212
213    croak "provides() requires a 'version' argument"
214      unless defined $args{version};
215
216    croak "provides() does not support version '$args{version}' metadata"
217        unless grep $args{version} eq $_, qw/1.4 2/;
218
219    $args{prefix} = 'lib' unless defined $args{prefix};
220
221    my $p;
222    if ( $args{dir} ) {
223      $p = $class->package_versions_from_directory($args{dir});
224    }
225    else {
226      croak "provides() requires 'files' to be an array reference\n"
227        unless ref $args{files} eq 'ARRAY';
228      $p = $class->package_versions_from_directory($args{files});
229    }
230
231    # Now, fix up files with prefix
232    if ( length $args{prefix} ) { # check in case disabled with q{}
233      $args{prefix} =~ s{/$}{};
234      for my $v ( values %$p ) {
235        $v->{file} = "$args{prefix}/$v->{file}";
236      }
237    }
238
239    return $p
240  }
241
242  sub package_versions_from_directory {
243    my ( $class, $dir, $files ) = @_;
244
245    my @files;
246
247    if ( $files ) {
248      @files = @$files;
249    }
250    else {
251      find( {
252        wanted => sub {
253          push @files, $_ if -f $_ && /\.pm$/;
254        },
255        no_chdir => 1,
256      }, $dir );
257    }
258
259    # First, we enumerate all packages & versions,
260    # separating into primary & alternative candidates
261    my( %prime, %alt );
262    foreach my $file (@files) {
263      my $mapped_filename = File::Spec->abs2rel( $file, $dir );
264      my @path = File::Spec->splitdir( $mapped_filename );
265      (my $prime_package = join( '::', @path )) =~ s/\.pm$//;
266
267      my $pm_info = $class->new_from_file( $file );
268
269      foreach my $package ( $pm_info->packages_inside ) {
270        next if $package eq 'main';  # main can appear numerous times, ignore
271        next if $package eq 'DB';    # special debugging package, ignore
272        next if grep /^_/, split( /::/, $package ); # private package, ignore
273
274        my $version = $pm_info->version( $package );
275
276        $prime_package = $package if lc($prime_package) eq lc($package);
277        if ( $package eq $prime_package ) {
278          if ( exists( $prime{$package} ) ) {
279            croak "Unexpected conflict in '$package'; multiple versions found.\n";
280          }
281          else {
282            $mapped_filename = "$package.pm" if lc("$package.pm") eq lc($mapped_filename);
283            $prime{$package}{file} = $mapped_filename;
284            $prime{$package}{version} = $version if defined( $version );
285          }
286        }
287        else {
288          push( @{$alt{$package}}, {
289                                    file    => $mapped_filename,
290                                    version => $version,
291                                   } );
292        }
293      }
294    }
295
296    # Then we iterate over all the packages found above, identifying conflicts
297    # and selecting the "best" candidate for recording the file & version
298    # for each package.
299    foreach my $package ( keys( %alt ) ) {
300      my $result = $resolve_module_versions->( $alt{$package} );
301
302      if ( exists( $prime{$package} ) ) { # primary package selected
303
304        if ( $result->{err} ) {
305        # Use the selected primary package, but there are conflicting
306        # errors among multiple alternative packages that need to be
307        # reported
308          log_info {
309            "Found conflicting versions for package '$package'\n" .
310            "  $prime{$package}{file} ($prime{$package}{version})\n" .
311            $result->{err}
312          };
313
314        }
315        elsif ( defined( $result->{version} ) ) {
316        # There is a primary package selected, and exactly one
317        # alternative package
318
319        if ( exists( $prime{$package}{version} ) &&
320             defined( $prime{$package}{version} ) ) {
321          # Unless the version of the primary package agrees with the
322          # version of the alternative package, report a conflict
323        if ( $compare_versions->(
324                 $prime{$package}{version}, '!=', $result->{version}
325               )
326             ) {
327
328            log_info {
329              "Found conflicting versions for package '$package'\n" .
330              "  $prime{$package}{file} ($prime{$package}{version})\n" .
331              "  $result->{file} ($result->{version})\n"
332            };
333          }
334
335        }
336        else {
337          # The prime package selected has no version so, we choose to
338          # use any alternative package that does have a version
339          $prime{$package}{file}    = $result->{file};
340          $prime{$package}{version} = $result->{version};
341        }
342
343        }
344        else {
345        # no alt package found with a version, but we have a prime
346        # package so we use it whether it has a version or not
347        }
348
349      }
350      else { # No primary package was selected, use the best alternative
351
352        if ( $result->{err} ) {
353          log_info {
354            "Found conflicting versions for package '$package'\n" .
355            $result->{err}
356          };
357        }
358
359        # Despite possible conflicting versions, we choose to record
360        # something rather than nothing
361        $prime{$package}{file}    = $result->{file};
362        $prime{$package}{version} = $result->{version}
363          if defined( $result->{version} );
364      }
365    }
366
367    # Normalize versions.  Can't use exists() here because of bug in YAML::Node.
368    # XXX "bug in YAML::Node" comment seems irrelevant -- dagolden, 2009-05-18
369    for (grep defined $_->{version}, values %prime) {
370      $_->{version} = $normalize_version->( $_->{version} );
371    }
372
373    return \%prime;
374  }
375}
376
377
378sub _init {
379  my $class    = shift;
380  my $module   = shift;
381  my $filename = shift;
382  my %props = @_;
383
384  my $handle = delete $props{handle};
385  my( %valid_props, @valid_props );
386  @valid_props = qw( collect_pod inc decode_pod );
387  @valid_props{@valid_props} = delete( @props{@valid_props} );
388  warn "Unknown properties: @{[keys %props]}\n" if scalar( %props );
389
390  my %data = (
391    module       => $module,
392    filename     => $filename,
393    version      => undef,
394    packages     => [],
395    versions     => {},
396    pod          => {},
397    pod_headings => [],
398    collect_pod  => 0,
399
400    %valid_props,
401  );
402
403  my $self = bless(\%data, $class);
404
405  if ( not $handle ) {
406    my $filename = $self->{filename};
407    open $handle, '<', $filename
408      or croak( "Can't open '$filename': $!" );
409
410    $self->_handle_bom($handle, $filename);
411  }
412  $self->_parse_fh($handle);
413
414  @{$self->{packages}} = __uniq(@{$self->{packages}});
415
416  unless($self->{module} and length($self->{module})) {
417    # CAVEAT (possible TODO): .pmc files not treated the same as .pm
418    if ($self->{filename} =~ /\.pm$/) {
419      my ($v, $d, $f) = File::Spec->splitpath($self->{filename});
420      $f =~ s/\..+$//;
421      my @candidates = grep /(^|::)$f$/, @{$self->{packages}};
422      $self->{module} = shift(@candidates); # this may be undef
423    }
424    else {
425      # this seems like an atrocious heuristic, albeit marginally better than
426      # what was here before. It should be rewritten entirely to be more like
427      # "if it's not a .pm file, it's not require()able as a name, therefore
428      # name() should be undef."
429      if ((grep /main/, @{$self->{packages}})
430          or (grep /main/, keys %{$self->{versions}})) {
431        $self->{module} = 'main';
432      }
433      else {
434        # TODO: this should maybe default to undef instead
435        $self->{module} = $self->{packages}[0] || '';
436      }
437    }
438  }
439
440  $self->{version} = $self->{versions}{$self->{module}}
441    if defined( $self->{module} );
442
443  return $self;
444}
445
446# class method
447sub _do_find_module {
448  my $class   = shift;
449  my $module  = shift || croak 'find_module_by_name() requires a package name';
450  my $dirs    = shift || \@INC;
451
452  my $file = File::Spec->catfile(split( /::/, $module));
453  foreach my $dir ( @$dirs ) {
454    my $testfile = File::Spec->catfile($dir, $file);
455    return [ File::Spec->rel2abs( $testfile ), $dir ]
456      if -e $testfile and !-d _;  # For stuff like ExtUtils::xsubpp
457    # CAVEAT (possible TODO): .pmc files are not discoverable here
458    $testfile .= '.pm';
459    return [ File::Spec->rel2abs( $testfile ), $dir ]
460      if -e $testfile;
461  }
462  return;
463}
464
465# class method
466sub find_module_by_name {
467  my $found = shift()->_do_find_module(@_) or return;
468  return $found->[0];
469}
470
471# class method
472sub find_module_dir_by_name {
473  my $found = shift()->_do_find_module(@_) or return;
474  return $found->[1];
475}
476
477
478# given a line of perl code, attempt to parse it if it looks like a
479# $VERSION assignment, returning sigil, full name, & package name
480sub _parse_version_expression {
481  my $self = shift;
482  my $line = shift;
483
484  my( $sigil, $variable_name, $package);
485  if ( $line =~ /$VERS_REGEXP/o ) {
486    ( $sigil, $variable_name, $package) = $2 ? ( $1, $2, $3 ) : ( $4, $5, $6 );
487    if ( $package ) {
488      $package = ($package eq '::') ? 'main' : $package;
489      $package =~ s/::$//;
490    }
491  }
492
493  return ( $sigil, $variable_name, $package );
494}
495
496# Look for a UTF-8/UTF-16BE/UTF-16LE BOM at the beginning of the stream.
497# If there's one, then skip it and set the :encoding layer appropriately.
498sub _handle_bom {
499  my ($self, $fh, $filename) = @_;
500
501  my $pos = tell $fh;
502  return unless defined $pos;
503
504  my $buf = ' ' x 2;
505  my $count = read $fh, $buf, length $buf;
506  return unless defined $count and $count >= 2;
507
508  my $encoding;
509  if ( $buf eq "\x{FE}\x{FF}" ) {
510    $encoding = 'UTF-16BE';
511  }
512  elsif ( $buf eq "\x{FF}\x{FE}" ) {
513    $encoding = 'UTF-16LE';
514  }
515  elsif ( $buf eq "\x{EF}\x{BB}" ) {
516    $buf = ' ';
517    $count = read $fh, $buf, length $buf;
518    if ( defined $count and $count >= 1 and $buf eq "\x{BF}" ) {
519      $encoding = 'UTF-8';
520    }
521  }
522
523  if ( defined $encoding ) {
524    if ( "$]" >= 5.008 ) {
525      binmode( $fh, ":encoding($encoding)" );
526    }
527  }
528  else {
529    seek $fh, $pos, SEEK_SET
530      or croak( sprintf "Can't reset position to the top of '$filename'" );
531  }
532
533  return $encoding;
534}
535
536sub _parse_fh {
537  my ($self, $fh) = @_;
538
539  my( $in_pod, $seen_end, $need_vers ) = ( 0, 0, 0 );
540  my( @packages, %vers, %pod, @pod );
541  my $package = 'main';
542  my $pod_sect = '';
543  my $pod_data = '';
544  my $in_end = 0;
545  my $encoding = '';
546
547  while (defined( my $line = <$fh> )) {
548    my $line_num = $.;
549
550    chomp( $line );
551
552    # From toke.c : any line that begins by "=X", where X is an alphabetic
553    # character, introduces a POD segment.
554    my $is_cut;
555    if ( $line =~ /^=([a-zA-Z].*)/ ) {
556      my $cmd = $1;
557      # Then it goes back to Perl code for "=cutX" where X is a non-alphabetic
558      # character (which includes the newline, but here we chomped it away).
559      $is_cut = $cmd =~ /^cut(?:[^a-zA-Z]|$)/;
560      $in_pod = !$is_cut;
561    }
562
563    if ( $in_pod ) {
564
565      if ( $line =~ /^=head[1-4]\s+(.+)\s*$/ ) {
566        push( @pod, $1 );
567        if ( $self->{collect_pod} && length( $pod_data ) ) {
568          $pod{$pod_sect} = $pod_data;
569          $pod_data = '';
570        }
571        $pod_sect = $1;
572      }
573      elsif ( $self->{collect_pod} ) {
574        if ( $self->{decode_pod} && $line =~ /^=encoding ([\w-]+)/ ) {
575          $encoding = $1;
576        }
577        $pod_data .= "$line\n";
578      }
579      next;
580    }
581    elsif ( $is_cut ) {
582      if ( $self->{collect_pod} && length( $pod_data ) ) {
583        $pod{$pod_sect} = $pod_data;
584        $pod_data = '';
585      }
586      $pod_sect = '';
587      next;
588    }
589
590    # Skip after __END__
591    next if $in_end;
592
593    # Skip comments in code
594    next if $line =~ /^\s*#/;
595
596    # Would be nice if we could also check $in_string or something too
597    if ($line eq '__END__') {
598      $in_end++;
599      next;
600    }
601
602    last if $line eq '__DATA__';
603
604    # parse $line to see if it's a $VERSION declaration
605    my( $version_sigil, $version_fullname, $version_package ) =
606      index($line, 'VERSION') >= 1
607        ? $self->_parse_version_expression( $line )
608        : ();
609
610    if ( $line =~ /$PKG_REGEXP/o ) {
611      $package = $1;
612      my $version = $2;
613      push( @packages, $package ) unless grep( $package eq $_, @packages );
614      $need_vers = defined $version ? 0 : 1;
615
616      if ( not exists $vers{$package} and defined $version ){
617        # Upgrade to a version object.
618        my $dwim_version = eval { _dwim_version($version) };
619        croak "Version '$version' from $self->{filename} does not appear to be valid:\n$line\n\nThe fatal error was: $@\n"
620          unless defined $dwim_version;  # "0" is OK!
621        $vers{$package} = $dwim_version;
622      }
623    }
624
625    # VERSION defined with full package spec, i.e. $Module::VERSION
626    elsif ( $version_fullname && $version_package ) {
627      # we do NOT save this package in found @packages
628      $need_vers = 0 if $version_package eq $package;
629
630      unless ( defined $vers{$version_package} && length $vers{$version_package} ) {
631        $vers{$version_package} = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line );
632      }
633    }
634
635    # first non-comment line in undeclared package main is VERSION
636    elsif ( $package eq 'main' && $version_fullname && !exists($vers{main}) ) {
637      $need_vers = 0;
638      my $v = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line );
639      $vers{$package} = $v;
640      push( @packages, 'main' );
641    }
642
643    # first non-comment line in undeclared package defines package main
644    elsif ( $package eq 'main' && !exists($vers{main}) && $line =~ /\w/ ) {
645      $need_vers = 1;
646      $vers{main} = '';
647      push( @packages, 'main' );
648    }
649
650    # only keep if this is the first $VERSION seen
651    elsif ( $version_fullname && $need_vers ) {
652      $need_vers = 0;
653      my $v = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line );
654
655      unless ( defined $vers{$package} && length $vers{$package} ) {
656        $vers{$package} = $v;
657      }
658    }
659  } # end loop over each line
660
661  if ( $self->{collect_pod} && length($pod_data) ) {
662    $pod{$pod_sect} = $pod_data;
663  }
664
665  if ( $self->{decode_pod} && $encoding ) {
666    require Encode;
667    $_ = Encode::decode( $encoding, $_ ) for values %pod;
668  }
669
670  $self->{versions} = \%vers;
671  $self->{packages} = \@packages;
672  $self->{pod} = \%pod;
673  $self->{pod_headings} = \@pod;
674}
675
676sub __uniq (@)
677{
678    my (%seen, $key);
679    grep !$seen{ $key = $_ }++, @_;
680}
681
682{
683my $pn = 0;
684sub _evaluate_version_line {
685  my $self = shift;
686  my( $sigil, $variable_name, $line ) = @_;
687
688  # We compile into a local sub because 'use version' would cause
689  # compiletime/runtime issues with local()
690  $pn++; # everybody gets their own package
691  my $eval = qq{ my \$dummy = q#  Hide from _packages_inside()
692    #; package Module::Metadata::_version::p${pn};
693    use version;
694    sub {
695      local $sigil$variable_name;
696      $line;
697      return \$$variable_name if defined \$$variable_name;
698      return \$Module::Metadata::_version::p${pn}::$variable_name;
699    };
700  };
701
702  $eval = $1 if $eval =~ m{^(.+)}s;
703
704  local $^W;
705  # Try to get the $VERSION
706  my $vsub = __clean_eval($eval);
707  # some modules say $VERSION <equal sign> $Foo::Bar::VERSION, but Foo::Bar isn't
708  # installed, so we need to hunt in ./lib for it
709  if ( $@ =~ /Can't locate/ && -d 'lib' ) {
710    local @INC = ('lib',@INC);
711    $vsub = __clean_eval($eval);
712  }
713  warn "Error evaling version line '$eval' in $self->{filename}: $@\n"
714    if $@;
715
716  (ref($vsub) eq 'CODE') or
717    croak "failed to build version sub for $self->{filename}";
718
719  my $result = eval { $vsub->() };
720  # FIXME: $eval is not the right thing to print here
721  croak "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n"
722    if $@;
723
724  # Upgrade it into a version object
725  my $version = eval { _dwim_version($result) };
726
727  # FIXME: $eval is not the right thing to print here
728  croak "Version '$result' from $self->{filename} does not appear to be valid:\n$eval\n\nThe fatal error was: $@\n"
729    unless defined $version; # "0" is OK!
730
731  return $version;
732}
733}
734
735# Try to DWIM when things fail the lax version test in obvious ways
736{
737  my @version_prep = (
738    # Best case, it just works
739    sub { return shift },
740
741    # If we still don't have a version, try stripping any
742    # trailing junk that is prohibited by lax rules
743    sub {
744      my $v = shift;
745      $v =~ s{([0-9])[a-z-].*$}{$1}i; # 1.23-alpha or 1.23b
746      return $v;
747    },
748
749    # Activestate apparently creates custom versions like '1.23_45_01', which
750    # cause version.pm to think it's an invalid alpha.  So check for that
751    # and strip them
752    sub {
753      my $v = shift;
754      my $num_dots = () = $v =~ m{(\.)}g;
755      my $num_unders = () = $v =~ m{(_)}g;
756      my $leading_v = substr($v,0,1) eq 'v';
757      if ( ! $leading_v && $num_dots < 2 && $num_unders > 1 ) {
758        $v =~ s{_}{}g;
759        $num_unders = () = $v =~ m{(_)}g;
760      }
761      return $v;
762    },
763
764    # Worst case, try numifying it like we would have before version objects
765    sub {
766      my $v = shift;
767      no warnings 'numeric';
768      return 0 + $v;
769    },
770
771  );
772
773  sub _dwim_version {
774    my ($result) = shift;
775
776    return $result if ref($result) eq 'version';
777
778    my ($version, $error);
779    for my $f (@version_prep) {
780      $result = $f->($result);
781      $version = eval { version->new($result) };
782      $error ||= $@ if $@; # capture first failure
783      last if defined $version;
784    }
785
786    croak $error unless defined $version;
787
788    return $version;
789  }
790}
791
792############################################################
793
794# accessors
795sub name            { $_[0]->{module}            }
796
797sub filename        { $_[0]->{filename}          }
798sub packages_inside { @{$_[0]->{packages}}       }
799sub pod_inside      { @{$_[0]->{pod_headings}}   }
800sub contains_pod    { 0+@{$_[0]->{pod_headings}} }
801
802sub version {
803    my $self = shift;
804    my $mod  = shift || $self->{module};
805    my $vers;
806    if ( defined( $mod ) && length( $mod ) &&
807         exists( $self->{versions}{$mod} ) ) {
808        return $self->{versions}{$mod};
809    }
810    else {
811        return undef;
812    }
813}
814
815sub pod {
816    my $self = shift;
817    my $sect = shift;
818    if ( defined( $sect ) && length( $sect ) &&
819         exists( $self->{pod}{$sect} ) ) {
820        return $self->{pod}{$sect};
821    }
822    else {
823        return undef;
824    }
825}
826
827sub is_indexable {
828  my ($self, $package) = @_;
829
830  my @indexable_packages = grep $_ ne 'main', $self->packages_inside;
831
832  # check for specific package, if provided
833  return !! grep $_ eq $package, @indexable_packages if $package;
834
835  # otherwise, check for any indexable packages at all
836  return !! @indexable_packages;
837}
838
8391;
840
841__END__
842
843=pod
844
845=encoding UTF-8
846
847=head1 NAME
848
849Module::Metadata - Gather package and POD information from perl module files
850
851=head1 VERSION
852
853version 1.000037
854
855=head1 SYNOPSIS
856
857  use Module::Metadata;
858
859  # information about a .pm file
860  my $info = Module::Metadata->new_from_file( $file );
861  my $version = $info->version;
862
863  # CPAN META 'provides' field for .pm files in a directory
864  my $provides = Module::Metadata->provides(
865    dir => 'lib', version => 2
866  );
867
868=head1 DESCRIPTION
869
870This module provides a standard way to gather metadata about a .pm file through
871(mostly) static analysis and (some) code execution.  When determining the
872version of a module, the C<$VERSION> assignment is C<eval>ed, as is traditional
873in the CPAN toolchain.
874
875=head1 CLASS METHODS
876
877=head2 C<< new_from_file($filename, collect_pod => 1, decode_pod => 1) >>
878
879Constructs a C<Module::Metadata> object given the path to a file.  Returns
880undef if the filename does not exist.
881
882C<collect_pod> is a optional boolean argument that determines whether POD
883data is collected and stored for reference.  POD data is not collected by
884default.  POD headings are always collected.
885
886If the file begins by an UTF-8, UTF-16BE or UTF-16LE byte-order mark, then
887it is skipped before processing, and the content of the file is also decoded
888appropriately starting from perl 5.8.
889
890Alternatively, if C<decode_pod> is set, it will decode the collected pod
891sections according to the C<=encoding> declaration.
892
893=head2 C<< new_from_handle($handle, $filename, collect_pod => 1, decode_pod => 1) >>
894
895This works just like C<new_from_file>, except that a handle can be provided
896as the first argument.
897
898Note that there is no validation to confirm that the handle is a handle or
899something that can act like one.  Passing something that isn't a handle will
900cause a exception when trying to read from it.  The C<filename> argument is
901mandatory or undef will be returned.
902
903You are responsible for setting the decoding layers on C<$handle> if
904required.
905
906=head2 C<< new_from_module($module, collect_pod => 1, inc => \@dirs, decode_pod => 1) >>
907
908Constructs a C<Module::Metadata> object given a module or package name.
909Returns undef if the module cannot be found.
910
911In addition to accepting the C<collect_pod> and C<decode_pod> arguments as
912described above, this method accepts a C<inc> argument which is a reference to
913an array of directories to search for the module.  If none are given, the
914default is @INC.
915
916If the file that contains the module begins by an UTF-8, UTF-16BE or
917UTF-16LE byte-order mark, then it is skipped before processing, and the
918content of the file is also decoded appropriately starting from perl 5.8.
919
920=head2 C<< find_module_by_name($module, \@dirs) >>
921
922Returns the path to a module given the module or package name. A list
923of directories can be passed in as an optional parameter, otherwise
924@INC is searched.
925
926Can be called as either an object or a class method.
927
928=head2 C<< find_module_dir_by_name($module, \@dirs) >>
929
930Returns the entry in C<@dirs> (or C<@INC> by default) that contains
931the module C<$module>. A list of directories can be passed in as an
932optional parameter, otherwise @INC is searched.
933
934Can be called as either an object or a class method.
935
936=head2 C<< provides( %options ) >>
937
938This is a convenience wrapper around C<package_versions_from_directory>
939to generate a CPAN META C<provides> data structure.  It takes key/value
940pairs.  Valid option keys include:
941
942=over
943
944=item version B<(required)>
945
946Specifies which version of the L<CPAN::Meta::Spec> should be used as
947the format of the C<provides> output.  Currently only '1.4' and '2'
948are supported (and their format is identical).  This may change in
949the future as the definition of C<provides> changes.
950
951The C<version> option is required.  If it is omitted or if
952an unsupported version is given, then C<provides> will throw an error.
953
954=item dir
955
956Directory to search recursively for F<.pm> files.  May not be specified with
957C<files>.
958
959=item files
960
961Array reference of files to examine.  May not be specified with C<dir>.
962
963=item prefix
964
965String to prepend to the C<file> field of the resulting output. This defaults
966to F<lib>, which is the common case for most CPAN distributions with their
967F<.pm> files in F<lib>.  This option ensures the META information has the
968correct relative path even when the C<dir> or C<files> arguments are
969absolute or have relative paths from a location other than the distribution
970root.
971
972=back
973
974For example, given C<dir> of 'lib' and C<prefix> of 'lib', the return value
975is a hashref of the form:
976
977  {
978    'Package::Name' => {
979      version => '0.123',
980      file => 'lib/Package/Name.pm'
981    },
982    'OtherPackage::Name' => ...
983  }
984
985=head2 C<< package_versions_from_directory($dir, \@files?) >>
986
987Scans C<$dir> for .pm files (unless C<@files> is given, in which case looks
988for those files in C<$dir> - and reads each file for packages and versions,
989returning a hashref of the form:
990
991  {
992    'Package::Name' => {
993      version => '0.123',
994      file => 'Package/Name.pm'
995    },
996    'OtherPackage::Name' => ...
997  }
998
999The C<DB> and C<main> packages are always omitted, as are any "private"
1000packages that have leading underscores in the namespace (e.g.
1001C<Foo::_private>)
1002
1003Note that the file path is relative to C<$dir> if that is specified.
1004This B<must not> be used directly for CPAN META C<provides>.  See
1005the C<provides> method instead.
1006
1007=head2 C<< log_info (internal) >>
1008
1009Used internally to perform logging; imported from Log::Contextual if
1010Log::Contextual has already been loaded, otherwise simply calls warn.
1011
1012=head1 OBJECT METHODS
1013
1014=head2 C<< name() >>
1015
1016Returns the name of the package represented by this module. If there
1017is more than one package, it makes a best guess based on the
1018filename. If it's a script (i.e. not a *.pm) the package name is
1019'main'.
1020
1021=head2 C<< version($package) >>
1022
1023Returns the version as defined by the $VERSION variable for the
1024package as returned by the C<name> method if no arguments are
1025given. If given the name of a package it will attempt to return the
1026version of that package if it is specified in the file.
1027
1028=head2 C<< filename() >>
1029
1030Returns the absolute path to the file.
1031Note that this file may not actually exist on disk yet, e.g. if the module was read from an in-memory filehandle.
1032
1033=head2 C<< packages_inside() >>
1034
1035Returns a list of packages. Note: this is a raw list of packages
1036discovered (or assumed, in the case of C<main>).  It is not
1037filtered for C<DB>, C<main> or private packages the way the
1038C<provides> method does.  Invalid package names are not returned,
1039for example "Foo:Bar".  Strange but valid package names are
1040returned, for example "Foo::Bar::", and are left up to the caller
1041on how to handle.
1042
1043=head2 C<< pod_inside() >>
1044
1045Returns a list of POD sections.
1046
1047=head2 C<< contains_pod() >>
1048
1049Returns true if there is any POD in the file.
1050
1051=head2 C<< pod($section) >>
1052
1053Returns the POD data in the given section.
1054
1055=head2 C<< is_indexable($package) >> or C<< is_indexable() >>
1056
1057Available since version 1.000020.
1058
1059Returns a boolean indicating whether the package (if provided) or any package
1060(otherwise) is eligible for indexing by PAUSE, the Perl Authors Upload Server.
1061Note This only checks for valid C<package> declarations, and does not take any
1062ownership information into account.
1063
1064=head1 SUPPORT
1065
1066Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Module-Metadata>
1067(or L<bug-Module-Metadata@rt.cpan.org|mailto:bug-Module-Metadata@rt.cpan.org>).
1068
1069There is also a mailing list available for users of this distribution, at
1070L<http://lists.perl.org/list/cpan-workers.html>.
1071
1072There is also an irc channel available for users of this distribution, at
1073L<C<#toolchain> on C<irc.perl.org>|irc://irc.perl.org/#toolchain>.
1074
1075=head1 AUTHOR
1076
1077Original code from Module::Build::ModuleInfo by Ken Williams
1078<kwilliams@cpan.org>, Randy W. Sims <RandyS@ThePierianSpring.org>
1079
1080Released as Module::Metadata by Matt S Trout (mst) <mst@shadowcat.co.uk> with
1081assistance from David Golden (xdg) <dagolden@cpan.org>.
1082
1083=head1 CONTRIBUTORS
1084
1085=for stopwords Karen Etheridge David Golden Vincent Pit Matt S Trout Chris Nehren Tomas Doran Olivier Mengué Graham Knop tokuhirom Tatsuhiko Miyagawa Christian Walde Leon Timmermans Peter Rabbitson Steve Hay Jerry D. Hedden Craig A. Berry Mitchell Steinbrunner Edward Zborowski Gareth Harper James Raspass 'BinGOs' Williams Josh Jore Kent Fredric
1086
1087=over 4
1088
1089=item *
1090
1091Karen Etheridge <ether@cpan.org>
1092
1093=item *
1094
1095David Golden <dagolden@cpan.org>
1096
1097=item *
1098
1099Vincent Pit <perl@profvince.com>
1100
1101=item *
1102
1103Matt S Trout <mst@shadowcat.co.uk>
1104
1105=item *
1106
1107Chris Nehren <apeiron@cpan.org>
1108
1109=item *
1110
1111Tomas Doran <bobtfish@bobtfish.net>
1112
1113=item *
1114
1115Olivier Mengué <dolmen@cpan.org>
1116
1117=item *
1118
1119Graham Knop <haarg@haarg.org>
1120
1121=item *
1122
1123tokuhirom <tokuhirom@gmail.com>
1124
1125=item *
1126
1127Tatsuhiko Miyagawa <miyagawa@bulknews.net>
1128
1129=item *
1130
1131Christian Walde <walde.christian@googlemail.com>
1132
1133=item *
1134
1135Leon Timmermans <fawaka@gmail.com>
1136
1137=item *
1138
1139Peter Rabbitson <ribasushi@cpan.org>
1140
1141=item *
1142
1143Steve Hay <steve.m.hay@googlemail.com>
1144
1145=item *
1146
1147Jerry D. Hedden <jdhedden@cpan.org>
1148
1149=item *
1150
1151Craig A. Berry <cberry@cpan.org>
1152
1153=item *
1154
1155Craig A. Berry <craigberry@mac.com>
1156
1157=item *
1158
1159David Mitchell <davem@iabyn.com>
1160
1161=item *
1162
1163David Steinbrunner <dsteinbrunner@pobox.com>
1164
1165=item *
1166
1167Edward Zborowski <ed@rubensteintech.com>
1168
1169=item *
1170
1171Gareth Harper <gareth@broadbean.com>
1172
1173=item *
1174
1175James Raspass <jraspass@gmail.com>
1176
1177=item *
1178
1179Chris 'BinGOs' Williams <chris@bingosnet.co.uk>
1180
1181=item *
1182
1183Josh Jore <jjore@cpan.org>
1184
1185=item *
1186
1187Kent Fredric <kentnl@cpan.org>
1188
1189=back
1190
1191=head1 COPYRIGHT & LICENSE
1192
1193Original code Copyright (c) 2001-2011 Ken Williams.
1194Additional code Copyright (c) 2010-2011 Matt Trout and David Golden.
1195All rights reserved.
1196
1197This library is free software; you can redistribute it and/or
1198modify it under the same terms as Perl itself.
1199
1200=cut
1201