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.17 $,10,4);
143 # Really cool fix from Ilya :)
144 unless (defined $Config{d_link}) {
150 my $read = maniread() or $manimiss++;
151 $read = {} if $manimiss;
153 rename "MANIFEST", "MANIFEST.bak" unless $manimiss;
154 open M, ">MANIFEST" or die "Could not open MANIFEST: $!";
155 my $matches = _maniskip();
156 my $found = manifind();
157 my($key,$val,$file,%all);
158 my %all = (%$found, %$read);
159 foreach $file (sort keys %all) {
160 next if &$matches($file);
162 warn "Added to MANIFEST: $file\n" unless exists $read->{$file};
164 my $text = $all{$file};
165 ($file,$text) = split(/\s+/,$text,2) if $Is_VMS;
166 my $tabs = (5 - (length($file)+1)/8);
167 $tabs = 1 if $tabs < 1;
168 $tabs = 0 unless $text;
169 print M $file, "\t" x $tabs, $text, "\n";
176 find(sub {return if -d $_;
177 (my $name = $File::Find::name) =~ s|./||;
178 warn "Debug: diskfile $name\n" if $Debug;
179 $name =~ s#(.*)\.$#\L$1# if $Is_VMS;
180 $found->{$name} = "";}, ".");
189 return @{(_manicheck(1))[0]};
193 return @{(_manicheck(2))[1]};
202 my $read = maniread();
204 my(@missfile,@missentry);
206 my $found = manifind();
207 foreach $file (sort keys %$read){
208 warn "Debug: manicheck checking from MANIFEST $file\n" if $Debug;
209 unless ( exists $found->{$file} ) {
210 warn "No such file: $file\n" unless $Quiet;
211 push @missfile, $file;
217 my $matches = _maniskip();
218 my $found = manifind();
219 my $skipwarn = $arg & 4;
220 foreach $file (sort keys %$found){
221 if (&$matches($file)){
222 warn "Skipping $file\n" if $skipwarn;
225 warn "Debug: manicheck checking from disk $file\n" if $Debug;
226 unless ( exists $read->{$file} ) {
227 warn "Not in MANIFEST: $file\n" unless $Quiet;
228 push @missentry, $file;
232 (\@missfile,\@missentry);
237 $mfile = "MANIFEST" unless defined $mfile;
240 unless (open M, $mfile){
246 if ($Is_VMS) { /^(\S+)/ and $read->{"\L$1"}=$_; }
247 else { /^(\S+)\s*(.*)/ and $read->{$1}=$2; }
253 # returns an anonymous sub that decides if an argument matches
256 my $matches = sub {0};
258 my $mfile = "MANIFEST.SKIP" unless defined $mfile;
260 return $matches unless -f $mfile;
261 open M, $mfile or return $matches;
268 my $opts = $Is_VMS ? 'oi ' : 'o ';
269 my $sub = "\$matches = "
270 . "sub { my(\$arg)=\@_; return 1 if "
271 . join (" || ", (map {s!/!\\/!g; "\$arg =~ m/$_/$opts"} @skip), 0)
274 print "Debug: $sub\n" if $Debug;
279 my($read,$target,$how)=@_;
280 croak "manicopy() called without target argument" unless defined $target;
281 $how = 'cp' unless defined $how && $how;
283 require File::Basename;
285 $target = VMS::Filespec::unixify($target) if $Is_VMS;
287 foreach $file (keys %$read){
288 $file = VMS::Filespec::unixify($file) if $Is_VMS;
289 my $dir = File::Basename::dirname($file);
290 File::Path::mkpath(["$target/$dir"],1,0755);
291 if ($Is_VMS) { vms_cp_if_diff($file,"$target/$file"); }
292 else { cp_if_diff($file, "$target/$file", $how); }
297 my($from,$to, $how)=@_;
298 -f $from || carp "$0: $from not found";
301 open(F,$from) or croak "Can't read $from: $!\n";
303 while (<F>) { $diff++,last if $_ ne <T>; }
304 $diff++ unless eof(T);
311 unlink($to) or confess "unlink $to: $!";
317 # Do the comparisons here rather than spawning off another process
322 open(F,$from) or croak "Can't read $from: $!\n";
324 while (<F>) { $diff++,last if $_ ne <T>; }
325 $diff++ unless eof(T);
331 system('copy',vmsify($from),vmsify($to)) & 1
332 or confess "Copy failed: $!";
337 my ($srcFile, $dstFile) = @_;
339 open (IN,"<$srcFile") or die "Can't open input $srcFile: $!\n";
340 open (OUT,">$dstFile") or die "Can't open output $dstFile: $!\n";
341 my ($perm,$access,$mod) = (stat IN)[2,8,9];
342 syswrite(OUT, $buf, $len) while $len = sysread(IN, $buf, 8192);
345 utime $access, $mod, $dstFile;
347 chmod( 0444 | ( $perm & 0111 ? 0111 : 0 ), $dstFile );
351 my ($srcFile, $dstFile) = @_;
352 link($srcFile, $dstFile);
353 local($_) = $dstFile; # chmod a+r,go-w+X (except "X" only applies to u=x)
354 my $mode= 0444 | (stat)[2] & 0700;
355 chmod( $mode | ( $mode & 0100 ? 0111 : 0 ), $_ );
359 my ($srcFile, $dstFile) = @_;
361 cp($srcFile, $dstFile);
363 ln($srcFile, $dstFile);