File Coverage

blib/lib/ExtUtils/Installed.pm
Criterion Covered Total %
statement 145 181 80.1
branch 67 104 64.4
condition 18 33 54.5
subroutine 22 25 88.0
pod 8 8 100.0
total 260 351 74.0


line stmt bran cond sub pod time code
1 1     1   93476 use strict;
  1         11  
  1         39  
2             package ExtUtils::Installed;
3              
4             #use warnings; # XXX requires 5.6
5 1     1   5 use Carp qw();
  1         2  
  1         17  
6 1     1   426 use ExtUtils::Packlist;
  1         2  
  1         31  
7 1     1   830 use ExtUtils::MakeMaker;
  1         111917  
  1         106  
8 1     1   9 use Config;
  1         2  
  1         39  
9 1     1   9 use File::Find;
  1         3  
  1         63  
10 1     1   7 use File::Basename;
  1         3  
  1         53  
11 1     1   5 use File::Spec;
  1         4  
  1         2343  
12              
13             my $Is_VMS = $^O eq 'VMS';
14             my $DOSISH = ($^O =~ /^(MSWin\d\d|os2|dos|mint)$/);
15              
16             require VMS::Filespec if $Is_VMS;
17              
18             our $VERSION = '2.18';
19             $VERSION = eval $VERSION;
20              
21             sub _is_prefix {
22 25     25   188 my ($self, $path, $prefix) = @_;
23 25 50 33     103 return unless defined $prefix && defined $path;
24              
25 25 50       47 if( $Is_VMS ) {
26 0         0 $prefix = VMS::Filespec::unixify($prefix);
27 0         0 $path = VMS::Filespec::unixify($path);
28             }
29              
30             # Unix path normalization.
31 25         78 $prefix = File::Spec->canonpath($prefix);
32              
33 25 100       130 return 1 if substr($path, 0, length($prefix)) eq $prefix;
34              
35 13 50       31 if ($DOSISH) {
36 0         0 $path =~ s|\\|/|g;
37 0         0 $prefix =~ s|\\|/|g;
38 0 0       0 return 1 if $path =~ m{^\Q$prefix\E}i;
39             }
40 13         51 return(0);
41             }
42              
43             sub _is_doc {
44 4     4   14 my ($self, $path) = @_;
45              
46 4         31 my $man1dir = $self->{':private:'}{Config}{man1direxp};
47 4         34 my $man3dir = $self->{':private:'}{Config}{man3direxp};
48 4 50 33     68 return(($man1dir && $self->_is_prefix($path, $man1dir))
49             ||
50             ($man3dir && $self->_is_prefix($path, $man3dir))
51             ? 1 : 0)
52             }
53              
54             sub _is_type {
55 14     14   2087 my ($self, $path, $type) = @_;
56 14 100       51 return 1 if $type eq "all";
57              
58 9 50       28 return($self->_is_doc($path)) if $type eq "doc";
59 9         25 my $conf= $self->{':private:'}{Config};
60 9 100       25 if ($type eq "prog") {
61             return($self->_is_prefix($path, $conf->{prefix} || $conf->{prefixexp})
62 8 100 66     68 && !($self->_is_doc($path)) ? 1 : 0);
63             }
64 1         5 return(0);
65             }
66              
67             sub _is_under {
68 10     10   33 my ($self, $path, @under) = @_;
69 10 100       29 $under[0] = "" if (! @under);
70 10         26 foreach my $dir (@under) {
71 15 100       29 return(1) if ($self->_is_prefix($path, $dir));
72             }
73              
74 3         40 return(0);
75             }
76              
77             sub _fix_dirs {
78 12     12   124 my ($self, @dirs)= @_;
79             # File::Find does not know how to deal with VMS filepaths.
80 12 50       38 if( $Is_VMS ) {
81             $_ = VMS::Filespec::unixify($_)
82 0         0 for @dirs;
83             }
84              
85 12 50       31 if ($DOSISH) {
86 0         0 s|\\|/|g for @dirs;
87             }
88 12 100       81 return wantarray ? @dirs : $dirs[0];
89             }
90              
91             sub _make_entry {
92 305     305   935 my ($self, $module, $packlist_file, $modfile)= @_;
93              
94 305         1944 my $data= {
95             module => $module,
96             packlist => scalar(ExtUtils::Packlist->new($packlist_file)),
97             packlist_file => $packlist_file,
98             };
99              
100 305 100       876 if (!$modfile) {
101 6         41 $data->{version} = $self->{':private:'}{Config}{version};
102             } else {
103 299         665 $data->{modfile} = $modfile;
104             # Find the top-level module file in @INC
105 299         668 $data->{version} = '';
106 299         405 foreach my $dir (@{$self->{':private:'}{INC}}) {
  299         862  
107 2903         23751 my $p = File::Spec->catfile($dir, $modfile);
108 2903 100       36019 if (-r $p) {
109 299 50       840 $module = _module_name($p, $module) if $Is_VMS;
110              
111 299         1883 $data->{version} = MM->parse_version($p);
112 299         98464 $data->{version_from} = $p;
113 299         1606 $data->{packlist_valid} = exists $data->{packlist}{$p};
114 299         841 last;
115             }
116             }
117             }
118 305         9465 $self->{$module}= $data;
119             }
120              
121             our $INSTALLED;
122             sub new {
123 6     6 1 84197 my ($class) = shift(@_);
124 6   33     100 $class = ref($class) || $class;
125              
126 6         69 my %args = @_;
127              
128 6 0 0     36 return $INSTALLED if $INSTALLED and ($args{default_get} || $args{default});
      33        
129              
130 6         71 my $self = bless {}, $class;
131              
132 6 50 33     98 $INSTALLED= $self if $args{default_set} || $args{default};
133              
134              
135 6 100       33 if ($args{config_override}) {
136 1 50       17 eval {
137 1         10 $self->{':private:'}{Config} = { %{$args{config_override}} };
  1         1172  
138             } or Carp::croak(
139             "The 'config_override' parameter must be a hash reference."
140             );
141             }
142             else {
143 5         124 $self->{':private:'}{Config} = \%Config;
144             }
145              
146 6         296 for my $tuple ([inc_override => INC => [ @INC ] ],
147             [ extra_libs => EXTRA => [] ])
148             {
149 12         59 my ($arg,$key,$val)=@$tuple;
150 12 100       68 if ( $args{$arg} ) {
    50          
151 2 50       15 eval {
152 2         10 $self->{':private:'}{$key} = [ @{$args{$arg}} ];
  2         47  
153             } or Carp::croak(
154             "The '$arg' parameter must be an array reference."
155             );
156             }
157             elsif ($val) {
158 10         85 $self->{':private:'}{$key} = $val;
159             }
160             }
161             {
162 6         27 my %dupe;
  6         17  
163 6         97 @{$self->{':private:'}{LIBDIRS}} =
164 59 100       196 grep { $_ ne '.' || ! $args{skip_cwd} }
165 84 100       1574 grep { -e $_ && !$dupe{$_}++ }
166 6         18 @{$self->{':private:'}{EXTRA}}, @{$self->{':private:'}{INC}};
  6         26  
  6         45  
167             }
168              
169 6         27 my @dirs= $self->_fix_dirs(@{$self->{':private:'}{LIBDIRS}});
  6         81  
170              
171             # Read the core packlist
172 6         41 my $archlib = $self->_fix_dirs($self->{':private:'}{Config}{archlibexp});
173 6         226 $self->_make_entry("Perl",File::Spec->catfile($archlib, '.packlist'));
174              
175 6         26 my $root;
176             # Read the module packlists
177             my $sub = sub {
178             # Only process module .packlists
179 21571 100 100 21571   557991 return if $_ ne ".packlist" || $File::Find::dir eq $archlib;
180              
181             # Hack of the leading bits of the paths & convert to a module name
182 607         1117 my $module = $File::Find::name;
183             my $found = $module =~ s!^.*?/auto/(.*)/.packlist!$1!s
184 607 100       4653 or do {
185             # warn "Woah! \$_=$_\n\$module=$module\n\$File::Find::dir=$File::Find::dir\n",
186             # join ("\n",@dirs);
187 10         199 return;
188             };
189              
190 597         1576 my $modfile = "$module.pm";
191 597         1795 $module =~ s!/!::!g;
192              
193 597 100       7812 return if $self->{$module}; #shadowing?
194 299         936 $self->_make_entry($module,$File::Find::name,$modfile);
195 6         97 };
196 6         21 while (@dirs) {
197 57         177 $root= shift @dirs;
198 57 50       855 next if !-d $root;
199 57         3811 find($sub,$root);
200             }
201              
202 6         96 return $self;
203             }
204              
205             # VMS's non-case preserving file-system means the package name can't
206             # be reconstructed from the filename.
207             sub _module_name {
208 0     0   0 my($file, $orig_module) = @_;
209              
210 0         0 my $module = '';
211 0 0       0 if (open PACKFH, $file) {
212 0         0 while () {
213 0 0       0 if (/package\s+(\S+)\s*;/) {
214 0         0 my $pack = $1;
215             # Make a sanity check, that lower case $module
216             # is identical to lowercase $pack before
217             # accepting it
218 0 0       0 if (lc($pack) eq lc($orig_module)) {
219 0         0 $module = $pack;
220 0         0 last;
221             }
222             }
223             }
224 0         0 close PACKFH;
225             }
226              
227 0 0       0 print STDERR "Couldn't figure out the package name for $file\n"
228             unless $module;
229              
230 0         0 return $module;
231             }
232              
233             sub modules {
234 2     2 1 4626 my ($self) = @_;
235 2 50       9 $self= $self->new(default=>1) if !ref $self;
236              
237             # Bug/feature of sort in scalar context requires this.
238             return wantarray
239 4         35 ? sort grep { not /^:private:$/ } keys %$self
240 2 100       17 : grep { not /^:private:$/ } keys %$self;
  4         25  
241             }
242              
243             sub files {
244 7     7 1 4798 my ($self, $module, $type, @under) = @_;
245 7 50       23 $self= $self->new(default=>1) if !ref $self;
246              
247             # Validate arguments
248 7 100       483 Carp::croak("$module is not installed") if (! exists($self->{$module}));
249 6 100       18 $type = "all" if (! defined($type));
250 6 100 100     153 Carp::croak('type must be "all", "prog" or "doc"')
      66        
251             if ($type ne "all" && $type ne "prog" && $type ne "doc");
252              
253 5         8 my (@files);
254 5         10 foreach my $file (keys(%{$self->{$module}{packlist}})) {
  5         22  
255 10 100 100     35 push(@files, $file)
256             if ($self->_is_type($file, $type) &&
257             $self->_is_under($file, @under));
258             }
259 5         26 return(@files);
260             }
261              
262             sub directories {
263 2     2 1 1623 my ($self, $module, $type, @under) = @_;
264 2 50       11 $self= $self->new(default=>1) if !ref $self;
265 2         4 my (%dirs);
266 2         7 foreach my $file ($self->files($module, $type, @under)) {
267 2         58 $dirs{dirname($file)}++;
268             }
269 2         19 return sort keys %dirs;
270             }
271              
272             sub directory_tree {
273 0     0 1 0 my ($self, $module, $type, @under) = @_;
274 0 0       0 $self= $self->new(default=>1) if !ref $self;
275 0         0 my (%dirs);
276 0         0 foreach my $dir ($self->directories($module, $type, @under)) {
277 0         0 $dirs{$dir}++;
278 0         0 my ($last) = ("");
279 0         0 while ($last ne $dir) {
280 0         0 $last = $dir;
281 0         0 $dir = dirname($dir);
282 0 0       0 last if !$self->_is_under($dir, @under);
283 0         0 $dirs{$dir}++;
284             }
285             }
286 0         0 return(sort(keys(%dirs)));
287             }
288              
289             sub validate {
290 2     2 1 2232 my ($self, $module, $remove) = @_;
291 2 50       9 $self= $self->new(default=>1) if !ref $self;
292 2 100       96 Carp::croak("$module is not installed") if (! exists($self->{$module}));
293 1         15 return($self->{$module}{packlist}->validate($remove));
294             }
295              
296             sub packlist {
297 2     2 1 1228 my ($self, $module) = @_;
298 2 50       10 $self= $self->new(default=>1) if !ref $self;
299 2 100       83 Carp::croak("$module is not installed") if (! exists($self->{$module}));
300 1         25 return($self->{$module}{packlist});
301             }
302              
303             sub version {
304 2     2 1 615 my ($self, $module) = @_;
305 2 50       10 $self= $self->new(default=>1) if !ref $self;
306 2 100       94 Carp::croak("$module is not installed") if (! exists($self->{$module}));
307 1         19 return($self->{$module}{version});
308             }
309              
310             sub _debug_dump {
311 0     0     my ($self, $module) = @_;
312 0 0         $self= $self->new(default=>1) if !ref $self;
313 0           local $self->{":private:"}{Config};
314 0           require Data::Dumper;
315 0           print Data::Dumper->new([$self])->Sortkeys(1)->Indent(1)->Dump();
316             }
317              
318              
319             1;
320              
321             __END__