1*0Sstevel@tonic-gatepackage ExtUtils::Manifest; 2*0Sstevel@tonic-gate 3*0Sstevel@tonic-gaterequire Exporter; 4*0Sstevel@tonic-gateuse Config; 5*0Sstevel@tonic-gateuse File::Find; 6*0Sstevel@tonic-gateuse File::Copy 'copy'; 7*0Sstevel@tonic-gateuse File::Spec; 8*0Sstevel@tonic-gateuse Carp; 9*0Sstevel@tonic-gateuse strict; 10*0Sstevel@tonic-gate 11*0Sstevel@tonic-gateuse vars qw($VERSION @ISA @EXPORT_OK 12*0Sstevel@tonic-gate $Is_MacOS $Is_VMS 13*0Sstevel@tonic-gate $Debug $Verbose $Quiet $MANIFEST $DEFAULT_MSKIP); 14*0Sstevel@tonic-gate 15*0Sstevel@tonic-gate$VERSION = 1.42; 16*0Sstevel@tonic-gate@ISA=('Exporter'); 17*0Sstevel@tonic-gate@EXPORT_OK = qw(mkmanifest 18*0Sstevel@tonic-gate manicheck filecheck fullcheck skipcheck 19*0Sstevel@tonic-gate manifind maniread manicopy maniadd 20*0Sstevel@tonic-gate ); 21*0Sstevel@tonic-gate 22*0Sstevel@tonic-gate$Is_MacOS = $^O eq 'MacOS'; 23*0Sstevel@tonic-gate$Is_VMS = $^O eq 'VMS'; 24*0Sstevel@tonic-gaterequire VMS::Filespec if $Is_VMS; 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gate$Debug = $ENV{PERL_MM_MANIFEST_DEBUG} || 0; 27*0Sstevel@tonic-gate$Verbose = defined $ENV{PERL_MM_MANIFEST_VERBOSE} ? 28*0Sstevel@tonic-gate $ENV{PERL_MM_MANIFEST_VERBOSE} : 1; 29*0Sstevel@tonic-gate$Quiet = 0; 30*0Sstevel@tonic-gate$MANIFEST = 'MANIFEST'; 31*0Sstevel@tonic-gate 32*0Sstevel@tonic-gatemy $Filename = __FILE__; 33*0Sstevel@tonic-gate$DEFAULT_MSKIP = (File::Spec->splitpath($Filename))[1]. 34*0Sstevel@tonic-gate "$MANIFEST.SKIP"; 35*0Sstevel@tonic-gate 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gate=head1 NAME 38*0Sstevel@tonic-gate 39*0Sstevel@tonic-gateExtUtils::Manifest - utilities to write and check a MANIFEST file 40*0Sstevel@tonic-gate 41*0Sstevel@tonic-gate=head1 SYNOPSIS 42*0Sstevel@tonic-gate 43*0Sstevel@tonic-gate use ExtUtils::Manifest qw(...funcs to import...); 44*0Sstevel@tonic-gate 45*0Sstevel@tonic-gate mkmanifest(); 46*0Sstevel@tonic-gate 47*0Sstevel@tonic-gate my @missing_files = manicheck; 48*0Sstevel@tonic-gate my @skipped = skipcheck; 49*0Sstevel@tonic-gate my @extra_files = filecheck; 50*0Sstevel@tonic-gate my($missing, $extra) = fullcheck; 51*0Sstevel@tonic-gate 52*0Sstevel@tonic-gate my $found = manifind(); 53*0Sstevel@tonic-gate 54*0Sstevel@tonic-gate my $manifest = maniread(); 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate manicopy($read,$target); 57*0Sstevel@tonic-gate 58*0Sstevel@tonic-gate maniadd({$file => $comment, ...}); 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate 61*0Sstevel@tonic-gate=head1 DESCRIPTION 62*0Sstevel@tonic-gate 63*0Sstevel@tonic-gate=head2 Functions 64*0Sstevel@tonic-gate 65*0Sstevel@tonic-gateExtUtils::Manifest exports no functions by default. The following are 66*0Sstevel@tonic-gateexported on request 67*0Sstevel@tonic-gate 68*0Sstevel@tonic-gate=over 4 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate=item mkmanifest 71*0Sstevel@tonic-gate 72*0Sstevel@tonic-gate mkmanifest(); 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gateWrites all files in and below the current directory to your F<MANIFEST>. 75*0Sstevel@tonic-gateIt works similar to 76*0Sstevel@tonic-gate 77*0Sstevel@tonic-gate find . > MANIFEST 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gateAll files that match any regular expression in a file F<MANIFEST.SKIP> 80*0Sstevel@tonic-gate(if it exists) are ignored. 81*0Sstevel@tonic-gate 82*0Sstevel@tonic-gateAny existing F<MANIFEST> file will be saved as F<MANIFEST.bak>. Lines 83*0Sstevel@tonic-gatefrom the old F<MANIFEST> file is preserved, including any comments 84*0Sstevel@tonic-gatethat are found in the existing F<MANIFEST> file in the new one. 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gate=cut 87*0Sstevel@tonic-gate 88*0Sstevel@tonic-gatesub _sort { 89*0Sstevel@tonic-gate return sort { lc $a cmp lc $b } @_; 90*0Sstevel@tonic-gate} 91*0Sstevel@tonic-gate 92*0Sstevel@tonic-gatesub mkmanifest { 93*0Sstevel@tonic-gate my $manimiss = 0; 94*0Sstevel@tonic-gate my $read = (-r 'MANIFEST' && maniread()) or $manimiss++; 95*0Sstevel@tonic-gate $read = {} if $manimiss; 96*0Sstevel@tonic-gate local *M; 97*0Sstevel@tonic-gate rename $MANIFEST, "$MANIFEST.bak" unless $manimiss; 98*0Sstevel@tonic-gate open M, ">$MANIFEST" or die "Could not open $MANIFEST: $!"; 99*0Sstevel@tonic-gate my $skip = _maniskip(); 100*0Sstevel@tonic-gate my $found = manifind(); 101*0Sstevel@tonic-gate my($key,$val,$file,%all); 102*0Sstevel@tonic-gate %all = (%$found, %$read); 103*0Sstevel@tonic-gate $all{$MANIFEST} = ($Is_VMS ? "$MANIFEST\t\t" : '') . 'This list of files' 104*0Sstevel@tonic-gate if $manimiss; # add new MANIFEST to known file list 105*0Sstevel@tonic-gate foreach $file (_sort keys %all) { 106*0Sstevel@tonic-gate if ($skip->($file)) { 107*0Sstevel@tonic-gate # Policy: only remove files if they're listed in MANIFEST.SKIP. 108*0Sstevel@tonic-gate # Don't remove files just because they don't exist. 109*0Sstevel@tonic-gate warn "Removed from $MANIFEST: $file\n" if $Verbose and exists $read->{$file}; 110*0Sstevel@tonic-gate next; 111*0Sstevel@tonic-gate } 112*0Sstevel@tonic-gate if ($Verbose){ 113*0Sstevel@tonic-gate warn "Added to $MANIFEST: $file\n" unless exists $read->{$file}; 114*0Sstevel@tonic-gate } 115*0Sstevel@tonic-gate my $text = $all{$file}; 116*0Sstevel@tonic-gate ($file,$text) = split(/\s+/,$text,2) if $Is_VMS && $text; 117*0Sstevel@tonic-gate $file = _unmacify($file); 118*0Sstevel@tonic-gate my $tabs = (5 - (length($file)+1)/8); 119*0Sstevel@tonic-gate $tabs = 1 if $tabs < 1; 120*0Sstevel@tonic-gate $tabs = 0 unless $text; 121*0Sstevel@tonic-gate print M $file, "\t" x $tabs, $text, "\n"; 122*0Sstevel@tonic-gate } 123*0Sstevel@tonic-gate close M; 124*0Sstevel@tonic-gate} 125*0Sstevel@tonic-gate 126*0Sstevel@tonic-gate# Geez, shouldn't this use File::Spec or File::Basename or something? 127*0Sstevel@tonic-gate# Why so careful about dependencies? 128*0Sstevel@tonic-gatesub clean_up_filename { 129*0Sstevel@tonic-gate my $filename = shift; 130*0Sstevel@tonic-gate $filename =~ s|^\./||; 131*0Sstevel@tonic-gate $filename =~ s/^:([^:]+)$/$1/ if $Is_MacOS; 132*0Sstevel@tonic-gate return $filename; 133*0Sstevel@tonic-gate} 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gate 136*0Sstevel@tonic-gate=item manifind 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate my $found = manifind(); 139*0Sstevel@tonic-gate 140*0Sstevel@tonic-gatereturns a hash reference. The keys of the hash are the files found 141*0Sstevel@tonic-gatebelow the current directory. 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate=cut 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gatesub manifind { 146*0Sstevel@tonic-gate my $p = shift || {}; 147*0Sstevel@tonic-gate my $found = {}; 148*0Sstevel@tonic-gate 149*0Sstevel@tonic-gate my $wanted = sub { 150*0Sstevel@tonic-gate my $name = clean_up_filename($File::Find::name); 151*0Sstevel@tonic-gate warn "Debug: diskfile $name\n" if $Debug; 152*0Sstevel@tonic-gate return if -d $_; 153*0Sstevel@tonic-gate 154*0Sstevel@tonic-gate if( $Is_VMS ) { 155*0Sstevel@tonic-gate $name =~ s#(.*)\.$#\L$1#; 156*0Sstevel@tonic-gate $name = uc($name) if $name =~ /^MANIFEST(\.SKIP)?$/i; 157*0Sstevel@tonic-gate } 158*0Sstevel@tonic-gate $found->{$name} = ""; 159*0Sstevel@tonic-gate }; 160*0Sstevel@tonic-gate 161*0Sstevel@tonic-gate # We have to use "$File::Find::dir/$_" in preprocess, because 162*0Sstevel@tonic-gate # $File::Find::name is unavailable. 163*0Sstevel@tonic-gate # Also, it's okay to use / here, because MANIFEST files use Unix-style 164*0Sstevel@tonic-gate # paths. 165*0Sstevel@tonic-gate find({wanted => $wanted}, 166*0Sstevel@tonic-gate $Is_MacOS ? ":" : "."); 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate return $found; 169*0Sstevel@tonic-gate} 170*0Sstevel@tonic-gate 171*0Sstevel@tonic-gate 172*0Sstevel@tonic-gate=item manicheck 173*0Sstevel@tonic-gate 174*0Sstevel@tonic-gate my @missing_files = manicheck(); 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gatechecks if all the files within a C<MANIFEST> in the current directory 177*0Sstevel@tonic-gatereally do exist. If C<MANIFEST> and the tree below the current 178*0Sstevel@tonic-gatedirectory are in sync it silently returns an empty list. 179*0Sstevel@tonic-gateOtherwise it returns a list of files which are listed in the 180*0Sstevel@tonic-gateC<MANIFEST> but missing from the directory, and by default also 181*0Sstevel@tonic-gateoutputs these names to STDERR. 182*0Sstevel@tonic-gate 183*0Sstevel@tonic-gate=cut 184*0Sstevel@tonic-gate 185*0Sstevel@tonic-gatesub manicheck { 186*0Sstevel@tonic-gate return _check_files(); 187*0Sstevel@tonic-gate} 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate=item filecheck 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate my @extra_files = filecheck(); 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gatefinds files below the current directory that are not mentioned in the 195*0Sstevel@tonic-gateC<MANIFEST> file. An optional file C<MANIFEST.SKIP> will be 196*0Sstevel@tonic-gateconsulted. Any file matching a regular expression in such a file will 197*0Sstevel@tonic-gatenot be reported as missing in the C<MANIFEST> file. The list of any 198*0Sstevel@tonic-gateextraneous files found is returned, and by default also reported to 199*0Sstevel@tonic-gateSTDERR. 200*0Sstevel@tonic-gate 201*0Sstevel@tonic-gate=cut 202*0Sstevel@tonic-gate 203*0Sstevel@tonic-gatesub filecheck { 204*0Sstevel@tonic-gate return _check_manifest(); 205*0Sstevel@tonic-gate} 206*0Sstevel@tonic-gate 207*0Sstevel@tonic-gate 208*0Sstevel@tonic-gate=item fullcheck 209*0Sstevel@tonic-gate 210*0Sstevel@tonic-gate my($missing, $extra) = fullcheck(); 211*0Sstevel@tonic-gate 212*0Sstevel@tonic-gatedoes both a manicheck() and a filecheck(), returning then as two array 213*0Sstevel@tonic-gaterefs. 214*0Sstevel@tonic-gate 215*0Sstevel@tonic-gate=cut 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gatesub fullcheck { 218*0Sstevel@tonic-gate return [_check_files()], [_check_manifest()]; 219*0Sstevel@tonic-gate} 220*0Sstevel@tonic-gate 221*0Sstevel@tonic-gate 222*0Sstevel@tonic-gate=item skipcheck 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate my @skipped = skipcheck(); 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gatelists all the files that are skipped due to your C<MANIFEST.SKIP> 227*0Sstevel@tonic-gatefile. 228*0Sstevel@tonic-gate 229*0Sstevel@tonic-gate=cut 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gatesub skipcheck { 232*0Sstevel@tonic-gate my($p) = @_; 233*0Sstevel@tonic-gate my $found = manifind(); 234*0Sstevel@tonic-gate my $matches = _maniskip(); 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate my @skipped = (); 237*0Sstevel@tonic-gate foreach my $file (_sort keys %$found){ 238*0Sstevel@tonic-gate if (&$matches($file)){ 239*0Sstevel@tonic-gate warn "Skipping $file\n"; 240*0Sstevel@tonic-gate push @skipped, $file; 241*0Sstevel@tonic-gate next; 242*0Sstevel@tonic-gate } 243*0Sstevel@tonic-gate } 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate return @skipped; 246*0Sstevel@tonic-gate} 247*0Sstevel@tonic-gate 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gatesub _check_files { 250*0Sstevel@tonic-gate my $p = shift; 251*0Sstevel@tonic-gate my $dosnames=(defined(&Dos::UseLFN) && Dos::UseLFN()==0); 252*0Sstevel@tonic-gate my $read = maniread() || {}; 253*0Sstevel@tonic-gate my $found = manifind($p); 254*0Sstevel@tonic-gate 255*0Sstevel@tonic-gate my(@missfile) = (); 256*0Sstevel@tonic-gate foreach my $file (_sort keys %$read){ 257*0Sstevel@tonic-gate warn "Debug: manicheck checking from $MANIFEST $file\n" if $Debug; 258*0Sstevel@tonic-gate if ($dosnames){ 259*0Sstevel@tonic-gate $file = lc $file; 260*0Sstevel@tonic-gate $file =~ s=(\.(\w|-)+)=substr ($1,0,4)=ge; 261*0Sstevel@tonic-gate $file =~ s=((\w|-)+)=substr ($1,0,8)=ge; 262*0Sstevel@tonic-gate } 263*0Sstevel@tonic-gate unless ( exists $found->{$file} ) { 264*0Sstevel@tonic-gate warn "No such file: $file\n" unless $Quiet; 265*0Sstevel@tonic-gate push @missfile, $file; 266*0Sstevel@tonic-gate } 267*0Sstevel@tonic-gate } 268*0Sstevel@tonic-gate 269*0Sstevel@tonic-gate return @missfile; 270*0Sstevel@tonic-gate} 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gatesub _check_manifest { 274*0Sstevel@tonic-gate my($p) = @_; 275*0Sstevel@tonic-gate my $read = maniread() || {}; 276*0Sstevel@tonic-gate my $found = manifind($p); 277*0Sstevel@tonic-gate my $skip = _maniskip(); 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate my @missentry = (); 280*0Sstevel@tonic-gate foreach my $file (_sort keys %$found){ 281*0Sstevel@tonic-gate next if $skip->($file); 282*0Sstevel@tonic-gate warn "Debug: manicheck checking from disk $file\n" if $Debug; 283*0Sstevel@tonic-gate unless ( exists $read->{$file} ) { 284*0Sstevel@tonic-gate my $canon = $Is_MacOS ? "\t" . _unmacify($file) : ''; 285*0Sstevel@tonic-gate warn "Not in $MANIFEST: $file$canon\n" unless $Quiet; 286*0Sstevel@tonic-gate push @missentry, $file; 287*0Sstevel@tonic-gate } 288*0Sstevel@tonic-gate } 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate return @missentry; 291*0Sstevel@tonic-gate} 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate 294*0Sstevel@tonic-gate=item maniread 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate my $manifest = maniread(); 297*0Sstevel@tonic-gate my $manifest = maniread($manifest_file); 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gatereads a named C<MANIFEST> file (defaults to C<MANIFEST> in the current 300*0Sstevel@tonic-gatedirectory) and returns a HASH reference with files being the keys and 301*0Sstevel@tonic-gatecomments being the values of the HASH. Blank lines and lines which 302*0Sstevel@tonic-gatestart with C<#> in the C<MANIFEST> file are discarded. 303*0Sstevel@tonic-gate 304*0Sstevel@tonic-gate=cut 305*0Sstevel@tonic-gate 306*0Sstevel@tonic-gatesub maniread { 307*0Sstevel@tonic-gate my ($mfile) = @_; 308*0Sstevel@tonic-gate $mfile ||= $MANIFEST; 309*0Sstevel@tonic-gate my $read = {}; 310*0Sstevel@tonic-gate local *M; 311*0Sstevel@tonic-gate unless (open M, $mfile){ 312*0Sstevel@tonic-gate warn "$mfile: $!"; 313*0Sstevel@tonic-gate return $read; 314*0Sstevel@tonic-gate } 315*0Sstevel@tonic-gate local $_; 316*0Sstevel@tonic-gate while (<M>){ 317*0Sstevel@tonic-gate chomp; 318*0Sstevel@tonic-gate next if /^\s*#/; 319*0Sstevel@tonic-gate 320*0Sstevel@tonic-gate my($file, $comment) = /^(\S+)\s*(.*)/; 321*0Sstevel@tonic-gate next unless $file; 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gate if ($Is_MacOS) { 324*0Sstevel@tonic-gate $file = _macify($file); 325*0Sstevel@tonic-gate $file =~ s/\\([0-3][0-7][0-7])/sprintf("%c", oct($1))/ge; 326*0Sstevel@tonic-gate } 327*0Sstevel@tonic-gate elsif ($Is_VMS) { 328*0Sstevel@tonic-gate require File::Basename; 329*0Sstevel@tonic-gate my($base,$dir) = File::Basename::fileparse($file); 330*0Sstevel@tonic-gate # Resolve illegal file specifications in the same way as tar 331*0Sstevel@tonic-gate $dir =~ tr/./_/; 332*0Sstevel@tonic-gate my(@pieces) = split(/\./,$base); 333*0Sstevel@tonic-gate if (@pieces > 2) { $base = shift(@pieces) . '.' . join('_',@pieces); } 334*0Sstevel@tonic-gate my $okfile = "$dir$base"; 335*0Sstevel@tonic-gate warn "Debug: Illegal name $file changed to $okfile\n" if $Debug; 336*0Sstevel@tonic-gate $file = $okfile; 337*0Sstevel@tonic-gate $file = lc($file) unless $file =~ /^MANIFEST(\.SKIP)?$/; 338*0Sstevel@tonic-gate } 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate $read->{$file} = $comment; 341*0Sstevel@tonic-gate } 342*0Sstevel@tonic-gate close M; 343*0Sstevel@tonic-gate $read; 344*0Sstevel@tonic-gate} 345*0Sstevel@tonic-gate 346*0Sstevel@tonic-gate# returns an anonymous sub that decides if an argument matches 347*0Sstevel@tonic-gatesub _maniskip { 348*0Sstevel@tonic-gate my @skip ; 349*0Sstevel@tonic-gate my $mfile = "$MANIFEST.SKIP"; 350*0Sstevel@tonic-gate local(*M,$_); 351*0Sstevel@tonic-gate open M, $mfile or open M, $DEFAULT_MSKIP or return sub {0}; 352*0Sstevel@tonic-gate while (<M>){ 353*0Sstevel@tonic-gate chomp; 354*0Sstevel@tonic-gate next if /^#/; 355*0Sstevel@tonic-gate next if /^\s*$/; 356*0Sstevel@tonic-gate push @skip, _macify($_); 357*0Sstevel@tonic-gate } 358*0Sstevel@tonic-gate close M; 359*0Sstevel@tonic-gate my $opts = $Is_VMS ? '(?i)' : ''; 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate # Make sure each entry is isolated in its own parentheses, in case 362*0Sstevel@tonic-gate # any of them contain alternations 363*0Sstevel@tonic-gate my $regex = join '|', map "(?:$_)", @skip; 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate return sub { $_[0] =~ qr{$opts$regex} }; 366*0Sstevel@tonic-gate} 367*0Sstevel@tonic-gate 368*0Sstevel@tonic-gate=item manicopy 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate manicopy($src, $dest_dir); 371*0Sstevel@tonic-gate manicopy($src, $dest_dir, $how); 372*0Sstevel@tonic-gate 373*0Sstevel@tonic-gatecopies the files that are the keys in the HASH I<%$src> to the 374*0Sstevel@tonic-gate$dest_dir. The HASH reference $read is typically returned by the 375*0Sstevel@tonic-gatemaniread() function. This function is useful for producing a directory 376*0Sstevel@tonic-gatetree identical to the intended distribution tree. The third parameter 377*0Sstevel@tonic-gate$how can be used to specify a different methods of "copying". Valid 378*0Sstevel@tonic-gatevalues are C<cp>, which actually copies the files, C<ln> which creates 379*0Sstevel@tonic-gatehard links, and C<best> which mostly links the files but copies any 380*0Sstevel@tonic-gatesymbolic link to make a tree without any symbolic link. Best is the 381*0Sstevel@tonic-gatedefault. 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate=cut 384*0Sstevel@tonic-gate 385*0Sstevel@tonic-gatesub manicopy { 386*0Sstevel@tonic-gate my($read,$target,$how)=@_; 387*0Sstevel@tonic-gate croak "manicopy() called without target argument" unless defined $target; 388*0Sstevel@tonic-gate $how ||= 'cp'; 389*0Sstevel@tonic-gate require File::Path; 390*0Sstevel@tonic-gate require File::Basename; 391*0Sstevel@tonic-gate 392*0Sstevel@tonic-gate $target = VMS::Filespec::unixify($target) if $Is_VMS; 393*0Sstevel@tonic-gate File::Path::mkpath([ $target ],! $Quiet,$Is_VMS ? undef : 0755); 394*0Sstevel@tonic-gate foreach my $file (keys %$read){ 395*0Sstevel@tonic-gate if ($Is_MacOS) { 396*0Sstevel@tonic-gate if ($file =~ m!:!) { 397*0Sstevel@tonic-gate my $dir = _maccat($target, $file); 398*0Sstevel@tonic-gate $dir =~ s/[^:]+$//; 399*0Sstevel@tonic-gate File::Path::mkpath($dir,1,0755); 400*0Sstevel@tonic-gate } 401*0Sstevel@tonic-gate cp_if_diff($file, _maccat($target, $file), $how); 402*0Sstevel@tonic-gate } else { 403*0Sstevel@tonic-gate $file = VMS::Filespec::unixify($file) if $Is_VMS; 404*0Sstevel@tonic-gate if ($file =~ m!/!) { # Ilya, that hurts, I fear, or maybe not? 405*0Sstevel@tonic-gate my $dir = File::Basename::dirname($file); 406*0Sstevel@tonic-gate $dir = VMS::Filespec::unixify($dir) if $Is_VMS; 407*0Sstevel@tonic-gate File::Path::mkpath(["$target/$dir"],! $Quiet,$Is_VMS ? undef : 0755); 408*0Sstevel@tonic-gate } 409*0Sstevel@tonic-gate cp_if_diff($file, "$target/$file", $how); 410*0Sstevel@tonic-gate } 411*0Sstevel@tonic-gate } 412*0Sstevel@tonic-gate} 413*0Sstevel@tonic-gate 414*0Sstevel@tonic-gatesub cp_if_diff { 415*0Sstevel@tonic-gate my($from, $to, $how)=@_; 416*0Sstevel@tonic-gate -f $from or carp "$0: $from not found"; 417*0Sstevel@tonic-gate my($diff) = 0; 418*0Sstevel@tonic-gate local(*F,*T); 419*0Sstevel@tonic-gate open(F,"< $from\0") or die "Can't read $from: $!\n"; 420*0Sstevel@tonic-gate if (open(T,"< $to\0")) { 421*0Sstevel@tonic-gate local $_; 422*0Sstevel@tonic-gate while (<F>) { $diff++,last if $_ ne <T>; } 423*0Sstevel@tonic-gate $diff++ unless eof(T); 424*0Sstevel@tonic-gate close T; 425*0Sstevel@tonic-gate } 426*0Sstevel@tonic-gate else { $diff++; } 427*0Sstevel@tonic-gate close F; 428*0Sstevel@tonic-gate if ($diff) { 429*0Sstevel@tonic-gate if (-e $to) { 430*0Sstevel@tonic-gate unlink($to) or confess "unlink $to: $!"; 431*0Sstevel@tonic-gate } 432*0Sstevel@tonic-gate STRICT_SWITCH: { 433*0Sstevel@tonic-gate best($from,$to), last STRICT_SWITCH if $how eq 'best'; 434*0Sstevel@tonic-gate cp($from,$to), last STRICT_SWITCH if $how eq 'cp'; 435*0Sstevel@tonic-gate ln($from,$to), last STRICT_SWITCH if $how eq 'ln'; 436*0Sstevel@tonic-gate croak("ExtUtils::Manifest::cp_if_diff " . 437*0Sstevel@tonic-gate "called with illegal how argument [$how]. " . 438*0Sstevel@tonic-gate "Legal values are 'best', 'cp', and 'ln'."); 439*0Sstevel@tonic-gate } 440*0Sstevel@tonic-gate } 441*0Sstevel@tonic-gate} 442*0Sstevel@tonic-gate 443*0Sstevel@tonic-gatesub cp { 444*0Sstevel@tonic-gate my ($srcFile, $dstFile) = @_; 445*0Sstevel@tonic-gate my ($perm,$access,$mod) = (stat $srcFile)[2,8,9]; 446*0Sstevel@tonic-gate copy($srcFile,$dstFile); 447*0Sstevel@tonic-gate utime $access, $mod + ($Is_VMS ? 1 : 0), $dstFile; 448*0Sstevel@tonic-gate # chmod a+rX-w,go-w 449*0Sstevel@tonic-gate chmod( 0444 | ( $perm & 0111 ? 0111 : 0 ), $dstFile ) 450*0Sstevel@tonic-gate unless ($^O eq 'MacOS'); 451*0Sstevel@tonic-gate} 452*0Sstevel@tonic-gate 453*0Sstevel@tonic-gatesub ln { 454*0Sstevel@tonic-gate my ($srcFile, $dstFile) = @_; 455*0Sstevel@tonic-gate return &cp if $Is_VMS or ($^O eq 'MSWin32' and Win32::IsWin95()); 456*0Sstevel@tonic-gate link($srcFile, $dstFile); 457*0Sstevel@tonic-gate 458*0Sstevel@tonic-gate # chmod a+r,go-w+X (except "X" only applies to u=x) 459*0Sstevel@tonic-gate local($_) = $dstFile; 460*0Sstevel@tonic-gate my $mode= 0444 | (stat)[2] & 0700; 461*0Sstevel@tonic-gate if (! chmod( $mode | ( $mode & 0100 ? 0111 : 0 ), $_ )) { 462*0Sstevel@tonic-gate unlink $dstFile; 463*0Sstevel@tonic-gate return; 464*0Sstevel@tonic-gate } 465*0Sstevel@tonic-gate 1; 466*0Sstevel@tonic-gate} 467*0Sstevel@tonic-gate 468*0Sstevel@tonic-gateunless (defined $Config{d_link}) { 469*0Sstevel@tonic-gate # Really cool fix from Ilya :) 470*0Sstevel@tonic-gate local $SIG{__WARN__} = sub { 471*0Sstevel@tonic-gate warn @_ unless $_[0] =~ /^Subroutine .* redefined/; 472*0Sstevel@tonic-gate }; 473*0Sstevel@tonic-gate *ln = \&cp; 474*0Sstevel@tonic-gate} 475*0Sstevel@tonic-gate 476*0Sstevel@tonic-gate 477*0Sstevel@tonic-gate 478*0Sstevel@tonic-gate 479*0Sstevel@tonic-gatesub best { 480*0Sstevel@tonic-gate my ($srcFile, $dstFile) = @_; 481*0Sstevel@tonic-gate if (-l $srcFile) { 482*0Sstevel@tonic-gate cp($srcFile, $dstFile); 483*0Sstevel@tonic-gate } else { 484*0Sstevel@tonic-gate ln($srcFile, $dstFile) or cp($srcFile, $dstFile); 485*0Sstevel@tonic-gate } 486*0Sstevel@tonic-gate} 487*0Sstevel@tonic-gate 488*0Sstevel@tonic-gatesub _macify { 489*0Sstevel@tonic-gate my($file) = @_; 490*0Sstevel@tonic-gate 491*0Sstevel@tonic-gate return $file unless $Is_MacOS; 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate $file =~ s|^\./||; 494*0Sstevel@tonic-gate if ($file =~ m|/|) { 495*0Sstevel@tonic-gate $file =~ s|/+|:|g; 496*0Sstevel@tonic-gate $file = ":$file"; 497*0Sstevel@tonic-gate } 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate $file; 500*0Sstevel@tonic-gate} 501*0Sstevel@tonic-gate 502*0Sstevel@tonic-gatesub _maccat { 503*0Sstevel@tonic-gate my($f1, $f2) = @_; 504*0Sstevel@tonic-gate 505*0Sstevel@tonic-gate return "$f1/$f2" unless $Is_MacOS; 506*0Sstevel@tonic-gate 507*0Sstevel@tonic-gate $f1 .= ":$f2"; 508*0Sstevel@tonic-gate $f1 =~ s/([^:]:):/$1/g; 509*0Sstevel@tonic-gate return $f1; 510*0Sstevel@tonic-gate} 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gatesub _unmacify { 513*0Sstevel@tonic-gate my($file) = @_; 514*0Sstevel@tonic-gate 515*0Sstevel@tonic-gate return $file unless $Is_MacOS; 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate $file =~ s|^:||; 518*0Sstevel@tonic-gate $file =~ s|([/ \n])|sprintf("\\%03o", unpack("c", $1))|ge; 519*0Sstevel@tonic-gate $file =~ y|:|/|; 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate $file; 522*0Sstevel@tonic-gate} 523*0Sstevel@tonic-gate 524*0Sstevel@tonic-gate 525*0Sstevel@tonic-gate=item maniadd 526*0Sstevel@tonic-gate 527*0Sstevel@tonic-gate maniadd({ $file => $comment, ...}); 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gateAdds an entry to an existing F<MANIFEST> unless its already there. 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate$file will be normalized (ie. Unixified). B<UNIMPLEMENTED> 532*0Sstevel@tonic-gate 533*0Sstevel@tonic-gate=cut 534*0Sstevel@tonic-gate 535*0Sstevel@tonic-gatesub maniadd { 536*0Sstevel@tonic-gate my($additions) = shift; 537*0Sstevel@tonic-gate 538*0Sstevel@tonic-gate _normalize($additions); 539*0Sstevel@tonic-gate _fix_manifest($MANIFEST); 540*0Sstevel@tonic-gate 541*0Sstevel@tonic-gate my $manifest = maniread(); 542*0Sstevel@tonic-gate my @needed = grep { !exists $manifest->{$_} } keys %$additions; 543*0Sstevel@tonic-gate return 1 unless @needed; 544*0Sstevel@tonic-gate 545*0Sstevel@tonic-gate open(MANIFEST, ">>$MANIFEST") or 546*0Sstevel@tonic-gate die "maniadd() could not open $MANIFEST: $!"; 547*0Sstevel@tonic-gate 548*0Sstevel@tonic-gate foreach my $file (_sort @needed) { 549*0Sstevel@tonic-gate my $comment = $additions->{$file} || ''; 550*0Sstevel@tonic-gate printf MANIFEST "%-40s %s\n", $file, $comment; 551*0Sstevel@tonic-gate } 552*0Sstevel@tonic-gate close MANIFEST or die "Error closing $MANIFEST: $!"; 553*0Sstevel@tonic-gate 554*0Sstevel@tonic-gate return 1; 555*0Sstevel@tonic-gate} 556*0Sstevel@tonic-gate 557*0Sstevel@tonic-gate 558*0Sstevel@tonic-gate# Sometimes MANIFESTs are missing a trailing newline. Fix this. 559*0Sstevel@tonic-gatesub _fix_manifest { 560*0Sstevel@tonic-gate my $manifest_file = shift; 561*0Sstevel@tonic-gate 562*0Sstevel@tonic-gate open MANIFEST, $MANIFEST or die "Could not open $MANIFEST: $!"; 563*0Sstevel@tonic-gate 564*0Sstevel@tonic-gate # Yes, we should be using seek(), but I'd like to avoid loading POSIX 565*0Sstevel@tonic-gate # to get SEEK_* 566*0Sstevel@tonic-gate my @manifest = <MANIFEST>; 567*0Sstevel@tonic-gate close MANIFEST; 568*0Sstevel@tonic-gate 569*0Sstevel@tonic-gate unless( $manifest[-1] =~ /\n\z/ ) { 570*0Sstevel@tonic-gate open MANIFEST, ">>$MANIFEST" or die "Could not open $MANIFEST: $!"; 571*0Sstevel@tonic-gate print MANIFEST "\n"; 572*0Sstevel@tonic-gate close MANIFEST; 573*0Sstevel@tonic-gate } 574*0Sstevel@tonic-gate} 575*0Sstevel@tonic-gate 576*0Sstevel@tonic-gate 577*0Sstevel@tonic-gate# UNIMPLEMENTED 578*0Sstevel@tonic-gatesub _normalize { 579*0Sstevel@tonic-gate return; 580*0Sstevel@tonic-gate} 581*0Sstevel@tonic-gate 582*0Sstevel@tonic-gate 583*0Sstevel@tonic-gate=back 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate=head2 MANIFEST 586*0Sstevel@tonic-gate 587*0Sstevel@tonic-gateAnything between white space and an end of line within a C<MANIFEST> 588*0Sstevel@tonic-gatefile is considered to be a comment. Filenames and comments are 589*0Sstevel@tonic-gateseparated by one or more TAB characters in the output. 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gate 592*0Sstevel@tonic-gate=head2 MANIFEST.SKIP 593*0Sstevel@tonic-gate 594*0Sstevel@tonic-gateThe file MANIFEST.SKIP may contain regular expressions of files that 595*0Sstevel@tonic-gateshould be ignored by mkmanifest() and filecheck(). The regular 596*0Sstevel@tonic-gateexpressions should appear one on each line. Blank lines and lines 597*0Sstevel@tonic-gatewhich start with C<#> are skipped. Use C<\#> if you need a regular 598*0Sstevel@tonic-gateexpression to start with a sharp character. A typical example: 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate # Version control files and dirs. 601*0Sstevel@tonic-gate \bRCS\b 602*0Sstevel@tonic-gate \bCVS\b 603*0Sstevel@tonic-gate ,v$ 604*0Sstevel@tonic-gate \B\.svn\b 605*0Sstevel@tonic-gate 606*0Sstevel@tonic-gate # Makemaker generated files and dirs. 607*0Sstevel@tonic-gate ^MANIFEST\. 608*0Sstevel@tonic-gate ^Makefile$ 609*0Sstevel@tonic-gate ^blib/ 610*0Sstevel@tonic-gate ^MakeMaker-\d 611*0Sstevel@tonic-gate 612*0Sstevel@tonic-gate # Temp, old and emacs backup files. 613*0Sstevel@tonic-gate ~$ 614*0Sstevel@tonic-gate \.old$ 615*0Sstevel@tonic-gate ^#.*#$ 616*0Sstevel@tonic-gate ^\.# 617*0Sstevel@tonic-gate 618*0Sstevel@tonic-gateIf no MANIFEST.SKIP file is found, a default set of skips will be 619*0Sstevel@tonic-gateused, similar to the example above. If you want nothing skipped, 620*0Sstevel@tonic-gatesimply make an empty MANIFEST.SKIP file. 621*0Sstevel@tonic-gate 622*0Sstevel@tonic-gate 623*0Sstevel@tonic-gate=head2 EXPORT_OK 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gateC<&mkmanifest>, C<&manicheck>, C<&filecheck>, C<&fullcheck>, 626*0Sstevel@tonic-gateC<&maniread>, and C<&manicopy> are exportable. 627*0Sstevel@tonic-gate 628*0Sstevel@tonic-gate=head2 GLOBAL VARIABLES 629*0Sstevel@tonic-gate 630*0Sstevel@tonic-gateC<$ExtUtils::Manifest::MANIFEST> defaults to C<MANIFEST>. Changing it 631*0Sstevel@tonic-gateresults in both a different C<MANIFEST> and a different 632*0Sstevel@tonic-gateC<MANIFEST.SKIP> file. This is useful if you want to maintain 633*0Sstevel@tonic-gatedifferent distributions for different audiences (say a user version 634*0Sstevel@tonic-gateand a developer version including RCS). 635*0Sstevel@tonic-gate 636*0Sstevel@tonic-gateC<$ExtUtils::Manifest::Quiet> defaults to 0. If set to a true value, 637*0Sstevel@tonic-gateall functions act silently. 638*0Sstevel@tonic-gate 639*0Sstevel@tonic-gateC<$ExtUtils::Manifest::Debug> defaults to 0. If set to a true value, 640*0Sstevel@tonic-gateor if PERL_MM_MANIFEST_DEBUG is true, debugging output will be 641*0Sstevel@tonic-gateproduced. 642*0Sstevel@tonic-gate 643*0Sstevel@tonic-gate=head1 DIAGNOSTICS 644*0Sstevel@tonic-gate 645*0Sstevel@tonic-gateAll diagnostic output is sent to C<STDERR>. 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate=over 4 648*0Sstevel@tonic-gate 649*0Sstevel@tonic-gate=item C<Not in MANIFEST:> I<file> 650*0Sstevel@tonic-gate 651*0Sstevel@tonic-gateis reported if a file is found which is not in C<MANIFEST>. 652*0Sstevel@tonic-gate 653*0Sstevel@tonic-gate=item C<Skipping> I<file> 654*0Sstevel@tonic-gate 655*0Sstevel@tonic-gateis reported if a file is skipped due to an entry in C<MANIFEST.SKIP>. 656*0Sstevel@tonic-gate 657*0Sstevel@tonic-gate=item C<No such file:> I<file> 658*0Sstevel@tonic-gate 659*0Sstevel@tonic-gateis reported if a file mentioned in a C<MANIFEST> file does not 660*0Sstevel@tonic-gateexist. 661*0Sstevel@tonic-gate 662*0Sstevel@tonic-gate=item C<MANIFEST:> I<$!> 663*0Sstevel@tonic-gate 664*0Sstevel@tonic-gateis reported if C<MANIFEST> could not be opened. 665*0Sstevel@tonic-gate 666*0Sstevel@tonic-gate=item C<Added to MANIFEST:> I<file> 667*0Sstevel@tonic-gate 668*0Sstevel@tonic-gateis reported by mkmanifest() if $Verbose is set and a file is added 669*0Sstevel@tonic-gateto MANIFEST. $Verbose is set to 1 by default. 670*0Sstevel@tonic-gate 671*0Sstevel@tonic-gate=back 672*0Sstevel@tonic-gate 673*0Sstevel@tonic-gate=head1 ENVIRONMENT 674*0Sstevel@tonic-gate 675*0Sstevel@tonic-gate=over 4 676*0Sstevel@tonic-gate 677*0Sstevel@tonic-gate=item B<PERL_MM_MANIFEST_DEBUG> 678*0Sstevel@tonic-gate 679*0Sstevel@tonic-gateTurns on debugging 680*0Sstevel@tonic-gate 681*0Sstevel@tonic-gate=back 682*0Sstevel@tonic-gate 683*0Sstevel@tonic-gate=head1 SEE ALSO 684*0Sstevel@tonic-gate 685*0Sstevel@tonic-gateL<ExtUtils::MakeMaker> which has handy targets for most of the functionality. 686*0Sstevel@tonic-gate 687*0Sstevel@tonic-gate=head1 AUTHOR 688*0Sstevel@tonic-gate 689*0Sstevel@tonic-gateAndreas Koenig <F<andreas.koenig@anima.de>> 690*0Sstevel@tonic-gate 691*0Sstevel@tonic-gate=cut 692*0Sstevel@tonic-gate 693*0Sstevel@tonic-gate1; 694