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