File Coverage

blib/lib/PAR/Indexer.pm
Criterion Covered Total %
statement 82 98 83.6
branch 27 40 67.5
condition 13 33 39.3
subroutine 12 13 92.3
pod 3 3 100.0
total 137 187 73.2


line stmt bran cond sub pod time code
1             package PAR::Indexer;
2              
3 3     3   69649 use 5.006;
  3         11  
  3         138  
4 3     3   17 use strict;
  3         6  
  3         104  
5 3     3   26 use warnings;
  3         5  
  3         111  
6              
7 3     3   17 use Carp qw/croak/;
  3         9  
  3         250  
8 3     3   15 use File::Spec ();
  3         6  
  3         57  
9 3     3   15 use File::Path ();
  3         4  
  3         49  
10 3     3   13 use Cwd ();
  3         4  
  3         53  
11 3     3   3204 use PAR::Dist ();
  3         25131  
  3         87  
12 3     3   3364 use ExtUtils::Manifest;
  3         39659  
  3         3427  
13             require ExtUtils::MM;
14             require Exporter;
15              
16             our @ISA = qw(Exporter);
17             our @EXPORT = ();
18             our %EXPORT_TAGS = (
19             all => [
20             qw(scan_par_for_packages scan_par_for_scripts dependencies_from_meta_yml)
21             ],
22             );
23             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
24             our $VERSION = '0.91';
25              
26             =head1 NAME
27              
28             PAR::Indexer - Scan a PAR distro for packages and scripts
29              
30             =head1 SYNOPSIS
31              
32             use PAR::Indexer qw(scan_par_for_packages scan_par_for_scripts dependencies_from_meta_yml);
33             my $pkgs_hash = scan_par_for_packages($parfile);
34             my $scripts_hash = scan_par_for_scripts($parfile);
35            
36             my $dependencies = dependencies_from_meta_yml(\%meta_yml_hash);
37              
38             =head1 DESCRIPTION
39              
40             This module contains code for scanning a PAR distribution for
41             packages and scripts. The code was adapted from the PAUSE indexer.
42              
43             This module is used by PAR::Repository for injection of new PAR
44             distributions.
45              
46             =head2 EXPORT
47              
48             None by default, but you can choose to export subroutines
49             with the typical C semantics.
50              
51             =head1 FUNCTIONS
52              
53             =cut
54              
55             =head2 scan_par_for_packages
56              
57             First argument must be the path and file name of a PAR
58             distribution. Scans that distribution for .pm files and scans
59             those for packages and versions. Returns a hash of
60             the package names as keys and hash refs as values. The hashes contain
61             the path to the file in the PAR as the key "file" and (if found)
62             the version of the package is the key "version".
63              
64             Returns undef on error.
65              
66             (The structure returned should be exactly what you get when you
67             transform the C section of a F file
68             into a Perl data structure using a YAML reader.)
69              
70             =cut
71              
72             sub scan_par_for_packages {
73 1     1 1 788 my $par = shift;
74              
75 1         11119 my $old_path = Cwd::cwd();
76              
77 1         225 my (undef, $tmpdir) = PAR::Dist::_unzip_to_tmpdir(dist => $par);
78 1         103616 chdir($tmpdir);
79 1         9 my @pmfiles = grep { /\.pm$/i } keys %{ExtUtils::Manifest::manifind()};
  4         859  
  1         11  
80              
81 1         7 my %pkg;
82 1         4 foreach my $pmfile (@pmfiles) {
83 2         17 my $hash = _parse_packages_from_pm($pmfile);
84 2 50       8 next if not defined $hash;
85 2         10 foreach my $namespace (keys %$hash) {
86 3         7 my $main_ns = $pkg{$namespace};
87 3         5 my $this_pm = $hash->{$namespace};
88 3 0 33     14 if (not defined $main_ns->{version} or $main_ns->{version} eq 'undef'
      0        
      0        
      33        
89             or (
90             defined $this_pm->{version}
91             and $this_pm->{version} ne 'undef'
92             and $main_ns->{version} < $this_pm->{version})
93             ) {
94 3         15 $pkg{$namespace} = $this_pm;
95             }
96             }
97             }
98              
99 1         33 chdir($old_path);
100 1         1727 File::Path::rmtree([$tmpdir]);
101 1         12 return \%pkg;
102             }
103              
104              
105             sub _parse_packages_from_pm {
106 2     2   5 my $file = shift;
107 2         2 my %pkg;
108 2 50       104 open my $fh, '<', $file or return undef;
109              
110             # stealing from PAUSE indexer.
111 2         15 local $/ = "\n";
112 2         3 my $inpod = 0;
113 2         35 PLINE: while (<$fh>) {
114 512         555 chomp;
115 512         654 my($pline) = $_;
116 512 100       1242 $inpod = $pline =~ /^=(?!cut)/ ? 1 : $pline =~ /^=cut/ ? 0 : $inpod;
    100          
117 512 100 100     1837 next if $inpod or substr($pline,0,4) eq "=cut";
118              
119 300         387 $pline =~ s/\#.*//;
120 300 100       918 next if $pline =~ /^\s*$/;
121 243 50       499 last PLINE if $pline =~ /\b__(END|DATA)__\b/;
122              
123 243         234 my $pkg;
124 243 100       1701 if (
125             $pline =~ m{
126             (.*)
127             \bpackage\s+
128             ([\w\:\']+)
129             \s*
130             ( $ | [\}\;] )
131             }x) {
132 3         8 $pkg = $2;
133             }
134              
135 243 100       748 if ($pkg) {
136             # Found something
137              
138             # from package
139 3         6 $pkg =~ s/\'/::/;
140 3 50       11 next PLINE unless $pkg =~ /^[A-Za-z]/;
141 3 50       13 next PLINE unless $pkg =~ /\w$/;
142 3 50       9 next PLINE if $pkg eq "main";
143 3         26 $pkg{$pkg}{file} = $file;
144 3         66 my $version = MM->parse_version($file);
145 3 50       5221 $pkg{$pkg}{version} = $version if defined $version;
146             }
147             }
148              
149 2         24 close $fh;
150 2         14 return \%pkg;
151             }
152              
153              
154             =head2 scan_par_for_scripts
155              
156             First argument must be the path and file name of a PAR
157             distribution. Scans that distribution for executable files
158             and scans
159             those for versions. Returns a hash of
160             the script names as keys and hash refs as values. The hashes contain
161             the path to the file in the PAR as the key "file" and (if found)
162             the version of the script as the key "version".
163              
164             Returns undef on error.
165              
166             =cut
167              
168             sub scan_par_for_scripts {
169 0     0 1 0 my $par = shift;
170              
171 0         0 my $old_path = Cwd::cwd();
172              
173 0         0 my (undef, $tmpdir) = PAR::Dist::_unzip_to_tmpdir(dist => $par);
174 0         0 chdir($tmpdir);
175 0 0       0 my @scripts = grep { /^script\/(?!\.)/i or /^bin\/(?!\.)/i }
  0         0  
176 0         0 keys %{ExtUtils::Manifest::manifind()};
177              
178 0         0 my %scr;
179 0         0 foreach my $script (@scripts) {
180 0         0 (undef, undef, my $scriptname) = File::Spec->splitpath($script);
181              
182 0         0 my $version = MM->parse_version($script);
183 0 0 0     0 if ( not defined $scr{$scriptname}{version}
      0        
184             or (defined $version and $scr{$scriptname}{version} < $version) )
185             {
186 0         0 $scr{$scriptname} = {
187             file => $script,
188             version => $version,
189             };
190             }
191             }
192              
193 0         0 chdir($old_path);
194 0         0 File::Path::rmtree([$tmpdir]);
195 0         0 return \%scr;
196             }
197              
198              
199             =head2 dependencies_from_meta_yml
200              
201             Determine the dependencies declared in F. Expects
202             a reference to a hash containing the parsed YAML tree as
203             first argument.
204              
205             Returns essentially the merged C, C,
206             and C hashes from the F. The order of precedence
207             is C< build_requires > configure_requires>>. If none
208             of the three sections is found, the function returns false. If any one of
209             them was found (even if empty), a hash reference will be returned.
210              
211             =cut
212              
213             sub dependencies_from_meta_yml {
214 10     10 1 5993 my $meta = shift;
215 10 100 66     56 return() unless defined $meta and ref($meta) eq 'HASH';
216              
217 9 100 66     40 return() if not exists $meta->{requires}
      66        
218             and not exists $meta->{build_requires}
219             and not exists $meta->{configure_requires};
220              
221 8         13 my $req = {};
222              
223 8         13 foreach my $source (qw(requires build_requires configure_requires)) {
224             next
225 24 100 66     90 if not exists $meta->{$source} or not ref($meta->{$source}) eq 'HASH';
226 15         20 my $this_req = $meta->{$source};
227              
228 15         36 foreach my $module (keys %$this_req) {
229 14 100       68 $req->{$module} = $this_req->{$module}
230             if not exists $req->{$module};
231             }
232             }
233              
234 8         21 return $req;
235             }
236              
237             1;
238             __END__