source: for-distributions/trunk/bin/windows/perl/lib/ExtUtils/Manifest.pm@ 14489

Last change on this file since 14489 was 14489, checked in by oranfry, 17 years ago

upgrading to perl 5.8

File size: 16.7 KB
Line 
1package ExtUtils::Manifest;
2
3require Exporter;
4use Config;
5use File::Basename;
6use File::Copy 'copy';
7use File::Find;
8use File::Spec;
9use Carp;
10use strict;
11
12use vars qw($VERSION @ISA @EXPORT_OK
13 $Is_MacOS $Is_VMS
14 $Debug $Verbose $Quiet $MANIFEST $DEFAULT_MSKIP);
15
16$VERSION = '1.46';
17@ISA=('Exporter');
18@EXPORT_OK = qw(mkmanifest
19 manicheck filecheck fullcheck skipcheck
20 manifind maniread manicopy maniadd
21 );
22
23$Is_MacOS = $^O eq 'MacOS';
24$Is_VMS = $^O eq 'VMS';
25require VMS::Filespec if $Is_VMS;
26
27$Debug = $ENV{PERL_MM_MANIFEST_DEBUG} || 0;
28$Verbose = defined $ENV{PERL_MM_MANIFEST_VERBOSE} ?
29 $ENV{PERL_MM_MANIFEST_VERBOSE} : 1;
30$Quiet = 0;
31$MANIFEST = 'MANIFEST';
32
33$DEFAULT_MSKIP = File::Spec->catfile( dirname(__FILE__), "$MANIFEST.SKIP" );
34
35
36=head1 NAME
37
38ExtUtils::Manifest - utilities to write and check a MANIFEST file
39
40=head1 SYNOPSIS
41
42 use ExtUtils::Manifest qw(...funcs to import...);
43
44 mkmanifest();
45
46 my @missing_files = manicheck;
47 my @skipped = skipcheck;
48 my @extra_files = filecheck;
49 my($missing, $extra) = fullcheck;
50
51 my $found = manifind();
52
53 my $manifest = maniread();
54
55 manicopy($read,$target);
56
57 maniadd({$file => $comment, ...});
58
59
60=head1 DESCRIPTION
61
62=head2 Functions
63
64ExtUtils::Manifest exports no functions by default. The following are
65exported on request
66
67=over 4
68
69=item mkmanifest
70
71 mkmanifest();
72
73Writes all files in and below the current directory to your F<MANIFEST>.
74It works similar to
75
76 find . > MANIFEST
77
78All files that match any regular expression in a file F<MANIFEST.SKIP>
79(if it exists) are ignored.
80
81Any existing F<MANIFEST> file will be saved as F<MANIFEST.bak>. Lines
82from the old F<MANIFEST> file is preserved, including any comments
83that are found in the existing F<MANIFEST> file in the new one.
84
85=cut
86
87sub _sort {
88 return sort { lc $a cmp lc $b } @_;
89}
90
91sub mkmanifest {
92 my $manimiss = 0;
93 my $read = (-r 'MANIFEST' && maniread()) or $manimiss++;
94 $read = {} if $manimiss;
95 local *M;
96 rename $MANIFEST, "$MANIFEST.bak" unless $manimiss;
97 open M, ">$MANIFEST" or die "Could not open $MANIFEST: $!";
98 my $skip = _maniskip();
99 my $found = manifind();
100 my($key,$val,$file,%all);
101 %all = (%$found, %$read);
102 $all{$MANIFEST} = ($Is_VMS ? "$MANIFEST\t\t" : '') . 'This list of files'
103 if $manimiss; # add new MANIFEST to known file list
104 foreach $file (_sort keys %all) {
105 if ($skip->($file)) {
106 # Policy: only remove files if they're listed in MANIFEST.SKIP.
107 # Don't remove files just because they don't exist.
108 warn "Removed from $MANIFEST: $file\n" if $Verbose and exists $read->{$file};
109 next;
110 }
111 if ($Verbose){
112 warn "Added to $MANIFEST: $file\n" unless exists $read->{$file};
113 }
114 my $text = $all{$file};
115 ($file,$text) = split(/\s+/,$text,2) if $Is_VMS && $text;
116 $file = _unmacify($file);
117 my $tabs = (5 - (length($file)+1)/8);
118 $tabs = 1 if $tabs < 1;
119 $tabs = 0 unless $text;
120 print M $file, "\t" x $tabs, $text, "\n";
121 }
122 close M;
123}
124
125# Geez, shouldn't this use File::Spec or File::Basename or something?
126# Why so careful about dependencies?
127sub clean_up_filename {
128 my $filename = shift;
129 $filename =~ s|^\./||;
130 $filename =~ s/^:([^:]+)$/$1/ if $Is_MacOS;
131 return $filename;
132}
133
134
135=item manifind
136
137 my $found = manifind();
138
139returns a hash reference. The keys of the hash are the files found
140below the current directory.
141
142=cut
143
144sub manifind {
145 my $p = shift || {};
146 my $found = {};
147
148 my $wanted = sub {
149 my $name = clean_up_filename($File::Find::name);
150 warn "Debug: diskfile $name\n" if $Debug;
151 return if -d $_;
152
153 if( $Is_VMS ) {
154 $name =~ s#(.*)\.$#\L$1#;
155 $name = uc($name) if $name =~ /^MANIFEST(\.SKIP)?$/i;
156 }
157 $found->{$name} = "";
158 };
159
160 # We have to use "$File::Find::dir/$_" in preprocess, because
161 # $File::Find::name is unavailable.
162 # Also, it's okay to use / here, because MANIFEST files use Unix-style
163 # paths.
164 find({wanted => $wanted},
165 $Is_MacOS ? ":" : ".");
166
167 return $found;
168}
169
170
171=item manicheck
172
173 my @missing_files = manicheck();
174
175checks if all the files within a C<MANIFEST> in the current directory
176really do exist. If C<MANIFEST> and the tree below the current
177directory are in sync it silently returns an empty list.
178Otherwise it returns a list of files which are listed in the
179C<MANIFEST> but missing from the directory, and by default also
180outputs these names to STDERR.
181
182=cut
183
184sub manicheck {
185 return _check_files();
186}
187
188
189=item filecheck
190
191 my @extra_files = filecheck();
192
193finds files below the current directory that are not mentioned in the
194C<MANIFEST> file. An optional file C<MANIFEST.SKIP> will be
195consulted. Any file matching a regular expression in such a file will
196not be reported as missing in the C<MANIFEST> file. The list of any
197extraneous files found is returned, and by default also reported to
198STDERR.
199
200=cut
201
202sub filecheck {
203 return _check_manifest();
204}
205
206
207=item fullcheck
208
209 my($missing, $extra) = fullcheck();
210
211does both a manicheck() and a filecheck(), returning then as two array
212refs.
213
214=cut
215
216sub fullcheck {
217 return [_check_files()], [_check_manifest()];
218}
219
220
221=item skipcheck
222
223 my @skipped = skipcheck();
224
225lists all the files that are skipped due to your C<MANIFEST.SKIP>
226file.
227
228=cut
229
230sub skipcheck {
231 my($p) = @_;
232 my $found = manifind();
233 my $matches = _maniskip();
234
235 my @skipped = ();
236 foreach my $file (_sort keys %$found){
237 if (&$matches($file)){
238 warn "Skipping $file\n";
239 push @skipped, $file;
240 next;
241 }
242 }
243
244 return @skipped;
245}
246
247
248sub _check_files {
249 my $p = shift;
250 my $dosnames=(defined(&Dos::UseLFN) && Dos::UseLFN()==0);
251 my $read = maniread() || {};
252 my $found = manifind($p);
253
254 my(@missfile) = ();
255 foreach my $file (_sort keys %$read){
256 warn "Debug: manicheck checking from $MANIFEST $file\n" if $Debug;
257 if ($dosnames){
258 $file = lc $file;
259 $file =~ s=(\.(\w|-)+)=substr ($1,0,4)=ge;
260 $file =~ s=((\w|-)+)=substr ($1,0,8)=ge;
261 }
262 unless ( exists $found->{$file} ) {
263 warn "No such file: $file\n" unless $Quiet;
264 push @missfile, $file;
265 }
266 }
267
268 return @missfile;
269}
270
271
272sub _check_manifest {
273 my($p) = @_;
274 my $read = maniread() || {};
275 my $found = manifind($p);
276 my $skip = _maniskip();
277
278 my @missentry = ();
279 foreach my $file (_sort keys %$found){
280 next if $skip->($file);
281 warn "Debug: manicheck checking from disk $file\n" if $Debug;
282 unless ( exists $read->{$file} ) {
283 my $canon = $Is_MacOS ? "\t" . _unmacify($file) : '';
284 warn "Not in $MANIFEST: $file$canon\n" unless $Quiet;
285 push @missentry, $file;
286 }
287 }
288
289 return @missentry;
290}
291
292
293=item maniread
294
295 my $manifest = maniread();
296 my $manifest = maniread($manifest_file);
297
298reads a named C<MANIFEST> file (defaults to C<MANIFEST> in the current
299directory) and returns a HASH reference with files being the keys and
300comments being the values of the HASH. Blank lines and lines which
301start with C<#> in the C<MANIFEST> file are discarded.
302
303=cut
304
305sub maniread {
306 my ($mfile) = @_;
307 $mfile ||= $MANIFEST;
308 my $read = {};
309 local *M;
310 unless (open M, $mfile){
311 warn "$mfile: $!";
312 return $read;
313 }
314 local $_;
315 while (<M>){
316 chomp;
317 next if /^\s*#/;
318
319 my($file, $comment) = /^(\S+)\s*(.*)/;
320 next unless $file;
321
322 if ($Is_MacOS) {
323 $file = _macify($file);
324 $file =~ s/\\([0-3][0-7][0-7])/sprintf("%c", oct($1))/ge;
325 }
326 elsif ($Is_VMS) {
327 require File::Basename;
328 my($base,$dir) = File::Basename::fileparse($file);
329 # Resolve illegal file specifications in the same way as tar
330 $dir =~ tr/./_/;
331 my(@pieces) = split(/\./,$base);
332 if (@pieces > 2) { $base = shift(@pieces) . '.' . join('_',@pieces); }
333 my $okfile = "$dir$base";
334 warn "Debug: Illegal name $file changed to $okfile\n" if $Debug;
335 $file = $okfile;
336 $file = lc($file) unless $file =~ /^MANIFEST(\.SKIP)?$/;
337 }
338
339 $read->{$file} = $comment;
340 }
341 close M;
342 $read;
343}
344
345# returns an anonymous sub that decides if an argument matches
346sub _maniskip {
347 my @skip ;
348 my $mfile = "$MANIFEST.SKIP";
349 local(*M,$_);
350 open M, $mfile or open M, $DEFAULT_MSKIP or return sub {0};
351 while (<M>){
352 chomp;
353 next if /^#/;
354 next if /^\s*$/;
355 push @skip, _macify($_);
356 }
357 close M;
358 my $opts = $Is_VMS ? '(?i)' : '';
359
360 # Make sure each entry is isolated in its own parentheses, in case
361 # any of them contain alternations
362 my $regex = join '|', map "(?:$_)", @skip;
363
364 return sub { $_[0] =~ qr{$opts$regex} };
365}
366
367=item manicopy
368
369 manicopy(\%src, $dest_dir);
370 manicopy(\%src, $dest_dir, $how);
371
372Copies the files that are the keys in %src to the $dest_dir. %src is
373typically returned by the maniread() function.
374
375 manicopy( maniread(), $dest_dir );
376
377This function is useful for producing a directory tree identical to the
378intended distribution tree.
379
380$how can be used to specify a different methods of "copying". Valid
381values are C<cp>, which actually copies the files, C<ln> which creates
382hard links, and C<best> which mostly links the files but copies any
383symbolic link to make a tree without any symbolic link. C<cp> is the
384default.
385
386=cut
387
388sub manicopy {
389 my($read,$target,$how)=@_;
390 croak "manicopy() called without target argument" unless defined $target;
391 $how ||= 'cp';
392 require File::Path;
393 require File::Basename;
394
395 $target = VMS::Filespec::unixify($target) if $Is_VMS;
396 File::Path::mkpath([ $target ],! $Quiet,$Is_VMS ? undef : 0755);
397 foreach my $file (keys %$read){
398 if ($Is_MacOS) {
399 if ($file =~ m!:!) {
400 my $dir = _maccat($target, $file);
401 $dir =~ s/[^:]+$//;
402 File::Path::mkpath($dir,1,0755);
403 }
404 cp_if_diff($file, _maccat($target, $file), $how);
405 } else {
406 $file = VMS::Filespec::unixify($file) if $Is_VMS;
407 if ($file =~ m!/!) { # Ilya, that hurts, I fear, or maybe not?
408 my $dir = File::Basename::dirname($file);
409 $dir = VMS::Filespec::unixify($dir) if $Is_VMS;
410 File::Path::mkpath(["$target/$dir"],! $Quiet,$Is_VMS ? undef : 0755);
411 }
412 cp_if_diff($file, "$target/$file", $how);
413 }
414 }
415}
416
417sub cp_if_diff {
418 my($from, $to, $how)=@_;
419 -f $from or carp "$0: $from not found";
420 my($diff) = 0;
421 local(*F,*T);
422 open(F,"< $from\0") or die "Can't read $from: $!\n";
423 if (open(T,"< $to\0")) {
424 local $_;
425 while (<F>) { $diff++,last if $_ ne <T>; }
426 $diff++ unless eof(T);
427 close T;
428 }
429 else { $diff++; }
430 close F;
431 if ($diff) {
432 if (-e $to) {
433 unlink($to) or confess "unlink $to: $!";
434 }
435 STRICT_SWITCH: {
436 best($from,$to), last STRICT_SWITCH if $how eq 'best';
437 cp($from,$to), last STRICT_SWITCH if $how eq 'cp';
438 ln($from,$to), last STRICT_SWITCH if $how eq 'ln';
439 croak("ExtUtils::Manifest::cp_if_diff " .
440 "called with illegal how argument [$how]. " .
441 "Legal values are 'best', 'cp', and 'ln'.");
442 }
443 }
444}
445
446sub cp {
447 my ($srcFile, $dstFile) = @_;
448 my ($access,$mod) = (stat $srcFile)[8,9];
449
450 copy($srcFile,$dstFile);
451 utime $access, $mod + ($Is_VMS ? 1 : 0), $dstFile;
452 _manicopy_chmod($dstFile);
453}
454
455
456sub ln {
457 my ($srcFile, $dstFile) = @_;
458 return &cp if $Is_VMS or ($^O eq 'MSWin32' and Win32::IsWin95());
459 link($srcFile, $dstFile);
460
461 unless( _manicopy_chmod($dstFile) ) {
462 unlink $dstFile;
463 return;
464 }
465 1;
466}
467
468# 1) Strip off all group and world permissions.
469# 2) Let everyone read it.
470# 3) If the owner can execute it, everyone can.
471sub _manicopy_chmod {
472 my($file) = shift;
473
474 my $perm = 0444 | (stat $file)[2] & 0700;
475 chmod( $perm | ( $perm & 0100 ? 0111 : 0 ), $file );
476}
477
478# Files that are often modified in the distdir. Don't hard link them.
479my @Exceptions = qw(MANIFEST META.yml SIGNATURE);
480sub best {
481 my ($srcFile, $dstFile) = @_;
482
483 my $is_exception = grep $srcFile =~ /$_/, @Exceptions;
484 if ($is_exception or !$Config{d_link} or -l $srcFile) {
485 cp($srcFile, $dstFile);
486 } else {
487 ln($srcFile, $dstFile) or cp($srcFile, $dstFile);
488 }
489}
490
491sub _macify {
492 my($file) = @_;
493
494 return $file unless $Is_MacOS;
495
496 $file =~ s|^\./||;
497 if ($file =~ m|/|) {
498 $file =~ s|/+|:|g;
499 $file = ":$file";
500 }
501
502 $file;
503}
504
505sub _maccat {
506 my($f1, $f2) = @_;
507
508 return "$f1/$f2" unless $Is_MacOS;
509
510 $f1 .= ":$f2";
511 $f1 =~ s/([^:]:):/$1/g;
512 return $f1;
513}
514
515sub _unmacify {
516 my($file) = @_;
517
518 return $file unless $Is_MacOS;
519
520 $file =~ s|^:||;
521 $file =~ s|([/ \n])|sprintf("\\%03o", unpack("c", $1))|ge;
522 $file =~ y|:|/|;
523
524 $file;
525}
526
527
528=item maniadd
529
530 maniadd({ $file => $comment, ...});
531
532Adds an entry to an existing F<MANIFEST> unless its already there.
533
534$file will be normalized (ie. Unixified). B<UNIMPLEMENTED>
535
536=cut
537
538sub maniadd {
539 my($additions) = shift;
540
541 _normalize($additions);
542 _fix_manifest($MANIFEST);
543
544 my $manifest = maniread();
545 my @needed = grep { !exists $manifest->{$_} } keys %$additions;
546 return 1 unless @needed;
547
548 open(MANIFEST, ">>$MANIFEST") or
549 die "maniadd() could not open $MANIFEST: $!";
550
551 foreach my $file (_sort @needed) {
552 my $comment = $additions->{$file} || '';
553 printf MANIFEST "%-40s %s\n", $file, $comment;
554 }
555 close MANIFEST or die "Error closing $MANIFEST: $!";
556
557 return 1;
558}
559
560
561# Sometimes MANIFESTs are missing a trailing newline. Fix this.
562sub _fix_manifest {
563 my $manifest_file = shift;
564
565 open MANIFEST, $MANIFEST or die "Could not open $MANIFEST: $!";
566
567 # Yes, we should be using seek(), but I'd like to avoid loading POSIX
568 # to get SEEK_*
569 my @manifest = <MANIFEST>;
570 close MANIFEST;
571
572 unless( $manifest[-1] =~ /\n\z/ ) {
573 open MANIFEST, ">>$MANIFEST" or die "Could not open $MANIFEST: $!";
574 print MANIFEST "\n";
575 close MANIFEST;
576 }
577}
578
579
580# UNIMPLEMENTED
581sub _normalize {
582 return;
583}
584
585
586=back
587
588=head2 MANIFEST
589
590A list of files in the distribution, one file per line. The MANIFEST
591always uses Unix filepath conventions even if you're not on Unix. This
592means F<foo/bar> style not F<foo\bar>.
593
594Anything between white space and an end of line within a C<MANIFEST>
595file is considered to be a comment. Any line beginning with # is also
596a comment.
597
598 # this a comment
599 some/file
600 some/other/file comment about some/file
601
602
603=head2 MANIFEST.SKIP
604
605The file MANIFEST.SKIP may contain regular expressions of files that
606should be ignored by mkmanifest() and filecheck(). The regular
607expressions should appear one on each line. Blank lines and lines
608which start with C<#> are skipped. Use C<\#> if you need a regular
609expression to start with a C<#>.
610
611For example:
612
613 # Version control files and dirs.
614 \bRCS\b
615 \bCVS\b
616 ,v$
617 \B\.svn\b
618
619 # Makemaker generated files and dirs.
620 ^MANIFEST\.
621 ^Makefile$
622 ^blib/
623 ^MakeMaker-\d
624
625 # Temp, old and emacs backup files.
626 ~$
627 \.old$
628 ^#.*#$
629 ^\.#
630
631If no MANIFEST.SKIP file is found, a default set of skips will be
632used, similar to the example above. If you want nothing skipped,
633simply make an empty MANIFEST.SKIP file.
634
635
636=head2 EXPORT_OK
637
638C<&mkmanifest>, C<&manicheck>, C<&filecheck>, C<&fullcheck>,
639C<&maniread>, and C<&manicopy> are exportable.
640
641=head2 GLOBAL VARIABLES
642
643C<$ExtUtils::Manifest::MANIFEST> defaults to C<MANIFEST>. Changing it
644results in both a different C<MANIFEST> and a different
645C<MANIFEST.SKIP> file. This is useful if you want to maintain
646different distributions for different audiences (say a user version
647and a developer version including RCS).
648
649C<$ExtUtils::Manifest::Quiet> defaults to 0. If set to a true value,
650all functions act silently.
651
652C<$ExtUtils::Manifest::Debug> defaults to 0. If set to a true value,
653or if PERL_MM_MANIFEST_DEBUG is true, debugging output will be
654produced.
655
656=head1 DIAGNOSTICS
657
658All diagnostic output is sent to C<STDERR>.
659
660=over 4
661
662=item C<Not in MANIFEST:> I<file>
663
664is reported if a file is found which is not in C<MANIFEST>.
665
666=item C<Skipping> I<file>
667
668is reported if a file is skipped due to an entry in C<MANIFEST.SKIP>.
669
670=item C<No such file:> I<file>
671
672is reported if a file mentioned in a C<MANIFEST> file does not
673exist.
674
675=item C<MANIFEST:> I<$!>
676
677is reported if C<MANIFEST> could not be opened.
678
679=item C<Added to MANIFEST:> I<file>
680
681is reported by mkmanifest() if $Verbose is set and a file is added
682to MANIFEST. $Verbose is set to 1 by default.
683
684=back
685
686=head1 ENVIRONMENT
687
688=over 4
689
690=item B<PERL_MM_MANIFEST_DEBUG>
691
692Turns on debugging
693
694=back
695
696=head1 SEE ALSO
697
698L<ExtUtils::MakeMaker> which has handy targets for most of the functionality.
699
700=head1 AUTHOR
701
702Andreas Koenig C<[email protected]>
703
704Currently maintained by Michael G Schwern C<[email protected]>
705
706=cut
707
7081;
Note: See TracBrowser for help on using the repository browser.