1 package ExtUtils::Manifest;
5 ExtUtils::Manifest - utilities to write and check a MANIFEST file
9 C<require ExtUtils::Manifest;>
11 C<ExtUtils::Manifest::mkmanifest;>
13 C<ExtUtils::Manifest::manicheck;>
15 C<ExtUtils::Manifest::filecheck;>
17 C<ExtUtils::Manifest::fullcheck;>
19 C<ExtUtils::Manifest::skipcheck;>
21 C<ExtUtild::Manifest::manifind();>
23 C<ExtUtils::Manifest::maniread($file);>
25 C<ExtUtils::Manifest::manicopy($read,$target,$how);>
29 Mkmanifest() writes all files in and below the current directory to a
30 file named in the global variable $ExtUtils::Manifest::MANIFEST (which
31 defaults to C<MANIFEST>) in the current directory. It works similar to
35 but in doing so checks each line in an existing C<MANIFEST> file and
36 includes any comments that are found in the existing C<MANIFEST> file
37 in the new one. Anything between white space and an end of line within
38 a C<MANIFEST> file is considered to be a comment. Filenames and
39 comments are seperated by one or more TAB characters in the
40 output. All files that match any regular expression in a file
41 C<MANIFEST.SKIP> (if such a file exists) are ignored.
43 Manicheck() checks if all the files within a C<MANIFEST> in the current
44 directory really do exist.
46 Filecheck() finds files below the current directory that are not
47 mentioned in the C<MANIFEST> file. An optional file C<MANIFEST.SKIP>
48 will be consulted. Any file matching a regular expression in such a
49 file will not be reported as missing in the C<MANIFEST> file.
51 Fullcheck() does both a manicheck() and a filecheck().
53 Skipcheck() lists all the files that are skipped due to your
54 C<MANIFEST.SKIP> file.
56 Manifind() retruns a hash reference. The keys of the hash are the
57 files found below the current directory.
59 Maniread($file) reads a named C<MANIFEST> file (defaults to
60 C<MANIFEST> in the current directory) and returns a HASH reference
61 with files being the keys and comments being the values of the HASH.
63 I<Manicopy($read,$target,$how)> copies the files that are the keys in
64 the HASH I<%$read> to the named target directory. The HASH reference
65 I<$read> is typically returned by the maniread() function. This
66 function is useful for producing a directory tree identical to the
67 intended distribution tree. The third parameter $how can be used to
68 specify a different methods of "copying". Valid values are C<cp>,
69 which actually copies the files, C<ln> which creates hard links, and
70 C<best> which mostly links the files but copies any symbolic link to
71 make a tree without any symbolic link. Best is the default.
75 The file MANIFEST.SKIP may contain regular expressions of files that
76 should be ignored by mkmanifest() and filecheck(). The regular
77 expressions should appear one on each line. A typical example:
90 C<&mkmanifest>, C<&manicheck>, C<&filecheck>, C<&fullcheck>,
91 C<&maniread>, and C<&manicopy> are exportable.
93 =head1 GLOBAL VARIABLES
95 C<$ExtUtils::Manifest::MANIFEST> defaults to C<MANIFEST>. Changing it
96 results in both a different C<MANIFEST> and a different
97 C<MANIFEST.SKIP> file. This is useful if you want to maintain
98 different distributions for different audiences (say a user version
99 and a developer version including RCS).
101 <$ExtUtils::Manifest::Quiet> defaults to 0. If set to a true value,
102 all functions act silently.
106 All diagnostic output is sent to C<STDERR>.
110 =item C<Not in MANIFEST:> I<file>
112 is reported if a file is found, that is missing in the C<MANIFEST>
113 file which is excluded by a regular expression in the file
116 =item C<No such file:> I<file>
118 is reported if a file mentioned in a C<MANIFEST> file does not
121 =item C<MANIFEST:> I<$!>
123 is reported if C<MANIFEST> could not be opened.
125 =item C<Added to MANIFEST:> I<file>
127 is reported by mkmanifest() if $Verbose is set and a file is added
128 to MANIFEST. $Verbose is set to 1 by default.
134 L<ExtUtils::MakeMaker> which has handy targets for most of the functionality.
138 Andreas Koenig F<E<lt>koenig@franz.ww.TU-Berlin.DEE<gt>>
144 @EXPORT_OK = ('mkmanifest', 'manicheck', 'fullcheck', 'filecheck',
145 'skipcheck', 'maniread', 'manicopy');
153 $Is_VMS = $Config{'osname'} eq 'VMS';
155 $VERSION = $VERSION = substr(q$Revision: 1.22 $,10,4);
159 $MANIFEST = 'MANIFEST';
161 # Really cool fix from Ilya :)
162 unless (defined $Config{d_link}) {
168 my $read = maniread() or $manimiss++;
169 $read = {} if $manimiss;
171 rename $MANIFEST, "$MANIFEST.bak" unless $manimiss;
172 open M, ">$MANIFEST" or die "Could not open $MANIFEST: $!";
173 my $matches = _maniskip();
174 my $found = manifind();
175 my($key,$val,$file,%all);
176 my %all = (%$found, %$read);
177 $all{$MANIFEST} = ($Is_VMS ? "$MANIFEST\t\t" : '') . 'This list of files'
178 if $manimiss; # add new MANIFEST to known file list
179 foreach $file (sort keys %all) {
180 next if &$matches($file);
182 warn "Added to $MANIFEST: $file\n" unless exists $read->{$file};
184 my $text = $all{$file};
185 ($file,$text) = split(/\s+/,$text,2) if $Is_VMS && $text;
186 my $tabs = (5 - (length($file)+1)/8);
187 $tabs = 1 if $tabs < 1;
188 $tabs = 0 unless $text;
189 print M $file, "\t" x $tabs, $text, "\n";
196 find(sub {return if -d $_;
197 (my $name = $File::Find::name) =~ s|./||;
198 warn "Debug: diskfile $name\n" if $Debug;
199 $name =~ s#(.*)\.$#\L$1# if $Is_VMS;
200 $found->{$name} = "";}, ".");
209 return @{(_manicheck(1))[0]};
213 return @{(_manicheck(2))[1]};
222 my $read = maniread();
224 my(@missfile,@missentry);
226 my $found = manifind();
227 foreach $file (sort keys %$read){
228 warn "Debug: manicheck checking from $MANIFEST $file\n" if $Debug;
229 unless ( exists $found->{$file} ) {
230 warn "No such file: $file\n" unless $Quiet;
231 push @missfile, $file;
237 my $matches = _maniskip();
238 my $found = manifind();
239 my $skipwarn = $arg & 4;
240 foreach $file (sort keys %$found){
241 if (&$matches($file)){
242 warn "Skipping $file\n" if $skipwarn;
245 warn "Debug: manicheck checking from disk $file\n" if $Debug;
246 unless ( exists $read->{$file} ) {
247 warn "Not in $MANIFEST: $file\n" unless $Quiet;
248 push @missentry, $file;
252 (\@missfile,\@missentry);
257 $mfile = $MANIFEST unless defined $mfile;
260 unless (open M, $mfile){
266 if ($Is_VMS) { /^(\S+)/ and $read->{"\L$1"}=$_; }
267 else { /^(\S+)\s*(.*)/ and $read->{$1}=$2; }
273 # returns an anonymous sub that decides if an argument matches
276 my $matches = sub {0};
278 my $mfile = "$MANIFEST.SKIP" unless defined $mfile;
280 return $matches unless -f $mfile;
281 open M, $mfile or return $matches;
288 my $opts = $Is_VMS ? 'oi ' : 'o ';
289 my $sub = "\$matches = "
290 . "sub { my(\$arg)=\@_; return 1 if "
291 . join (" || ", (map {s!/!\\/!g; "\$arg =~ m/$_/$opts"} @skip), 0)
294 print "Debug: $sub\n" if $Debug;
299 my($read,$target,$how)=@_;
300 croak "manicopy() called without target argument" unless defined $target;
301 $how = 'cp' unless defined $how && $how;
303 require File::Basename;
305 $target = VMS::Filespec::unixify($target) if $Is_VMS;
306 umask 0 unless $Is_VMS;
307 File::Path::mkpath([ $target ],1,$Is_VMS ? undef : 0755);
308 foreach $file (keys %$read){
309 $file = VMS::Filespec::unixify($file) if $Is_VMS;
310 if ($file =~ m!/!) { # Ilya, that hurts, I fear, or maybe not?
311 my $dir = File::Basename::dirname($file);
312 $dir = VMS::Filespec::unixify($dir) if $Is_VMS;
313 File::Path::mkpath(["$target/$dir"],1,$Is_VMS ? undef : 0755);
315 if ($Is_VMS) { vms_cp_if_diff($file,"$target/$file"); }
316 else { cp_if_diff($file, "$target/$file", $how); }
321 my($from,$to, $how)=@_;
322 -f $from || carp "$0: $from not found";
325 open(F,$from) or croak "Can't read $from: $!\n";
327 while (<F>) { $diff++,last if $_ ne <T>; }
328 $diff++ unless eof(T);
335 unlink($to) or confess "unlink $to: $!";
341 # Do the comparisons here rather than spawning off another process
346 open(F,$from) or croak "Can't read $from: $!\n";
348 while (<F>) { $diff++,last if $_ ne <T>; }
349 $diff++ unless eof(T);
355 system('copy',VMS::Filespec::vmsify($from),VMS::Filespec::vmsify($to)) & 1
356 or confess "Copy failed: $!";
361 my ($srcFile, $dstFile) = @_;
363 open (IN,"<$srcFile") or die "Can't open input $srcFile: $!\n";
364 open (OUT,">$dstFile") or die "Can't open output $dstFile: $!\n";
365 my ($perm,$access,$mod) = (stat IN)[2,8,9];
366 syswrite(OUT, $buf, $len) while $len = sysread(IN, $buf, 8192);
369 utime $access, $mod, $dstFile;
371 chmod( 0444 | ( $perm & 0111 ? 0111 : 0 ), $dstFile );
375 my ($srcFile, $dstFile) = @_;
376 link($srcFile, $dstFile);
377 local($_) = $dstFile; # chmod a+r,go-w+X (except "X" only applies to u=x)
378 my $mode= 0444 | (stat)[2] & 0700;
379 chmod( $mode | ( $mode & 0100 ? 0111 : 0 ), $_ );
383 my ($srcFile, $dstFile) = @_;
385 cp($srcFile, $dstFile);
387 ln($srcFile, $dstFile);