Unit tests for Literate Haskell
[ohcount] / test / expected_dir / perl_module.pm / perl / code
1 package PAR::Dist;
2 require Exporter;
3 use vars qw/$VERSION @ISA @EXPORT @EXPORT_OK/;
4 $VERSION    = '0.29';
5 @ISA        = 'Exporter';
6 @EXPORT     = qw/
7 blib_to_par
8 install_par
9 uninstall_par
10 sign_par
11 verify_par
12 merge_par
13 remove_man
14 get_meta
15 generate_blib_stub
16 /;
17 @EXPORT_OK = qw/
18 parse_dist_name
19 contains_binaries
20 /;
21 use strict;
22 use Carp qw/carp croak/;
23 use File::Spec;
24 sub blib_to_par {
25 @_ = (path => @_) if @_ == 1;
26 my %args = @_;
27 require Config;
28 my $dist;
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";
34 my $cwd;
35 if (defined $path) {
36 require Cwd;
37 $cwd = Cwd::cwd();
38 chdir $path;
39 }
40 _build_blib() unless -d "blib";
41 my @files;
42 open MANIFEST, ">", File::Spec->catfile("blib", "MANIFEST") or die $!;
43 open META, ">", File::Spec->catfile("blib", "META.yml") or die $!;
44 require File::Find;
45 File::Find::find( sub {
46 next unless $File::Find::name;
47 (-r && !-d) and push ( @files, substr($File::Find::name, 5) );
48 } , 'blib' );
49 print MANIFEST join(
50 "\n",
51 '    <!-- accessible as jar:file:///NAME.par!/MANIFEST in compliant browsers -->',
52 (sort @files),
53 q(    # <html><body onload="var X=document.body.innerHTML.split(/\n/);var Y='<iframe src=&quot;META.yml&quot; style=&quot;float:right;height:40%;width:40%&quot;></iframe><ul>';for(var x in X){if(!X[x].match(/^\s*#/)&&X[x].length)Y+='<li><a href=&quot;'+X[x]+'&quot;>'+X[x]+'</a>'}document.body.innerHTML=Y">)
54 );
55 close MANIFEST;
56 if (open(OLD_META, "META.yml")) {
57 while (<OLD_META>) {
58 if (/^distribution_type:/) {
59 print META "distribution_type: par\n";
60 }
61 else {
62 print META $_;
63 }
64 if (/^name:\s+(.*)/) {
65 $name ||= $1;
66 $name =~ s/::/-/g;
67 }
68 elsif (/^version:\s+.*Module::Build::Version/) {
69 while (<OLD_META>) {
70 /^\s+original:\s+(.*)/ or next;
71 $version ||= $1;
72 last;
73 }
74 }
75 elsif (/^version:\s+(.*)/) {
76 $version ||= $1;
77 }
78 }
79 close OLD_META;
80 close META;
81 }
82 if ((!$name or !$version) and open(MAKEFILE, "Makefile")) {
83 while (<MAKEFILE>) {
84 if (/^DISTNAME\s+=\s+(.*)$/) {
85 $name ||= $1;
86 }
87 elsif (/^VERSION\s+=\s+(.*)$/) {
88 $version ||= $1;
89 }
90 }
91 }
92 if (not defined($name) or not defined($version)) {
93 my $what;
94 if (not defined $name) {
95 $what = 'name';
96 $what .= ' and version' if not defined $version;
97 }
98 elsif (not defined $version) {
99 $what = 'version';
100 }
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.");
102 return();
103 }
104 $name =~ s/\s+$//;
105 $version =~ s/\s+$//;
106 my $file = "$name-$version-$suffix";
107 unlink $file if -f $file;
108 print META << "YAML" if fileno(META);
109 name: $name
110 version: $version
111 build_requires: {}
112 conflicts: {}
113 dist_name: $file
114 distribution_type: par
115 dynamic_config: 0
116 generated_by: 'PAR::Dist version $PAR::Dist::VERSION'
117 license: unknown
118 YAML
119 close META;
120 mkdir('blib', 0777);
121 chdir('blib');
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 );
129 $file = $dist;
130 }
131 my $pathname = File::Spec->rel2abs($file);
132 if ($^O eq 'MSWin32') {
133 $pathname =~ s!\\!/!g;
134 $pathname =~ s!:!|!g;
135 };
136 print << ".";
137 Successfully created binary distribution '$file'.
138 Its contents are accessible in compliant browsers as:
139 jar:file://$pathname!/MANIFEST
140 .
141 chdir $cwd if $cwd;
142 return $file;
143 }
144 sub _build_blib {
145 if (-e 'Build') {
146 system($^X, "Build");
147 }
148 elsif (-e 'Makefile') {
149 system($Config::Config{make});
150 }
151 elsif (-e 'Build.PL') {
152 system($^X, "Build.PL");
153 system($^X, "Build");
154 }
155 elsif (-e 'Makefile.PL') {
156 system($^X, "Makefile.PL");
157 system($Config::Config{make});
158 }
159 }
160 sub install_par {
161 my %args = &_args;
162 _install_or_uninstall(%args, action => 'install');
163 }
164 sub uninstall_par {
165 my %args = &_args;
166 _install_or_uninstall(%args, action => 'uninstall');
167 }
168 sub _install_or_uninstall {
169 my %args = &_args;
170 my $name = $args{name};
171 my $action = $args{action};
172 my %ENV_copy = %ENV;
173 $ENV{PERL_INSTALL_ROOT} = $args{prefix} if defined $args{prefix};
174 require Cwd;
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')) ) {
178 while (<META>) {
179 next unless /^name:\s+(.*)/;
180 $name = $1;
181 $name =~ s/\s+$//;
182 last;
183 }
184 close META;
185 }
186 return if not defined $name or $name eq '';
187 if (-d 'script') {
188 require ExtUtils::MY;
189 foreach my $file (glob("script/*")) {
190 next unless -T $file;
191 ExtUtils::MY->fixin($file);
192 chmod(0555, $file);
193 }
194 }
195 $name =~ s{::|-}{/}g;
196 require ExtUtils::Install;
197 my $rv;
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);
203 }
204 elsif ($action eq 'uninstall') {
205 require Config;
206 $rv = ExtUtils::Install::uninstall(
207 $args{packlist_read}||"$Config::Config{installsitearch}/auto/$name/.packlist"
208 );
209 }
210 %ENV = %ENV_copy;
211 chdir($old_dir);
212 File::Path::rmtree([$tmpdir]);
213 return $rv;
214 }
215 sub _installation_target {
216 require Config;
217 my $dir = shift;
218 my $name = shift;
219 my $user = shift || {};
220 my %sources = (
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',
229 );
230 my $target = {
231 read => $Config::Config{sitearchexp}."/auto/$name/.packlist",
232 write => $Config::Config{installsitearch}."/auto/$name/.packlist",
233 $sources{inst_lib}
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},
242 };
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} };
247 }
248 elsif (exists $sources{$key}) {
249 $target->{ $sources{$key} } = $value;
250 }
251 }
252 return $target;
253 }
254 sub _directory_not_empty {
255 require File::Find;
256 my($dir) = @_;
257 my $files = 0;
258 File::Find::find(sub {
259 return if $_ eq ".exists";
260 if (-f) {
261 $File::Find::prune++;
262 $files = 1;
263 }
264 }, $dir);
265 return $files;
266 }
267 sub sign_par {
268 my %args = &_args;
269 _verify_or_sign(%args, action => 'sign');
270 }
271 sub verify_par {
272 my %args = &_args;
273 $! = _verify_or_sign(%args, action => 'verify');
274 return ( $! == Module::Signature::SIGNATURE_OK() );
275 }
276 sub merge_par {
277 my $base_par = shift;
278 my @additional_pars = @_;
279 require Cwd;
280 require File::Copy;
281 require File::Path;
282 require File::Find;
283 if (not defined $base_par) {
284 croak "First argument to merge_par() must be the .par archive to modify.";
285 }
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.";
288 }
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.";
292 }
293 }
294 my $old_cwd = Cwd::cwd();
295 (undef, my $base_dir) = _unzip_to_tmpdir(
296 dist => $base_par, subdir => 'blib'
297 );
298 my $blibdir = File::Spec->catdir($base_dir, 'blib');
299 File::Copy::move(
300 File::Spec->catfile($blibdir, 'META.yml'),
301 File::Spec->catfile($base_dir, 'META.yml')
302 );
303 unlink File::Spec->catfile($blibdir, 'MANIFEST');
304 foreach my $par (@additional_pars) {
305 chdir($old_cwd);
306 (undef, my $add_dir) = _unzip_to_tmpdir(
307 dist => $par
308 );
309 my @files;
310 my @dirs;
311 File::Find::find(
312 {wanted =>sub {
313 my $file = $File::Find::name;
314 push @files, $file if -f $file;
315 push @dirs, $file if -d _;
316 }},
317 $add_dir
318 );
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 );
326 mkdir($target);
327 }
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 ),
334 $f
335 );
336 File::Copy::copy($file, $target)
337 or die "Could not copy '$file' to '$target': $!";
338 }
339 chdir($old_cwd);
340 File::Path::rmtree([$add_dir]);
341 }
342 unlink File::Spec->catfile($blibdir, 'MANIFEST');
343 unlink File::Spec->catfile($blibdir, 'META.yml');
344 chdir($base_dir);
345 my $resulting_par_file = Cwd::abs_path(blib_to_par());
346 chdir($old_cwd);
347 File::Copy::move($resulting_par_file, $base_par);
348 File::Path::rmtree([$base_dir]);
349 }
350 sub remove_man {
351 my %args = &_args;
352 my $par = $args{dist};
353 require Cwd;
354 require File::Copy;
355 require File::Path;
356 require File::Find;
357 if (not defined $par) {
358 croak "First argument to remove_man() must be the .par archive to modify.";
359 }
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.";
362 }
363 my $old_cwd = Cwd::cwd();
364 (undef, my $base_dir) = _unzip_to_tmpdir(
365 dist => $par, subdir => 'blib'
366 );
367 my $blibdir = File::Spec->catdir($base_dir, 'blib');
368 File::Copy::move(
369 File::Spec->catfile($blibdir, 'META.yml'),
370 File::Spec->catfile($base_dir, 'META.yml')
371 );
372 unlink File::Spec->catfile($blibdir, 'MANIFEST');
373 opendir DIRECTORY, 'blib' or die $!;
374 my @dirs = grep { /^blib\/(?:man\d*|html)$/ }
375 grep { -d $_ }
376 map  { File::Spec->catfile('blib', $_) }
377 readdir DIRECTORY;
378 close DIRECTORY;
379 File::Path::rmtree(\@dirs);
380 chdir($base_dir);
381 my $resulting_par_file = Cwd::abs_path(blib_to_par());
382 chdir($old_cwd);
383 File::Copy::move($resulting_par_file, $par);
384 File::Path::rmtree([$base_dir]);
385 }
386 sub get_meta {
387 my %args = &_args;
388 my $dist = $args{dist};
389 return undef if not defined $dist or not -r $dist;
390 require Cwd;
391 require File::Path;
392 my $old_cwd = Cwd::cwd();
393 (undef, my $base_dir) = _unzip_to_tmpdir(
394 dist => $dist, subdir => 'blib'
395 );
396 my $blibdir = File::Spec->catdir($base_dir, 'blib');
397 my $meta = File::Spec->catfile($blibdir, 'META.yml');
398 if (not -r $meta) {
399 return undef;
400 }
401 open FH, '<', $meta
402 or die "Could not open file '$meta' for reading: $!";
403 local $/ = undef;
404 my $meta_text = <FH>;
405 close FH;
406 chdir($old_cwd);
407 File::Path::rmtree([$base_dir]);
408 return $meta_text;
409 }
410 sub _unzip {
411 my %args = &_args;
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)
418 }
419 if (eval { require Archive::Zip; 1 }) {
420 my $zip = Archive::Zip->new;
421 local %SIG;
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();
425 }
426 else {
427 return if system(unzip => $dist, '-d', $path);
428 }
429 return 1;
430 }
431 sub _zip {
432 my %args = &_args;
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 $!;
438 }
439 else {
440 system(qw(zip -r), $dist, File::Spec->curdir) and die $!;
441 }
442 }
443 sub _args {
444 if (not @_) {
445 @_ = (glob('*.par'))[0];
446 }
447 @_ = (dist => @_) if @_ == 1;
448 my %args = @_;
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];
455 }
456 else {
457 $args{name} =~ s/^.*\/([^\/]+)$/$1/;
458 $args{name} =~ s/^([0-9A-Za-z_-]+)-\d+\..+$/$1/;
459 }
460 }
461 if ($args{dist} and not $args{dist} =~ /\.[a-zA-Z_][^.]*$/) {
462 require Config;
463 my $suffix = $args{suffix};
464 $suffix ||= "$Config::Config{archname}-$Config::Config{version}.par";
465 $args{dist} .= "-$suffix";
466 }
467 if ($args{dist} and $args{dist} =~ m!^\w+://!) {
468 $args{dist} = _fetch(dist => $args{dist})
469 }
470 return %args;
471 }
472 my %escapes;
473 sub _fetch {
474 my %args = @_;
475 if ($args{dist} =~ s/^file:\/\///) {
476 return $args{dist} if -e $args{dist};
477 return;
478 }
479 require LWP::Simple;
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";
491 }
492 return $file if -e $file;
493 return;
494 }
495 sub _verify_or_sign {
496 my %args = &_args;
497 require File::Path;
498 require Module::Signature;
499 die "Module::Signature version 0.25 required"
500 unless Module::Signature->VERSION >= 0.25;
501 require Cwd;
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 $!;
509 local $/;
510 my $out = <FH>;
511 if ($out !~ /^SIGNATURE(?:\s|$)/m) {
512 $out =~ s/^(?!\s)/SIGNATURE\n/m;
513 open FH, '>MANIFEST' or die $!;
514 print FH $out;
515 }
516 close FH;
517 $args{overwrite} = 1 unless exists $args{overwrite};
518 $args{skip}      = 0 unless exists $args{skip};
519 }
520 my $rv = Module::Signature->can($action)->(%args);
521 _zip(dist => $dist) if $action eq 'sign';
522 File::Path::rmtree([$tmpdir]);
523 chdir($cwd);
524 return $rv;
525 }
526 sub _unzip_to_tmpdir {
527 my %args = &_args;
528 require File::Temp;
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': $!";
533 my $path = $tmpdir;
534 $path = File::Spec->catdir($tmpdir, $args{subdir}) if defined $args{subdir};
535 _unzip(dist => $dist, path => $path);
536 chdir $tmpdir;
537 return ($dist, $tmpdir);
538 }
539 sub parse_dist_name {
540 my $file = shift;
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);
547 while (@elem) {
548 my $e = shift @elem;
549 if (
550 $e =~ /^$version$/o
551 and not(# if not next token also a version
552 @elem and $elem[0] =~ /^$version$/o
553 )
554 ) {
555 $dv = $e;
556 last;
557 }
558 push @dn, $e;
559 }
560 my $dn;
561 $dn = join('-', @dn) if @dn;
562 if (not @elem) {
563 return( $dn, $dv, undef, undef);
564 }
565 while (@elem) {
566 my $e = shift @elem;
567 if ($e =~ /^$version|any_version$/) {
568 $pv = $e;
569 last;
570 }
571 push @arch, $e;
572 }
573 my $arch;
574 $arch = join('-', @arch) if @arch;
575 return($dn, $dv, $arch, $pv);
576 }
577 sub generate_blib_stub {
578 my %args = &_args;
579 my $dist = $args{dist};
580 require Config;
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'";
594 return();
595 }
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);
601 name: $name
602 version: $version
603 build_requires: {}
604 conflicts: {}
605 dist_name: $name-$version-$suffix.par
606 distribution_type: par
607 dynamic_config: 0
608 generated_by: 'PAR::Dist version $PAR::Dist::VERSION'
609 license: unknown
610 YAML
611 close META;
612 }
613 mkdir('blib');
614 mkdir(File::Spec->catdir('blib', 'lib'));
615 mkdir(File::Spec->catdir('blib', 'script'));
616 return 1;
617 }
618 sub contains_binaries {
619 require File::Find;
620 my %args = &_args;
621 my $dist = $args{dist};
622 return undef if not defined $dist or not -r $dist;
623 require Cwd;
624 require File::Path;
625 my $old_cwd = Cwd::cwd();
626 (undef, my $base_dir) = _unzip_to_tmpdir(
627 dist => $dist, subdir => 'blib'
628 );
629 my $blibdir = File::Spec->catdir($base_dir, 'blib');
630 my $archdir = File::Spec->catdir($blibdir, 'arch');
631 my $found = 0;
632 File::Find::find(
633 sub {
634 $found++ if -f $_ and not /^\.exists$/;
635 },
636 $archdir
637 );
638 chdir($old_cwd);
639 File::Path::rmtree([$base_dir]);
640 return $found ? 1 : 0;
641 }
642 1;