5 use vars qw( $VERSION $VERBOSE @EXPORT_OK);
14 use File::Basename ();
24 # For things like vmsify()
25 require VMS::Filespec;
26 VMS::Filespec->import;
31 *{import} = \&Exporter::import;
40 my ($space) = $string =~ m/^(\s+)/;
41 $string =~ s/^$space//gm;
45 ########################################################################
51 $options{name} ||= 'Simple';
52 $options{dir} ||= Cwd::cwd();
59 my $self = bless( \%data, $package );
61 # So we can clean up later even if the caller chdir()s
62 $self->{dir} = File::Spec->rel2abs($self->{dir});
64 tie %{$self->{filedata}}, 'Tie::CPHash';
66 tie %{$self->{pending}{change}}, 'Tie::CPHash';
68 if ( -d $self->dirname ) {
69 warn "Warning: Removing existing directory '@{[$self->dirname]}'\n";
73 $self->_gen_default_filedata();
78 sub _gen_default_filedata {
81 # TODO maybe a public method like this (but with a better name?)
82 my $add_unless = sub {
84 my ($member, $data) = @_;
85 $self->add_file($member, $data) unless($self->{filedata}{$member});
88 $self->$add_unless('Build.PL', undent(<<" ---"));
92 my \$builder = Module::Build->new(
93 module_name => '$self->{name}',
97 \$builder->create_build_script();
100 my $module_filename =
101 join( '/', ('lib', split(/::/, $self->{name})) ) . '.pm';
103 unless ( $self->{xs} ) {
104 $self->$add_unless($module_filename, undent(<<" ---"));
105 package $self->{name};
107 use vars qw( \$VERSION );
118 $self->{name} - Perl extension for blah blah blah
122 Stub documentation for $self->{name}.
126 A. U. Thor, a.u.thor\@a.galaxy.far.far.away
131 $self->$add_unless('t/basic.t', undent(<<" ---"));
132 use Test::More tests => 1;
140 $self->$add_unless($module_filename, undent(<<" ---"));
141 package $self->{name};
148 \@ISA = qw(Exporter DynaLoader);
149 \@EXPORT_OK = qw( okay );
151 bootstrap $self->{name} \$VERSION;
159 $self->{name} - Perl extension for blah blah blah
163 Stub documentation for $self->{name}.
167 A. U. Thor, a.u.thor\@a.galaxy.far.far.away
173 join( '/', ('lib', split(/::/, $self->{name})) ) . '.xs';
174 $self->$add_unless($xs_filename, undent(<<" ---"));
179 MODULE = $self->{name} PACKAGE = $self->{name}
184 RETVAL = newSVpv( "ok", 0 );
203 # 5.6 is missing const char * in its typemap
204 $self->$add_unless('typemap', undent(<<" ---"));
208 $self->$add_unless('t/basic.t', undent(<<" ---"));
209 use Test::More tests => 2;
215 ok( $self->{name}::okay() eq 'ok' );
222 my $manifest = shift;
224 my $fh = IO::File->new( ">$manifest" ) or do {
226 die "Can't write '$manifest'\n";
229 my @files = ( 'MANIFEST', keys %{$self->{filedata}} );
230 my $data = join( "\n", sort @files ) . "\n";
234 $self->{filedata}{MANIFEST} = $data;
235 $self->{pending}{change}{MANIFEST} = 1;
238 sub name { shift()->{name} }
242 my $dist = join( '-', split( /::/, $self->{name} ) );
243 return File::Spec->catdir( $self->{dir}, $dist );
248 my $filename = shift;
249 return File::Spec->catfile( split( /\//, $filename ) );
256 my $dist_dirname = $self->dirname;
258 if ( $opts{clean} ) {
259 $self->clean() if -d $dist_dirname;
261 # TODO: This might leave dangling directories. Eg if the removed file
262 # is 'lib/Simple/Simon.pm', The directory 'lib/Simple' will be left
263 # even if there are no files left in it. However, clean() will remove it.
264 my @files = keys %{$self->{pending}{remove}};
265 foreach my $file ( @files ) {
266 my $real_filename = $self->_real_filename( $file );
267 my $fullname = File::Spec->catfile( $dist_dirname, $real_filename );
268 if ( -e $fullname ) {
269 1 while unlink( $fullname );
271 print "Unlinking pending file '$file'\n" if $VERBOSE;
272 delete( $self->{pending}{remove}{$file} );
276 foreach my $file ( keys( %{$self->{filedata}} ) ) {
277 my $real_filename = $self->_real_filename( $file );
278 my $fullname = File::Spec->catfile( $dist_dirname, $real_filename );
280 if ( ! -e $fullname ||
281 ( -e $fullname && $self->{pending}{change}{$file} ) ) {
283 print "Changed file '$file'.\n" if $VERBOSE;
285 my $dirname = File::Basename::dirname( $fullname );
286 unless ( -d $dirname ) {
287 File::Path::mkpath( $dirname ) or do {
289 die "Can't create '$dirname'\n";
293 if ( -e $fullname ) {
294 1 while unlink( $fullname );
297 my $fh = IO::File->new(">$fullname") or do {
299 die "Can't write '$fullname'\n";
301 print $fh $self->{filedata}{$file};
305 delete( $self->{pending}{change}{$file} );
308 my $manifest = File::Spec->catfile( $dist_dirname, 'MANIFEST' );
309 unless ( $self->{skip_manifest} ) {
310 if ( -e $manifest ) {
311 1 while unlink( $manifest );
313 $self->_gen_manifest( $manifest );
320 my $here = Cwd::abs_path();
321 my $there = File::Spec->rel2abs( $self->dirname() );
324 chdir( $there ) or die "Can't change directory to '$there'\n";
326 die "Distribution not found in '$there'\n";
330 tie %names, 'Tie::CPHash';
331 foreach my $file ( keys %{$self->{filedata}} ) {
332 my $filename = $self->_real_filename( $file );
333 my $dirname = File::Basename::dirname( $filename );
335 $names{$filename} = 0;
337 print "Splitting '$dirname'\n" if $VERBOSE;
338 my @dirs = File::Spec->splitdir( $dirname );
340 my $dir = ( scalar(@dirs) == 1
342 : File::Spec->catdir( @dirs ) );
344 print "Setting directory name '$dir' in \%names\n" if $VERBOSE;
351 File::Find::finddepth( sub {
352 my $name = File::Spec->canonpath( $File::Find::name );
356 $name = vmspath($name) if -d $name;
357 $name = File::Spec->rel2abs($name) if $name eq File::Spec->curdir();
360 if ( not exists $names{$name} ) {
361 print "Removing '$name'\n" if $VERBOSE;
362 File::Path::rmtree( $_ );
364 }, ($^O eq "VMS" ? './' : File::Spec->curdir) );
371 croak("invalid usage -- remove()") if(@_);
372 $self->chdir_original if($self->did_chdir);
373 File::Path::rmtree( $self->dirname );
374 # might as well check
375 croak("\nthis test should have used chdir_in()") unless(Cwd::getcwd);
380 die "Unimplemented.\n";
385 $self->change_file( @_ );
391 unless ( exists $self->{filedata}{$file} ) {
392 warn "Can't remove '$file': It does not exist.\n" if $VERBOSE;
394 delete( $self->{filedata}{$file} );
395 $self->{pending}{remove}{$file} = 1;
398 sub change_build_pl {
399 my ($self, $opts) = @_;
401 local $Data::Dumper::Terse = 1;
402 (my $args = Dumper($opts)) =~ s/^\s*\{|\}\s*$//g;
404 $self->change_file( 'Build.PL', undent(<<" ---") );
407 my \$b = Module::Build->new(
410 \$b->create_build_script();
418 $self->{filedata}{$file} = $data;
419 $self->{pending}{change}{$file} = 1;
425 $self->{original_dir} ||= Cwd::cwd; # only once
426 my $dir = $self->dirname;
427 chdir($dir) or die "Can't chdir to '$dir': $!";
429 ########################################################################
434 return exists($self->{original_dir});
436 ########################################################################
441 croak("never called chdir_in()") unless($self->{original_dir});
442 my $dir = $self->{original_dir};
443 chdir($dir) or die "Can't chdir to '$dir': $!";
445 ########################################################################
454 DistGen - Creates simple distributions for testing.
460 my $dist = DistGen->new(dir => $tmp);
462 $dist->add_file('t/some_test.t', $contents);
466 chdir($dist->dirname) or
467 die "Cannot chdir to '@{[$dist->dirname]}': $!";
471 chdir($cwd) or die "cannot return to $cwd";
480 Create a new object. Does not write its contents (see L</regen()>.)
482 my $tmp = MBTest->tmpdir;
483 my $dist = DistGen->new(
489 The parameters are as follows.
495 The name of the module this distribution represents. The default is
496 'Simple'. This should be a "Foo::Bar" (module) name, not a "Foo-Bar"
501 The (parent) directory in which to create the distribution directory.
502 The default is File::Spec->curdir. The distribution will be created
503 under this according to the "dist" form of C<name> (e.g. "Foo-Bar".)
507 If true, generates an XS based module.
511 =head2 Manipulating the Distribution
513 These methods immediately affect the filesystem.
517 Regenerate all missing or changed files.
519 $dist->regen(clean => 1);
521 If the optional C<clean> argument is given, it also removes any
522 extraneous files that do not belong to the distribution.
526 Change directory into the dist root.
530 =head2 chdir_original
532 Returns to whatever directory you were in before chdir_in() (regardless
535 $dist->chdir_original;
539 Removes any files that are not part of the distribution.
547 [Unimplemented] Returns the object to its initial state, or given a
548 $filename it returns that file to it's initial state if it is one of
552 $dist->revert($filename);
558 Removes the entire distribution directory.
562 Note that C<$filename> should always be specified with unix-style paths,
563 and are relative to the distribution root directory. Eg 'lib/Module.pm'
565 No filesystem action is performed until the distribution is regenerated.
569 Add a $filename containing $content to the distribution.
571 $dist->add_file( $filename, $content );
575 Removes C<$filename> from the distribution.
577 $dist->remove_file( $filename );
581 Changes the contents of $filename to $content. No action is performed
582 until the distribution is regenerated.
584 $dist->change_file( $filename, $content );
590 Returns the name of the distribution.
594 Returns the directory where the distribution is created.
596 $dist->dirname; # e.g. t/_tmp/Simple
602 Removes leading whitespace from a multi-line string according to the
603 amount of whitespace on the first line.
605 my $string = undent(" foo(\n bar => 'baz'\n )");
612 # vim:ts=2:sw=2:et:sta