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 C<MANIFEST> in the current directory. It works similar to
34 but in doing so checks each line in an existing C<MANIFEST> file and
35 includes any comments that are found in the existing C<MANIFEST> file
36 in the new one. Anything between white space and an end of line within
37 a C<MANIFEST> file is considered to be a comment. Filenames and
38 comments are seperated by one or more TAB characters in the
39 output. All files that match any regular expression in a file
40 C<MANIFEST.SKIP> (if such a file exists) are ignored.
42 Manicheck() checks if all the files within a C<MANIFEST> in the current
43 directory really do exist.
45 Filecheck() finds files below the current directory that are not
46 mentioned in the C<MANIFEST> file. An optional file C<MANIFEST.SKIP>
47 will be consulted. Any file matching a regular expression in such a
48 file will not be reported as missing in the C<MANIFEST> file.
50 Fullcheck() does both a manicheck() and a filecheck().
52 Skipcheck() lists all the files that are skipped due to your
53 C<MANIFEST.SKIP> file.
55 Manifind() retruns a hash reference. The keys of the hash are the
56 files found below the current directory.
58 Maniread($file) reads a named C<MANIFEST> file (defaults to
59 C<MANIFEST> in the current directory) and returns a HASH reference
60 with files being the keys and comments being the values of the HASH.
62 I<Manicopy($read,$target,$how)> copies the files that are the keys in
63 the HASH I<%$read> to the named target directory. The HASH reference
64 I<$read> is typically returned by the maniread() function. This
65 function is useful for producing a directory tree identical to the
66 intended distribution tree. The third parameter $how can be used to
67 specify a different methods of "copying". Valid values are C<cp>,
68 which actually copies the files, C<ln> which creates hard links, and
69 C<best> which mostly links the files but copies any symbolic link to
70 make a tree without any symbolic link. Best is the default.
74 The file MANIFEST.SKIP may contain regular expressions of files that
75 should be ignored by mkmanifest() and filecheck(). The regular
76 expressions should appear one on each line. A typical example:
89 C<&mkmanifest>, C<&manicheck>, C<&filecheck>, C<&fullcheck>,
90 C<&maniread>, and C<&manicopy> are exportable.
94 All diagnostic output is sent to C<STDERR>.
98 =item C<Not in MANIFEST:> I<file>
100 is reported if a file is found, that is missing in the C<MANIFEST>
101 file which is excluded by a regular expression in the file
104 =item C<No such file:> I<file>
106 is reported if a file mentioned in a C<MANIFEST> file does not
109 =item C<MANIFEST:> I<$!>
111 is reported if C<MANIFEST> could not be opened.
113 =item C<Added to MANIFEST:> I<file>
115 is reported by mkmanifest() if $Verbose is set and a file is added
116 to MANIFEST. $Verbose is set to 1 by default.
122 Andreas Koenig F<E<lt>koenig@franz.ww.TU-Berlin.DEE<gt>>
128 @EXPORT_OK = ('mkmanifest', 'manicheck', 'fullcheck', 'filecheck',
129 'skipcheck', 'maniread', 'manicopy');
137 $Is_VMS = $Config{'osname'} eq 'VMS';
139 $VERSION = $VERSION = substr(q$Revision: 1.15 $,10,4);
145 my $read = maniread() or $manimiss++;
146 $read = {} if $manimiss;
147 my $matches = _maniskip();
148 my $found = manifind();
149 my($key,$val,$file,%all);
150 my %all = (%$found, %$read);
152 rename "MANIFEST", "MANIFEST.bak" unless $manimiss;
153 open M, ">MANIFEST" or die "Could not open MANIFEST: $!";
154 foreach $file (sort keys %all) {
155 next if &$matches($file);
157 warn "Added to MANIFEST: $file\n" unless exists $read->{$file};
159 my $text = $all{$file};
160 ($file,$text) = split(/\s+/,$text,2) if $Is_VMS;
161 my $tabs = (5 - (length($file)+1)/8);
162 $tabs = 1 if $tabs < 1;
163 $tabs = 0 unless $text;
164 print M $file, "\t" x $tabs, $text, "\n";
171 find(sub {return if -d $_;
172 (my $name = $File::Find::name) =~ s|./||;
173 warn "Debug: diskfile $name\n" if $Debug;
174 $name =~ s#(.*)\.$#\L$1# if $Is_VMS;
175 $found->{$name} = "";}, ".");
184 return @{(_manicheck(1))[0]};
188 return @{(_manicheck(2))[1]};
197 my $read = maniread();
199 my(@missfile,@missentry);
201 my $found = manifind();
202 foreach $file (sort keys %$read){
203 warn "Debug: manicheck checking from MANIFEST $file\n" if $Debug;
204 unless ( exists $found->{$file} ) {
205 warn "No such file: $file\n" unless $Quiet;
206 push @missfile, $file;
212 my $matches = _maniskip();
213 my $found = manifind();
214 my $skipwarn = $arg & 4;
215 foreach $file (sort keys %$found){
216 if (&$matches($file)){
217 warn "Skipping $file\n" if $skipwarn;
220 warn "Debug: manicheck checking from disk $file\n" if $Debug;
221 unless ( exists $read->{$file} ) {
222 warn "Not in MANIFEST: $file\n" unless $Quiet;
223 push @missentry, $file;
227 (\@missfile,\@missentry);
232 $mfile = "MANIFEST" unless defined $mfile;
235 unless (open M, $mfile){
241 if ($Is_VMS) { /^(\S+)/ and $read->{"\L$1"}=$_; }
242 else { /^(\S+)\s*(.*)/ and $read->{$1}=$2; }
248 # returns an anonymous sub that decides if an argument matches
251 my $matches = sub {0};
253 my $mfile = "MANIFEST.SKIP" unless defined $mfile;
255 return $matches unless -f $mfile;
256 open M, $mfile or return $matches;
263 my $opts = $Is_VMS ? 'oi ' : 'o ';
264 my $sub = "\$matches = "
265 . "sub { my(\$arg)=\@_; return 1 if "
266 . join (" || ", (map {s!/!\\/!g; "\$arg =~ m/$_/$opts"} @skip), 0)
269 print "Debug: $sub\n" if $Debug;
274 my($read,$target,$how)=@_;
275 croak "manicopy() called without target argument" unless defined $target;
276 $how = 'cp' unless defined $how && $how;
278 require File::Basename;
280 $target = VMS::Filespec::unixify($target) if $Is_VMS;
282 foreach $file (keys %$read){
283 $file = VMS::Filespec::unixify($file) if $Is_VMS;
284 my $dir = File::Basename::dirname($file);
285 File::Path::mkpath(["$target/$dir"],1,0755);
286 if ($Is_VMS) { vms_cp_if_diff($file,"$target/$file"); }
287 else { cp_if_diff($file, "$target/$file", $how); }
292 my($from,$to, $how)=@_;
293 -f $from || carp "$0: $from not found";
296 open(F,$from) or croak "Can't read $from: $!\n";
298 while (<F>) { $diff++,last if $_ ne <T>; }
299 $diff++ unless eof(T);
306 unlink($to) or confess "unlink $to: $!";
312 # Do the comparisons here rather than spawning off another process
317 open(F,$from) or croak "Can't read $from: $!\n";
319 while (<F>) { $diff++,last if $_ ne <T>; }
320 $diff++ unless eof(T);
326 system('copy',vmsify($from),vmsify($to)) & 1
327 or confess "Copy failed: $!";
332 my ($srcFile, $dstFile) = @_;
334 open (IN,"<$srcFile") or die "Can't open input $srcFile: $!\n";
335 open (OUT,">$dstFile") or die "Can't open output $dstFile: $!\n";
336 my ($perm,$access,$mod) = (stat IN)[2,8,9];
337 syswrite(OUT, $buf, $len) while $len = sysread(IN, $buf, 8192);
340 utime $access, $mod, $dstFile;
342 chmod( 0444 | ( $perm & 0111 ? 0111 : 0 ), $dstFile );
346 my ($srcFile, $dstFile) = @_;
347 link($srcFile, $dstFile);
348 local($_) = $dstFile; # chmod a+r,go-w+X (except "X" only applies to u=x)
349 my $mode= 0444 | (stat)[2] & 0700;
350 chmod( $mode | ( $mode & 0100 ? 0111 : 0 ), $_ );
354 my ($srcFile, $dstFile) = @_;
356 cp($srcFile, $dstFile);
358 ln($srcFile, $dstFile);