#!/usr/bin/perl -w


# line 4
use strict;
use Getopt::Long;
use Carp;
use File::Temp qw(tempdir);
use File::Path qw(mkpath rmtree);

our %Opt;

our $Id = q$Id: buildaperl 29 2003-02-16 23:12:15Z k $;

my $DEFAULT_CONFIG = "-Dinstallusrbinperl=n -Uversiononly -Doptimize=-g -des -Duse64bitint -Dusedevel";

GetOptions(
           \%Opt,
           "config=s",
           "apcdir=s",
           "branch=s",
           "h!",
           "noconfigure!",
           "notest!",
           "noinstall!",
           "prefix=s",
           "remo!",
           "start=s",
           "target=s",
           "verbose!",
           "version!",
          ) or die Usage();

if ($Opt{h}) {
  print Usage();
  exit;
}

if ($Opt{version}) {
  print $Id, "\n";
  exit;
}

@ARGV == 1 or die Usage();

sub Usage {
  qq{"Usage: $0 [options] version
     [--config=...]        # Configure options except --prefix; default:
        $DEFAULT_CONFIG
     [--apcdir=...]        # where to find APC; defaults to "APC"
     [--branch=...]        # where to install: perl, maint-5.6, etc.
     [--h]                 # this help message
     [--noconfigure!]      # Run without ./Configure
     [--notest]            # Run without make test
     [--noinstall]         # Run without ./installperl
     [--prefix=...]        # prefix of the inst directory; default ./installed-perls
     [--remo]              # remove source dir at the end
     [--start]             # start argument to patchaperlup
     [--target=...]        # e.g. miniperl
     [--verbose]           # noise

Examples:

     5.7.0\@7100 # take 5.7.0 and patch up to 7100, build it
     5.7.0\@     # take 5.7.0 and use all not applied patches
                 # in its "diffs/" direcory and build it

}; #
}

our @SYSTEMTIMES;
sub mysystem ($);

$Opt{branch} ||= "perl";

$Opt{apcdir} ||= "APC";

use Cwd;
my $pwd = cwd;
$Opt{prefix} ||= "$pwd/installed-perls";

$Opt{config} ||= $DEFAULT_CONFIG;

my($arg) = shift;

unless ($arg && $arg =~ /\@/) {
  die "$0: argument must contain an \@";
}

my($ver,$lev) = $arg =~ /^([^\@]*)@(\d*)$/;
die "ver not defined" unless defined $ver;
die "lev not defined" unless defined $lev;

use Perl::Repository::APC;
my $apc = Perl::Repository::APC->new($Opt{apcdir});

# determine simultaneously $ver and $lev
my $diffdir;
use Perl::Repository::APC::BAP;
my $bap = Perl::Repository::APC::BAP->new($apc);
($ver,my $this,$lev)=$bap->translate($Opt{branch},$ver,$lev); # may die
$diffdir = "$Opt{apcdir}/$this/diffs";
warn "DEBUG: target interpreted as $ver\@$lev with dir[$this]\n";
my $upto_arg = " --upto=$lev";
my($branch_letter) = substr($Opt{branch},0,1);
my $target_dir = "perl-$branch_letter-$ver\@$lev";
die "Directory $target_dir exists but would be needed for building. Giving up "
    if -e $target_dir;

my($src,$bdir,$startarg);

if ($ver eq "0") {
  $src = "";
  $bdir = "emptydir";
  mkdir $bdir or die "Could not create $bdir: $!";
  $startarg = " --start 0 ";
} else {
  my $tarball = $apc->tarball($ver) or die "Could not determine tarball for $ver";
  $src = "$Opt{apcdir}/$ver/$tarball";
  die "src[$src] not found" unless -f $src;
  open my $TAR, "tar -tzf $src |" or die;
  $bdir = <$TAR>;
  chomp $bdir;
  $bdir =~ s|^\./||;
  $bdir =~ s|/.*$||;
  close $TAR;
  die "Cannot untar $src because $bdir exists" if -d $bdir;
  mysystem "tar -xzf $src";
  $startarg = $Opt{start} ? " --start $Opt{start}" : "";
}

my $verbose_switch = $Opt{verbose} ? "--verbose " : "";
mysystem "patchaperlup $verbose_switch --branch='$Opt{branch}' --perldir $bdir --diffdir $diffdir$upto_arg$startarg";
rename $bdir, $target_dir or die "Could not rename to $target_dir";
system "chmod -R u+w $target_dir";
chdir $target_dir or die "Could not chdir to $target_dir";

unless ($Opt{noconfigure}) {
  mkpath "$Opt{prefix}/$Opt{branch}";
  my $tempdir = tempdir( "pXXXXXX", DIR => "$Opt{prefix}/$Opt{branch}");

  # too many versions come without a wince/perl.ico:
  if (open my $fh, "MANIFEST") {
    my @manifest =  <$fh>;
    if (grep { /^wince\/perl\.ico\s/ } @manifest
        and not -f "wince/perl.ico") {
      close $fh;
      chmod 0644, "MANIFEST" or die "Could not chmod 0644 MANIFEST: $!";
      open $fh, ">", "MANIFEST" or die "Could not open >MANIFEST: $!";
      print $fh grep { ! /^wince\/perl\.ico\s/ } @manifest;
      close $fh;
    }
  }

  # here we do not need $target_dir, because branch is visible in directory
  mysystem "./Configure -Dprefix=$tempdir/perl-$ver\@$lev $Opt{config}";
  my $target = "";
  if ($Opt{target}) {
    $target = " $Opt{target}";
  }
  mysystem "make$target";
  mysystem "make test" unless $Opt{notest};
  mysystem "./installperl" unless $Opt{noinstall};
}

if ($Opt{remo}){
  chdir $pwd;
  my $rmtree = $target_dir;
  warn "Removing $rmtree\n";
  rmtree $rmtree;
}

sub mysystem ($) {
  my $system = shift;
  warn "Running $system\n";
  my $start = time;
  system($system)==0 or Carp::confess("system[$system] failed");
  push @SYSTEMTIMES, $system, time-$start;
  for (my $i = 0; $i < @SYSTEMTIMES; $i+=2){
    printf "%3d secs for[%s]\n", @SYSTEMTIMES[$i+1, $i];
  }
}

__END__

=head1 NAME

buildaperl - Build an arbitrary perl version from APC

=head1 SYNOPSIS

 buildaperl 5.7.0@7100
 buildaperl 5.8.0@
 buildaperl @
 buildaperl --h

=head1 DESCRIPTION

This script builds the sources for any perl version between 5.004 and
bleadperl.

The --h option displays all available options.

The argument consists of PERL_VERSION@PATCHNUMBER. The @ is mandatory.

If PERL_VERSION is missing, the script picks the most recent version
in the branch. If the PATCHNUMBER is missing, all available patches
for a given base will be applied. There is one important restriction:
the script can not iterate over more than one APC directory when
patching. This means you cannot build 5.6.0@18000. If you want @18000,
just specify @18000 as the argument. If the --branch argument and the
versio@patch argument do not fit together, buildaperl dies.

The most convenient setup to run this script is to start it in a
directory that contains a single subdirectory: APC. APC should be a
full or partial mirror (***partial mirror is untested***) of I< All
Perl Changes >. APC is located at

  rsync://ftp.linux.activestate.com/all-of-the-APC-for-mirrors-only/

Please contact activestate before mirroring the archive and ask for
permission. And be aware that a full mirror neeeds several hundred MB
and bandwidth.

The current directory should be empty (except for APC) because it is
also used to actually build the desired version. Buildaperl does
rename these directories to

   perl-X-PERL_VERSION@PATCHNUMBER (e.g. perl-p-5.7.2@15915)
        \ \            \
         \ \            `-> e.g. 15915
          \ `-> e.g. 5.7.2
           `-> either "p" for trunk or "m" for a maitainance branch

and lets these directories lying around (unless the
--remo switch is used). This is not a bug, it's a feature: if
buildaperl tries to build a perl that has already been built, it will
die if this directory is still lying around.

Other files and directories will also be created by default, namely
the directory to install all created perls into (unless the
--noinstall option is given), C< installed-perls >, and as a temporary
file the output of patchaperlup in C< patchaperlup.$$.out >.

=head1 AUTHOR

Andreas Koenig <andk@cpan.org>

=cut

