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