#!/usr/bin/perl -w


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

our %Opt;

our $Id = q$Id: buildaperl 66 2003-03-09 07:48:03Z 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

Version may be '\@', '5.7.0\@', '5.7.0\@7100', '\@7100', etc.

}; #
}

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);
# This may die:
($ver,my $this,my $firstpatch,$lev)=$bap->translate($Opt{branch},$ver,$lev);
$diffdir = "$Opt{apcdir}/$this/diffs";
warn "Info: Target $ver\@$lev, firstpatch $firstpatch, dir $this, ".
    "branch $Opt{branch}\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}" : " --start $firstpatch";
}

my $verbose_switch = $Opt{verbose} ? "--verbose " : "";
$0 = "buildaperl: branch $Opt{branch} upto $lev";
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";
if ($Opt{branch} eq "perl" &&
    -e "patchlevel.h" &&
    $lev >= 18749
   ) {
  system($^X, "-x", "patchlevel.h", "patchaperlup: --upto $lev")==0 or die;
}

unless ($Opt{noconfigure}) {
  my $absprefix = File::Spec->file_name_is_absolute($Opt{prefix}) ? 
      $Opt{prefix} : File::Spec->catfile($pwd,$Opt{prefix});
  mkpath "$absprefix/$Opt{branch}";
  my $tempdir = tempdir( "pXXXXXX", DIR => "$absprefix/$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 "sh 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 "./perl 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/

Beware that you will need about a gigabyte of storage if you want to
rsync all of the archive. If you end up doing that, kindly send a note
to Gurusamy Sarathy <gsar@ActiveState.com> describing whether it is a
public or private mirror, so that the list of mirrors can be kept up
to date.

Your working 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 maintenance 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 recognize the fact from
seeing the associated directory name. It builds a perl only if this
directory does not yet exist, otherwise it will die.

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 if you use
apc2svn, maybe also files created by makepatch.

=head1 PREREQUISITES

Same prerequisites as mentioned in patchaperlup.

=head1 AUTHOR

Andreas Koenig <andk@cpan.org>

=cut

