3 use vars qw/$VERSION @ISA @EXPORT @EXPORT_OK/;
22 use Carp qw/carp croak/;
25 @_ = (path => @_) if @_ == 1;
29 my $path = $args{path};
30 $dist = File::Spec->rel2abs($args{dist}) if $args{dist};
31 my $name = $args{name};
32 my $version = $args{version};
33 my $suffix = $args{suffix} || "$Config::Config{archname}-$Config::Config{version}.par";
40 _build_blib() unless -d "blib";
42 open MANIFEST, ">", File::Spec->catfile("blib", "MANIFEST") or die $!;
43 open META, ">", File::Spec->catfile("blib", "META.yml") or die $!;
45 File::Find::find( sub {
46 next unless $File::Find::name;
47 (-r && !-d) and push ( @files, substr($File::Find::name, 5) );
51 ' <!-- accessible as jar:file:///NAME.par!/MANIFEST in compliant browsers -->',
53 q( # <html><body onload="var X=document.body.innerHTML.split(/\n/);var Y='<iframe src="META.yml" style="float:right;height:40%;width:40%"></iframe><ul>';for(var x in X){if(!X[x].match(/^\s*#/)&&X[x].length)Y+='<li><a href="'+X[x]+'">'+X[x]+'</a>'}document.body.innerHTML=Y">)
56 if (open(OLD_META, "META.yml")) {
58 if (/^distribution_type:/) {
59 print META "distribution_type: par\n";
64 if (/^name:\s+(.*)/) {
68 elsif (/^version:\s+.*Module::Build::Version/) {
70 /^\s+original:\s+(.*)/ or next;
75 elsif (/^version:\s+(.*)/) {
82 if ((!$name or !$version) and open(MAKEFILE, "Makefile")) {
84 if (/^DISTNAME\s+=\s+(.*)$/) {
87 elsif (/^VERSION\s+=\s+(.*)$/) {
92 if (not defined($name) or not defined($version)) {
94 if (not defined $name) {
96 $what .= ' and version' if not defined $version;
98 elsif (not defined $version) {
101 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.");
105 $version =~ s/\s+$//;
106 my $file = "$name-$version-$suffix";
107 unlink $file if -f $file;
108 print META << "YAML" if fileno(META);
114 distribution_type: par
116 generated_by: 'PAR::Dist version $PAR::Dist::VERSION'
122 _zip(dist => File::Spec->catfile(File::Spec->updir, $file)) or die $!;
123 chdir(File::Spec->updir);
124 unlink File::Spec->catfile("blib", "MANIFEST");
125 unlink File::Spec->catfile("blib", "META.yml");
126 $dist ||= File::Spec->catfile($cwd, $file) if $cwd;
127 if ($dist and $file ne $dist) {
128 rename( $file => $dist );
131 my $pathname = File::Spec->rel2abs($file);
132 if ($^O eq 'MSWin32') {
133 $pathname =~ s!\\!/!g;
134 $pathname =~ s!:!|!g;
137 Successfully created binary distribution '$file'.
138 Its contents are accessible in compliant browsers as:
139 jar:file://$pathname!/MANIFEST
146 system($^X, "Build");
148 elsif (-e 'Makefile') {
149 system($Config::Config{make});
151 elsif (-e 'Build.PL') {
152 system($^X, "Build.PL");
153 system($^X, "Build");
155 elsif (-e 'Makefile.PL') {
156 system($^X, "Makefile.PL");
157 system($Config::Config{make});
162 _install_or_uninstall(%args, action => 'install');
166 _install_or_uninstall(%args, action => 'uninstall');
168 sub _install_or_uninstall {
170 my $name = $args{name};
171 my $action = $args{action};
173 $ENV{PERL_INSTALL_ROOT} = $args{prefix} if defined $args{prefix};
175 my $old_dir = Cwd::cwd();
176 my ($dist, $tmpdir) = _unzip_to_tmpdir( dist => $args{dist}, subdir => 'blib' );
177 if ( open (META, File::Spec->catfile('blib', 'META.yml')) ) {
179 next unless /^name:\s+(.*)/;
186 return if not defined $name or $name eq '';
188 require ExtUtils::MY;
189 foreach my $file (glob("script/*")) {
190 next unless -T $file;
191 ExtUtils::MY->fixin($file);
195 $name =~ s{::|-}{/}g;
196 require ExtUtils::Install;
198 if ($action eq 'install') {
199 my $target = _installation_target( File::Spec->curdir, $name, \%args );
200 my $custom_targets = $args{custom_targets} || {};
201 $target->{$_} = $custom_targets->{$_} foreach keys %{$custom_targets};
202 $rv = ExtUtils::Install::install($target, 1, 0, 0);
204 elsif ($action eq 'uninstall') {
206 $rv = ExtUtils::Install::uninstall(
207 $args{packlist_read}||"$Config::Config{installsitearch}/auto/$name/.packlist"
212 File::Path::rmtree([$tmpdir]);
215 sub _installation_target {
219 my $user = shift || {};
221 inst_lib => File::Spec->catdir($dir,"blib","lib"),
222 inst_archlib => File::Spec->catdir($dir,"blib","arch"),
223 inst_bin => File::Spec->catdir($dir,'blib','bin'),
224 inst_script => File::Spec->catdir($dir,'blib','script'),
225 inst_man1dir => File::Spec->catdir($dir,'blib','man1'),
226 inst_man3dir => File::Spec->catdir($dir,'blib','man3'),
227 packlist_read => 'read',
228 packlist_write => 'write',
231 read => $Config::Config{sitearchexp}."/auto/$name/.packlist",
232 write => $Config::Config{installsitearch}."/auto/$name/.packlist",
234 => (_directory_not_empty($sources{inst_archlib}))
235 ? $Config::Config{installsitearch}
236 : $Config::Config{installsitelib},
237 $sources{inst_archlib} => $Config::Config{installsitearch},
238 $sources{inst_bin} => $Config::Config{installbin} ,
239 $sources{inst_script} => $Config::Config{installscript},
240 $sources{inst_man1dir} => $Config::Config{installman1dir},
241 $sources{inst_man3dir} => $Config::Config{installman3dir},
243 foreach my $key (keys %$user) {
244 my $value = $user->{$key};
245 if (not defined $value and $key ne 'packlist_read' and $key ne 'packlist_write') {
246 delete $target->{ $sources{$key} };
248 elsif (exists $sources{$key}) {
249 $target->{ $sources{$key} } = $value;
254 sub _directory_not_empty {
258 File::Find::find(sub {
259 return if $_ eq ".exists";
261 $File::Find::prune++;
269 _verify_or_sign(%args, action => 'sign');
273 $! = _verify_or_sign(%args, action => 'verify');
274 return ( $! == Module::Signature::SIGNATURE_OK() );
277 my $base_par = shift;
278 my @additional_pars = @_;
283 if (not defined $base_par) {
284 croak "First argument to merge_par() must be the .par archive to modify.";
286 if (not -f $base_par or not -r _ or not -w _) {
287 croak "'$base_par' is not a file or you do not have enough permissions to read and modify it.";
289 foreach (@additional_pars) {
290 if (not -f $_ or not -r _) {
291 croak "'$_' is not a file or you do not have enough permissions to read it.";
294 my $old_cwd = Cwd::cwd();
295 (undef, my $base_dir) = _unzip_to_tmpdir(
296 dist => $base_par, subdir => 'blib'
298 my $blibdir = File::Spec->catdir($base_dir, 'blib');
300 File::Spec->catfile($blibdir, 'META.yml'),
301 File::Spec->catfile($base_dir, 'META.yml')
303 unlink File::Spec->catfile($blibdir, 'MANIFEST');
304 foreach my $par (@additional_pars) {
306 (undef, my $add_dir) = _unzip_to_tmpdir(
313 my $file = $File::Find::name;
314 push @files, $file if -f $file;
315 push @dirs, $file if -d _;
319 my ($vol, $subdir, undef) = File::Spec->splitpath( $add_dir, 1);
320 my @dir = File::Spec->splitdir( $subdir );
321 foreach my $dir (@dirs) {
322 my ($v, $d, undef) = File::Spec->splitpath( $dir, 1 );
323 my @d = File::Spec->splitdir( $d );
324 shift @d foreach @dir; # remove tmp dir from path
325 my $target = File::Spec->catdir( $blibdir, @d );
328 foreach my $file (@files) {
329 my ($v, $d, $f) = File::Spec->splitpath( $file );
330 my @d = File::Spec->splitdir( $d );
331 shift @d foreach @dir; # remove tmp dir from path
332 my $target = File::Spec->catfile(
333 File::Spec->catdir( $blibdir, @d ),
336 File::Copy::copy($file, $target)
337 or die "Could not copy '$file' to '$target': $!";
340 File::Path::rmtree([$add_dir]);
342 unlink File::Spec->catfile($blibdir, 'MANIFEST');
343 unlink File::Spec->catfile($blibdir, 'META.yml');
345 my $resulting_par_file = Cwd::abs_path(blib_to_par());
347 File::Copy::move($resulting_par_file, $base_par);
348 File::Path::rmtree([$base_dir]);
352 my $par = $args{dist};
357 if (not defined $par) {
358 croak "First argument to remove_man() must be the .par archive to modify.";
360 if (not -f $par or not -r _ or not -w _) {
361 croak "'$par' is not a file or you do not have enough permissions to read and modify it.";
363 my $old_cwd = Cwd::cwd();
364 (undef, my $base_dir) = _unzip_to_tmpdir(
365 dist => $par, subdir => 'blib'
367 my $blibdir = File::Spec->catdir($base_dir, 'blib');
369 File::Spec->catfile($blibdir, 'META.yml'),
370 File::Spec->catfile($base_dir, 'META.yml')
372 unlink File::Spec->catfile($blibdir, 'MANIFEST');
373 opendir DIRECTORY, 'blib' or die $!;
374 my @dirs = grep { /^blib\/(?:man\d*|html)$/ }
376 map { File::Spec->catfile('blib', $_) }
379 File::Path::rmtree(\@dirs);
381 my $resulting_par_file = Cwd::abs_path(blib_to_par());
383 File::Copy::move($resulting_par_file, $par);
384 File::Path::rmtree([$base_dir]);
388 my $dist = $args{dist};
389 return undef if not defined $dist or not -r $dist;
392 my $old_cwd = Cwd::cwd();
393 (undef, my $base_dir) = _unzip_to_tmpdir(
394 dist => $dist, subdir => 'blib'
396 my $blibdir = File::Spec->catdir($base_dir, 'blib');
397 my $meta = File::Spec->catfile($blibdir, 'META.yml');
402 or die "Could not open file '$meta' for reading: $!";
404 my $meta_text = <FH>;
407 File::Path::rmtree([$base_dir]);
412 my $dist = $args{dist};
413 my $path = $args{path} || File::Spec->curdir;
414 return unless -f $dist;
415 if (eval { require Archive::Unzip::Burst; 1 }) {
416 my $return = Archive::Unzip::Burst::unzip($dist, $path);
417 return if $return; # true return value == error (a la system call)
419 if (eval { require Archive::Zip; 1 }) {
420 my $zip = Archive::Zip->new;
422 $SIG{__WARN__} = sub { print STDERR $_[0] unless $_[0] =~ /\bstat\b/ };
423 return unless $zip->read($dist) == Archive::Zip::AZ_OK()
424 and $zip->extractTree('', "$path/") == Archive::Zip::AZ_OK();
427 return if system(unzip => $dist, '-d', $path);
433 my $dist = $args{dist};
434 if (eval { require Archive::Zip; 1 }) {
435 my $zip = Archive::Zip->new;
436 $zip->addTree( File::Spec->curdir, '' );
437 $zip->writeToFileNamed( $dist ) == Archive::Zip::AZ_OK() or die $!;
440 system(qw(zip -r), $dist, File::Spec->curdir) and die $!;
445 @_ = (glob('*.par'))[0];
447 @_ = (dist => @_) if @_ == 1;
449 $args{name} ||= $args{dist};
450 if (defined $args{name}) {
451 $args{name} =~ s/^\w+:\/\///;
452 my @elems = parse_dist_name($args{name});
453 if (defined $elems[0]) {
454 $args{name} = $elems[0];
457 $args{name} =~ s/^.*\/([^\/]+)$/$1/;
458 $args{name} =~ s/^([0-9A-Za-z_-]+)-\d+\..+$/$1/;
461 if ($args{dist} and not $args{dist} =~ /\.[a-zA-Z_][^.]*$/) {
463 my $suffix = $args{suffix};
464 $suffix ||= "$Config::Config{archname}-$Config::Config{version}.par";
465 $args{dist} .= "-$suffix";
467 if ($args{dist} and $args{dist} =~ m!^\w+://!) {
468 $args{dist} = _fetch(dist => $args{dist})
475 if ($args{dist} =~ s/^file:\/\///) {
476 return $args{dist} if -e $args{dist};
480 $ENV{PAR_TEMP} ||= File::Spec->catdir(File::Spec->tmpdir, 'par');
481 mkdir $ENV{PAR_TEMP}, 0777;
482 %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255 unless %escapes;
483 $args{dist} =~ s{^cpan://((([a-zA-Z])[a-zA-Z])[-_a-zA-Z]+)/}
484 {http://www.cpan.org/modules/by-authors/id/\U$3/$2/$1\E/};
485 my $file = $args{dist};
486 $file =~ s/([^\w\.])/$escapes{$1}/g;
487 $file = File::Spec->catfile( $ENV{PAR_TEMP}, $file);
488 my $rc = LWP::Simple::mirror( $args{dist}, $file );
489 if (!LWP::Simple::is_success($rc) and $rc != 304) {
490 die "Error $rc: ", LWP::Simple::status_message($rc), " ($args{dist})\n";
492 return $file if -e $file;
495 sub _verify_or_sign {
498 require Module::Signature;
499 die "Module::Signature version 0.25 required"
500 unless Module::Signature->VERSION >= 0.25;
502 my $cwd = Cwd::cwd();
503 my $action = $args{action};
504 my ($dist, $tmpdir) = _unzip_to_tmpdir($args{dist});
505 $action ||= (-e 'SIGNATURE' ? 'verify' : 'sign');
506 if ($action eq 'sign') {
507 open FH, '>SIGNATURE' unless -e 'SIGNATURE';
508 open FH, 'MANIFEST' or die $!;
511 if ($out !~ /^SIGNATURE(?:\s|$)/m) {
512 $out =~ s/^(?!\s)/SIGNATURE\n/m;
513 open FH, '>MANIFEST' or die $!;
517 $args{overwrite} = 1 unless exists $args{overwrite};
518 $args{skip} = 0 unless exists $args{skip};
520 my $rv = Module::Signature->can($action)->(%args);
521 _zip(dist => $dist) if $action eq 'sign';
522 File::Path::rmtree([$tmpdir]);
526 sub _unzip_to_tmpdir {
529 my $dist = File::Spec->rel2abs($args{dist});
530 my $tmpdirname = File::Spec->catdir(File::Spec->tmpdir, "parXXXXX");
531 my $tmpdir = File::Temp::mkdtemp($tmpdirname)
532 or die "Could not create temporary directory from template '$tmpdirname': $!";
534 $path = File::Spec->catdir($tmpdir, $args{subdir}) if defined $args{subdir};
535 _unzip(dist => $dist, path => $path);
537 return ($dist, $tmpdir);
539 sub parse_dist_name {
541 return(undef, undef, undef, undef) if not defined $file;
542 (undef, undef, $file) = File::Spec->splitpath($file);
543 my $version = qr/v?(?:\d+(?:_\d+)?|\d*(?:\.\d+(?:_\d+)?)+)/;
544 $file =~ s/\.(?:par|tar\.gz|tar)$//i;
545 my @elem = split /-/, $file;
546 my (@dn, $dv, @arch, $pv);
551 and not(# if not next token also a version
552 @elem and $elem[0] =~ /^$version$/o
561 $dn = join('-', @dn) if @dn;
563 return( $dn, $dv, undef, undef);
567 if ($e =~ /^$version|any_version$/) {
574 $arch = join('-', @arch) if @arch;
575 return($dn, $dv, $arch, $pv);
577 sub generate_blib_stub {
579 my $dist = $args{dist};
581 my $name = $args{name};
582 my $version = $args{version};
583 my $suffix = $args{suffix};
584 my ($parse_name, $parse_version, $archname, $perlversion)
585 = parse_dist_name($dist);
586 $name ||= $parse_name;
587 $version ||= $parse_version;
588 $suffix = "$archname-$perlversion"
589 if (not defined $suffix or $suffix eq '')
590 and $archname and $perlversion;
591 $suffix ||= "$Config::Config{archname}-$Config::Config{version}";
592 if ( grep { not defined $_ } ($name, $version, $suffix) ) {
593 warn "Could not determine distribution meta information from distribution name '$dist'";
596 $suffix =~ s/\.par$//;
597 if (not -f 'META.yml') {
598 open META, '>', 'META.yml'
599 or die "Could not open META.yml file for writing: $!";
600 print META << "YAML" if fileno(META);
605 dist_name: $name-$version-$suffix.par
606 distribution_type: par
608 generated_by: 'PAR::Dist version $PAR::Dist::VERSION'
614 mkdir(File::Spec->catdir('blib', 'lib'));
615 mkdir(File::Spec->catdir('blib', 'script'));
618 sub contains_binaries {
621 my $dist = $args{dist};
622 return undef if not defined $dist or not -r $dist;
625 my $old_cwd = Cwd::cwd();
626 (undef, my $base_dir) = _unzip_to_tmpdir(
627 dist => $dist, subdir => 'blib'
629 my $blibdir = File::Spec->catdir($base_dir, 'blib');
630 my $archdir = File::Spec->catdir($blibdir, 'arch');
634 $found++ if -f $_ and not /^\.exists$/;
639 File::Path::rmtree([$base_dir]);
640 return $found ? 1 : 0;