The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w
# See copyright, etc in below POD section.
######################################################################

use Carp;
use Config;
use Getopt::Long;
use IO::File;
use File::Find;

# For testing, we're being called from our release script directory, so
use lib "../lib";
use lib "blib/lib";

use Module::LocalBuild;
use strict;
use vars qw ($Project $Debug $VERSION);

$VERSION = '1.013';

######################################################################

my $opt_clean;
my @Opt_Builds;
my $Opt_Destdir;
my $Opt_Locker;
my @Opt_MakeFlags;

#print join(' ',@ARGV),"\n";
Getopt::Long::config ("no_auto_abbrev");
if (!GetOptions (
		 "help"		=> \&usage,
		 "build=s"	=> sub { push @Opt_Builds, $_[1]; },
		 "destdir=s"	=> \$Opt_Destdir,
		 "clean!"	=> \$opt_clean,
		 "debug"	=> sub { $Debug=1; },
		 "locker=s"	=> \$Opt_Locker,
		 "makeflag=s"	=> sub { push @Opt_MakeFlags, $_[1]; },
		 "version"	=> sub { print "Version $VERSION\n"; exit(0); },
		 "<>"		=> sub { die "%Error: Unknown parameter: $_[0]\n"; },
		 )) {
    die "%Error: Bad usage, try 'mlbuilder --help'\n";
}

my $action;
if ($opt_clean) {
    $action = 1;
    clean();
}
if ($#Opt_Builds > -1) {
    $action = 1;
    all_build();
}
$action or die "%Error: Need --build or --clean action\n";

exit(0);

#----------------------------------------------------------------------

sub usage {
    eval { use Pod::Usage; };
    print "Version $VERSION\n";
    pod2usage(-verbose=>2, -exitval => 2);
    exit (1);
}

######################################################################

sub clean {
    my $dir = shift || '.';
    print "-"x20, " Clean\n";
    _chmod_dir($dir);
    foreach my $pkg (glob "$dir/*") {
	_clean_package($pkg);
    }
}

sub _clean_after_build {
    my $dir = shift;
    print "-"x20, " Clean\n";
    _chmod_dir($dir);
}

sub _chmod_dir {
    my $dir = shift;
    run_system ("chmod -Rf og=u $dir", 1);  # Fix permissions
}

sub _clean_package {
    my $pkg = shift;
    if (-r "$pkg/Makefile") {
	run_system ("cd $pkg && perl Makefile.PL && make clean", 1);
    }
    # Recursively remove Makefile.old's.  They might not just be directly under $pkg.
    find ({wanted =>
	       sub {
		   (my $basename = $File::Find::name) =~ s!.*/!!;
		   if ($basename eq 'Makefile.old'
		       || $basename eq 'MANIFEST.bak'
		       || $basename =~ /^\.objcache_/
		       ) {
		       unlink $File::Find::name;   # Ok if error
		   }
	       },
	   follow => 0,
	   no_chdir => 1,},
	  $pkg);
}

######################################################################

sub all_build {
    print "\n";
    print "-"x70,"\n";
    print "Something in the perltools directory has changed.\n";
    print "This will take a minute.\n\n";
    print "Building perl into $\n" if $Debug;

    my $built_file = "$Opt_Destdir/.built";

    # Only one build at a time
    my $pre_build_mtime = (stat($built_file))[9] || 0;

    my $lock;
    if ($Opt_Locker) {
	eval "use $Opt_Locker; 1;"
	    or die "%Error: mlbuilder can't load requested --locker module\n$@";
	$lock = Locker::Site->lock(lock=>"mlbuilder__$Project");
    }

    my $post_build_mtime = (stat($built_file))[9] || 0;
    if ($pre_build_mtime != $post_build_mtime) {
	# Some other process built it for us while locked, we can simply fall thru
	print " DONE Perltools Build: Another process beat us to it\n\n";
    } else {
	mkdir $Opt_Destdir, 0777;

	#### Preclean... in case we aborted a build under a different os
	# We can't check OS is the same, because 'make clean' doesn't
	# work across different perl versions
	foreach my $pkg (@Opt_Builds) {
	    _clean_package($pkg);
	}

	foreach my $pkg (@Opt_Builds) {
	    _build_package ($pkg);
	}

	run_system ("chmod -Rf a+w $Opt_Destdir", 1);  # Fix permissions, ok if error
	foreach my $pkg (@Opt_Builds) {
	    _install_package ($pkg, $Opt_Destdir)
	}

	_clean_after_build($Opt_Destdir);
	foreach my $pkg (@Opt_Builds) {
	    _clean_package($pkg);
	}

	run_system("touch $built_file");
	print "-"x20, " DONE Perltools Build\n\n";
    }

    $lock->unlock() if $lock;
}

sub _build_package {
    my $pkg = shift;
    my $arch = $Config{archname};

    (my $basepkg = $pkg) =~ s!.*/!!;
    print "-"x20, " Build $basepkg (for $arch )\n";

    if (!-r "$pkg/README") {
	IO::File->new(">$pkg/README")->close;  # Prevent README not in MANIFEST errors
    }
    run_system ("cd $pkg && $^X Makefile.PL"
		." && make ".(join ' ',@Opt_MakeFlags)." pure_all");
}

sub _install_package {
    my $pkg = shift;
    my $destdir = shift;

    (my $basepkg = $pkg) =~ s!.*/!!;
    print "-"x20, " Install $basepkg\n";

    run_system("/bin/cp -rp $pkg/blib $destdir");
}

sub run_system {
    my $command = shift;
    my $nofail = shift;
    print "\t$command\n";
    local $! = undef;
    system $command;
    my $status = $?; my $msgx = $!;
    ($status == 0 || $nofail) or croak "vbuild: %Error: Command Failed $status $msgx, stopped";
}

#######################################################################
1;
__END__

=pod

=head1 NAME

mlbuilder - Build Perl Modules in a local way

=head1 SYNOPSYS

  mlbuilder --clean

  or
    use Module::LocalBuilder;
    Module::LocalBuilder::need(...);

=head1 DESCRIPTION

Mlbuilder is called by Module::LocalBuild to execute Perl builds.  It
may also be used with the --clean option.

=head1 ARGUMENTS

=over 4

=item --build I<package_directory>

For use by Module::LocalBuild to execute Perl builds.  Build the specified
package.  This option may be specified multiple times.

=item --clean

All Perl modules under the current directory will be entered, and 'make
clean'ed.

=item --locker I<lock_module>

For use by Module::LocalBuild to execute Perl builds.  The module name for
lock services.

=item --makeflag I<flag>

When building, add specified flags to the make line.  May be specified
multiple times.

=item --version

Displays program version and exits.

=back

=head1 DISTRIBUTION

Copyright 2000-2010 by Wilson Snyder.  This program is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.

=head1 AUTHORS

Wilson Snyder <wsnyder@wsnyder.org>

=head1 SEE ALSO

L<Module::LocalBuild>

=cut

######################################################################