File Coverage

blib/lib/ExtUtils/InstallPaths.pm
Criterion Covered Total %
statement 136 180 75.5
branch 41 86 47.6
condition 13 28 46.4
subroutine 28 30 93.3
pod 6 6 100.0
total 224 330 67.8


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