Fellow monks,
I've been working with build systems and different enviroments as of lately. Essentially what I'm trying to do is create a build system that will automatically fetch all of the required perl modules and dependancies from cpan and install them automatically.
Consider the following build script:
#!/usr/bin/env perl
# Build.PL
use strict;
use warnings;
use lib 'boot/lib';
use Module::Build;
use File::Spec();
our %provides = (
'LWP' => 'libwww-perl',
);
my $build_pkg = 'Module::Build';
my $build = $build_pkg->new (
module_name => 'World::Builder',
license => 'perl',
requires => {
'Class::Accessor' => '==0.22',
'LWP' => '==5.803',
},
recursive_test_files => 1,
create_readme => 1,
test_files => qw/t/,
PL_files => {
'config/rc.PL' => 'rc',
},
add_to_cleanup => [ '*.tmp' , 'rc' ],
);
$build->notes('no_db' => $build->args('no_db') );
our $install_base = $build->{properties}{install_base};
our $base_dir = $build->{properties}{base_dir};
our $build_dir = "/tmp/wb_build_$$";
my $plib = $ENV{PERL5LIB};
$ENV{PERL5LIB} = "$ENV{PWD}/lib:$install_base/lib/site_perl:$ENV{PWD}/
+boot/lib:$plib";
mkdir $build_dir;
check_install_base($build);
check_perl($build);
check_prereq_failures($build);
checked_system("rm -rf $build_dir");
$build->create_build_script;
exit;
sub check_prereq_failures {
my($build) = @_;
return unless $build->prereq_failures();
$ENV{PATH} = "$install_base/bin:$ENV{PATH}";
if(my $failures = $build->prereq_failures()->{requires}) {
my @prereqs = sort keys %{$failures};
foreach my $prereq (@prereqs) {
my $prereq_ver = $failures->{$prereq}{need};
$prereq_ver =~ s/^[^\d]*//; # remove any '==',
+ '>=' etc.
print STDERR "Installing $prereq_ver\n";
eval {
# check %provides for override
_install_prereq($provides{$prereq} ||
+$prereq, $prereq_ver);
};
}
print STDERR "restarting after installation of prerequ
+isites\n";
sleep 1;
exec $0, @ARGV;
}
}
sub _install_prereq {
my($module, $version) = @_;
$module =~ s/::/-/g;
my $orig_dir = $ENV{PWD};
$ENV{SABLOTLIBPATH} = "$base_dir/app/lib";
$ENV{SABLOTINCPATH} = "$base_dir/app/include";
my $perl = $build->find_perl_interpreter;
checked_system("tar xzf $base_dir/vendor/cpan/$module-$version.tar
+.gz -C $build_dir");
chdir "$build_dir/$module-$version";
checked_system("yes '' | $perl Makefile.PL");
checked_system("make");
checked_system("make install");
chdir $orig_dir;
}
# we want to make sure an install_base was specified
# defaulting to './app'. dirty hack job
sub check_install_base {
my($build) = @_;
unless($build->{properties}{install_base}) {
my $install_base = $build->{properties}{base_dir} . '/
+app';
$install_base = File::Spec->rel2abs(File::Spec->canonp
+ath($install_base));
$install_base =~ s{/[^/]+/\.\./}{/}; # remove '..'s
print STDERR "restarting with default install_base '$i
+nstall_base'\n";
exec $0, @ARGV, "--install_base=$install_base";
}
}
# will check that we're running the correct installation of perl, if n
+ot it will
# build it and restart with the correct perl
sub check_perl {
my($build) = @_;
my $orig_dir = $ENV{PWD};
my $prospective_perl = File::Spec->canonpath( File::Spec->rel2
+abs($build->install_destination('bin')) ) . "/perl";
# are we running the correct perl?
if($build->find_perl_interpreter ne $prospective_perl) {
if(! -x $prospective_perl) {
checked_system("tar xzf $base_dir/vendor/perl*
+.tar.gz -C $build_dir");
chdir glob("$build_dir/perl*");
checked_system("./Configure -de -Dusethreads -
+Dprefix=$install_base -Aundef:installusrbinperl");
checked_system("make -j2");
checked_system("make install");
chdir $orig_dir;
}
print STDERR "restarting in terms of correct Perl\n";
exec $prospective_perl, $0, @ARGV;
}
}
sub checked_system {
my($cmd) = @_;
if(my $err = system($cmd)) {
print "command \"$cmd\" failed: $err: $!\n";
die $err;
}
}
1;
What I'm doing is trying to create an application with its own perl build and all of the modules that it requires. This way I can control versions and such and really keep things tight and clean. It currently has a vendor directory with the perl sources and a sub directory called cpan with the cpan modules used.
This feels kinda messy. I've come here before asking for this sort of thing and I'll ask again. What can I do to simplify this process and clean up this build script. Some things I'd like to do are limiting the output and such of the system() calls as well as create a summary/statistics bit at the end of the build for errors and such. Does anyone have any advise on where I can take something like this?