$Is_MacOS $Is_VMS
$Debug $Verbose $Quiet $MANIFEST $DEFAULT_MSKIP);
-$VERSION = '1.51_01';
+$VERSION = '1.54';
@ISA=('Exporter');
@EXPORT_OK = qw(mkmanifest
manicheck filecheck fullcheck skipcheck
manifind maniread manicopy maniadd
+ maniskip
);
$Is_MacOS = $^O eq 'MacOS';
mkmanifest();
Writes all files in and below the current directory to your F<MANIFEST>.
-It works similar to
+It works similar to the result of the Unix command
find . > MANIFEST
All files that match any regular expression in a file F<MANIFEST.SKIP>
(if it exists) are ignored.
-Any existing F<MANIFEST> file will be saved as F<MANIFEST.bak>. Lines
-from the old F<MANIFEST> file is preserved, including any comments
-that are found in the existing F<MANIFEST> file in the new one.
+Any existing F<MANIFEST> file will be saved as F<MANIFEST.bak>.
=cut
my $bakbase = $MANIFEST;
$bakbase =~ s/\./_/g if $Is_VMS; # avoid double dots
rename $MANIFEST, "$bakbase.bak" unless $manimiss;
- open M, ">$MANIFEST" or die "Could not open $MANIFEST: $!";
- my $skip = _maniskip();
+ open M, "> $MANIFEST" or die "Could not open $MANIFEST: $!";
+ my $skip = maniskip();
my $found = manifind();
my($key,$val,$file,%all);
%all = (%$found, %$read);
my $tabs = (5 - (length($file)+1)/8);
$tabs = 1 if $tabs < 1;
$tabs = 0 unless $text;
+ if ($file =~ /\s/) {
+ $file =~ s/([\\'])/\\$1/g;
+ $file = "'$file'";
+ }
print M $file, "\t" x $tabs, $text, "\n";
}
close M;
sub skipcheck {
my($p) = @_;
my $found = manifind();
- my $matches = _maniskip();
+ my $matches = maniskip();
my @skipped = ();
foreach my $file (_sort keys %$found){
my($p) = @_;
my $read = maniread() || {};
my $found = manifind($p);
- my $skip = _maniskip();
+ my $skip = maniskip();
my @missentry = ();
foreach my $file (_sort keys %$found){
$mfile ||= $MANIFEST;
my $read = {};
local *M;
- unless (open M, $mfile){
+ unless (open M, "< $mfile"){
warn "Problem opening $mfile: $!";
return $read;
}
chomp;
next if /^\s*#/;
- my($file, $comment) = /^(\S+)\s*(.*)/;
+ my($file, $comment);
+
+ # filename may contain spaces if enclosed in ''
+ # (in which case, \\ and \' are escapes)
+ if (($file, $comment) = /^'(\\[\\']|.+)+'\s*(.*)/) {
+ $file =~ s/\\([\\'])/$1/g;
+ }
+ else {
+ ($file, $comment) = /^(\S+)\s*(.*)/;
+ }
next unless $file;
if ($Is_MacOS) {
$read;
}
+=item maniskip
+
+ my $skipchk = maniskip();
+ my $skipchk = maniskip($manifest_skip_file);
+
+ if ($skipchk->($file)) { .. }
+
+reads a named C<MANIFEST.SKIP> file (defaults to C<MANIFEST.SKIP> in
+the current directory) and returns a CODE reference that tests whether
+a given filename should be skipped.
+
+=cut
+
# returns an anonymous sub that decides if an argument matches
-sub _maniskip {
+sub maniskip {
my @skip ;
- my $mfile = "$MANIFEST.SKIP";
+ my $mfile = shift || "$MANIFEST.SKIP";
_check_mskip_directives($mfile) if -f $mfile;
local(*M, $_);
- open M, $mfile or open M, $DEFAULT_MSKIP or return sub {0};
+ open M, "< $mfile" or open M, "< $DEFAULT_MSKIP" or return sub {0};
while (<M>){
chomp;
s/\r//;
next if /^#/;
next if /^\s*$/;
+ s/^'//;
+ s/'$//;
push @skip, _macify($_);
}
close M;
local (*M, $_);
my @lines = ();
my $flag = 0;
- unless (open M, $mfile) {
+ unless (open M, "< $mfile") {
warn "Problem opening $mfile: $!";
return;
}
$bakbase =~ s/\./_/g if $Is_VMS; # avoid double dots
rename $mfile, "$bakbase.bak";
warn "Debug: Saving original $mfile as $bakbase.bak\n" if $Debug;
- unless (open M, ">$mfile") {
+ unless (open M, "> $mfile") {
warn "Problem opening $mfile: $!";
return;
}
return;
}
local (*M, $_);
- unless (open M, $mskip) {
+ unless (open M, "< $mskip") {
warn "Problem opening $mskip: $!";
return;
}
sub cp_if_diff {
my($from, $to, $how)=@_;
- -f $from or carp "$0: $from not found";
+ if (! -f $from) {
+ carp "$from not found";
+ return;
+ }
my($diff) = 0;
local(*F,*T);
open(F,"< $from\0") or die "Can't read $from: $!\n";
foreach my $file (_sort @needed) {
my $comment = $additions->{$file} || '';
+ if ($file =~ /\s/) {
+ $file =~ s/([\\'])/\\$1/g;
+ $file = "'$file'";
+ }
printf MANIFEST "%-40s %s\n", $file, $comment;
}
close MANIFEST or die "Error closing $MANIFEST: $!";
Anything between white space and an end of line within a C<MANIFEST>
file is considered to be a comment. Any line beginning with # is also
-a comment.
+a comment. Beginning with ExtUtils::Manifest 1.52, a filename may
+contain whitespace characters if it is enclosed in single quotes; single
+quotes or backslashes in that filename must be backslash-escaped.
# this a comment
some/file
some/other/file comment about some/file
+ 'some/third file' comment
=head2 MANIFEST.SKIP
use strict;
-use Test::More tests => 66;
+use Test::More tests => 94;
use Cwd;
use File::Spec;
my ($file, $data) = @_;
$data ||= 'foo';
1 while unlink $file; # or else we'll get multiple versions on VMS
- open( T, '>'.$file) or return;
+ open( T, '> '.$file) or return;
print T $data;
++$Files{$file};
close T;
BEGIN {
use_ok( 'ExtUtils::Manifest',
qw( mkmanifest manicheck filecheck fullcheck
- maniread manicopy skipcheck maniadd) );
+ maniread manicopy skipcheck maniadd maniskip) );
}
my $cwd = Cwd::getcwd();
eval { (undef, $warn) = catch_warning( sub {
manicopy( $files, 'copy', 'cp' ) })
};
-like( $@, qr/^Can't read none: /, 'croaked about none' );
# a newline comes through, so get rid of it
chomp($warn);
-
-# the copy should have given one warning and one error
+# the copy should have given a warning
+like($warn, qr/^none not found/, 'carped about none' );
+($res, $warn) = catch_warning( \&skipcheck );
like($warn, qr/^Skipping MANIFEST.SKIP/i, 'warned about MANIFEST.SKIP' );
# tell ExtUtils::Manifest to use a different file
is( $files->{yarrow}, 'hock',' with comment' );
is( $files->{foobar}, '', ' preserved old entries' );
+my %funky_files;
+# test including a filename with a space
+SKIP: {
+ add_file( 'foo bar' => "space" )
+ or skip "couldn't create spaced test file", 2;
+ local $ExtUtils::Manifest::MANIFEST = "albatross";
+ maniadd({ 'foo bar' => "contains space"});
+ is( maniread()->{'foo bar'}, "contains space",
+ 'spaced manifest filename' );
+ add_file( 'albatross.bak', '' );
+ ($res, $warn) = catch_warning( \&mkmanifest );
+ like( $warn, qr/\A(Added to.*\n)+\z/m,
+ 'no warnings about funky filename' );
+ $funky_files{'space'} = 'foo bar';
+}
+
+# test including a filename with a space and a quote
+SKIP: {
+ add_file( 'foo\' baz\'quux' => "quote" )
+ or skip "couldn't create quoted test file", 1;
+ local $ExtUtils::Manifest::MANIFEST = "albatross";
+ maniadd({ 'foo\' baz\'quux' => "contains quote"});
+ is( maniread()->{'foo\' baz\'quux'}, "contains quote",
+ 'quoted manifest filename' );
+ $funky_files{'space_quote'} = 'foo\' baz\'quux';
+}
+
+# test including a filename with a space and a backslash
+SKIP: {
+ add_file( 'foo bar\\baz' => "backslash" )
+ or skip "couldn't create backslash test file", 1;
+ local $ExtUtils::Manifest::MANIFEST = "albatross";
+ maniadd({ 'foo bar\\baz' => "contains backslash"});
+ is( maniread()->{'foo bar\\baz'}, "contains backslash",
+ 'backslashed manifest filename' );
+ $funky_files{'space_backslash'} = 'foo bar\\baz';
+}
+
+# test including a filename with a space, quote, and a backslash
+SKIP: {
+ add_file( 'foo bar\\baz\'quux' => "backslash/quote" )
+ or skip "couldn't create backslash/quote test file", 1;
+ local $ExtUtils::Manifest::MANIFEST = "albatross";
+ maniadd({ 'foo bar\\baz\'quux' => "backslash and quote"});
+ is( maniread()->{'foo bar\\baz\'quux'}, "backslash and quote",
+ 'backslashed and quoted manifest filename' );
+ $funky_files{'space_quote_backslash'} = 'foo bar\\baz\'quux';
+}
+
+my @funky_keys = qw(space space_quote space_backslash space_quote_backslash);
# test including an external manifest.skip file in MANIFEST.SKIP
{
maniadd({ foo => undef , albatross => undef,
'mymanifest.skip' => undef, 'mydefault.skip' => undef});
+ for (@funky_keys) {
+ maniadd( {$funky_files{$_} => $_} ) if defined $funky_files{$_};
+ }
+
add_file('mymanifest.skip' => "^foo\n");
add_file('mydefault.skip' => "^my\n");
- $ExtUtils::Manifest::DEFAULT_MSKIP =
+ local $ExtUtils::Manifest::DEFAULT_MSKIP =
File::Spec->catfile($cwd, qw(mantest mydefault.skip));
my $skip = File::Spec->catfile($cwd, qw(mantest mymanifest.skip));
add_file('MANIFEST.SKIP' =>
like( $warn, qr/Skipping \b$_\b/,
"Skipping $_" );
}
+ for my $funky_key (@funky_keys) {
+ SKIP: {
+ my $funky_file = $funky_files{$funky_key};
+ skip "'$funky_key' not created", 1 unless $funky_file;
+ like( $warn, qr/Skipping \b\Q$funky_file\E\b/,
+ "Skipping $funky_file");
+ }
+ }
($res, $warn) = catch_warning( \&mkmanifest );
for (qw(albatross foo foobar mymanifest.skip mydefault.skip)) {
like( $warn, qr/Removed from MANIFEST: \b$_\b/,
"Removed $_ from MANIFEST" );
}
+ for my $funky_key (@funky_keys) {
+ SKIP: {
+ my $funky_file = $funky_files{$funky_key};
+ skip "'$funky_key' not created", 1 unless $funky_file;
+ like( $warn, qr/Removed from MANIFEST: \b\Q$funky_file\E\b/,
+ "Removed $funky_file from MANIFEST");
+ }
+ }
my $files = maniread;
ok( ! exists $files->{albatross}, 'albatross excluded via MANIFEST.SKIP' );
ok( exists $files->{yarrow}, 'yarrow included in MANIFEST' );
'mymanifest.skip excluded via mydefault.skip' );
ok( ! exists $files->{'mydefault.skip'},
'mydefault.skip excluded via mydefault.skip' );
+
+ # test exclusion of funky files
+ for my $funky_key (@funky_keys) {
+ SKIP: {
+ my $funky_file = $funky_files{$funky_key};
+ skip "'$funky_key' not created", 1 unless $funky_file;
+ ok( ! exists $files->{$funky_file},
+ "'$funky_file' excluded via mymanifest.skip" );
+ }
+ }
+
+ # tests for maniskip
+ my $skipchk = maniskip();
+ is ( $skipchk->('albatross'), 1,
+ 'albatross excluded via MANIFEST.SKIP' );
+ is( $skipchk->('yarrow'), '',
+ 'yarrow included in MANIFEST' );
+ is( $skipchk->('bar'), '',
+ 'bar included in MANIFEST' );
+ $skipchk = maniskip('mymanifest.skip');
+ is( $skipchk->('foobar'), 1,
+ 'foobar excluded via mymanifest.skip' );
+ is( $skipchk->('foo'), 1,
+ 'foo excluded via mymanifest.skip' );
+ is( $skipchk->('mymanifest.skip'), '',
+ 'mymanifest.skip included via mydefault.skip' );
+ is( $skipchk->('mydefault.skip'), '',
+ 'mydefault.skip included via mydefault.skip' );
+ $skipchk = maniskip('mydefault.skip');
+ is( $skipchk->('foobar'), '',
+ 'foobar included via mydefault.skip' );
+ is( $skipchk->('foo'), '',
+ 'foo included via mydefault.skip' );
+ is( $skipchk->('mymanifest.skip'), 1,
+ 'mymanifest.skip excluded via mydefault.skip' );
+ is( $skipchk->('mydefault.skip'), 1,
+ 'mydefault.skip excluded via mydefault.skip' );
+
my $extsep = $Is_VMS ? '_' : '.';
$Files{"$_.bak"}++ for ('MANIFEST', "MANIFEST${extsep}SKIP");
}