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