This is patch.2b1d to perl5.002beta1.
[p5sagit/p5-mst-13.2.git] / lib / ExtUtils / Manifest.pm
CommitLineData
005c1a0e 1package ExtUtils::Manifest;
2
3=head1 NAME
4
5ExtUtils::Manifest - utilities to write and check a MANIFEST file
6
7=head1 SYNOPSIS
8
9C<require ExtUtils::Manifest;>
10
11C<ExtUtils::Manifest::mkmanifest;>
12
13C<ExtUtils::Manifest::manicheck;>
14
15C<ExtUtils::Manifest::filecheck;>
16
17C<ExtUtils::Manifest::fullcheck;>
18
4633a7c4 19C<ExtUtils::Manifest::skipcheck;>
20
21C<ExtUtild::Manifest::manifind();>
22
005c1a0e 23C<ExtUtils::Manifest::maniread($file);>
24
8e07c86e 25C<ExtUtils::Manifest::manicopy($read,$target,$how);>
005c1a0e 26
27=head1 DESCRIPTION
28
29Mkmanifest() writes all files in and below the current directory to a
30file named C<MANIFEST> in the current directory. It works similar to
31
32 find . -print
33
34but in doing so checks each line in an existing C<MANIFEST> file and
35includes any comments that are found in the existing C<MANIFEST> file
36in the new one. Anything between white space and an end of line within
37a C<MANIFEST> file is considered to be a comment. Filenames and
38comments are seperated by one or more TAB characters in the
39output. All files that match any regular expression in a file
40C<MANIFEST.SKIP> (if such a file exists) are ignored.
41
42Manicheck() checks if all the files within a C<MANIFEST> in the current
43directory really do exist.
44
45Filecheck() finds files below the current directory that are not
46mentioned in the C<MANIFEST> file. An optional file C<MANIFEST.SKIP>
47will be consulted. Any file matching a regular expression in such a
48file will not be reported as missing in the C<MANIFEST> file.
49
50Fullcheck() does both a manicheck() and a filecheck().
51
4633a7c4 52Skipcheck() lists all the files that are skipped due to your
53C<MANIFEST.SKIP> file.
54
55Manifind() retruns a hash reference. The keys of the hash are the
56files found below the current directory.
57
005c1a0e 58Maniread($file) reads a named C<MANIFEST> file (defaults to
59C<MANIFEST> in the current directory) and returns a HASH reference
60with files being the keys and comments being the values of the HASH.
61
8e07c86e 62I<Manicopy($read,$target,$how)> copies the files that are the keys in
63the HASH I<%$read> to the named target directory. The HASH reference
005c1a0e 64I<$read> is typically returned by the maniread() function. This
65function is useful for producing a directory tree identical to the
8e07c86e 66intended distribution tree. The third parameter $how can be used to
4633a7c4 67specify a different methods of "copying". Valid values are C<cp>,
68which actually copies the files, C<ln> which creates hard links, and
69C<best> which mostly links the files but copies any symbolic link to
70make a tree without any symbolic link. Best is the default.
005c1a0e 71
72=head1 MANIFEST.SKIP
73
74The file MANIFEST.SKIP may contain regular expressions of files that
75should be ignored by mkmanifest() and filecheck(). The regular
76expressions should appear one on each line. A typical example:
77
78 \bRCS\b
79 ^MANIFEST\.
80 ^Makefile$
81 ~$
82 \.html$
83 \.old$
84 ^blib/
85 ^MakeMaker-\d
86
87=head1 EXPORT_OK
88
89C<&mkmanifest>, C<&manicheck>, C<&filecheck>, C<&fullcheck>,
90C<&maniread>, and C<&manicopy> are exportable.
91
92=head1 DIAGNOSTICS
93
94All diagnostic output is sent to C<STDERR>.
95
96=over
8e07c86e 97
005c1a0e 98=item C<Not in MANIFEST:> I<file>
8e07c86e 99
005c1a0e 100is reported if a file is found, that is missing in the C<MANIFEST>
101file which is excluded by a regular expression in the file
102C<MANIFEST.SKIP>.
103
104=item C<No such file:> I<file>
8e07c86e 105
005c1a0e 106is reported if a file mentioned in a C<MANIFEST> file does not
107exist.
108
109=item C<MANIFEST:> I<$!>
8e07c86e 110
005c1a0e 111is reported if C<MANIFEST> could not be opened.
112
113=item C<Added to MANIFEST:> I<file>
8e07c86e 114
005c1a0e 115is reported by mkmanifest() if $Verbose is set and a file is added
116to MANIFEST. $Verbose is set to 1 by default.
117
118=back
119
120=head1 AUTHOR
121
122Andreas Koenig F<E<lt>koenig@franz.ww.TU-Berlin.DEE<gt>>
123
124=cut
125
126require Exporter;
127@ISA=('Exporter');
128@EXPORT_OK = ('mkmanifest', 'manicheck', 'fullcheck', 'filecheck',
8e07c86e 129 'skipcheck', 'maniread', 'manicopy');
005c1a0e 130
8e07c86e 131use Config;
005c1a0e 132use File::Find;
133use Carp;
134
135$Debug = 0;
136$Verbose = 1;
8e07c86e 137$Is_VMS = $Config{'osname'} eq 'VMS';
005c1a0e 138
864a5fa8 139$VERSION = $VERSION = substr(q$Revision: 1.16 $,10,4);
005c1a0e 140
141$Quiet = 0;
142
143sub mkmanifest {
144 my $manimiss = 0;
145 my $read = maniread() or $manimiss++;
146 $read = {} if $manimiss;
864a5fa8 147 local *M;
148 rename "MANIFEST", "MANIFEST.bak" unless $manimiss;
149 open M, ">MANIFEST" or die "Could not open MANIFEST: $!";
005c1a0e 150 my $matches = _maniskip();
151 my $found = manifind();
152 my($key,$val,$file,%all);
153 my %all = (%$found, %$read);
005c1a0e 154 foreach $file (sort keys %all) {
155 next if &$matches($file);
156 if ($Verbose){
157 warn "Added to MANIFEST: $file\n" unless exists $read->{$file};
158 }
8e07c86e 159 my $text = $all{$file};
160 ($file,$text) = split(/\s+/,$text,2) if $Is_VMS;
005c1a0e 161 my $tabs = (5 - (length($file)+1)/8);
162 $tabs = 1 if $tabs < 1;
8e07c86e 163 $tabs = 0 unless $text;
164 print M $file, "\t" x $tabs, $text, "\n";
005c1a0e 165 }
166 close M;
167}
168
169sub manifind {
170 local $found = {};
4633a7c4 171 find(sub {return if -d $_;
005c1a0e 172 (my $name = $File::Find::name) =~ s|./||;
173 warn "Debug: diskfile $name\n" if $Debug;
8e07c86e 174 $name =~ s#(.*)\.$#\L$1# if $Is_VMS;
005c1a0e 175 $found->{$name} = "";}, ".");
176 $found;
177}
178
179sub fullcheck {
180 _manicheck(3);
181}
182
183sub manicheck {
184 return @{(_manicheck(1))[0]};
185}
186
187sub filecheck {
188 return @{(_manicheck(2))[1]};
189}
190
8e07c86e 191sub skipcheck {
192 _manicheck(6);
193}
194
005c1a0e 195sub _manicheck {
196 my($arg) = @_;
197 my $read = maniread();
198 my $file;
199 my(@missfile,@missentry);
200 if ($arg & 1){
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} ) {
8e07c86e 205 warn "No such file: $file\n" unless $Quiet;
206 push @missfile, $file;
005c1a0e 207 }
208 }
209 }
210 if ($arg & 2){
211 $read ||= {};
212 my $matches = _maniskip();
213 my $found = manifind();
8e07c86e 214 my $skipwarn = $arg & 4;
005c1a0e 215 foreach $file (sort keys %$found){
8e07c86e 216 if (&$matches($file)){
217 warn "Skipping $file\n" if $skipwarn;
218 next;
219 }
005c1a0e 220 warn "Debug: manicheck checking from disk $file\n" if $Debug;
221 unless ( exists $read->{$file} ) {
8e07c86e 222 warn "Not in MANIFEST: $file\n" unless $Quiet;
223 push @missentry, $file;
005c1a0e 224 }
225 }
226 }
227 (\@missfile,\@missentry);
228}
229
230sub maniread {
231 my ($mfile) = @_;
232 $mfile = "MANIFEST" unless defined $mfile;
233 my $read = {};
234 local *M;
235 unless (open M, $mfile){
236 warn "$mfile: $!";
237 return $read;
238 }
239 while (<M>){
240 chomp;
8e07c86e 241 if ($Is_VMS) { /^(\S+)/ and $read->{"\L$1"}=$_; }
242 else { /^(\S+)\s*(.*)/ and $read->{$1}=$2; }
005c1a0e 243 }
244 close M;
245 $read;
246}
247
248# returns an anonymous sub that decides if an argument matches
249sub _maniskip {
250 my ($mfile) = @_;
251 my $matches = sub {0};
252 my @skip ;
253 my $mfile = "MANIFEST.SKIP" unless defined $mfile;
254 local *M;
255 return $matches unless -f $mfile;
256 open M, $mfile or return $matches;
257 while (<M>){
258 chomp;
259 next if /^\s*$/;
260 push @skip, $_;
261 }
262 close M;
8e07c86e 263 my $opts = $Is_VMS ? 'oi ' : 'o ';
005c1a0e 264 my $sub = "\$matches = "
265 . "sub { my(\$arg)=\@_; return 1 if "
8e07c86e 266 . join (" || ", (map {s!/!\\/!g; "\$arg =~ m/$_/$opts"} @skip), 0)
005c1a0e 267 . " }";
268 eval $sub;
269 print "Debug: $sub\n" if $Debug;
270 $matches;
271}
272
273sub manicopy {
8e07c86e 274 my($read,$target,$how)=@_;
005c1a0e 275 croak "manicopy() called without target argument" unless defined $target;
8e07c86e 276 $how = 'cp' unless defined $how && $how;
005c1a0e 277 require File::Path;
278 require File::Basename;
279 my(%dirs,$file);
8e07c86e 280 $target = VMS::Filespec::unixify($target) if $Is_VMS;
281 umask 0;
005c1a0e 282 foreach $file (keys %$read){
8e07c86e 283 $file = VMS::Filespec::unixify($file) if $Is_VMS;
005c1a0e 284 my $dir = File::Basename::dirname($file);
8e07c86e 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); }
005c1a0e 288 }
289}
290
291sub cp_if_diff {
8e07c86e 292 my($from,$to, $how)=@_;
005c1a0e 293 -f $from || carp "$0: $from not found";
8e07c86e 294 my($diff) = 0;
295 local(*F,*T);
296 open(F,$from) or croak "Can't read $from: $!\n";
297 if (open(T,$to)) {
298 while (<F>) { $diff++,last if $_ ne <T>; }
299 $diff++ unless eof(T);
300 close T;
301 }
302 else { $diff++; }
303 close F;
304 if ($diff) {
305 if (-e $to) {
306 unlink($to) or confess "unlink $to: $!";
307 }
308 &$how($from, $to);
309 }
310}
311
312# Do the comparisons here rather than spawning off another process
313sub vms_cp_if_diff {
314 my($from,$to) = @_;
315 my($diff) = 0;
316 local(*F,*T);
317 open(F,$from) or croak "Can't read $from: $!\n";
318 if (open(T,$to)) {
319 while (<F>) { $diff++,last if $_ ne <T>; }
320 $diff++ unless eof(T);
321 close T;
322 }
323 else { $diff++; }
324 close F;
325 if ($diff) {
326 system('copy',vmsify($from),vmsify($to)) & 1
327 or confess "Copy failed: $!";
005c1a0e 328 }
329}
330
8e07c86e 331sub cp {
332 my ($srcFile, $dstFile) = @_;
333 my $buf;
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);
338 close IN;
339 close OUT;
340 utime $access, $mod, $dstFile;
341 # chmod a+rX-w,go-w
342 chmod( 0444 | ( $perm & 0111 ? 0111 : 0 ), $dstFile );
343}
344
345sub ln {
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 ), $_ );
351}
352
4633a7c4 353sub best {
354 my ($srcFile, $dstFile) = @_;
355 if (-l $srcFile) {
356 cp($srcFile, $dstFile);
357 } else {
358 ln($srcFile, $dstFile);
359 }
360}
361
005c1a0e 3621;