File Coverage

blib/lib/PAR/Dist.pm
Criterion Covered Total %
statement 274 523 52.3
branch 80 254 31.5
condition 32 157 20.3
subroutine 21 39 53.8
pod 11 11 100.0
total 418 984 42.4


line stmt bran cond sub pod time code
1             package PAR::Dist;
2 3     3   614854 use 5.006;
  3         10  
3 3     3   17 use strict;
  3         7  
  3         129  
4             require Exporter;
5 3     3   17 use vars qw/$VERSION @ISA @EXPORT @EXPORT_OK $DEBUG/;
  3         7  
  3         432  
6              
7             $VERSION = '0.53';
8             @ISA = 'Exporter';
9             @EXPORT = qw/
10             blib_to_par
11             install_par
12             uninstall_par
13             sign_par
14             verify_par
15             merge_par
16             remove_man
17             get_meta
18             generate_blib_stub
19             /;
20              
21             @EXPORT_OK = qw/
22             parse_dist_name
23             contains_binaries
24             /;
25              
26             $DEBUG = 0;
27              
28 3     3   19 use Carp qw/carp croak/;
  3         6  
  3         219  
29 3     3   21 use File::Spec;
  3         4  
  3         21296  
30              
31             =head1 NAME
32              
33             PAR::Dist - Create and manipulate PAR distributions
34              
35             =head1 SYNOPSIS
36              
37             As a shell command:
38              
39             % perl -MPAR::Dist -eblib_to_par
40              
41             In programs:
42              
43             use PAR::Dist;
44              
45             my $dist = blib_to_par(); # make a PAR file using ./blib/
46             install_par($dist); # install it into the system
47             uninstall_par($dist); # uninstall it from the system
48             sign_par($dist); # sign it using Module::Signature
49             verify_par($dist); # verify it using Module::Signature
50              
51             install_par("http://foo.com/DBI-1.37-MSWin32-5.8.0.par"); # works too
52             install_par("http://foo.com/DBI-1.37"); # auto-appends archname + perlver
53             install_par("cpan://SMUELLER/PAR-Packer-0.975"); # uses CPAN author directory
54              
55             =head1 DESCRIPTION
56              
57             This module creates and manipulates I. They are
58             architecture-specific B files, containing everything under F
59             of CPAN distributions after their C or C stage, a
60             F describing metadata of the original CPAN distribution,
61             and a F detailing all files within it. Digitally signed PAR
62             distributions will also contain a F file.
63              
64             The naming convention for such distributions is:
65              
66             $NAME-$VERSION-$ARCH-$PERL_VERSION.par
67              
68             For example, C corresponds to the
69             0.01 release of C on CPAN, built for perl 5.8.0 running on
70             C.
71              
72             =head1 FUNCTIONS
73              
74             Several functions are exported by default. Unless otherwise noted,
75             they can take either a hash of
76             named arguments, a single argument (taken as C<$path> by C
77             and C<$dist> by other functions), or no arguments (in which case
78             the first PAR file in the current directory is used).
79              
80             Therefore, under a directory containing only a single F, all
81             invocations below are equivalent:
82              
83             % perl -MPAR::Dist -e"install_par( dist => 'test.par' )"
84             % perl -MPAR::Dist -e"install_par( 'test.par' )"
85             % perl -MPAR::Dist -einstall_par;
86              
87             If C<$dist> resembles a URL, C is called to mirror it
88             locally under C<$ENV{PAR_TEMP}> (or C<$TEMP/par/> if unspecified), and the
89             function will act on the fetched local file instead. If the URL begins
90             with C, it will be expanded automatically to the author's CPAN
91             directory (e.g. C).
92              
93             If C<$dist> does not have a file extension beginning with a letter or
94             underscore, a dash and C<$suffix> ($ARCH-$PERL_VERSION.par by default)
95             will be appended to it.
96              
97             =head2 blib_to_par
98              
99             Takes key/value pairs as parameters or a single parameter indicating the
100             path that contains the F subdirectory.
101              
102             Builds a PAR distribution from the F subdirectory under C, or
103             under the current directory if unspecified. If F does not exist,
104             it automatically runs F, F, F or F to
105             create it.
106              
107             Returns the filename of the generated PAR distribution.
108              
109             Valid parameters are:
110              
111             =over 2
112              
113             =item path
114              
115             Sets the path which contains the F subdirectory from which the PAR
116             distribution will be generated.
117              
118             =item name, version, suffix
119              
120             These attributes set the name, version and platform specific suffix
121             of the distribution. Name and version can be automatically
122             determined from the distributions F or F files.
123              
124             The suffix is generated from your architecture name and your version of
125             perl by default.
126              
127             =item dist
128              
129             The output filename for the PAR distribution.
130              
131             =item quiet
132              
133             Set to true to suppress as much output as possible.
134              
135             =back
136              
137             =cut
138              
139             sub blib_to_par {
140 1 50   1 1 6 @_ = (path => @_) if @_ == 1;
141              
142 1         6 my %args = @_;
143 1         10 require Config;
144              
145              
146             # don't use 'my $foo ... if ...' it creates a static variable!
147 1   50     5 my $quiet = $args{quiet} || 0;
148 1         2 my $dist;
149 1         3 my $path = $args{path};
150 1 50       5 $dist = File::Spec->rel2abs($args{dist}) if $args{dist};
151 1         3 my $name = $args{name};
152 1         3 my $version = $args{version};
153 1   33     27 my $suffix = $args{suffix} || "$Config::Config{archname}-$Config::Config{version}.par";
154 1         5 my $cwd;
155              
156 1 50       3 if (defined $path) {
157 0         0 require Cwd;
158 0         0 $cwd = Cwd::cwd();
159 0         0 chdir $path;
160             }
161              
162 1 50       19 _build_blib() unless -d "blib";
163              
164 1         2 my @files;
165 1 50       171 open MANIFEST, ">", File::Spec->catfile("blib", "MANIFEST") or die $!;
166 1 50       185 open META, ">", File::Spec->catfile("blib", "META.yml") or die $!;
167              
168 1         13 require File::Find;
169             File::Find::find( sub {
170 15 50   15   67 next unless $File::Find::name;
171 15 100 66     1482 (-r && !-d) and push ( @files, substr($File::Find::name, 5) );
172 1         87 } , 'blib' );
173              
174 1         30 print MANIFEST join(
175             "\n",
176             ' ',
177             (sort @files),
178             q( # )
179             );
180 1         49 close MANIFEST;
181              
182             # if MYMETA.yml exists, that takes precedence over META.yml
183 1         9 my $meta_file_name = "META.yml";
184 1         3 my $mymeta_file_name = "MYMETA.yml";
185 1 50       30 $meta_file_name = -s $mymeta_file_name ? $mymeta_file_name : $meta_file_name;
186 1 50       35 if (open(OLD_META, $meta_file_name)) {
187 1         34 while () {
188 40 50       104 if (/^distribution_type:/) {
189 0         0 print META "distribution_type: par\n";
190             }
191             else {
192 40         61 print META $_;
193             }
194              
195 40 100       147 if (/^name:\s+(.*)/) {
    100          
196 1   33     16 $name ||= $1;
197 1         5 $name =~ s/::/-/g;
198             }
199             elsif (/^version:\s+(\S*)/) {
200 1   33     19 $version ||= $1;
201 1         23 $version =~ s/^['"]|['"]$//g;
202             }
203             }
204 1         14 close OLD_META;
205 1         45 close META;
206             }
207              
208 1 50 33     19 if ((!$name or !$version) and open(MAKEFILE, "Makefile")) {
      33        
209 0         0 while () {
210 0 0       0 if (/^DISTNAME\s+=\s+(.*)$/) {
    0          
211 0   0     0 $name ||= $1;
212             }
213             elsif (/^VERSION\s+=\s+(.*)$/) {
214 0   0     0 $version ||= $1;
215             }
216             }
217             }
218              
219 1 50 33     10 if (not defined($name) or not defined($version)) {
220             # could not determine name or version. Error.
221 0         0 my $what;
222 0 0       0 if (not defined $name) {
    0          
223 0         0 $what = 'name';
224 0 0       0 $what .= ' and version' if not defined $version;
225             }
226             elsif (not defined $version) {
227 0         0 $what = 'version';
228             }
229              
230 0         0 carp("I was unable to determine the $what of the PAR distribution. Please create a Makefile or META.yml file from which we can infer the information or just specify the missing information as an option to blib_to_par.");
231 0         0 return();
232             }
233              
234 1         5 $name =~ s/\s+$//;
235 1         2 $version =~ s/\s+$//;
236              
237 1         3 my $file = "$name-$version-$suffix";
238 1 50       36 unlink $file if -f $file;
239              
240 1 50       5 print META << "YAML" if fileno(META);
241             name: $name
242             version: '$version'
243             build_requires: {}
244             conflicts: {}
245             dist_name: $file
246             distribution_type: par
247             dynamic_config: 0
248             generated_by: 'PAR::Dist version $PAR::Dist::VERSION'
249             license: unknown
250             YAML
251 1         2 close META;
252              
253 1         9 mkdir('blib', 0777);
254 1         7 chdir('blib');
255 1         10 require Cwd;
256 1         32 my $zipoutfile = File::Spec->catfile(File::Spec->updir, $file);
257 1         7 _zip(dist => $zipoutfile);
258 1         26 chdir(File::Spec->updir);
259              
260 1         114 unlink File::Spec->catfile("blib", "MANIFEST");
261 1         96 unlink File::Spec->catfile("blib", "META.yml");
262              
263 1 50 0     12 $dist ||= File::Spec->catfile($cwd, $file) if $cwd;
264              
265 1 50 33     9 if ($dist and $file ne $dist) {
266 0 0       0 if ( File::Copy::copy($file, $dist) ) {
267 0         0 unlink $file;
268             } else {
269 0         0 die "Cannot copy $file: $!";
270             }
271              
272 0         0 $file = $dist;
273             }
274              
275 1         38 my $pathname = File::Spec->rel2abs($file);
276 1 50       8 if ($^O eq 'MSWin32') {
277 0         0 $pathname =~ s!\\!/!g;
278 0         0 $pathname =~ s!:!|!g;
279             };
280 1 50       4 print << "." if !$quiet;
281             Successfully created binary distribution '$file'.
282             Its contents are accessible in compliant browsers as:
283             jar:file://$pathname!/MANIFEST
284             .
285              
286 1 50       4 chdir $cwd if $cwd;
287 1         41 return $file;
288             }
289              
290             sub _build_blib {
291 0 0   0   0 if (-e 'Build') {
    0          
    0          
    0          
292 0         0 _system_wrapper($^X, "Build");
293             }
294             elsif (-e 'Makefile') {
295 0         0 _system_wrapper($Config::Config{make});
296             }
297             elsif (-e 'Build.PL') {
298 0         0 _system_wrapper($^X, "Build.PL");
299 0         0 _system_wrapper($^X, "Build");
300             }
301             elsif (-e 'Makefile.PL') {
302 0         0 _system_wrapper($^X, "Makefile.PL");
303 0         0 _system_wrapper($Config::Config{make});
304             }
305             }
306              
307             =head2 install_par
308              
309             Installs a PAR distribution into the system, using
310             C.
311              
312             If only a single parameter is given, it is treated as the value for the
313             C parameter.
314              
315             Valid named parameters are:
316              
317             =over 2
318              
319             =item dist
320              
321             The .par file to install. The heuristics outlined in the B
322             section above apply.
323              
324             =item prefix
325              
326             This string will be prepended to all installation paths.
327             If it isn't specified, the environment variable
328             C is used as a prefix.
329              
330             =item uninstall_shadows
331              
332             This corresponds to the C option of L. Quoting its manual:
333             If C is set to true, any differing versions throughout C<@INC>
334             will be uninstalled. This is C.
335              
336             =item verbose
337              
338             This corresponds to the C option of L. According to its manual:
339             If C is true, will print out each file removed. This is C.
340             C values going up to 5 show increasingly more diagnostics output.
341              
342             Default verbosity for PAR::Dist is 1.
343              
344             =back
345              
346             If you're just going to install into the running perl like everything else,
347             you can stop reading the rest of this section now.
348              
349             Additionally, you can use several parameters to change the default
350             installation destinations. You don't usually have to worry about this
351             unless you are installing into a user-local directory.
352             The following section outlines the parameter names and default settings:
353              
354             Parameter From To
355             inst_lib blib/lib $Config{installsitelib} (*)
356             inst_archlib blib/arch $Config{installsitearch}
357             inst_script blib/script $Config{installscript}
358             inst_bin blib/bin $Config{installbin}
359             inst_man1dir blib/man1 $Config{installman1dir}
360             inst_man3dir blib/man3 $Config{installman3dir}
361             packlist_read $Config{sitearchexp}/auto/$name/.packlist
362             packlist_write $Config{installsitearch}/auto/$name/.packlist
363              
364             The C parameter is used to control where the F<.packlist>
365             file is written to. (Necessary for uninstallation.)
366             The C parameter specifies a .packlist file to merge in if
367             it exists. By setting any of the above installation targets to C,
368             you can remove that target altogether. For example, passing
369             C<< inst_man1dir => undef, inst_man3dir => undef >> means that the contained
370             manual pages won't be installed. This is not available for the packlists.
371              
372             Again, the defaults will be the normal I paths from C<%Config>.
373              
374             (*) If the C<.par>'s I section (normally C)
375             isn't empty, the code in I (normally C) is also installed
376             into the I path. This makes sense for XS modules.
377             If, however, you override C, this automatic conversion is
378             also overridden! You can use the named parameter
379             C 1> to re-enable the conversion
380             for custom I's.
381              
382             Finally, you may specify a C parameter. Its value should be
383             a reference to a hash of custom installation targets such as
384              
385             custom_targets => { 'blib/my_data' => '/some/path/my_data' }
386              
387             You can use this to install the F<.par> archives contents to arbitrary
388             locations.
389              
390             =cut
391              
392             sub install_par {
393 0     0 1 0 my %args = &_args;
394 0         0 _install_or_uninstall(%args, action => 'install');
395             }
396              
397             =head2 uninstall_par
398              
399             Uninstalls all previously installed contents of a PAR distribution,
400             using C.
401              
402             Takes almost the same parameters as C, but naturally,
403             the installation target parameters do not apply. The only exception
404             to this is the C parameter which specifies the
405             F<.packlist> file to read the list of installed files from.
406             It defaults to C<$Config::Config{installsitearch}/auto/$name/.packlist>.
407              
408             Additionally, the C parameter of C
409             isn't available.
410              
411             =cut
412              
413             sub uninstall_par {
414 0     0 1 0 my %args = &_args;
415 0         0 _install_or_uninstall(%args, action => 'uninstall');
416             }
417              
418             sub _install_or_uninstall {
419 0     0   0 my %args = &_args;
420 0         0 my $name = $args{name};
421 0         0 my $action = $args{action};
422              
423 0         0 my %ENV_copy = %ENV;
424 0 0       0 $ENV{PERL_INSTALL_ROOT} = $args{prefix} if defined $args{prefix};
425              
426 0         0 require Cwd;
427 0         0 my $old_dir = Cwd::cwd();
428              
429 0         0 my ($dist, $tmpdir) = _unzip_to_tmpdir( dist => $args{dist}, subdir => 'blib' );
430              
431 0 0       0 if ( open (META, File::Spec->catfile('blib', 'META.yml')) ) {
432 0         0 while () {
433 0 0       0 next unless /^name:\s+(.*)/;
434 0         0 $name = $1;
435 0         0 $name =~ s/\s+$//;
436 0         0 last;
437             }
438 0         0 close META;
439             }
440 0 0 0     0 return if not defined $name or $name eq '';
441              
442 0 0       0 if (-d 'script') {
443 0         0 require ExtUtils::MY;
444 0         0 foreach my $file (glob("script/*")) {
445 0 0       0 next unless -T $file;
446 0         0 ExtUtils::MY->fixin($file);
447 0         0 chmod(0555, $file);
448             }
449             }
450              
451 0         0 $name =~ s{::|-}{/}g;
452 0         0 require ExtUtils::Install;
453              
454 0 0       0 if ($action eq 'install') {
    0          
455 0         0 my $target = _installation_target( File::Spec->curdir, $name, \%args );
456 0   0     0 my $custom_targets = $args{custom_targets} || {};
457 0         0 $target->{$_} = $custom_targets->{$_} foreach keys %{$custom_targets};
  0         0  
458              
459 0         0 my $uninstall_shadows = $args{uninstall_shadows};
460 0         0 my $verbose = $args{verbose};
461 0         0 ExtUtils::Install::install($target, $verbose, 0, $uninstall_shadows);
462             }
463             elsif ($action eq 'uninstall') {
464 0         0 require Config;
465 0         0 my $verbose = $args{verbose};
466             ExtUtils::Install::uninstall(
467 0   0     0 $args{packlist_read}||"$Config::Config{installsitearch}/auto/$name/.packlist",
468             $verbose
469             );
470             }
471              
472 0         0 %ENV = %ENV_copy;
473              
474 0         0 chdir($old_dir);
475 0         0 File::Path::rmtree([$tmpdir]);
476              
477 0         0 return 1;
478             }
479              
480             # Returns the default installation target as used by
481             # ExtUtils::Install::install(). First parameter should be the base
482             # directory containing the blib/ we're installing from.
483             # Second parameter should be the name of the distribution for the packlist
484             # paths. Third parameter may be a hash reference with user defined keys for
485             # the target hash. In fact, any contents that do not start with 'inst_' are
486             # skipped.
487             sub _installation_target {
488 0     0   0 require Config;
489 0         0 my $dir = shift;
490 0         0 my $name = shift;
491 0   0     0 my $user = shift || {};
492              
493             # accepted sources (and user overrides)
494 0         0 my %sources = (
495             inst_lib => File::Spec->catdir($dir,"blib","lib"),
496             inst_archlib => File::Spec->catdir($dir,"blib","arch"),
497             inst_bin => File::Spec->catdir($dir,'blib','bin'),
498             inst_script => File::Spec->catdir($dir,'blib','script'),
499             inst_man1dir => File::Spec->catdir($dir,'blib','man1'),
500             inst_man3dir => File::Spec->catdir($dir,'blib','man3'),
501             packlist_read => 'read',
502             packlist_write => 'write',
503             );
504              
505              
506 0         0 my $par_has_archlib = _directory_not_empty( $sources{inst_archlib} );
507              
508             # default targets
509             my $target = {
510             read => $Config::Config{sitearchexp}."/auto/$name/.packlist",
511             write => $Config::Config{installsitearch}."/auto/$name/.packlist",
512             $sources{inst_lib} =>
513             ($par_has_archlib
514             ? $Config::Config{installsitearch}
515             : $Config::Config{installsitelib}),
516             $sources{inst_archlib} => $Config::Config{installsitearch},
517             $sources{inst_bin} => $Config::Config{installbin} ,
518             $sources{inst_script} => $Config::Config{installscript},
519             $sources{inst_man1dir} => $Config::Config{installman1dir},
520             $sources{inst_man3dir} => $Config::Config{installman3dir},
521 0 0       0 };
522              
523             # Included for future support for ${flavour}perl external lib installation
524             # if ($Config::Config{flavour_perl}) {
525             # my $ext = File::Spec->catdir($dir, 'blib', 'ext');
526             # # from => to
527             # $sources{inst_external_lib} = File::Spec->catdir($ext, 'lib');
528             # $sources{inst_external_bin} = File::Spec->catdir($ext, 'bin');
529             # $sources{inst_external_include} = File::Spec->catdir($ext, 'include');
530             # $sources{inst_external_src} = File::Spec->catdir($ext, 'src');
531             # $target->{ $sources{inst_external_lib} } = $Config::Config{flavour_install_lib};
532             # $target->{ $sources{inst_external_bin} } = $Config::Config{flavour_install_bin};
533             # $target->{ $sources{inst_external_include} } = $Config::Config{flavour_install_include};
534             # $target->{ $sources{inst_external_src} } = $Config::Config{flavour_install_src};
535             # }
536              
537             # insert user overrides
538 0         0 foreach my $key (keys %$user) {
539 0         0 my $value = $user->{$key};
540 0 0 0     0 if (not defined $value and $key ne 'packlist_read' and $key ne 'packlist_write') {
    0 0        
541             # undef means "remove"
542 0         0 delete $target->{ $sources{$key} };
543             }
544             elsif (exists $sources{$key}) {
545             # overwrite stuff, don't let the user create new entries
546 0         0 $target->{ $sources{$key} } = $value;
547             }
548             }
549              
550             # apply the automatic inst_lib => inst_archlib conversion again
551             # if the user asks for it and there is an archlib in the .par
552 0 0 0     0 if ($user->{auto_inst_lib_conversion} and $par_has_archlib) {
553 0         0 $target->{inst_lib} = $target->{inst_archlib};
554             }
555              
556 0         0 return $target;
557             }
558              
559             sub _directory_not_empty {
560 0     0   0 require File::Find;
561 0         0 my($dir) = @_;
562 0         0 my $files = 0;
563             File::Find::find(sub {
564 0 0   0   0 return if $_ eq ".exists";
565 0 0       0 if (-f) {
566 0         0 $File::Find::prune++;
567 0         0 $files = 1;
568             }
569 0         0 }, $dir);
570 0         0 return $files;
571             }
572              
573             =head2 sign_par
574              
575             Digitally sign a PAR distribution using C or B,
576             via B.
577              
578             =cut
579              
580             sub sign_par {
581 0     0 1 0 my %args = &_args;
582 0         0 _verify_or_sign(%args, action => 'sign');
583             }
584              
585             =head2 verify_par
586              
587             Verify the digital signature of a PAR distribution using C or
588             B, via B.
589              
590             Returns a boolean value indicating whether verification passed; C<$!>
591             is set to the return code of C.
592              
593             =cut
594              
595             sub verify_par {
596 0     0 1 0 my %args = &_args;
597 0         0 $! = _verify_or_sign(%args, action => 'verify');
598 0         0 return ( $! == Module::Signature::SIGNATURE_OK() );
599             }
600              
601             =head2 merge_par
602              
603             I Since version 0.32 of PAR::Dist, this function requires a YAML
604             reader. The order of precedence is:
605              
606             YAML:XS YAML YAML::Syck YAML::Tiny
607              
608             Merges two or more PAR distributions into one. First argument must
609             be the name of the distribution you want to merge all others into.
610             Any following arguments will be interpreted as the file names of
611             further PAR distributions to merge into the first one.
612              
613             merge_par('foo.par', 'bar.par', 'baz.par')
614              
615             This will merge the distributions C, C and C
616             into the distribution C. C will be overwritten!
617              
618             The original META.yml of C is retained, but augmented with any
619             C, C, C, C, and
620             C sections from the other C<.par> files.
621              
622             =cut
623              
624             sub merge_par {
625 1     1 1 231295 my $base_par = shift;
626 1         3 my @additional_pars = @_;
627 1         37 require Cwd;
628 1         5 require File::Copy;
629 1         4 require File::Path;
630 1         5 require File::Find;
631              
632             # parameter checking
633 1 50       6 if (not defined $base_par) {
634 0         0 croak "First argument to merge_par() must be the .par archive to modify.";
635             }
636              
637 1 50 33     28 if (not -f $base_par or not -r _ or not -w _) {
      33        
638 0         0 croak "'$base_par' is not a file or you do not have enough permissions to read and modify it.";
639             }
640              
641 1         2 foreach (@additional_pars) {
642 1 50 33     10 if (not -f $_ or not -r _) {
643 0         0 croak "'$_' is not a file or you do not have enough permissions to read it.";
644             }
645             }
646              
647             # The unzipping will change directories. Remember old dir.
648 1         4885 my $old_cwd = Cwd::cwd();
649              
650             # Unzip the base par to a temp. dir.
651 1         53 (undef, my $base_dir) = _unzip_to_tmpdir(
652             dist => $base_par, subdir => 'blib'
653             );
654 1         11 my $blibdir = File::Spec->catdir($base_dir, 'blib');
655              
656             # move the META.yml to the (main) temp. dir.
657 1         14 my $main_meta_file = File::Spec->catfile($base_dir, 'META.yml');
658 1         15 File::Copy::move(
659             File::Spec->catfile($blibdir, 'META.yml'),
660             $main_meta_file
661             );
662             # delete (incorrect) MANIFEST
663 1         196 unlink File::Spec->catfile($blibdir, 'MANIFEST');
664              
665             # extract additional pars and merge
666 1         5 foreach my $par (@additional_pars) {
667             # restore original directory because the par path
668             # might have been relative!
669 1         10 chdir($old_cwd);
670 1         3 (undef, my $add_dir) = _unzip_to_tmpdir(
671             dist => $par
672             );
673              
674             # merge the meta (at least the provides info) into the main meta.yml
675 1         17 my $meta_file = File::Spec->catfile($add_dir, 'META.yml');
676 1 50       21 if (-f $meta_file) {
677 1         13 _merge_meta($main_meta_file, $meta_file);
678             }
679              
680 1         4 my @files;
681             my @dirs;
682             # I hate File::Find
683             # And I hate writing portable code, too.
684             File::Find::find(
685             {wanted =>sub {
686 9     9   28 my $file = $File::Find::name;
687 9 100       140 push @files, $file if -f $file;
688 9 100       667 push @dirs, $file if -d _;
689             }},
690 1         236 $add_dir
691             );
692 1         40 my ($vol, $subdir, undef) = File::Spec->splitpath( $add_dir, 1);
693 1         10 my @dir = File::Spec->splitdir( $subdir );
694              
695             # merge directory structure
696 1         4 foreach my $dir (@dirs) {
697 5         39 my ($v, $d, undef) = File::Spec->splitpath( $dir, 1 );
698 5         30 my @d = File::Spec->splitdir( $d );
699 5         19 shift @d foreach @dir; # remove tmp dir from path
700 5         59 my $target = File::Spec->catdir( $blibdir, @d );
701 5         352 mkdir($target);
702             }
703              
704             # merge files
705 1         5 foreach my $file (@files) {
706 4         1508 my ($v, $d, $f) = File::Spec->splitpath( $file );
707 4         31 my @d = File::Spec->splitdir( $d );
708 4         18 shift @d foreach @dir; # remove tmp dir from path
709 4         64 my $target = File::Spec->catfile(
710             File::Spec->catdir( $blibdir, @d ),
711             $f
712             );
713 4 50       32 File::Copy::copy($file, $target)
714             or die "Could not copy '$file' to '$target': $!";
715              
716             }
717 1         527 chdir($old_cwd);
718 1         2103 File::Path::rmtree([$add_dir]);
719             }
720              
721             # delete (copied) MANIFEST and META.yml
722 1         130 unlink File::Spec->catfile($blibdir, 'MANIFEST');
723 1         138 unlink File::Spec->catfile($blibdir, 'META.yml');
724              
725 1         15 chdir($base_dir);
726 1         13 my $resulting_par_file = Cwd::abs_path(blib_to_par(quiet => 1));
727 1         15 chdir($old_cwd);
728 1         13 File::Copy::move($resulting_par_file, $base_par);
729              
730 1         2843 File::Path::rmtree([$base_dir]);
731             }
732              
733              
734             sub _merge_meta {
735 1     1   3 my $meta_orig_file = shift;
736 1         38 my $meta_extra_file = shift;
737 1 50 33     24 return() if not defined $meta_orig_file or not -f $meta_orig_file;
738 1 50 33     22 return 1 if not defined $meta_extra_file or not -f $meta_extra_file;
739              
740 1         12 my $yaml_functions = _get_yaml_functions();
741              
742             die "Cannot merge META.yml files without a YAML reader/writer"
743             if !exists $yaml_functions->{LoadFile}
744 1 50 33     12 or !exists $yaml_functions->{DumpFile};
745              
746 1         6 my $orig_meta = $yaml_functions->{LoadFile}->($meta_orig_file);
747 1         6205 my $extra_meta = $yaml_functions->{LoadFile}->($meta_extra_file);
748              
749             # I seem to remember there was this incompatibility between the different
750             # YAML implementations with regards to "document" handling:
751 1 50       2484 my $orig_tree = (ref($orig_meta) eq 'ARRAY' ? $orig_meta->[0] : $orig_meta);
752 1 50       48 my $extra_tree = (ref($extra_meta) eq 'ARRAY' ? $extra_meta->[0] : $extra_meta);
753              
754 1         6 _merge_provides($orig_tree, $extra_tree);
755 1         4 _merge_requires($orig_tree, $extra_tree);
756              
757 1         6 $yaml_functions->{DumpFile}->($meta_orig_file, $orig_meta);
758              
759 1         3258 return 1;
760             }
761              
762             # merge the two-level provides sections of META.yml
763             sub _merge_provides {
764 1     1   2 my $orig_hash = shift;
765 1         3 my $extra_hash = shift;
766              
767 1 50       12 return() if not exists $extra_hash->{provides};
768 1   50     10 $orig_hash->{provides} ||= {};
769              
770 1         3 my $orig_provides = $orig_hash->{provides};
771 1         3 my $extra_provides = $extra_hash->{provides};
772              
773             # two level clone is enough wrt META spec 1.4
774             # overwrite the original provides since we're also overwriting the files.
775 1         9 foreach my $module (keys %$extra_provides) {
776 3         8 my $extra_mod_hash = $extra_provides->{$module};
777 3         5 my %mod_hash;
778 3         16 $mod_hash{$_} = $extra_mod_hash->{$_} for keys %$extra_mod_hash;
779 3         11 $orig_provides->{$module} = \%mod_hash;
780             }
781             }
782              
783             # merge the single-level requires-like sections of META.yml
784             sub _merge_requires {
785 1     1   2 my $orig_hash = shift;
786 1         3 my $extra_hash = shift;
787              
788 1         9 foreach my $type (qw(requires build_requires configure_requires recommends)) {
789 4 100       13 next if not exists $extra_hash->{$type};
790 2   50     12 $orig_hash->{$type} ||= {};
791              
792             # one level clone is enough wrt META spec 1.4
793 2         4 foreach my $module (keys %{ $extra_hash->{$type} }) {
  2         12  
794             # FIXME there should be a version comparison here, BUT how are we going to do that without a guaranteed version.pm?
795 5         18 $orig_hash->{$type}{$module} = $extra_hash->{$type}{$module}; # assign version and module name
796             }
797             }
798             }
799              
800             =head2 remove_man
801              
802             Remove the man pages from a PAR distribution. Takes one named
803             parameter: I which should be the name (and path) of the
804             PAR distribution file. The calling conventions outlined in
805             the C section above apply.
806              
807             The PAR archive will be
808             extracted, stripped of all C and C subdirectories
809             and then repackaged into the original file.
810              
811             =cut
812              
813             sub remove_man {
814 0     0 1 0 my %args = &_args;
815 0         0 my $par = $args{dist};
816 0         0 require Cwd;
817 0         0 require File::Copy;
818 0         0 require File::Path;
819 0         0 require File::Find;
820              
821             # parameter checking
822 0 0       0 if (not defined $par) {
823 0         0 croak "First argument to remove_man() must be the .par archive to modify.";
824             }
825              
826 0 0 0     0 if (not -f $par or not -r _ or not -w _) {
      0        
827 0         0 croak "'$par' is not a file or you do not have enough permissions to read and modify it.";
828             }
829              
830             # The unzipping will change directories. Remember old dir.
831 0         0 my $old_cwd = Cwd::cwd();
832              
833             # Unzip the base par to a temp. dir.
834 0         0 (undef, my $base_dir) = _unzip_to_tmpdir(
835             dist => $par, subdir => 'blib'
836             );
837 0         0 my $blibdir = File::Spec->catdir($base_dir, 'blib');
838              
839             # move the META.yml to the (main) temp. dir.
840 0         0 File::Copy::move(
841             File::Spec->catfile($blibdir, 'META.yml'),
842             File::Spec->catfile($base_dir, 'META.yml')
843             );
844             # delete (incorrect) MANIFEST
845 0         0 unlink File::Spec->catfile($blibdir, 'MANIFEST');
846              
847 0 0       0 opendir DIRECTORY, 'blib' or die $!;
848 0         0 my @dirs = grep { /^blib\/(?:man\d*|html)$/ }
849 0         0 grep { -d $_ }
850 0         0 map { File::Spec->catfile('blib', $_) }
  0         0  
851             readdir DIRECTORY;
852 0         0 close DIRECTORY;
853              
854 0         0 File::Path::rmtree(\@dirs);
855              
856 0         0 chdir($base_dir);
857 0         0 my $resulting_par_file = Cwd::abs_path(blib_to_par());
858 0         0 chdir($old_cwd);
859 0         0 File::Copy::move($resulting_par_file, $par);
860              
861 0         0 File::Path::rmtree([$base_dir]);
862             }
863              
864              
865             =head2 get_meta
866              
867             Opens a PAR archive and extracts the contained META.yml file.
868             Returns the META.yml file as a string.
869              
870             Takes one named parameter: I. If only one parameter is
871             passed, it is treated as the I parameter. (Have a look
872             at the description in the C section above.)
873              
874             Returns undef if no PAR archive or no META.yml within the
875             archive were found.
876              
877             =cut
878              
879             sub get_meta {
880 1     1 1 9 my %args = &_args;
881 1         3 my $dist = $args{dist};
882 1 50 33     27 return undef if not defined $dist or not -r $dist;
883 1         8 require Cwd;
884 1         6 require File::Path;
885              
886             # The unzipping will change directories. Remember old dir.
887 1         5692 my $old_cwd = Cwd::cwd();
888              
889             # Unzip the base par to a temp. dir.
890 1         38 (undef, my $base_dir) = _unzip_to_tmpdir(
891             dist => $dist, subdir => 'blib'
892             );
893 1         12 my $blibdir = File::Spec->catdir($base_dir, 'blib');
894              
895 1         11 my $meta = File::Spec->catfile($blibdir, 'META.yml');
896              
897 1 50       36 if (not -r $meta) {
898 0         0 return undef;
899             }
900              
901 1 50       27 open FH, '<', $meta
902             or die "Could not open file '$meta' for reading: $!";
903              
904 1         5 local $/ = undef;
905 1         17 my $meta_text = ;
906 1         8 close FH;
907              
908 1         9 chdir($old_cwd);
909              
910 1         2047 File::Path::rmtree([$base_dir]);
911              
912 1         19 return $meta_text;
913             }
914              
915              
916              
917             sub _unzip {
918 3     3   6 my %args = &_args;
919 3         9 my $dist = $args{dist};
920 3   33     10 my $path = $args{path} || File::Spec->curdir;
921 3 50       56 return unless -f $dist;
922              
923             # Try fast unzipping first
924 3 50       5 if (eval { require Archive::Unzip::Burst; 1 }) {
  3         393  
  0         0  
925 0         0 my $return = !Archive::Unzip::Burst::unzip($dist, $path);
926 0 0       0 return if $return; # true return value == error (a la system call)
927             }
928             # Then slow unzipping
929 3 50       10 if (eval { require Archive::Zip; 1 }) {
  3         9  
  3         8  
930 3         32 my $zip = Archive::Zip->new;
931 3         209 local %SIG;
932 3 0   0   54 $SIG{__WARN__} = sub { print STDERR $_[0] unless $_[0] =~ /\bstat\b/ };
  0         0  
933 3 50 33     24 return unless $zip->read($dist) == Archive::Zip::AZ_OK()
934             and $zip->extractTree('', "$path/") == Archive::Zip::AZ_OK();
935             }
936             # Then fall back to the system
937             else {
938 0         0 undef $!;
939 0 0       0 if (_system_wrapper(unzip => $dist, '-d', $path)) {
940 0         0 die "Failed to unzip '$dist' to path '$path': Could neither load "
941             . "Archive::Zip nor (successfully) run the system 'unzip' (unzip said: $!)";
942             }
943             }
944              
945 3         52467 return 1;
946             }
947              
948             sub _zip {
949 1     1   4 my %args = &_args;
950 1         3 my $dist = $args{dist};
951              
952 1 50       3 if (eval { require Archive::Zip; 1 }) {
  1         6  
  1         5  
953 1         8 my $zip = Archive::Zip->new;
954 1         52 $zip->addTree( File::Spec->curdir, '' );
955 1 50       10575 $zip->writeToFileNamed( $dist ) == Archive::Zip::AZ_OK() or die $!;
956             }
957             else {
958 0         0 undef $!;
959 0 0       0 if (_system_wrapper(qw(zip -r), $dist, File::Spec->curdir)) {
960 0         0 die "Failed to zip '" .File::Spec->curdir(). "' to '$dist': Could neither load "
961             . "Archive::Zip nor (successfully) run the system 'zip' (zip said: $!)";
962             }
963             }
964 1         13962 return 1;
965             }
966              
967              
968             # This sub munges the arguments to most of the PAR::Dist functions
969             # into a hash. On the way, it downloads PAR archives as necessary, etc.
970             sub _args {
971             # default to the first .par in the CWD
972 8 50   8   46 if (not @_) {
973 0         0 @_ = (glob('*.par'))[0];
974             }
975              
976             # single argument => it's a distribution file name or URL
977 8 100       48 @_ = (dist => @_) if @_ == 1;
978              
979 8         63 my %args = @_;
980 8   33     79 $args{name} ||= $args{dist};
981              
982             # If we are installing from an URL, we want to munge the
983             # distribution name so that it is in form "Module-Name"
984 8 50       22 if (defined $args{name}) {
985 8         32 $args{name} =~ s/^\w+:\/\///;
986 8         36 my @elems = parse_dist_name($args{name});
987             # @elems is name, version, arch, perlversion
988 8 50       24 if (defined $elems[0]) {
989 8         20 $args{name} = $elems[0];
990             }
991             else {
992 0         0 $args{name} =~ s/^.*\/([^\/]+)$/$1/;
993 0         0 $args{name} =~ s/^([0-9A-Za-z_-]+)-\d+\..+$/$1/;
994             }
995             }
996              
997             # append suffix if there is none
998 8 50 33     64 if ($args{dist} and not $args{dist} =~ /\.[a-zA-Z_][^.]*$/) {
999 0         0 require Config;
1000 0         0 my $suffix = $args{suffix};
1001 0   0     0 $suffix ||= "$Config::Config{archname}-$Config::Config{version}.par";
1002 0         0 $args{dist} .= "-$suffix";
1003             }
1004              
1005             # download if it's an URL
1006 8 50 33     36 if ($args{dist} and $args{dist} =~ m!^\w+://!) {
1007             $args{dist} = _fetch(dist => $args{dist})
1008 0         0 }
1009              
1010 8         156 return %args;
1011             }
1012              
1013              
1014             # Download PAR archive, but only if necessary (mirror!)
1015             my %escapes;
1016             sub _fetch {
1017 0     0   0 my %args = @_;
1018              
1019 0 0       0 if ($args{dist} =~ s/^file:\/\///) {
1020 0 0       0 return $args{dist} if -e $args{dist};
1021 0         0 return;
1022             }
1023 0         0 require LWP::Simple;
1024              
1025 0   0     0 $ENV{PAR_TEMP} ||= File::Spec->catdir(File::Spec->tmpdir, 'par');
1026 0         0 mkdir $ENV{PAR_TEMP}, 0777;
1027 0 0       0 %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255 unless %escapes;
  0         0  
1028              
1029 0         0 $args{dist} =~ s{^cpan://((([a-zA-Z])[a-zA-Z])[-_a-zA-Z]+)/}
1030             {http://www.cpan.org/modules/by-authors/id/\U$3/$2/$1\E/};
1031              
1032 0         0 my $file = $args{dist};
1033 0         0 $file =~ s/([^\w\.])/$escapes{$1}/g;
1034 0         0 $file = File::Spec->catfile( $ENV{PAR_TEMP}, $file);
1035 0         0 my $rc = LWP::Simple::mirror( $args{dist}, $file );
1036              
1037 0 0 0     0 if (!LWP::Simple::is_success($rc) and $rc != 304) {
1038 0         0 die "Error $rc: ", LWP::Simple::status_message($rc), " ($args{dist})\n";
1039             }
1040              
1041 0 0       0 return $file if -e $file;
1042 0         0 return;
1043             }
1044              
1045             sub _verify_or_sign {
1046 0     0   0 my %args = &_args;
1047              
1048 0         0 require File::Path;
1049 0         0 require Module::Signature;
1050 0 0       0 die "Module::Signature version 0.25 required"
1051             unless Module::Signature->VERSION >= 0.25;
1052              
1053 0         0 require Cwd;
1054 0         0 my $cwd = Cwd::cwd();
1055 0         0 my $action = $args{action};
1056 0         0 my ($dist, $tmpdir) = _unzip_to_tmpdir($args{dist});
1057 0 0 0     0 $action ||= (-e 'SIGNATURE' ? 'verify' : 'sign');
1058              
1059 0 0       0 if ($action eq 'sign') {
1060 0 0       0 open FH, '>SIGNATURE' unless -e 'SIGNATURE';
1061 0 0       0 open FH, 'MANIFEST' or die $!;
1062              
1063 0         0 local $/;
1064 0         0 my $out = ;
1065 0 0       0 if ($out !~ /^SIGNATURE(?:\s|$)/m) {
1066 0         0 $out =~ s/^(?!\s)/SIGNATURE\n/m;
1067 0 0       0 open FH, '>MANIFEST' or die $!;
1068 0         0 print FH $out;
1069             }
1070 0         0 close FH;
1071              
1072 0 0       0 $args{overwrite} = 1 unless exists $args{overwrite};
1073 0 0       0 $args{skip} = 0 unless exists $args{skip};
1074             }
1075              
1076 0         0 my $rv = Module::Signature->can($action)->(%args);
1077 0 0       0 _zip(dist => $dist) if $action eq 'sign';
1078 0         0 File::Path::rmtree([$tmpdir]);
1079              
1080 0         0 chdir($cwd);
1081 0         0 return $rv;
1082             }
1083              
1084             sub _unzip_to_tmpdir {
1085 3     3   29 my %args = &_args;
1086              
1087 3         33 require File::Temp;
1088 3         9 require Cwd;
1089              
1090 3         117 my $dist = File::Spec->rel2abs($args{dist});
1091 3 50       35 my $tmpdir = File::Temp::tempdir("parXXXXX", TMPDIR => 1, CLEANUP => 1)
1092             or die "Could not create temporary directory: $!";
1093 3         2642 $tmpdir = Cwd::abs_path($tmpdir); # symlinks cause Archive::Zip issues on some systems
1094 3         6 my $path = $tmpdir;
1095 3 100       20 $path = File::Spec->catdir($tmpdir, $args{subdir}) if defined $args{subdir};
1096              
1097 3         15 _unzip(dist => $dist, path => $path);
1098              
1099 3         28 chdir $tmpdir;
1100 3         15 return ($dist, $tmpdir);
1101             }
1102              
1103              
1104              
1105             =head2 parse_dist_name
1106              
1107             First argument must be a distribution file name. The file name
1108             is parsed into I, I,
1109             I, and I.
1110              
1111             Returns the results as a list in the above order.
1112             If any or all of the above cannot be determined, returns undef instead
1113             of the undetermined elements.
1114              
1115             Supported formats are:
1116              
1117             Math-Symbolic-0.502-x86_64-linux-gnu-thread-multi-5.8.7
1118              
1119             Math-Symbolic-0.502
1120              
1121             The ".tar.gz" or ".par" extensions as well as any
1122             preceding paths are stripped before parsing. Starting with C
1123             0.22, versions containing a preceding C are parsed correctly.
1124              
1125             This function is not exported by default.
1126              
1127             =cut
1128              
1129             sub parse_dist_name {
1130 13     13 1 2428 my $file = shift;
1131 13 50       27 return(undef, undef, undef, undef) if not defined $file;
1132              
1133 13         237 (undef, undef, $file) = File::Spec->splitpath($file);
1134              
1135 13         65 my $version = qr/v?(?:\d+(?:_\d+)?|\d*(?:\.\d+(?:_\d+)?)+)/;
1136 13         93 $file =~ s/\.(?:par|tar\.gz|tar)$//i;
1137 13         39 my @elem = split /-/, $file;
1138 13         19 my (@dn, $dv, @arch, $pv);
1139 13         35 while (@elem) {
1140 26         36 my $e = shift @elem;
1141 26 100 100     329 if (
      100        
1142             $e =~ /^$version$/o
1143             and not(# if not next token also a version
1144             # (assumes an arch string doesnt start with a version...)
1145             @elem and $elem[0] =~ /^$version$/o
1146             )
1147             ) {
1148 6         8 $dv = $e;
1149 6         10 last;
1150             }
1151 20         56 push @dn, $e;
1152             }
1153              
1154 13         25 my $dn;
1155 13 50       38 $dn = join('-', @dn) if @dn;
1156              
1157 13 100       24 if (not @elem) {
1158 8         42 return( $dn, $dv, undef, undef);
1159             }
1160              
1161 5         12 while (@elem) {
1162 24         22 my $e = shift @elem;
1163 24 100       160 if ($e =~ /^(?:$version|any_version)$/) {
1164 5         7 $pv = $e;
1165 5         7 last;
1166             }
1167 19         32 push @arch, $e;
1168             }
1169              
1170 5         5 my $arch;
1171 5 50       14 $arch = join('-', @arch) if @arch;
1172              
1173 5         28 return($dn, $dv, $arch, $pv);
1174             }
1175              
1176             =head2 generate_blib_stub
1177              
1178             Creates a F subdirectory in the current directory
1179             and prepares a F with meta information for a
1180             new PAR distribution. First argument should be the name of the
1181             PAR distribution in a format understood by C.
1182             Alternatively, named arguments resembling those of
1183             C are accepted.
1184              
1185             After running C and injecting files into
1186             the F directory, you can create a PAR distribution
1187             using C.
1188             This function is useful for creating custom PAR distributions
1189             from scratch. (I.e. not from an unpacked CPAN distribution)
1190             Example:
1191              
1192             use PAR::Dist;
1193             use File::Copy 'copy';
1194              
1195             generate_blib_stub(
1196             name => 'MyApp', version => '1.00'
1197             );
1198             copy('MyApp.pm', 'blib/lib/MyApp.pm');
1199             blib_to_par(); # generates the .par file!
1200              
1201             C will not overwrite existing files.
1202              
1203             =cut
1204              
1205             sub generate_blib_stub {
1206 0     0 1 0 my %args = &_args;
1207 0         0 my $dist = $args{dist};
1208 0         0 require Config;
1209              
1210 0         0 my $name = $args{name};
1211 0         0 my $version = $args{version};
1212 0         0 my $suffix = $args{suffix};
1213              
1214 0         0 my ($parse_name, $parse_version, $archname, $perlversion)
1215             = parse_dist_name($dist);
1216              
1217 0   0     0 $name ||= $parse_name;
1218 0   0     0 $version ||= $parse_version;
1219 0 0 0     0 $suffix = "$archname-$perlversion"
      0        
      0        
1220             if (not defined $suffix or $suffix eq '')
1221             and $archname and $perlversion;
1222              
1223 0   0     0 $suffix ||= "$Config::Config{archname}-$Config::Config{version}";
1224 0 0       0 if ( grep { not defined $_ } ($name, $version, $suffix) ) {
  0         0  
1225 0         0 warn "Could not determine distribution meta information from distribution name '$dist'";
1226 0         0 return();
1227             }
1228 0         0 $suffix =~ s/\.par$//;
1229              
1230 0 0       0 if (not -f 'META.yml') {
1231 0 0       0 open META, '>', 'META.yml'
1232             or die "Could not open META.yml file for writing: $!";
1233 0 0       0 print META << "YAML" if fileno(META);
1234             name: $name
1235             version: '$version'
1236             build_requires: {}
1237             conflicts: {}
1238             dist_name: $name-$version-$suffix.par
1239             distribution_type: par
1240             dynamic_config: 0
1241             generated_by: 'PAR::Dist version $PAR::Dist::VERSION'
1242             license: unknown
1243             YAML
1244 0         0 close META;
1245             }
1246              
1247 0         0 mkdir('blib');
1248 0         0 mkdir(File::Spec->catdir('blib', 'lib'));
1249 0         0 mkdir(File::Spec->catdir('blib', 'script'));
1250              
1251 0         0 return 1;
1252             }
1253              
1254              
1255             =head2 contains_binaries
1256              
1257             This function is not exported by default.
1258              
1259             Opens a PAR archive tries to determine whether that archive
1260             contains platform-specific binary code.
1261              
1262             Takes one named parameter: I. If only one parameter is
1263             passed, it is treated as the I parameter. (Have a look
1264             at the description in the C section above.)
1265              
1266             Throws a fatal error if the PAR archive could not be found.
1267              
1268             Returns one if the PAR was found to contain binary code
1269             and zero otherwise.
1270              
1271             =cut
1272              
1273             sub contains_binaries {
1274 0     0 1 0 require File::Find;
1275 0         0 my %args = &_args;
1276 0         0 my $dist = $args{dist};
1277 0 0 0     0 return undef if not defined $dist or not -r $dist;
1278 0         0 require Cwd;
1279 0         0 require File::Path;
1280              
1281             # The unzipping will change directories. Remember old dir.
1282 0         0 my $old_cwd = Cwd::cwd();
1283              
1284             # Unzip the base par to a temp. dir.
1285 0         0 (undef, my $base_dir) = _unzip_to_tmpdir(
1286             dist => $dist, subdir => 'blib'
1287             );
1288 0         0 my $blibdir = File::Spec->catdir($base_dir, 'blib');
1289 0         0 my $archdir = File::Spec->catdir($blibdir, 'arch');
1290              
1291 0         0 my $found = 0;
1292              
1293             File::Find::find(
1294             sub {
1295 0 0 0 0   0 $found++ if -f $_ and not /^\.exists$/;
1296             },
1297 0         0 $archdir
1298             );
1299              
1300 0         0 chdir($old_cwd);
1301              
1302 0         0 File::Path::rmtree([$base_dir]);
1303              
1304 0 0       0 return $found ? 1 : 0;
1305             }
1306              
1307             sub _system_wrapper {
1308 0 0   0   0 if ($DEBUG) {
1309 0         0 Carp::cluck("Running system call '@_' from:");
1310             }
1311 0         0 return system(@_);
1312             }
1313              
1314             # stolen from Module::Install::Can
1315             # very much internal and subject to change or removal
1316             sub _MI_can_run {
1317 0     0   0 require ExtUtils::MakeMaker;
1318 0         0 my ($cmd) = @_;
1319              
1320 0         0 my $_cmd = $cmd;
1321 0 0 0     0 return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
1322              
1323 0         0 for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
1324 0         0 my $abs = File::Spec->catfile($dir, $cmd);
1325 0 0 0     0 return $abs if (-x $abs or $abs = MM->maybe_command($abs));
1326             }
1327              
1328 0         0 return;
1329             }
1330              
1331              
1332             # Tries to load any YAML reader writer I know of
1333             # returns nothing on failure or hash reference containing
1334             # a subset of Load, Dump, LoadFile, DumpFile
1335             # entries with sub references on success.
1336             sub _get_yaml_functions {
1337             # reasoning for the ranking here:
1338             # - XS is the de-facto standard nowadays.
1339             # - YAML.pm is slow and aging
1340             # - syck is fast and reasonably complete
1341             # - Tiny is only a very small subset
1342             # - Parse... is only a reader and only deals with the same subset as ::Tiny
1343 3     3   743 my @modules = qw(YAML::XS YAML YAML::Tiny YAML::Syck Parse::CPAN::Meta);
1344              
1345 3         11 my %yaml_functions;
1346 3         13 foreach my $module (@modules) {
1347 9         865 eval "require $module;";
1348 9 100       7956 if (!$@) {
1349 3 100       86 warn "PAR::Dist testers/debug info: Using '$module' as YAML implementation" if $DEBUG;
1350 3         16 foreach my $sub (qw(Load Dump LoadFile DumpFile)) {
1351 3     3   28 no strict 'refs';
  3         5  
  3         1345  
1352 12         18 my $subref = *{"${module}::$sub"}{CODE};
  12         58  
1353 12 50 33     69 if (defined $subref and ref($subref) eq 'CODE') {
1354 12         49 $yaml_functions{$sub} = $subref;
1355             }
1356             }
1357 3         11 $yaml_functions{yaml_provider} = $module;
1358 3         8 last;
1359             }
1360             } # end foreach module candidates
1361 3 50       18 if (not keys %yaml_functions) {
1362 0         0 warn "Cannot find a working YAML reader/writer implementation. Tried to load all of '@modules'";
1363             }
1364 3         13 return(\%yaml_functions);
1365             }
1366              
1367             sub _check_tools {
1368 1     1   200 my $tools = _get_yaml_functions();
1369 1 50       3 if ($DEBUG) {
1370 1         3 foreach (qw/Load Dump LoadFile DumpFile/) {
1371 4 50       13 warn "No YAML support for $_ found.\n" if not defined $tools->{$_};
1372             }
1373             }
1374              
1375 1         3 $tools->{zip} = undef;
1376             # A::Zip 1.28 was a broken release...
1377 1 50 33     3 if (eval {require Archive::Zip; 1;} and $Archive::Zip::VERSION ne '1.28') {
  1 0 0     1203  
  1         121285  
1378 1 50       28 warn "Using Archive::Zip as ZIP tool.\n" if $DEBUG;
1379 1         6 $tools->{zip} = 'Archive::Zip';
1380             }
1381             elsif (_MI_can_run("zip") and _MI_can_run("unzip")) {
1382 0 0       0 warn "Using zip/unzip as ZIP tool.\n" if $DEBUG;
1383 0         0 $tools->{zip} = 'zip';
1384             }
1385             else {
1386 0 0       0 warn "Found neither Archive::Zip (version != 1.28) nor ZIP/UNZIP as valid ZIP tools.\n" if $DEBUG;
1387 0         0 $tools->{zip} = undef;
1388             }
1389              
1390 1         6 return $tools;
1391             }
1392              
1393             1;
1394              
1395             =head1 SEE ALSO
1396              
1397             L, L, L, L
1398              
1399             =head1 AUTHORS
1400              
1401             Audrey Tang Ecpan@audreyt.orgE 2003-2007
1402              
1403             Steffen Mueller Esmueller@cpan.orgE 2005-2011
1404              
1405             PAR has a mailing list, Epar@perl.orgE, that you can write to;
1406             send an empty mail to Epar-subscribe@perl.orgE to join the list
1407             and participate in the discussion.
1408             Archives of the mailing list are available at
1409             Ehttps://www.mail-archive.com/par@perl.org/E or Ehttps://groups.google.com/g/perl.parE.
1410              
1411             Please send bug reports to Ehttps://github.com/rschupp/PAR-Dist/issuesE.
1412              
1413             =head1 COPYRIGHT
1414              
1415             Copyright 2003-2011 by Audrey Tang Eautrijus@autrijus.orgE.
1416              
1417             This program is free software; you can redistribute it and/or modify it
1418             under the same terms as Perl itself.
1419              
1420             See L
1421              
1422             =cut