1 package ExtUtils::Manifest;
13 use Exporter 5.57 'import';
15 our $VERSION = '1.64';
16 our @EXPORT_OK = qw(mkmanifest
17 manicheck filecheck fullcheck skipcheck
18 manifind maniread manicopy maniadd
22 our $Is_MacOS = $^O eq 'MacOS';
23 our $Is_VMS = $^O eq 'VMS';
26 our $Is_VMS_nodot = 0; # No dots in dir names or double dots in files
29 require VMS::Filespec if $Is_VMS;
37 if (eval { local $SIG{__DIE__}; require VMS::Feature; }) {
38 $vms_unix_rpt = VMS::Feature::current("filename_unix_report");
39 $vms_efs = VMS::Feature::current("efs_charset");
40 $vms_case = VMS::Feature::current("efs_case_preserve");
42 my $unix_rpt = $ENV{'DECC$FILENAME_UNIX_REPORT'} || '';
43 my $efs_charset = $ENV{'DECC$EFS_CHARSET'} || '';
44 my $efs_case = $ENV{'DECC$EFS_CASE_PRESERVE'} || '';
45 $vms_unix_rpt = $unix_rpt =~ /^[ET1]/i;
46 $vms_efs = $efs_charset =~ /^[ET1]/i;
47 $vms_case = $efs_case =~ /^[ET1]/i;
49 $Is_VMS_lc = 0 if ($vms_case);
50 $Is_VMS_mode = 0 if ($vms_unix_rpt);
51 $Is_VMS_nodot = 0 if ($vms_efs);
54 our $Debug = $ENV{PERL_MM_MANIFEST_DEBUG} || 0;
55 our $Verbose = defined $ENV{PERL_MM_MANIFEST_VERBOSE} ?
56 $ENV{PERL_MM_MANIFEST_VERBOSE} : 1;
58 our $MANIFEST = 'MANIFEST';
60 our $DEFAULT_MSKIP = File::Spec->catfile( dirname(__FILE__), "$MANIFEST.SKIP" );
65 ExtUtils::Manifest - utilities to write and check a MANIFEST file
69 use ExtUtils::Manifest qw(...funcs to import...);
73 my @missing_files = manicheck;
74 my @skipped = skipcheck;
75 my @extra_files = filecheck;
76 my($missing, $extra) = fullcheck;
78 my $found = manifind();
80 my $manifest = maniread();
82 manicopy($read,$target);
84 maniadd({$file => $comment, ...});
91 ExtUtils::Manifest exports no functions by default. The following are
100 Writes all files in and below the current directory to your F<MANIFEST>.
101 It works similar to the result of the Unix command
105 All files that match any regular expression in a file F<MANIFEST.SKIP>
106 (if it exists) are ignored.
108 Any existing F<MANIFEST> file will be saved as F<MANIFEST.bak>.
113 return sort { lc $a cmp lc $b } @_;
118 my $read = (-r 'MANIFEST' && maniread()) or $manimiss++;
119 $read = {} if $manimiss;
121 my $bakbase = $MANIFEST;
122 $bakbase =~ s/\./_/g if $Is_VMS_nodot; # avoid double dots
123 rename $MANIFEST, "$bakbase.bak" unless $manimiss;
124 open M, "> $MANIFEST" or die "Could not open $MANIFEST: $!";
125 my $skip = maniskip();
126 my $found = manifind();
127 my($key,$val,$file,%all);
128 %all = (%$found, %$read);
129 $all{$MANIFEST} = ($Is_VMS_mode ? "$MANIFEST\t\t" : '') .
131 if $manimiss; # add new MANIFEST to known file list
132 foreach $file (_sort keys %all) {
133 if ($skip->($file)) {
134 # Policy: only remove files if they're listed in MANIFEST.SKIP.
135 # Don't remove files just because they don't exist.
136 warn "Removed from $MANIFEST: $file\n" if $Verbose and exists $read->{$file};
140 warn "Added to $MANIFEST: $file\n" unless exists $read->{$file};
142 my $text = $all{$file};
143 $file = _unmacify($file);
144 my $tabs = (5 - (length($file)+1)/8);
145 $tabs = 1 if $tabs < 1;
146 $tabs = 0 unless $text;
148 $file =~ s/([\\'])/\\$1/g;
151 print M $file, "\t" x $tabs, $text, "\n";
156 # Geez, shouldn't this use File::Spec or File::Basename or something?
157 # Why so careful about dependencies?
158 sub clean_up_filename {
159 my $filename = shift;
160 $filename =~ s|^\./||;
161 $filename =~ s/^:([^:]+)$/$1/ if $Is_MacOS;
163 $filename =~ s/\.$//; # trim trailing dot
164 $filename = VMS::Filespec::unixify($filename); # unescape spaces, etc.
166 $filename = lc($filename);
167 $filename = uc($filename) if $filename =~ /^MANIFEST(\.SKIP)?$/i;
176 my $found = manifind();
178 returns a hash reference. The keys of the hash are the files found
179 below the current directory.
188 my $name = clean_up_filename($File::Find::name);
189 warn "Debug: diskfile $name\n" if $Debug;
191 $found->{$name} = "";
194 # We have to use "$File::Find::dir/$_" in preprocess, because
195 # $File::Find::name is unavailable.
196 # Also, it's okay to use / here, because MANIFEST files use Unix-style
198 find({wanted => $wanted},
199 $Is_MacOS ? ":" : ".");
207 my @missing_files = manicheck();
209 checks if all the files within a C<MANIFEST> in the current directory
210 really do exist. If C<MANIFEST> and the tree below the current
211 directory are in sync it silently returns an empty list.
212 Otherwise it returns a list of files which are listed in the
213 C<MANIFEST> but missing from the directory, and by default also
214 outputs these names to STDERR.
219 return _check_files();
225 my @extra_files = filecheck();
227 finds files below the current directory that are not mentioned in the
228 C<MANIFEST> file. An optional file C<MANIFEST.SKIP> will be
229 consulted. Any file matching a regular expression in such a file will
230 not be reported as missing in the C<MANIFEST> file. The list of any
231 extraneous files found is returned, and by default also reported to
237 return _check_manifest();
243 my($missing, $extra) = fullcheck();
245 does both a manicheck() and a filecheck(), returning then as two array
251 return [_check_files()], [_check_manifest()];
257 my @skipped = skipcheck();
259 lists all the files that are skipped due to your C<MANIFEST.SKIP>
266 my $found = manifind();
267 my $matches = maniskip();
270 foreach my $file (_sort keys %$found){
271 if (&$matches($file)){
272 warn "Skipping $file\n" unless $Quiet;
273 push @skipped, $file;
284 my $dosnames=(defined(&Dos::UseLFN) && Dos::UseLFN()==0);
285 my $read = maniread() || {};
286 my $found = manifind($p);
289 foreach my $file (_sort keys %$read){
290 warn "Debug: manicheck checking from $MANIFEST $file\n" if $Debug;
293 $file =~ s=(\.(\w|-)+)=substr ($1,0,4)=ge;
294 $file =~ s=((\w|-)+)=substr ($1,0,8)=ge;
296 unless ( exists $found->{$file} ) {
297 warn "No such file: $file\n" unless $Quiet;
298 push @missfile, $file;
306 sub _check_manifest {
308 my $read = maniread() || {};
309 my $found = manifind($p);
310 my $skip = maniskip();
313 foreach my $file (_sort keys %$found){
314 next if $skip->($file);
315 warn "Debug: manicheck checking from disk $file\n" if $Debug;
316 unless ( exists $read->{$file} ) {
317 my $canon = $Is_MacOS ? "\t" . _unmacify($file) : '';
318 warn "Not in $MANIFEST: $file$canon\n" unless $Quiet;
319 push @missentry, $file;
329 my $manifest = maniread();
330 my $manifest = maniread($manifest_file);
332 reads a named C<MANIFEST> file (defaults to C<MANIFEST> in the current
333 directory) and returns a HASH reference with files being the keys and
334 comments being the values of the HASH. Blank lines and lines which
335 start with C<#> in the C<MANIFEST> file are discarded.
341 $mfile ||= $MANIFEST;
344 unless (open M, "< $mfile"){
345 warn "Problem opening $mfile: $!";
355 # filename may contain spaces if enclosed in ''
356 # (in which case, \\ and \' are escapes)
357 if (($file, $comment) = /^'(\\[\\']|.+)+'\s*(.*)/) {
358 $file =~ s/\\([\\'])/$1/g;
361 ($file, $comment) = /^(\S+)\s*(.*)/;
366 $file = _macify($file);
367 $file =~ s/\\([0-3][0-7][0-7])/sprintf("%c", oct($1))/ge;
369 elsif ($Is_VMS_mode) {
370 require File::Basename;
371 my($base,$dir) = File::Basename::fileparse($file);
372 # Resolve illegal file specifications in the same way as tar
375 my(@pieces) = split(/\./,$base);
377 { $base = shift(@pieces) . '.' . join('_',@pieces); }
378 my $okfile = "$dir$base";
379 warn "Debug: Illegal name $file changed to $okfile\n" if $Debug;
384 $file = uc($file) if $file =~ /^MANIFEST(\.SKIP)?$/i;
388 $read->{$file} = $comment;
396 my $skipchk = maniskip();
397 my $skipchk = maniskip($manifest_skip_file);
399 if ($skipchk->($file)) { .. }
401 reads a named C<MANIFEST.SKIP> file (defaults to C<MANIFEST.SKIP> in
402 the current directory) and returns a CODE reference that tests whether
403 a given filename should be skipped.
407 # returns an anonymous sub that decides if an argument matches
410 my $mfile = shift || "$MANIFEST.SKIP";
411 _check_mskip_directives($mfile) if -f $mfile;
413 open M, "< $mfile" or open M, "< $DEFAULT_MSKIP" or return sub {0};
417 $_ =~ qr{^\s*(?:(?:'([^\\']*(?:\\.[^\\']*)*)')|([^#\s]\S*))?(?:(?:\s*)|(?:\s+(.*?)\s*))$};
422 $filename =~ s/\\(['\\])/$1/g;
424 next if (not defined($filename) or not $filename);
425 push @skip, _macify($filename);
428 return sub {0} unless (scalar @skip > 0);
430 my $opts = $Is_VMS_mode ? '(?i)' : '';
432 # Make sure each entry is isolated in its own parentheses, in case
433 # any of them contain alternations
434 my $regex = join '|', map "(?:$_)", @skip;
436 return sub { $_[0] =~ qr{$opts$regex} };
439 # checks for the special directives
441 # #!include /path/to/some/manifest.skip
442 # in a custom MANIFEST.SKIP for, for including
443 # the content of, respectively, the default MANIFEST.SKIP
444 # and an external manifest.skip file
445 sub _check_mskip_directives {
450 unless (open M, "< $mfile") {
451 warn "Problem opening $mfile: $!";
455 if (/^#!include_default\s*$/) {
456 if (my @default = _include_mskip_file()) {
457 push @lines, @default;
458 warn "Debug: Including default MANIFEST.SKIP\n" if $Debug;
463 if (/^#!include\s+(.*)\s*$/) {
464 my $external_file = $1;
465 if (my @external = _include_mskip_file($external_file)) {
466 push @lines, @external;
467 warn "Debug: Including external $external_file\n" if $Debug;
476 my $bakbase = $mfile;
477 $bakbase =~ s/\./_/g if $Is_VMS_nodot; # avoid double dots
478 rename $mfile, "$bakbase.bak";
479 warn "Debug: Saving original $mfile as $bakbase.bak\n" if $Debug;
480 unless (open M, "> $mfile") {
481 warn "Problem opening $mfile: $!";
484 print M $_ for (@lines);
489 # returns an array containing the lines of an external
490 # manifest.skip file, if given, or $DEFAULT_MSKIP
491 sub _include_mskip_file {
492 my $mskip = shift || $DEFAULT_MSKIP;
494 warn qq{Included file "$mskip" not found - skipping};
498 unless (open M, "< $mskip") {
499 warn "Problem opening $mskip: $!";
503 push @lines, "\n#!start included $mskip\n";
504 push @lines, $_ while <M>;
506 push @lines, "#!end included $mskip\n\n";
512 manicopy(\%src, $dest_dir);
513 manicopy(\%src, $dest_dir, $how);
515 Copies the files that are the keys in %src to the $dest_dir. %src is
516 typically returned by the maniread() function.
518 manicopy( maniread(), $dest_dir );
520 This function is useful for producing a directory tree identical to the
521 intended distribution tree.
523 $how can be used to specify a different methods of "copying". Valid
524 values are C<cp>, which actually copies the files, C<ln> which creates
525 hard links, and C<best> which mostly links the files but copies any
526 symbolic link to make a tree without any symbolic link. C<cp> is the
532 my($read,$target,$how)=@_;
533 croak "manicopy() called without target argument" unless defined $target;
536 require File::Basename;
538 $target = VMS::Filespec::unixify($target) if $Is_VMS_mode;
539 File::Path::mkpath([ $target ],! $Quiet,$Is_VMS ? undef : 0755);
540 foreach my $file (keys %$read){
543 my $dir = _maccat($target, $file);
545 File::Path::mkpath($dir,1,0755);
547 cp_if_diff($file, _maccat($target, $file), $how);
549 $file = VMS::Filespec::unixify($file) if $Is_VMS_mode;
550 if ($file =~ m!/!) { # Ilya, that hurts, I fear, or maybe not?
551 my $dir = File::Basename::dirname($file);
552 $dir = VMS::Filespec::unixify($dir) if $Is_VMS_mode;
553 File::Path::mkpath(["$target/$dir"],! $Quiet,$Is_VMS ? undef : 0755);
555 cp_if_diff($file, "$target/$file", $how);
561 my($from, $to, $how)=@_;
563 carp "$from not found";
568 open(F,"< $from\0") or die "Can't read $from: $!\n";
569 if (open(T,"< $to\0")) {
571 while (<F>) { $diff++,last if $_ ne <T>; }
572 $diff++ unless eof(T);
579 unlink($to) or confess "unlink $to: $!";
582 best($from,$to), last STRICT_SWITCH if $how eq 'best';
583 cp($from,$to), last STRICT_SWITCH if $how eq 'cp';
584 ln($from,$to), last STRICT_SWITCH if $how eq 'ln';
585 croak("ExtUtils::Manifest::cp_if_diff " .
586 "called with illegal how argument [$how]. " .
587 "Legal values are 'best', 'cp', and 'ln'.");
593 my ($srcFile, $dstFile) = @_;
594 my ($access,$mod) = (stat $srcFile)[8,9];
596 copy($srcFile,$dstFile);
597 utime $access, $mod + ($Is_VMS ? 1 : 0), $dstFile;
598 _manicopy_chmod($srcFile, $dstFile);
603 my ($srcFile, $dstFile) = @_;
604 # Fix-me - VMS can support links.
605 return &cp if $Is_VMS or ($^O eq 'MSWin32' and Win32::IsWin95());
606 link($srcFile, $dstFile);
608 unless( _manicopy_chmod($srcFile, $dstFile) ) {
615 # 1) Strip off all group and world permissions.
616 # 2) Let everyone read it.
617 # 3) If the owner can execute it, everyone can.
618 sub _manicopy_chmod {
619 my($srcFile, $dstFile) = @_;
621 my $perm = 0444 | (stat $srcFile)[2] & 0700;
622 chmod( $perm | ( $perm & 0100 ? 0111 : 0 ), $dstFile );
625 # Files that are often modified in the distdir. Don't hard link them.
626 my @Exceptions = qw(MANIFEST META.yml SIGNATURE);
628 my ($srcFile, $dstFile) = @_;
630 my $is_exception = grep $srcFile =~ /$_/, @Exceptions;
631 if ($is_exception or !$Config{d_link} or -l $srcFile) {
632 cp($srcFile, $dstFile);
634 ln($srcFile, $dstFile) or cp($srcFile, $dstFile);
641 return $file unless $Is_MacOS;
655 return "$f1/$f2" unless $Is_MacOS;
658 $f1 =~ s/([^:]:):/$1/g;
665 return $file unless $Is_MacOS;
668 $file =~ s|([/ \n])|sprintf("\\%03o", unpack("c", $1))|ge;
677 maniadd({ $file => $comment, ...});
679 Adds an entry to an existing F<MANIFEST> unless its already there.
681 $file will be normalized (ie. Unixified). B<UNIMPLEMENTED>
686 my($additions) = shift;
688 _normalize($additions);
689 _fix_manifest($MANIFEST);
691 my $manifest = maniread();
692 my @needed = grep { !exists $manifest->{$_} } keys %$additions;
693 return 1 unless @needed;
695 open(MANIFEST, ">>$MANIFEST") or
696 die "maniadd() could not open $MANIFEST: $!";
698 foreach my $file (_sort @needed) {
699 my $comment = $additions->{$file} || '';
701 $file =~ s/([\\'])/\\$1/g;
704 printf MANIFEST "%-40s %s\n", $file, $comment;
706 close MANIFEST or die "Error closing $MANIFEST: $!";
712 # Make sure this MANIFEST is consistently written with native
713 # newlines and has a terminal newline.
715 my $manifest_file = shift;
717 open MANIFEST, $MANIFEST or die "Could not open $MANIFEST: $!";
719 my @manifest = split /(\015\012|\012|\015)/, <MANIFEST>, -1;
721 my $must_rewrite = "";
722 if ($manifest[-1] eq ""){
723 # sane case: last line had a terminal newline
725 for (my $i=1; $i<=$#manifest; $i+=2) {
726 unless ($manifest[$i] eq "\n") {
727 $must_rewrite = "not a newline at pos $i";
732 $must_rewrite = "last line without newline";
735 if ( $must_rewrite ) {
736 1 while unlink $MANIFEST; # avoid multiple versions on VMS
737 open MANIFEST, ">", $MANIFEST or die "(must_rewrite=$must_rewrite) Could not open >$MANIFEST: $!";
738 for (my $i=0; $i<=$#manifest; $i+=2) {
739 print MANIFEST "$manifest[$i]\n";
741 close MANIFEST or die "could not write $MANIFEST: $!";
756 A list of files in the distribution, one file per line. The MANIFEST
757 always uses Unix filepath conventions even if you're not on Unix. This
758 means F<foo/bar> style not F<foo\bar>.
760 Anything between white space and an end of line within a C<MANIFEST>
761 file is considered to be a comment. Any line beginning with # is also
762 a comment. Beginning with ExtUtils::Manifest 1.52, a filename may
763 contain whitespace characters if it is enclosed in single quotes; single
764 quotes or backslashes in that filename must be backslash-escaped.
768 some/other/file comment about some/file
769 'some/third file' comment
774 The file MANIFEST.SKIP may contain regular expressions of files that
775 should be ignored by mkmanifest() and filecheck(). The regular
776 expressions should appear one on each line. Blank lines and lines
777 which start with C<#> are skipped. Use C<\#> if you need a regular
778 expression to start with a C<#>.
782 # Version control files and dirs.
788 # Makemaker generated files and dirs.
794 # Temp, old and emacs backup files.
800 If no MANIFEST.SKIP file is found, a default set of skips will be
801 used, similar to the example above. If you want nothing skipped,
802 simply make an empty MANIFEST.SKIP file.
804 In one's own MANIFEST.SKIP file, certain directives
805 can be used to include the contents of other MANIFEST.SKIP
806 files. At present two such directives are recognized.
810 =item #!include_default
812 This inserts the contents of the default MANIFEST.SKIP file
814 =item #!include /Path/to/another/manifest.skip
816 This inserts the contents of the specified external file
820 The included contents will be inserted into the MANIFEST.SKIP
821 file in between I<#!start included /path/to/manifest.skip>
822 and I<#!end included /path/to/manifest.skip> markers.
823 The original MANIFEST.SKIP is saved as MANIFEST.SKIP.bak.
827 C<&mkmanifest>, C<&manicheck>, C<&filecheck>, C<&fullcheck>,
828 C<&maniread>, and C<&manicopy> are exportable.
830 =head2 GLOBAL VARIABLES
832 C<$ExtUtils::Manifest::MANIFEST> defaults to C<MANIFEST>. Changing it
833 results in both a different C<MANIFEST> and a different
834 C<MANIFEST.SKIP> file. This is useful if you want to maintain
835 different distributions for different audiences (say a user version
836 and a developer version including RCS).
838 C<$ExtUtils::Manifest::Quiet> defaults to 0. If set to a true value,
839 all functions act silently.
841 C<$ExtUtils::Manifest::Debug> defaults to 0. If set to a true value,
842 or if PERL_MM_MANIFEST_DEBUG is true, debugging output will be
847 All diagnostic output is sent to C<STDERR>.
851 =item C<Not in MANIFEST:> I<file>
853 is reported if a file is found which is not in C<MANIFEST>.
855 =item C<Skipping> I<file>
857 is reported if a file is skipped due to an entry in C<MANIFEST.SKIP>.
859 =item C<No such file:> I<file>
861 is reported if a file mentioned in a C<MANIFEST> file does not
864 =item C<MANIFEST:> I<$!>
866 is reported if C<MANIFEST> could not be opened.
868 =item C<Added to MANIFEST:> I<file>
870 is reported by mkmanifest() if $Verbose is set and a file is added
871 to MANIFEST. $Verbose is set to 1 by default.
879 =item B<PERL_MM_MANIFEST_DEBUG>
887 L<ExtUtils::MakeMaker> which has handy targets for most of the functionality.
891 Andreas Koenig C<andreas.koenig@anima.de>
893 Maintained by Michael G Schwern C<schwern@pobox.com> within the
894 ExtUtils-MakeMaker package and, as a separate CPAN package, by
895 Randy Kobes C<r.kobes@uwinnipeg.ca>.