1 package ExtUtils::Manifest;
11 use vars qw($VERSION @ISA @EXPORT_OK
13 $Debug $Verbose $Quiet $MANIFEST $DEFAULT_MSKIP);
17 @EXPORT_OK = qw(mkmanifest
18 manicheck filecheck fullcheck skipcheck
19 manifind maniread manicopy maniadd
22 $Is_MacOS = $^O eq 'MacOS';
23 $Is_VMS = $^O eq 'VMS';
24 require VMS::Filespec if $Is_VMS;
26 $Debug = $ENV{PERL_MM_MANIFEST_DEBUG} || 0;
27 $Verbose = defined $ENV{PERL_MM_MANIFEST_VERBOSE} ?
28 $ENV{PERL_MM_MANIFEST_VERBOSE} : 1;
30 $MANIFEST = 'MANIFEST';
32 my $Filename = __FILE__;
33 $DEFAULT_MSKIP = (File::Spec->splitpath($Filename))[1].
39 ExtUtils::Manifest - utilities to write and check a MANIFEST file
43 use ExtUtils::Manifest qw(...funcs to import...);
47 my @missing_files = manicheck;
48 my @skipped = skipcheck;
49 my @extra_files = filecheck;
50 my($missing, $extra) = fullcheck;
52 my $found = manifind();
54 my $manifest = maniread();
56 manicopy($read,$target);
58 maniadd({$file => $comment, ...});
65 ExtUtils::Manifest exports no functions by default. The following are
74 Writes all files in and below the current directory to your F<MANIFEST>.
79 All files that match any regular expression in a file F<MANIFEST.SKIP>
80 (if it exists) are ignored.
82 Any existing F<MANIFEST> file will be saved as F<MANIFEST.bak>. Lines
83 from the old F<MANIFEST> file is preserved, including any comments
84 that are found in the existing F<MANIFEST> file in the new one.
89 return sort { lc $a cmp lc $b } @_;
94 my $read = (-r 'MANIFEST' && maniread()) or $manimiss++;
95 $read = {} if $manimiss;
97 rename $MANIFEST, "$MANIFEST.bak" unless $manimiss;
98 open M, ">$MANIFEST" or die "Could not open $MANIFEST: $!";
99 my $skip = _maniskip();
100 my $found = manifind();
101 my($key,$val,$file,%all);
102 %all = (%$found, %$read);
103 $all{$MANIFEST} = ($Is_VMS ? "$MANIFEST\t\t" : '') . 'This list of files'
104 if $manimiss; # add new MANIFEST to known file list
105 foreach $file (_sort keys %all) {
106 if ($skip->($file)) {
107 # Policy: only remove files if they're listed in MANIFEST.SKIP.
108 # Don't remove files just because they don't exist.
109 warn "Removed from $MANIFEST: $file\n" if $Verbose and exists $read->{$file};
113 warn "Added to $MANIFEST: $file\n" unless exists $read->{$file};
115 my $text = $all{$file};
116 ($file,$text) = split(/\s+/,$text,2) if $Is_VMS && $text;
117 $file = _unmacify($file);
118 my $tabs = (5 - (length($file)+1)/8);
119 $tabs = 1 if $tabs < 1;
120 $tabs = 0 unless $text;
121 print M $file, "\t" x $tabs, $text, "\n";
126 # Geez, shouldn't this use File::Spec or File::Basename or something?
127 # Why so careful about dependencies?
128 sub clean_up_filename {
129 my $filename = shift;
130 $filename =~ s|^\./||;
131 $filename =~ s/^:([^:]+)$/$1/ if $Is_MacOS;
138 my $found = manifind();
140 returns a hash reference. The keys of the hash are the files found
141 below the current directory.
150 my $name = clean_up_filename($File::Find::name);
151 warn "Debug: diskfile $name\n" if $Debug;
155 $name =~ s#(.*)\.$#\L$1#;
156 $name = uc($name) if $name =~ /^MANIFEST(\.SKIP)?$/i;
158 $found->{$name} = "";
161 # We have to use "$File::Find::dir/$_" in preprocess, because
162 # $File::Find::name is unavailable.
163 # Also, it's okay to use / here, because MANIFEST files use Unix-style
165 find({wanted => $wanted},
166 $Is_MacOS ? ":" : ".");
174 my @missing_files = manicheck();
176 checks if all the files within a C<MANIFEST> in the current directory
177 really do exist. If C<MANIFEST> and the tree below the current
178 directory are in sync it exits silently, returning an empty list.
179 Otherwise it returns a list of files which are listed in the
180 C<MANIFEST> but missing from the directory, and by default also
181 outputs these names to STDERR.
186 return _check_files();
192 my @extra_files = filecheck();
194 finds files below the current directory that are not mentioned in the
195 C<MANIFEST> file. An optional file C<MANIFEST.SKIP> will be
196 consulted. Any file matching a regular expression in such a file will
197 not be reported as missing in the C<MANIFEST> file. The list of any
198 extraneous files found is returned, and by default also reported to
204 return _check_manifest();
210 my($missing, $extra) = fullcheck();
212 does both a manicheck() and a filecheck(), returning then as two array
218 return [_check_files()], [_check_manifest()];
224 my @skipped = skipcheck();
226 lists all the files that are skipped due to your C<MANIFEST.SKIP>
233 my $found = manifind();
234 my $matches = _maniskip();
237 foreach my $file (_sort keys %$found){
238 if (&$matches($file)){
239 warn "Skipping $file\n";
240 push @skipped, $file;
251 my $dosnames=(defined(&Dos::UseLFN) && Dos::UseLFN()==0);
252 my $read = maniread() || {};
253 my $found = manifind($p);
256 foreach my $file (_sort keys %$read){
257 warn "Debug: manicheck checking from $MANIFEST $file\n" if $Debug;
260 $file =~ s=(\.(\w|-)+)=substr ($1,0,4)=ge;
261 $file =~ s=((\w|-)+)=substr ($1,0,8)=ge;
263 unless ( exists $found->{$file} ) {
264 warn "No such file: $file\n" unless $Quiet;
265 push @missfile, $file;
273 sub _check_manifest {
275 my $read = maniread() || {};
276 my $found = manifind($p);
277 my $skip = _maniskip();
280 foreach my $file (_sort keys %$found){
281 next if $skip->($file);
282 warn "Debug: manicheck checking from disk $file\n" if $Debug;
283 unless ( exists $read->{$file} ) {
284 my $canon = $Is_MacOS ? "\t" . _unmacify($file) : '';
285 warn "Not in $MANIFEST: $file$canon\n" unless $Quiet;
286 push @missentry, $file;
296 my $manifest = maniread();
297 my $manifest = maniread($manifest_file);
299 reads a named C<MANIFEST> file (defaults to C<MANIFEST> in the current
300 directory) and returns a HASH reference with files being the keys and
301 comments being the values of the HASH. Blank lines and lines which
302 start with C<#> in the C<MANIFEST> file are discarded.
308 $mfile ||= $MANIFEST;
311 unless (open M, $mfile){
319 my($file, $comment) = /^(\S+)\s*(.*)/;
323 $file = _macify($file);
324 $file =~ s/\\([0-3][0-7][0-7])/sprintf("%c", oct($1))/ge;
327 require File::Basename;
328 my($base,$dir) = File::Basename::fileparse($file);
329 # Resolve illegal file specifications in the same way as tar
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;
336 $file = lc($file) unless $file =~ /^MANIFEST(\.SKIP)?$/;
339 $read->{$file} = $comment;
345 # returns an anonymous sub that decides if an argument matches
348 my $mfile = "$MANIFEST.SKIP";
350 open M, $mfile or open M, $DEFAULT_MSKIP or return sub {0};
355 push @skip, _macify($_);
358 my $opts = $Is_VMS ? '(?i)' : '';
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;
364 return sub { $_[0] =~ qr{$opts$regex} };
369 manicopy($src, $dest_dir);
370 manicopy($src, $dest_dir, $how);
372 copies the files that are the keys in the HASH I<%$src> to the
373 $dest_dir. The HASH reference $read is typically returned by the
374 maniread() function. This function is useful for producing a directory
375 tree identical to the intended distribution tree. The third parameter
376 $how can be used to specify a different methods of "copying". Valid
377 values are C<cp>, which actually copies the files, C<ln> which creates
378 hard links, and C<best> which mostly links the files but copies any
379 symbolic link to make a tree without any symbolic link. Best is the
385 my($read,$target,$how)=@_;
386 croak "manicopy() called without target argument" unless defined $target;
389 require File::Basename;
391 $target = VMS::Filespec::unixify($target) if $Is_VMS;
392 File::Path::mkpath([ $target ],! $Quiet,$Is_VMS ? undef : 0755);
393 foreach my $file (keys %$read){
396 my $dir = _maccat($target, $file);
398 File::Path::mkpath($dir,1,0755);
400 cp_if_diff($file, _maccat($target, $file), $how);
402 $file = VMS::Filespec::unixify($file) if $Is_VMS;
403 if ($file =~ m!/!) { # Ilya, that hurts, I fear, or maybe not?
404 my $dir = File::Basename::dirname($file);
405 $dir = VMS::Filespec::unixify($dir) if $Is_VMS;
406 File::Path::mkpath(["$target/$dir"],! $Quiet,$Is_VMS ? undef : 0755);
408 cp_if_diff($file, "$target/$file", $how);
414 my($from, $to, $how)=@_;
415 -f $from or carp "$0: $from not found";
418 open(F,"< $from\0") or die "Can't read $from: $!\n";
419 if (open(T,"< $to\0")) {
420 while (<F>) { $diff++,last if $_ ne <T>; }
421 $diff++ unless eof(T);
428 unlink($to) or confess "unlink $to: $!";
431 best($from,$to), last STRICT_SWITCH if $how eq 'best';
432 cp($from,$to), last STRICT_SWITCH if $how eq 'cp';
433 ln($from,$to), last STRICT_SWITCH if $how eq 'ln';
434 croak("ExtUtils::Manifest::cp_if_diff " .
435 "called with illegal how argument [$how]. " .
436 "Legal values are 'best', 'cp', and 'ln'.");
442 my ($srcFile, $dstFile) = @_;
443 my ($perm,$access,$mod) = (stat $srcFile)[2,8,9];
444 copy($srcFile,$dstFile);
445 utime $access, $mod + ($Is_VMS ? 1 : 0), $dstFile;
447 chmod( 0444 | ( $perm & 0111 ? 0111 : 0 ), $dstFile )
448 unless ($^O eq 'MacOS');
452 my ($srcFile, $dstFile) = @_;
453 return &cp if $Is_VMS or ($^O eq 'MSWin32' and Win32::IsWin95());
454 link($srcFile, $dstFile);
456 # chmod a+r,go-w+X (except "X" only applies to u=x)
457 local($_) = $dstFile;
458 my $mode= 0444 | (stat)[2] & 0700;
459 if (! chmod( $mode | ( $mode & 0100 ? 0111 : 0 ), $_ )) {
466 unless (defined $Config{d_link}) {
467 # Really cool fix from Ilya :)
468 local $SIG{__WARN__} = sub {
469 warn @_ unless $_[0] =~ /^Subroutine .* redefined/;
478 my ($srcFile, $dstFile) = @_;
480 cp($srcFile, $dstFile);
482 ln($srcFile, $dstFile) or cp($srcFile, $dstFile);
489 return $file unless $Is_MacOS;
503 return "$f1/$f2" unless $Is_MacOS;
506 $f1 =~ s/([^:]:):/$1/g;
513 return $file unless $Is_MacOS;
516 $file =~ s|([/ \n])|sprintf("\\%03o", unpack("c", $1))|ge;
525 maniadd({ $file => $comment, ...});
527 Adds an entry to an existing F<MANIFEST>.
529 $file will be normalized (ie. Unixified). B<UNIMPLEMENTED>
534 my($additions) = shift;
536 _normalize($additions);
538 my $manifest = maniread();
539 open(MANIFEST, ">>$MANIFEST") or die "Could not open $MANIFEST: $!";
540 foreach my $file (_sort keys %$additions) {
541 my $comment = $additions->{$file} || '';
542 printf MANIFEST "%-40s%s\n", $file, $comment unless
543 exists $manifest->{$file};
558 Anything between white space and an end of line within a C<MANIFEST>
559 file is considered to be a comment. Filenames and comments are
560 separated by one or more TAB characters in the output.
565 The file MANIFEST.SKIP may contain regular expressions of files that
566 should be ignored by mkmanifest() and filecheck(). The regular
567 expressions should appear one on each line. Blank lines and lines
568 which start with C<#> are skipped. Use C<\#> if you need a regular
569 expression to start with a sharp character. A typical example:
571 # Version control files and dirs.
577 # Makemaker generated files and dirs.
583 # Temp, old and emacs backup files.
589 If no MANIFEST.SKIP file is found, a default set of skips will be
590 used, similar to the example above. If you want nothing skipped,
591 simply make an empty MANIFEST.SKIP file.
596 C<&mkmanifest>, C<&manicheck>, C<&filecheck>, C<&fullcheck>,
597 C<&maniread>, and C<&manicopy> are exportable.
599 =head2 GLOBAL VARIABLES
601 C<$ExtUtils::Manifest::MANIFEST> defaults to C<MANIFEST>. Changing it
602 results in both a different C<MANIFEST> and a different
603 C<MANIFEST.SKIP> file. This is useful if you want to maintain
604 different distributions for different audiences (say a user version
605 and a developer version including RCS).
607 C<$ExtUtils::Manifest::Quiet> defaults to 0. If set to a true value,
608 all functions act silently.
610 C<$ExtUtils::Manifest::Debug> defaults to 0. If set to a true value,
611 or if PERL_MM_MANIFEST_DEBUG is true, debugging output will be
616 All diagnostic output is sent to C<STDERR>.
620 =item C<Not in MANIFEST:> I<file>
622 is reported if a file is found which is not in C<MANIFEST>.
624 =item C<Skipping> I<file>
626 is reported if a file is skipped due to an entry in C<MANIFEST.SKIP>.
628 =item C<No such file:> I<file>
630 is reported if a file mentioned in a C<MANIFEST> file does not
633 =item C<MANIFEST:> I<$!>
635 is reported if C<MANIFEST> could not be opened.
637 =item C<Added to MANIFEST:> I<file>
639 is reported by mkmanifest() if $Verbose is set and a file is added
640 to MANIFEST. $Verbose is set to 1 by default.
648 =item B<PERL_MM_MANIFEST_DEBUG>
656 L<ExtUtils::MakeMaker> which has handy targets for most of the functionality.
660 Andreas Koenig <F<andreas.koenig@anima.de>>