File Coverage

blib/lib/ExtUtils/InstallPaths.pm
Criterion Covered Total %
statement 129 176 73.3
branch 38 82 46.3
condition 9 25 36.0
subroutine 27 30 90.0
pod 6 6 100.0
total 209 319 65.5


line stmt bran cond sub pod time code
1             package ExtUtils::InstallPaths;
2             $ExtUtils::InstallPaths::VERSION = '0.011';
3 1     1   40924 use 5.006;
  1         3  
  1         33  
4 1     1   5 use strict;
  1         1  
  1         32  
5 1     1   4 use warnings;
  1         2  
  1         26  
6              
7 1     1   4 use File::Spec ();
  1         1  
  1         9  
8 1     1   4 use Carp ();
  1         1  
  1         17  
9 1     1   7 use ExtUtils::Config 0.002;
  1         18  
  1         595  
10              
11             my %complex_accessors = map { $_ => 1 } qw/prefix_relpaths install_sets/;
12             my %hash_accessors = map { $_ => 1 } qw/install_path install_base_relpaths original_prefix /;
13              
14             my %defaults = (
15             installdirs => 'site',
16             install_base => undef,
17             prefix => undef,
18             verbose => 0,
19             blib => 'blib',
20             create_packlist => 1,
21             dist_name => undef,
22             module_name => undef,
23             destdir => undef,
24             install_path => sub { {} },
25             install_sets => \&_default_install_sets,
26             original_prefix => \&_default_original_prefix,
27             install_base_relpaths => \&_default_base_relpaths,
28             prefix_relpaths => \&_default_prefix_relpaths,
29             );
30              
31             sub _merge_shallow {
32 2     2   3 my ($name, $filter) = @_;
33             return sub {
34 1     1   1 my ($override, $config) = @_;
35 1         4 my $defaults = $defaults{$name}->($config);
36 1         17 $filter->($_) for grep $filter, values %$override;
37 1         7 return { %$defaults, %$override };
38             }
39 2         8 }
40              
41             sub _merge_deep {
42 2     2   3 my ($name, $filter) = @_;
43             return sub {
44 3     3   5 my ($override, $config) = @_;
45 3         7 my $defaults = $defaults{$name}->($config);
46             my $pair_for = sub {
47 9         8 my $key = shift;
48 9 100       6 my %override = %{ $override->{$key} || {} };
  9         35  
49 9   100     28 $filter && $filter->($_) for values %override;
50 8         10 return $key => { %{ $defaults->{$key} }, %override };
  8         54  
51 3         15 };
52 3         7 return { map { $pair_for->($_) } keys %$defaults };
  9         12  
53             }
54 2         7 }
55              
56             my %allowed_installdir = map { $_ => 1 } qw/core site vendor/;
57             my $must_be_relative = sub { Carp::croak('Value must be a relative path') if File::Spec->file_name_is_absolute($_[0]) };
58             my %deep_filter = map { $_ => $must_be_relative } qw/install_base_relpaths prefix_relpaths/;
59             my %filter = (
60             installdirs => sub {
61             my $value = shift;
62             $value = 'core', Carp::carp('Perhaps you meant installdirs to be "core" rather than "perl"?') if $value eq 'perl';
63             Carp::croak('installdirs must be one of "core", "site", or "vendor"') if not $allowed_installdir{$value};
64             return $value;
65             },
66             (map { $_ => _merge_shallow($_, $deep_filter{$_}) } qw/original_prefix install_base_relpaths/),
67             (map { $_ => _merge_deep($_, $deep_filter{$_}) } qw/install_sets prefix_relpaths/),
68             );
69              
70             sub new {
71 13     13 1 7563 my ($class, %args) = @_;
72 13   33     39 my $config = $args{config} || ExtUtils::Config->new;
73 177 100       602 my %self = (
    100          
    100          
74             config => $config,
75 13         46 map { $_ => exists $args{$_} ? $filter{$_} ? $filter{$_}->($args{$_}, $config) : $args{$_} : ref $defaults{$_} ? $defaults{$_}->($config) : $defaults{$_} } keys %defaults,
76             );
77 12 50 33     59 $self{module_name} ||= do { my $module_name = $self{dist_name}; $module_name =~ s/-/::/g; $module_name } if defined $self{dist_name};
  12         13  
  12         32  
  12         32  
78 12         58 return bless \%self, $class;
79             }
80              
81             for my $attribute (keys %defaults) {
82 1     1   5 no strict qw/refs/;
  1         2  
  1         1210  
83             *{$attribute} = $hash_accessors{$attribute} ?
84             sub {
85 118     118   113 my ($self, $key) = @_;
86 118 50       190 Carp::confess("$attribute needs key") if not defined $key;
87 118         270 return $self->{$attribute}{$key};
88             } :
89             $complex_accessors{$attribute} ?
90             sub {
91 79     79   107 my ($self, $installdirs, $key) = @_;
92 79 50       120 Carp::confess("$attribute needs installdir") if not defined $installdirs;
93 79 50       97 Carp::confess("$attribute needs key") if not defined $key;
94 79         225 return $self->{$attribute}{$installdirs}{$key};
95             } :
96             sub {
97 325     325   269 my $self = shift;
98 325         629 return $self->{$attribute};
99             };
100             }
101              
102             my $script = $] > 5.008000 ? 'script' : 'bin';
103             my @install_sets_keys = qw/lib arch bin script bindoc libdoc binhtml libhtml/;
104             my @install_sets_tail = ('bin', $script, qw/man1dir man3dir html1dir html3dir/);
105             my %install_sets_values = (
106             core => [ qw/privlib archlib /, @install_sets_tail ],
107             site => [ map { "site$_" } qw/lib arch/, @install_sets_tail ],
108             vendor => [ map { "vendor$_" } qw/lib arch/, @install_sets_tail ],
109             );
110              
111             sub _default_install_sets {
112 13     13   15 my $c = shift;
113              
114 13         12 my %ret;
115 13         22 for my $installdir (qw/core site vendor/) {
116 39         30 @{$ret{$installdir}}{@install_sets_keys} = map { $c->get("install$_") } @{ $install_sets_values{$installdir} };
  39         428  
  312         1941  
  39         56  
117             }
118 13         23 return \%ret;
119             }
120              
121             sub _default_base_relpaths {
122 13     13   13 my $config = shift;
123             return {
124 13         48 lib => ['lib', 'perl5'],
125             arch => ['lib', 'perl5', $config->get('archname')],
126             bin => ['bin'],
127             script => ['bin'],
128             bindoc => ['man', 'man1'],
129             libdoc => ['man', 'man3'],
130             binhtml => ['html'],
131             libhtml => ['html'],
132             };
133             }
134              
135             my %common_prefix_relpaths = (
136             bin => ['bin'],
137             script => ['bin'],
138             bindoc => ['man', 'man1'],
139             libdoc => ['man', 'man3'],
140             binhtml => ['html'],
141             libhtml => ['html'],
142             );
143              
144             sub _default_prefix_relpaths {
145 13     13   13 my $c = shift;
146              
147 13 50       23 my @libstyle = $c->get('installstyle') ? File::Spec->splitdir($c->get('installstyle')) : qw(lib perl5);
148 13         182 my $arch = $c->get('archname');
149 13         79 my $version = $c->get('version');
150              
151             return {
152 13         232 core => {
153             lib => [@libstyle],
154             arch => [@libstyle, $version, $arch],
155             %common_prefix_relpaths,
156             },
157             vendor => {
158             lib => [@libstyle],
159             arch => [@libstyle, $version, $arch],
160             %common_prefix_relpaths,
161             },
162             site => {
163             lib => [@libstyle, 'site_perl'],
164             arch => [@libstyle, 'site_perl', $version, $arch],
165             %common_prefix_relpaths,
166             },
167             };
168             }
169              
170             sub _default_original_prefix {
171 13     13   15 my $c = shift;
172              
173 13 50       39 my %ret = (
174             core => $c->get('installprefixexp'),
175             site => $c->get('siteprefixexp'),
176             vendor => $c->get('usevendorprefix') ? $c->get('vendorprefixexp') : '',
177             );
178              
179 13         3005 return \%ret;
180             }
181              
182             sub _log_verbose {
183 48     48   40 my $self = shift;
184 48 50       53 print @_ if $self->verbose;
185 48         43 return;
186             }
187              
188             # Given a file type, will return true if the file type would normally
189             # be installed when neither install-base nor prefix has been set.
190             # I.e. it will be true only if the path is set from Config.pm or
191             # set explicitly by the user via install-path.
192             sub is_default_installable {
193 0     0 1 0 my $self = shift;
194 0         0 my $type = shift;
195 0   0     0 my $installable = $self->install_destination($type) && ( $self->install_path($type) || $self->install_sets($self->installdirs, $type));
196 0 0       0 return $installable ? 1 : 0;
197             }
198              
199             sub _prefixify_default {
200 24     24   18 my $self = shift;
201 24         21 my $type = shift;
202 24         20 my $rprefix = shift;
203              
204 24         28 my $default = $self->prefix_relpaths($self->installdirs, $type);
205 24 50       40 if( !$default ) {
206 0         0 $self->_log_verbose(" no default install location for type '$type', using prefix '$rprefix'.\n");
207 0         0 return $rprefix;
208             } else {
209 24         18 return File::Spec->catdir(@{$default});
  24         160  
210             }
211             }
212              
213             # Translated from ExtUtils::MM_Unix::prefixify()
214             sub _prefixify_novms {
215 24     24   23 my($self, $path, $sprefix, $type) = @_;
216              
217 24         75 my $rprefix = $self->prefix;
218 24 50       52 $rprefix .= '/' if $sprefix =~ m{/$};
219              
220 24 50 33     135 $self->_log_verbose(" prefixify $path from $sprefix to $rprefix\n") if defined $path && length $path;
221              
222 24 50 33     292 if (not defined $path or length $path == 0 ) {
    50          
    50          
223 0         0 $self->_log_verbose(" no path to prefixify, falling back to default.\n");
224 0         0 return $self->_prefixify_default( $type, $rprefix );
225             } elsif( !File::Spec->file_name_is_absolute($path) ) {
226 0         0 $self->_log_verbose(" path is relative, not prefixifying.\n");
227             } elsif( $path !~ s{^\Q$sprefix\E\b}{}s ) {
228 24         34 $self->_log_verbose(" cannot prefixify, falling back to default.\n");
229 24         33 return $self->_prefixify_default( $type, $rprefix );
230             }
231              
232 0         0 $self->_log_verbose(" now $path in $rprefix\n");
233              
234 0         0 return $path;
235             }
236              
237             sub _catprefix_vms {
238 0     0   0 my ($self, $rprefix, $default) = @_;
239              
240 0         0 my ($rvol, $rdirs) = File::Spec->splitpath($rprefix);
241 0 0       0 if ($rvol) {
242 0         0 return File::Spec->catpath($rvol, File::Spec->catdir($rdirs, $default), '');
243             }
244             else {
245 0         0 return File::Spec->catdir($rdirs, $default);
246             }
247             }
248             sub _prefixify_vms {
249 0     0   0 my($self, $path, $sprefix, $type) = @_;
250 0         0 my $rprefix = $self->prefix;
251              
252 0 0       0 return '' unless defined $path;
253              
254 0         0 $self->_log_verbose(" prefixify $path from $sprefix to $rprefix\n");
255              
256 0         0 require VMS::Filespec;
257             # Translate $(PERLPREFIX) to a real path.
258 0 0       0 $rprefix = VMS::Filespec::vmspath($rprefix) if $rprefix;
259 0 0       0 $sprefix = VMS::Filespec::vmspath($sprefix) if $sprefix;
260              
261 0         0 $self->_log_verbose(" rprefix translated to $rprefix\n sprefix translated to $sprefix\n");
262              
263 0 0       0 if (length($path) == 0 ) {
    0          
    0          
264 0         0 $self->_log_verbose(" no path to prefixify.\n")
265             }
266             elsif (!File::Spec->file_name_is_absolute($path)) {
267 0         0 $self->_log_verbose(" path is relative, not prefixifying.\n");
268             }
269             elsif ($sprefix eq $rprefix) {
270 0         0 $self->_log_verbose(" no new prefix.\n");
271             }
272             else {
273 0         0 my ($path_vol, $path_dirs) = File::Spec->splitpath( $path );
274 0         0 my $vms_prefix = $self->config->get('vms_prefix');
275 0 0       0 if ($path_vol eq $vms_prefix.':') {
276 0         0 $self->_log_verbose(" $vms_prefix: seen\n");
277              
278 0 0       0 $path_dirs =~ s{^\[}{\[.} unless $path_dirs =~ m{^\[\.};
279 0         0 $path = $self->_catprefix_vms($rprefix, $path_dirs);
280             }
281             else {
282 0         0 $self->_log_verbose(" cannot prefixify.\n");
283 0         0 return File::Spec->catdir($self->prefix_relpaths($self->installdirs, $type));
284             }
285             }
286              
287 0         0 $self->_log_verbose(" now $path\n");
288              
289 0         0 return $path;
290             }
291              
292 1 50   1   615 BEGIN { *_prefixify = $^O eq 'VMS' ? \&_prefixify_vms : \&_prefixify_novms }
293              
294             # Translated from ExtUtils::MM_Any::init_INSTALL_from_PREFIX
295             sub prefix_relative {
296 24     24 1 20 my ($self, $installdirs, $type) = @_;
297              
298 24         36 my $relpath = $self->install_sets($installdirs, $type);
299              
300 24         31 return $self->_prefixify($relpath, $self->original_prefix($installdirs), $type);
301             }
302              
303             sub install_destination {
304 69     69 1 17051 my ($self, $type) = @_;
305              
306 69 50       96 return $self->install_path($type) if $self->install_path($type);
307              
308 69 100       104 if ( $self->install_base ) {
309 23         29 my $relpath = $self->install_base_relpaths($type);
310 23 50       46 return $relpath ? File::Spec->catdir($self->install_base, @{$relpath}) : undef;
  23         198  
311             }
312              
313 46 100       57 if ( $self->prefix ) {
314 24         37 my $relpath = $self->prefix_relative($self->installdirs, $type);
315 24 50       85 return $relpath ? File::Spec->catdir($self->prefix, $relpath) : undef;
316             }
317 22         33 return $self->install_sets($self->installdirs, $type);
318             }
319              
320             sub install_types {
321 3     3 1 6 my $self = shift;
322              
323 3         10 my %types = ( %{ $self->{install_path} },
  1         6  
324 0         0 $self->install_base ? %{ $self->{install_base_relpaths} }
325 2         4 : $self->prefix ? %{ $self->{prefix_relpaths}{ $self->installdirs } }
326 3 50       4 : %{ $self->{install_sets}{ $self->installdirs } });
    100          
327              
328 3         25 return sort keys %types;
329             }
330              
331             sub install_map {
332 2     2 1 12 my ($self, $blib) = @_;
333 2   33     12 $blib ||= $self->blib;
334              
335 2         3 my (%map, @skipping);
336 2         5 foreach my $type ($self->install_types) {
337 16         48 my $localdir = File::Spec->catdir($blib, $type);
338 16 100       125 next unless -e $localdir;
339              
340             # the line "...next if (($type eq 'bindoc'..." was one of many changes introduced for
341             # improving HTML generation on ActivePerl, see https://rt.cpan.org/Public/Bug/Display.html?id=53478
342             # Most changes were ok, but this particular line caused test failures in t/manifypods.t on windows,
343             # therefore it is commented out.
344              
345             # ********* next if (($type eq 'bindoc' || $type eq 'libdoc') && not $self->is_unixish);
346              
347 8 50       11 if (my $dest = $self->install_destination($type)) {
348 8         22 $map{$localdir} = $dest;
349             } else {
350 0         0 push @skipping, $type;
351             }
352             }
353              
354 2 50       7 warn "WARNING: Can't figure out install path for types: @skipping\nFiles will not be installed.\n" if @skipping;
355              
356             # Write the packlist into the same place as ExtUtils::MakeMaker.
357 2 50 33     3 if ($self->create_packlist and my $module_name = $self->module_name) {
358 2         5 my $archdir = $self->install_destination('arch');
359 2         8 my @ext = split /::/, $module_name;
360 2         24 $map{write} = File::Spec->catfile($archdir, 'auto', @ext, '.packlist');
361             }
362              
363             # Handle destdir
364 2 50 50     5 if (length(my $destdir = $self->destdir || '')) {
365 0         0 foreach (keys %map) {
366             # Need to remove volume from $map{$_} using splitpath, or else
367             # we'll create something crazy like C:\Foo\Bar\E:\Baz\Quux
368             # VMS will always have the file separate than the path.
369 0         0 my ($volume, $path, $file) = File::Spec->splitpath( $map{$_}, 0 );
370              
371             # catdir needs a list of directories, or it will create something
372             # crazy like volume:[Foo.Bar.volume.Baz.Quux]
373 0         0 my @dirs = File::Spec->splitdir($path);
374              
375             # First merge the directories
376 0         0 $path = File::Spec->catdir($destdir, @dirs);
377              
378             # Then put the file back on if there is one.
379 0 0       0 if ($file ne '') {
380 0         0 $map{$_} = File::Spec->catfile($path, $file)
381             } else {
382 0         0 $map{$_} = $path;
383             }
384             }
385             }
386              
387 2         6 $map{read} = ''; # To keep ExtUtils::Install quiet
388              
389 2         5 return \%map;
390             }
391              
392             1;
393              
394             # ABSTRACT: Build.PL install path logic made easy
395              
396             __END__