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