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