perl 5.003_07: lib/ExtUtils/typemap
[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
10use vars qw(@ISA @EXPORT_OK $VERSION $Debug $Verbose $Is_VMS $Quiet $MANIFEST $found);
11
12@ISA=('Exporter');
13@EXPORT_OK = ('mkmanifest', 'manicheck', 'fullcheck', 'filecheck',
14 'skipcheck', 'maniread', 'manicopy');
005c1a0e 15
16$Debug = 0;
17$Verbose = 1;
79dd614e 18$Is_VMS = $^O eq 'VMS';
005c1a0e 19
8a1da95f 20$VERSION = substr(q$Revision: 1.27 $,10,4);
005c1a0e 21
22$Quiet = 0;
23
cb1a09d0 24$MANIFEST = 'MANIFEST';
25
4e68a208 26# Really cool fix from Ilya :)
27unless (defined $Config{d_link}) {
81ff29e3 28 local($^W) = 0; # avoid sub redefined message
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();
89 my $file;
90 my(@missfile,@missentry);
91 if ($arg & 1){
92 my $found = manifind();
93 foreach $file (sort keys %$read){
cb1a09d0 94 warn "Debug: manicheck checking from $MANIFEST $file\n" if $Debug;
005c1a0e 95 unless ( exists $found->{$file} ) {
8e07c86e 96 warn "No such file: $file\n" unless $Quiet;
97 push @missfile, $file;
005c1a0e 98 }
99 }
100 }
101 if ($arg & 2){
102 $read ||= {};
103 my $matches = _maniskip();
104 my $found = manifind();
8e07c86e 105 my $skipwarn = $arg & 4;
005c1a0e 106 foreach $file (sort keys %$found){
8e07c86e 107 if (&$matches($file)){
108 warn "Skipping $file\n" if $skipwarn;
109 next;
110 }
005c1a0e 111 warn "Debug: manicheck checking from disk $file\n" if $Debug;
112 unless ( exists $read->{$file} ) {
cb1a09d0 113 warn "Not in $MANIFEST: $file\n" unless $Quiet;
8e07c86e 114 push @missentry, $file;
005c1a0e 115 }
116 }
117 }
118 (\@missfile,\@missentry);
119}
120
121sub maniread {
122 my ($mfile) = @_;
cb1a09d0 123 $mfile = $MANIFEST unless defined $mfile;
005c1a0e 124 my $read = {};
125 local *M;
126 unless (open M, $mfile){
127 warn "$mfile: $!";
128 return $read;
129 }
130 while (<M>){
131 chomp;
8e07c86e 132 if ($Is_VMS) { /^(\S+)/ and $read->{"\L$1"}=$_; }
133 else { /^(\S+)\s*(.*)/ and $read->{$1}=$2; }
005c1a0e 134 }
135 close M;
136 $read;
137}
138
139# returns an anonymous sub that decides if an argument matches
140sub _maniskip {
141 my ($mfile) = @_;
142 my $matches = sub {0};
143 my @skip ;
f1387719 144 $mfile = "$MANIFEST.SKIP" unless defined $mfile;
005c1a0e 145 local *M;
146 return $matches unless -f $mfile;
147 open M, $mfile or return $matches;
148 while (<M>){
149 chomp;
150 next if /^\s*$/;
151 push @skip, $_;
152 }
153 close M;
8e07c86e 154 my $opts = $Is_VMS ? 'oi ' : 'o ';
005c1a0e 155 my $sub = "\$matches = "
156 . "sub { my(\$arg)=\@_; return 1 if "
8e07c86e 157 . join (" || ", (map {s!/!\\/!g; "\$arg =~ m/$_/$opts"} @skip), 0)
005c1a0e 158 . " }";
159 eval $sub;
160 print "Debug: $sub\n" if $Debug;
161 $matches;
162}
163
164sub manicopy {
8e07c86e 165 my($read,$target,$how)=@_;
005c1a0e 166 croak "manicopy() called without target argument" unless defined $target;
8e07c86e 167 $how = 'cp' unless defined $how && $how;
005c1a0e 168 require File::Path;
169 require File::Basename;
170 my(%dirs,$file);
8e07c86e 171 $target = VMS::Filespec::unixify($target) if $Is_VMS;
84876ac5 172 umask 0 unless $Is_VMS;
173 File::Path::mkpath([ $target ],1,$Is_VMS ? undef : 0755);
005c1a0e 174 foreach $file (keys %$read){
8e07c86e 175 $file = VMS::Filespec::unixify($file) if $Is_VMS;
84876ac5 176 if ($file =~ m!/!) { # Ilya, that hurts, I fear, or maybe not?
177 my $dir = File::Basename::dirname($file);
178 $dir = VMS::Filespec::unixify($dir) if $Is_VMS;
179 File::Path::mkpath(["$target/$dir"],1,$Is_VMS ? undef : 0755);
180 }
8e07c86e 181 if ($Is_VMS) { vms_cp_if_diff($file,"$target/$file"); }
182 else { cp_if_diff($file, "$target/$file", $how); }
005c1a0e 183 }
184}
185
186sub cp_if_diff {
8a1da95f 187 my($from, $to, $how)=@_;
005c1a0e 188 -f $from || carp "$0: $from not found";
8e07c86e 189 my($diff) = 0;
190 local(*F,*T);
191 open(F,$from) or croak "Can't read $from: $!\n";
192 if (open(T,$to)) {
193 while (<F>) { $diff++,last if $_ ne <T>; }
194 $diff++ unless eof(T);
195 close T;
196 }
197 else { $diff++; }
198 close F;
199 if ($diff) {
200 if (-e $to) {
201 unlink($to) or confess "unlink $to: $!";
202 }
8a1da95f 203 STRICT_SWITCH: {
204 best($from,$to), last STRICT_SWITCH if $how eq 'best';
205 cp($from,$to), last STRICT_SWITCH if $how eq 'cp';
206 ln($from,$to), last STRICT_SWITCH if $how eq 'ln';
207 }
8e07c86e 208 }
209}
210
211# Do the comparisons here rather than spawning off another process
212sub vms_cp_if_diff {
213 my($from,$to) = @_;
214 my($diff) = 0;
215 local(*F,*T);
216 open(F,$from) or croak "Can't read $from: $!\n";
217 if (open(T,$to)) {
218 while (<F>) { $diff++,last if $_ ne <T>; }
219 $diff++ unless eof(T);
220 close T;
221 }
222 else { $diff++; }
223 close F;
224 if ($diff) {
84876ac5 225 system('copy',VMS::Filespec::vmsify($from),VMS::Filespec::vmsify($to)) & 1
8e07c86e 226 or confess "Copy failed: $!";
005c1a0e 227 }
228}
229
8e07c86e 230sub cp {
231 my ($srcFile, $dstFile) = @_;
79dd614e 232 my ($perm,$access,$mod) = (stat $srcFile)[2,8,9];
233 copy($srcFile,$dstFile);
8e07c86e 234 utime $access, $mod, $dstFile;
235 # chmod a+rX-w,go-w
236 chmod( 0444 | ( $perm & 0111 ? 0111 : 0 ), $dstFile );
237}
238
239sub ln {
240 my ($srcFile, $dstFile) = @_;
241 link($srcFile, $dstFile);
242 local($_) = $dstFile; # chmod a+r,go-w+X (except "X" only applies to u=x)
243 my $mode= 0444 | (stat)[2] & 0700;
244 chmod( $mode | ( $mode & 0100 ? 0111 : 0 ), $_ );
245}
246
4633a7c4 247sub best {
248 my ($srcFile, $dstFile) = @_;
249 if (-l $srcFile) {
250 cp($srcFile, $dstFile);
251 } else {
252 ln($srcFile, $dstFile);
253 }
254}
255
005c1a0e 2561;
79dd614e 257
258__END__
259
260=head1 NAME
261
262ExtUtils::Manifest - utilities to write and check a MANIFEST file
263
264=head1 SYNOPSIS
265
266C<require ExtUtils::Manifest;>
267
268C<ExtUtils::Manifest::mkmanifest;>
269
270C<ExtUtils::Manifest::manicheck;>
271
272C<ExtUtils::Manifest::filecheck;>
273
274C<ExtUtils::Manifest::fullcheck;>
275
276C<ExtUtils::Manifest::skipcheck;>
277
278C<ExtUtild::Manifest::manifind();>
279
280C<ExtUtils::Manifest::maniread($file);>
281
282C<ExtUtils::Manifest::manicopy($read,$target,$how);>
283
284=head1 DESCRIPTION
285
286Mkmanifest() writes all files in and below the current directory to a
287file named in the global variable $ExtUtils::Manifest::MANIFEST (which
288defaults to C<MANIFEST>) in the current directory. It works similar to
289
290 find . -print
291
292but in doing so checks each line in an existing C<MANIFEST> file and
293includes any comments that are found in the existing C<MANIFEST> file
294in the new one. Anything between white space and an end of line within
295a C<MANIFEST> file is considered to be a comment. Filenames and
296comments are seperated by one or more TAB characters in the
297output. All files that match any regular expression in a file
298C<MANIFEST.SKIP> (if such a file exists) are ignored.
299
f1387719 300Manicheck() checks if all the files within a C<MANIFEST> in the
301current directory really do exist. It only reports discrepancies and
302exits silently if MANIFEST and the tree below the current directory
303are in sync.
79dd614e 304
305Filecheck() finds files below the current directory that are not
306mentioned in the C<MANIFEST> file. An optional file C<MANIFEST.SKIP>
307will be consulted. Any file matching a regular expression in such a
308file will not be reported as missing in the C<MANIFEST> file.
309
310Fullcheck() does both a manicheck() and a filecheck().
311
312Skipcheck() lists all the files that are skipped due to your
313C<MANIFEST.SKIP> file.
314
315Manifind() retruns a hash reference. The keys of the hash are the
316files found below the current directory.
317
318Maniread($file) reads a named C<MANIFEST> file (defaults to
319C<MANIFEST> in the current directory) and returns a HASH reference
320with files being the keys and comments being the values of the HASH.
321
322I<Manicopy($read,$target,$how)> copies the files that are the keys in
323the HASH I<%$read> to the named target directory. The HASH reference
324I<$read> is typically returned by the maniread() function. This
325function is useful for producing a directory tree identical to the
326intended distribution tree. The third parameter $how can be used to
327specify a different methods of "copying". Valid values are C<cp>,
328which actually copies the files, C<ln> which creates hard links, and
329C<best> which mostly links the files but copies any symbolic link to
330make a tree without any symbolic link. Best is the default.
331
332=head1 MANIFEST.SKIP
333
334The file MANIFEST.SKIP may contain regular expressions of files that
335should be ignored by mkmanifest() and filecheck(). The regular
336expressions should appear one on each line. A typical example:
337
338 \bRCS\b
339 ^MANIFEST\.
340 ^Makefile$
341 ~$
342 \.html$
343 \.old$
344 ^blib/
345 ^MakeMaker-\d
346
347=head1 EXPORT_OK
348
349C<&mkmanifest>, C<&manicheck>, C<&filecheck>, C<&fullcheck>,
350C<&maniread>, and C<&manicopy> are exportable.
351
352=head1 GLOBAL VARIABLES
353
354C<$ExtUtils::Manifest::MANIFEST> defaults to C<MANIFEST>. Changing it
355results in both a different C<MANIFEST> and a different
356C<MANIFEST.SKIP> file. This is useful if you want to maintain
357different distributions for different audiences (say a user version
358and a developer version including RCS).
359
81ff29e3 360C<$ExtUtils::Manifest::Quiet> defaults to 0. If set to a true value,
79dd614e 361all functions act silently.
362
363=head1 DIAGNOSTICS
364
365All diagnostic output is sent to C<STDERR>.
366
367=over
368
369=item C<Not in MANIFEST:> I<file>
370
371is reported if a file is found, that is missing in the C<MANIFEST>
372file which is excluded by a regular expression in the file
373C<MANIFEST.SKIP>.
374
375=item C<No such file:> I<file>
376
377is reported if a file mentioned in a C<MANIFEST> file does not
378exist.
379
380=item C<MANIFEST:> I<$!>
381
382is reported if C<MANIFEST> could not be opened.
383
384=item C<Added to MANIFEST:> I<file>
385
386is reported by mkmanifest() if $Verbose is set and a file is added
387to MANIFEST. $Verbose is set to 1 by default.
388
389=back
390
391=head1 SEE ALSO
392
393L<ExtUtils::MakeMaker> which has handy targets for most of the functionality.
394
395=head1 AUTHOR
396
81ff29e3 397Andreas Koenig E<lt>F<koenig@franz.ww.TU-Berlin.DE>E<gt>
79dd614e 398
399=cut