#!/usr/bin/env perl

use strict;

use File::Spec;

sub package_dir {};
sub check_tools {};
sub check_prereq {};
sub c_modules {};
sub perl_modules {};
sub module_build_flags {};
sub package_real_module {};
sub patches {};
sub no_test {};
sub pre_install {};
sub post_install {};
sub cfg_help {};
sub force_build {};
sub module_build_input {};
sub require_in_path {};
sub ignore_module_build_fail {};
sub version_from {};
sub on_fetch_from_CPAN {};

sub help { print <<EOF; }
Usage: $0 [options] [[-b] --prefix=<prefix>]

Options:
    -h|--help            print this help

    --tmp <path>         use given directory for building
    -t|--test            test only (do not install anything)
    -L|--only-listed     install only modules listed on the command-line
    -i|--ignore-versions don't check versions (allow installing older)
    -l|--list            list modules installed by this script
    -s|--skip-mt         skip 'make test' when compiling perl modules
    -f|--force           ignore all errors
    -C|--CPAN            search newer versions on CPAN
    -B|--bash-env        print environment setting for given install prefix in bash syntax
    -T|--tcsh-env        print environment setting for given install prefix in tcsh syntax
    -b|--install-base    use INSTALL_BASE instead of PREFIX for Makefile.PL (recommended)
    -U|--check-utils     only check that all utilities required for building modules
                         are installed

Prefix:

  --prefix=<prefix> Configure with given prefix or install-base
                    (see -b and man ExtUtils::MakeMaker for the difference).
                    Everything will be installed to apropriate
                    subdirectories under the given prefix.

Alternatively, you can specify different prefixes for Perl modules and C libraries:

  --c-prefix=<prefix>       for C libraries

  --perl-prefix=<prefix>    prefix/or install-base for Perl modules

EOF

our $QUIET=0;
our $LISTED=0;
our $BASE_DIR_FLAG="PREFIX";  # or INSTALL_BASE if option -b was used
our $FORCE=0;
our $PERL_PREFIX=undef;
our $C_PREFIX="/usr";
our $DO_MAKE_TEST=1;
our $NO_VERSION=0;
our $PRINT_ENV=undef;
our $ONLY_CHECK_UTILS=0;
our $FETCH_FROM_CPAN=0;
our $BUILD_TMP;
our $TEST_ONLY=0;

if (! -f './install.cfg') {
  my $dir = $0; $dir =~ s{/[^/]*$}{};
  die "The current directory doesn't contain install configuration file install.cfg\n".
    (($dir ne "" and $dir ne ".") ? "Hint: Try 'cd $dir' first.\n" : '');
}

unless (eval {
  no warnings qw(redefine);
  require './install.cfg'
}) {
  die "Failed to load install configuration file ./install.cfg\n$@";
}

my @listed;

while (@ARGV) {

  if ($ARGV[0] eq '-h' or $ARGV[0] eq '--help') {
    help();
    cfg_help();
    exit 0;
  } elsif ($ARGV[0]=~/^-t$|^--test$/) {
    shift @ARGV;
    $TEST_ONLY=1;
  } elsif ($ARGV[0]=~/^-l$|^--list$/) {
    print "C libraries:\n";
    foreach my $s (c_modules()) {
      print "  ",get_pkg_file($s),"\n";
    }
    print "Perl modules:\n";
    foreach my $s (perl_modules()) {
      print "  ",get_pkg_file($s),"\n";
    }
    exit 0;
  } elsif ($ARGV[0]=~/^--tmp/) {
    if ($ARGV[0]=~/^--tmp=(.+)$/) {
      $BUILD_TMP=$1;
    } else {
      shift @ARGV;
      $BUILD_TMP=$ARGV[0];
    }
    shift @ARGV;
  } elsif ($ARGV[0]=~/^--prefix/) {
    my $prefix;
    if ($ARGV[0]=~/^--prefix=(.*)$/) {
      $C_PREFIX=$1;
      $PERL_PREFIX=$1;
    } else {
      shift @ARGV;
      $C_PREFIX=$ARGV[0];
      $PERL_PREFIX=$ARGV[0];
    }
    shift @ARGV;
  } elsif ($ARGV[0]=~/^--c-prefix=(.*)$/) {
    $C_PREFIX=$1;
    shift @ARGV;
  } elsif ($ARGV[0]=~/^--perl-prefix=(.*)$/) {
    $PERL_PREFIX=$1;
    shift @ARGV;
  } elsif ($ARGV[0]=~/^-f$|^--force$/) {
    $FORCE=1;
    shift @ARGV;
  } elsif ($ARGV[0]=~/^-L$|^--only-listed$/) {
    $LISTED=1;
    shift @ARGV;
  } elsif ($ARGV[0]=~/^-i$|^--ignore-versions$/) {
    $NO_VERSION=1;
    shift @ARGV;
  } elsif ($ARGV[0]=~/^-b$|^--install-base$/) {
    $BASE_DIR_FLAG='INSTALL_BASE';
    shift @ARGV;
  } elsif ($ARGV[0]=~/^-B$|^--bash-env$/) {
    $PRINT_ENV='bash';
    shift @ARGV;
  } elsif ($ARGV[0]=~/^-B$|^--tcsh-env$/) {
    $PRINT_ENV='tcsh';
    shift @ARGV;
  } elsif ($ARGV[0]=~/^-s$|^--skip-mt$/) {
    $DO_MAKE_TEST=0;
    shift @ARGV;
  } elsif ($ARGV[0]=~/^-U$|^--check-utils$/) {
    $ONLY_CHECK_UTILS=1;
    shift @ARGV;
  } elsif ($ARGV[0]=~/^-q$|^--quiet$/) {
    $QUIET=1;
    shift @ARGV;
  } elsif ($ARGV[0]=~/^-C$|^--CPAN$/) {
    $FETCH_FROM_CPAN=1;
    shift @ARGV;
  } elsif ($LISTED and ($ARGV[0]!~/^-/)) {
    push @listed, shift(@ARGV);
  } else {
    warn "Unexpected argument: $ARGV[0]\n";
    help();
    exit 1;
  }
}

for ($C_PREFIX, $PERL_PREFIX, $BUILD_TMP) {
  next unless defined($_) and length($_);
  if (m/^~(\w+)?/) { # tilde with optional username
    s [^~(\w*)]
      [$1 ?
	 ((getpwnam($1))[7] || "~$1") :
	   (getpwuid($>))[7]
	  ]ex;
  }
}


$BUILD_TMP ||= "/tmp/$ENV{USER}";

if ((defined($PERL_PREFIX) and $PERL_PREFIX=~/\s|['"\$]/) or $C_PREFIX=~/\s|['"\$]/) {
  my $err = "Please avoid white-space and special characters in --prefix,\n--c-prefix, or --perl-prefix.\n".
    "Some of the installed modules may not be able to handle such prefix properly!\n";
  if ($FORCE) {
    warn "WARNING:\n".$err."Continuing because of --force\n\n";
  } else {
    die "ERROR:\n".$err."\n"."Use --force to override this check.\n";
  }
}


my @C_MODULES = c_modules();
my @PERL_MODULES = perl_modules();


my %listed = map { ( $_=>1, $_."-" => 1 ) } @listed;
if ($LISTED) {
  @C_MODULES    = grep $listed{$_}, @C_MODULES;
  @PERL_MODULES = grep $listed{$_}, @PERL_MODULES;
}

my %MODULE_BUILD_FLAGS = module_build_flags();
my %PACKAGE_REAL_MODULE = package_real_module();
my %IGNORE_TEST_FAILS = no_test();
my %PATCHES = patches();
my %MODULE_BUILD_INPUT = module_build_input();
my %IGNORE_MODULE_BUILD_FAIL = ignore_module_build_fail();
my %CHECK_PREREQ = check_prereq();
my %VERSION_FROM = version_from();
my %installed_version;
my %force_install = force_build();

my @INSTALLED_C;
my @SKIPPED_C;
my @ERROR_C;
my @INSTALLED_P;
my @SKIPPED_P;
my @ERROR_P;
my $PACKAGE_DIR=package_dir();

if ( defined($PERL_PREFIX) and ! -d "$PERL_PREFIX" ) {
  die "Prefix $PERL_PREFIX is not a directory\n";
  exit 1;
}
if ( ! -d "$C_PREFIX" ) {
  die "Prefix $C_PREFIX is not a directory\n";
  exit 1;
}

use Config;
my @perllibpaths;

sub uniq { my %a; grep { !($a{$_}++) } @_ }

if (defined($PERL_PREFIX)) {
  if ($BASE_DIR_FLAG eq 'INSTALL_BASE') {
    @perllibpaths=("${PERL_PREFIX}/lib/perl5");
  } else {
    my @l = ($Config{installprivlib}, $Config{installsitearch}, $Config{installsitelib});
    my $p = $Config{installprefix};
    my $p2 = $Config{siteprefix};
    my @l2 = @l;
    s/^\Q$p\E// for (@l);
    s/^\Q$p2\E// for (@l2);
    @perllibpaths=uniq(
		   "${PERL_PREFIX}/$Config{installstyle}/site_perl",
		   map("${PERL_PREFIX}/$_", @l, @l2)
		  );
  }
}

# Setup install paths
do {
  my %path=();
  @path{ split(/:/,$ENV{PATH}) }=();
  if (!exists($path{"$C_PREFIX/bin"})) {
    $ENV{PATH}="$C_PREFIX/bin:$ENV{PATH}";
  }
#  unless (grep { $_ eq $C_PREFIX } split /:/, $ENV{LD_LIBRARY_PATH}) {
  $ENV{LD_LIBRARY_PATH}="$C_PREFIX/lib:$ENV{LD_LIBRARY_PATH}";
#  }

  if (@perllibpaths) {
    my %inc; $inc{@INC}=();
    for my $lib (map { ($_.'/'.$Config{archname}, $_) } map { ($_.'/'.$Config{version}, $_) } reverse @perllibpaths) {
      unshift @INC, $lib unless exists $inc{$lib};
    }
    %inc=(); @inc{ split(/:/,$ENV{PERL5LIB}) }=();
    $ENV{PERL5LIB}=join(':',
			grep { defined and length } (
			  (grep !exists( $inc{$_} ), @perllibpaths),
			  $ENV{PERL5LIB}
			 ));
  }
};

if ($PRINT_ENV) {
  print_env($PRINT_ENV);
  exit;
} else {
  print defined($PERL_PREFIX) ?
    "Using prefix $PERL_PREFIX for Perl modules\n" : 
      "Using default Perl intall path ($Config{installsitelib}) for Perl modules\n";
  print "Using prefix $C_PREFIX for C libraries\n";
}


$ENV{LANG}='C';

check_tools();
if ($BASE_DIR_FLAG eq 'INSTALL_BASE') {
  unless (eval 'use ExtUtils::MakeMaker 6.31; 1') {
    if ($FETCH_FROM_CPAN) {
      warn "The flag --install-base requires ExtUtils::MakeMaker >= 6.31, which is not available\n".
	"Prepending ExtUtils-MakeMaker to the list of fetched Perl modules!\n";
      @PERL_MODULES = ('ExtUtils-MakeMaker-', grep { $_ ne 'ExtUtils-MakeMaker-' } @PERL_MODULES);
    } elsif (get_pkg_file( 'ExtUtils-MakeMaker-' )) {
      warn "The flag --install-base requires ExtUtils::MakeMaker >= 6.31, which is not available\n".
	"Prepending ExtUtils-MakeMaker to the list of installed Perl modules!";
      @PERL_MODULES = ('ExtUtils-MakeMaker-', grep { $_ ne 'ExtUtils-MakeMaker-' } @PERL_MODULES);
    } else {
      die "The flag --install-base requires ExtUtils::MakeMaker >= 6.31, which is not available!\n";
    }
  }
}

if ($FETCH_FROM_CPAN) {
  fetch_from_CPAN();
  on_fetch_from_CPAN();
  exit;
}


{
  my @paths = split /:/,$ENV{PATH};
  my @missing;
  TOOL:
  for my $tool (@Config{qw(make gzip cc)},
		qw(true tee patch),
		require_in_path()) {
    # try to find the tool in path, fail otherwise
    next TOOL if File::Spec->file_name_is_absolute($tool) and -x $tool;
    if (!File::Spec->file_name_is_absolute($tool)) {
      for my $path (@paths) {
	if (-x File::Spec->rel2abs($tool,$path)) {
	  next TOOL;
	}
      }
    }
    push @missing, $tool;
  }
  if (@missing) {
    die "The following tools are missing on your system. The installation cannot continue.\n"
      .join('',(map "\t$_\n",@missing))
      ."Please install the missing tools and try again!";
  }
}

exit 0 if $ONLY_CHECK_UTILS;

#======================================================================

print "Running pre-install routine...\n";
pre_install();
print "Done.\n";

my ($file,$ver,$version);
my $do_install=1;
my @to_install;
my %will_install;

foreach my $s (@C_MODULES) {

  my $short_name = $s; $short_name=~s/-\s*$//;
  print "Checking $short_name: ";
  $file=get_pkg_file($s);

  $ver=get_file_version($s,$file);
  print "$ver available, ";
  $version=get_c_module_version($s);
  my $nice_version = $version || 'none';
  print "$nice_version installed => ";
  if (compare_c_versions($ver,$version)<=0) {
    if ($NO_VERSION or check_force_install($s)) {
      print "Forced installation of older or same version!\n";
    } else {
      print "No need to install\n";
      push @SKIPPED_C, $file;
      $installed_version{$s}=[0,$version];
      next;
    }
  }
  print "will install $file\n";
  $installed_version{$s}=[1,$version];
  install_c_module($file,$s) unless $TEST_ONLY;
}

foreach my $s (@PERL_MODULES) {
  my $short_name = $s; $short_name=~s/-\s*$//;
  print "Checking $short_name: ";
  $file=get_pkg_file($s);

  $ver=get_file_version($s,$file);
  print "$ver available, ";
  $version=get_current_version($PACKAGE_REAL_MODULE{$s} || $s);
  my $nice_version = $version || 'none';
  print "$nice_version installed => ";
  $ver =~ s/_\d+$//;
  if (compare_pm_versions($ver,$version)<=0) {
    if ($NO_VERSION or check_force_install($s)) {
      print "Forced installation of older or same version!\n";
    } else {
      print "No need to install\n";
      $installed_version{$s}=[0,$ver];
      push @SKIPPED_P, $file;
      next;
    }
  }
  print "will install $file\n";
  $installed_version{$s}=[1,$version];
  install_perl_module($file,$s) unless $TEST_ONLY;
}

if ($do_install and !$FORCE) {
  do_install();
} else {
  fail("Installation aborted due to missing prerequisities (use -f to force)!");
}

print "Running post-install routine...\n";
post_install();
print "Done.\n";

  print <<"EOF";

============================================================

INSTALLATION FINISHED.

EOF

if (@INSTALLED_C) {
  print "The following C libraries have been successfully installed:\n\n";
  print wrap(join(", ",map {my $s=$_; $s=~s/-$//; $s } @INSTALLED_C),"  ")."\n\n";
}
if (@SKIPPED_C) {
  print "The following C libraries were up-to-date:\n\n";
  print wrap(join(", ",map {my $s=$_; $s=~s/-$//; $s } @SKIPPED_C),"  ")."\n\n";
}
if (@ERROR_C) {
  print "The following C libraries had errors:\n\n";
  print wrap(join(", ",map {my $s=$_; $s=~s/-$//; $s } @ERROR_C),"  ")."\n\n";
}

if (@INSTALLED_P) {
  print "The following Perl modules have been installed:\n\n";
  print wrap(join(", ",map {my $s=$_; $s=~s/-$//; $s=~s/-/::/g; $s } @INSTALLED_P),"  ")."\n\n";
}
if (@SKIPPED_P) {
  print "The following Perl modules were up-to-date:\n\n";
  print wrap(join(", ",map {my $s=$_; $s=~s/-$//; $s=~s/-/::/g; $s } @SKIPPED_P),"  ")."\n\n";
}
if (@ERROR_P) {
  print "The following Perl modules had errors:\n\n";
  print wrap(join(", ",map {my $s=$_; $s=~s/-$//; $s=~s/-/::/g; $s } @ERROR_P),"  ")."\n\n";
}

if (!$QUIET) {
  my ($msg1,$msg2);
  if (@C_MODULES and $C_PREFIX !~ m{^(?:/usr|/usr/local|/)$}) {
    $msg1 = "LD_LIBRARY_PATH for installed C libraries"
  }
  if (@PERL_MODULES and defined($PERL_PREFIX)) {
    $msg2 = "PERL5LIB for installed Perl modules"
  }
  if ($msg1 or $msg2) {
    print "------------------------------------------------------------\n";
    print wrap("NOTE: If you chose to install to non-standard paths, make sure to set your"
	  ." environment variables accordingly ("
	    .join(" and ", grep { defined } ($msg1,$msg2))
	      ."), e.g.:\n")
      ."\n### Setup for Bash (put in your ~/.bash_profile or ~/.bashrc)\n";
    print_env('bash');
    print
      "\n### Setup for C-shell (put in your ~/.cshrc)\n";
    print_env('tcsh');
    print "\n";
    print "------------------------------------------------------------\n";
  }
  print <<"EOF";
============================================================

EOF
}

if ($TEST_ONLY) {
  print "THIS WAS ONLY A DRY-RUN.\n";
  print "NO FILES WERE ACTUALLY INSTALLED.\n\n";
}


#}

exit 0;

#============================================================
# Auxiliary subroutines:
#============================================================

sub print_env {
  my ($shell)=@_;
  if (defined($PERL_PREFIX)) {
    my $libs = join ':',@perllibpaths;
    if ($shell eq 'bash') {
      print <<"EOF";
export PERL5LIB="$libs\${PERL5LIB:+:\$PERL5LIB}"
EOF
    } elsif ($shell eq 'tcsh') {
      print <<"EOF";
if (\$?PERL5LIB) then
  setenv PERL5LIB "$libs:\${PERL5LIB}"
else
  setenv PERL5LIB "$libs"
endif
EOF
    }
  }
  if (@C_MODULES and $C_PREFIX !~ m{^(?:/usr|/usr/local|/)$}) {
    if ($shell eq 'bash') {
      print <<"EOF"
export LD_LIBRARY_PATH="$C_PREFIX/lib:\${LD_LIBRARY_PATH}"
EOF
    } elsif ($shell eq 'tcsh') {
      print <<"EOF"
if (\$?LD_LIBRARY_PATH) then
  setenv LD_LIBRARY_PATH "$C_PREFIX/lib:\${LD_LIBRARY_PATH}"
else
  setenv LD_LIBRARY_PATH "$C_PREFIX/lib:"
endif
EOF
    }
  }
}


sub fetch_from_CPAN {
    require CPAN;
    import CPAN;
    my @modules = @PERL_MODULES;
    # my @modules = perl_modules();
    # if ($BASE_DIR_FLAG eq 'INSTALL_BASE') {
    #   unless (eval 'use ExtUtils::MakeMaker 6.31; 1') {
    # 	warn "The flag --install-base requires ExtUtils::MakeMaker >= 6.31, which is not available\n".
    # 	  "Prepending ExtUtils-MakeMaker to the list of fetched Perl modules!\n";
    # 	@modules = 'ExtUtils-MakeMaker-', grep { $_ ne 'ExtUtils-MakeMaker-' } @modules
    #   }
    # }
    if ($PACKAGE_DIR) {
      unless (-d $PACKAGE_DIR) {
	mkdir $PACKAGE_DIR || die "Cannot create target directory '$PACKAGE_DIR' for downloaded packages: $!";
      }
      chdir $PACKAGE_DIR;
    }
    foreach my $module (@modules) {
      my $s = $module;
      if (exists $VERSION_FROM{$s}) {
	$s=$VERSION_FROM{$s} ;
      } else {
	$s=~s/-$//;
	$s=~s/-/::/g;
      }
      print "=====================================\n";
      print "Looking up $s\n";
      my $cpan = CPAN::Shell->expandany($s);
      my $wget = $CPAN::Config->{wget};
      my $file = $cpan->cpan_file if ($cpan);
      if (defined($file) and length($file)) {
	my $base = $file; $base=~s{.*/}{};
	if (-f $base) {
	  print "File $base already exists, skipping.\n";
	  next;
	}
	my @mirror_list = (@{$CPAN::Config->{urllist}}, "http://www.perl.com/CPAN/");
	foreach (@mirror_list) {
	  my $url = $_."authors/id/".$file;
	  print "Fetching URL '$url'\n";
	  system($wget." '$url'")==0 && last;
	  print "failed: exit status $?\n";
	}
      } else {
	warn "WARNING: Didn't find module $s on CPAN!";
      }
    }
}

sub fail {
  my ($message)=@_;
  print "\n\n";
  print "============================================================\n\n";
  print STDERR "Error: $message\n\n";
  exit 1;
}

sub get_c_module_version {
  my $s=$_[0];
  $s=~s/-$//g;
  $s=~s/^lib//;
  $s="${s}-config";
  $s=`$s --version`;
  chomp $s;
  return $s;
}

sub get_pkg_file {
  my $s=$_[0];
  my $dir = $PACKAGE_DIR || '.';
  for ($dir,$s) {
    s/\\/\\\\/g;
    s/"/\\"/g;
    s/\$/\\\$/g;
  }
  $s=`cd $dir >/dev/null; ls "$s"[0-9]*.tar.gz |tail -1`;
  chomp $s;
  return $s;
}

sub get_file_version {
  my $s=$_[1];
  $s=~s/^$_[0]//;
  $s=~s/^v//;
  $s=~s/.tar.gz//;
  return $s;
}

sub get_current_version {
  my $s=$_[0];
  if (exists $VERSION_FROM{$s}) {
    $s=$VERSION_FROM{$s};
  } else {
    $s=~s!-$!!g;
    $s=~s!-!::!g;
  }
  if (eval { require CPAN; 1; }) {
    return inst_version($s);
  }
  my $version="";

  my $file=$s;
  $file=~s!::!/!g;
  eval {
    require "${file}.pm";
    $version=${"${s}"."::VERSION"};
  };
  return $version;
}

sub shell_quote {
  my $arg = shift;
  $arg =~ s/\'/'\\''/g;
  return "'".$arg."'";
}

sub will_install {
  my ($type,$name)=@_;
  $type = lc($type);
  return $will_install{"$type:$name"};
}
sub do_install {
  while (@to_install) {
    my $job = shift @to_install;
    my ($info,$sub,@args)=@$job;
    warn "==============================\n";
    warn "$info\n";
    $sub->(@args);
    warn "\n";
  }
}
sub do_check_prereq {
  my ($name)=@_;
  if (exists $CHECK_PREREQ{$name}) {
    unless ($CHECK_PREREQ{$name}->()) {
      my $nice_name =$name; $nice_name=~s/-$//;
      warn "Module $nice_name is missing some prerequisities!\n\n";
      $do_install = 0;
    }
  }
}
sub install_perl_module {
  my ($file,$name)=@_;
  $will_install{"perl:$name"}=1;
  do_check_prereq($name);
  push @to_install,['Installing Perl Module '.$name,\&do_install_perl_module,$file,$name];
}
sub install_c_module {
  my ($file,$name)=@_;
  $will_install{"c:$name"}=1;
  do_check_prereq($name);
  push @to_install,['Installing C library '.$name,\&do_install_c_module,$file,$name];
}

sub do_install_perl_module {
  my ($file,$module)=@_;

  use Cwd;
  my $PWD= cwd;
  my $package_dir=$PWD;
  $package_dir.='/'.$PACKAGE_DIR if $PACKAGE_DIR;

  my $dir=$file;
  $dir=~s/.tar.gz$//g;

  my $patches = $PATCHES{$module};
  my $run_patches = '';
  $patches = [$patches] if (!(ref($patches) eq 'ARRAY') and $patches ne '');
  if (ref($patches) eq 'ARRAY') {
    foreach (@$patches) {
      print "PATCH: $_\n";
      $run_patches .= "patch -p0 < '$PWD'/$_ &&";
    }
  }
  my $buildpl_flags= '--'.lc($BASE_DIR_FLAG).' '.shell_quote($PERL_PREFIX).' '.$MODULE_BUILD_FLAGS{$module};
  my $makepl_flags= $BASE_DIR_FLAG.'='.shell_quote($PERL_PREFIX).' '.$MODULE_BUILD_FLAGS{$module};
  my $build_test = ($DO_MAKE_TEST && $IGNORE_TEST_FAILS{$module}!=2) ? (($FORCE || $IGNORE_TEST_FAILS{$module}) ? "(./Build test || true) && " : "./Build test && ") : "";
  my $make_test = ($DO_MAKE_TEST && $IGNORE_TEST_FAILS{$module}!=2) ? (($FORCE || $IGNORE_TEST_FAILS{$module}) ? "($Config{make} test || true) && " : "make test && ") : "";

  my $input = $MODULE_BUILD_INPUT{$module};
  $input = ((defined($input) and length($input))
    ? ($input =~ '[|;]\s*$|[<]$' ? $input : $input.' | ')
    : '');

  my $command = <<"EOF";
    mkdir -p '$BUILD_TMP' && \\
    cd '$BUILD_TMP' && \\
    $Config{gzip} -d -c '$package_dir/$file' | tar xf - && \\
    cd '$dir' && \\
    $run_patches \\
    ${input}(if [ -f Build.PL ]; then \\
       $^X Build.PL $buildpl_flags && \\
       ./Build &&
       $build_test
       ./Build install
     else
       echo LD_LIBRARY_PATH is "\$LD_LIBRARY_PATH"
       echo PERL5LIB is "\$PERL5LIB"
       $^X Makefile.PL $makepl_flags && \\
       $Config{make} &&
       $make_test
       $Config{make} install
     fi) && cd .. && rm -rf '$dir'
EOF

  print "\nStarting: ".$command."\n";

  system $command;
  my $ok = ($? >> 8 == 0);
  if ($ok) {
    push @INSTALLED_P, $file;
  } else {
    push @ERROR_P, $file;
  }
  $ok || $IGNORE_MODULE_BUILD_FAIL{$module} || $FORCE || fail("Installation of the package $file failed.");
}

sub do_install_c_module {
  my ($file,$module)=@_;

  use Cwd;
  my $PWD= cwd;
  my $package_dir=$PWD;
  $package_dir.='/'.$PACKAGE_DIR if $PACKAGE_DIR;

  my $dir=$file;
  $dir=~s/.tar.gz$//g;
#hack:
  print "$ENV{PATH}\n$ENV{LD_LIBRARY_PATH}";
  system "(mkdir -p '$BUILD_TMP' && cd '$BUILD_TMP' && ".
    "gunzip -c \"$package_dir\"/$file | tar xf - && ".
    " cd $dir && ".
    " ./configure --prefix=".shell_quote($C_PREFIX)." $MODULE_BUILD_FLAGS{$module} && ".
    "  make && ".
    " make install && cd .. && rm -rf $dir)";
  my $ok = ($? >> 8 == 0);
  if ($ok) {
    push @INSTALLED_C, $file;
  } else {
    push @ERROR_C, $file;
  }
  $ok || $FORCE || fail("Installation of the package $file failed.");
}


sub compare_c_versions {
  my ($v1, $v2) = @_;
  my @v1=split /[._]/,$v1;
  my @v2=split /[._]/,$v2;
  while (@v1 or @v2) {
    $_ = (shift(@v1) <=> shift(@v2));
    return $_ if $_;
  }
  return 0;
}


#============================================================
# Copied from CPAN.pm
sub inst_file {
  my($name) = @_;
  my($dir,@packpath);
  @packpath = split /::/, $name;
  $packpath[-1] .= ".pm";
  foreach $dir (@INC) {
    my $pmfile = File::Spec->catfile($dir,@packpath);
    if (-f $pmfile){
      return $pmfile;
    }
  }
  return;
}
sub inst_version {
  my($module) = @_;
  require CPAN;
  my $parsefile = inst_file($module) or return;
  local($^W) = 0;
  my $have;
  $have = MM->parse_version($parsefile) || "undef";
  $have =~ s/^ | $//g;
  $have = CPAN::Version->readable($have);
  $have =~ s/\s*//g;
  return $have;
}
#============================================================


sub compare_pm_versions {
  my ($v1,$v2)=@_;
  if (eval { require CPAN; 1 }) {
    return CPAN::Version->vcmp($v1,$v2);
  } else {
    return vcmp($v1,$v2);
  }
}

sub check_force_install {
  my ($s)=@_;
  my $when = $force_install{$s};
  return 0 unless $when;
  return 1 if $when and !ref($when);
  return 1 if ref($when) eq 'ARRAY' and
      grep { exists($installed_version{$_})
	     and $installed_version{$_}[0] } @$when;
  if (ref($when) eq 'HASH') {
      foreach my $lib (keys %$when) {
	return 1 if
	  (ref($when->{$lib}) eq 'CODE' and
	   $when->{$lib}->($installed_version{$lib}[1]))
      }
  }
  return 0;
}

#======================================================================
# this code is copied from CPAN::Version
sub vcmp {
    my($l,$r) = @_;
    local($^W) = 0;
    return 0 if $l eq $r; # short circuit for quicker success
    for ($l,$r) {
        s/_//g;
    }
    for ($l,$r) {
        next unless tr/.// > 1 || /^v/;
        s/^v?/v/;
        1 while s/\.0+(\d)/.$1/; # remove leading zeroes per group
    }
    if ($l=~/^v/ <=> $r=~/^v/) {
        for ($l,$r) {
            next if /^v/;
            $_ = float2vv($_);
        }
    }
    my $lvstring = "v0";
    my $rvstring = "v0";
    if ($] >= 5.006
     && $l =~ /^v/
     && $r =~ /^v/) {
        $lvstring = vstring($l);
        $rvstring = vstring($r);
    }
    return (
            ($l ne "undef") <=> ($r ne "undef")
            ||
            $lvstring cmp $rvstring
            ||
            $l <=> $r
            ||
            $l cmp $r
    );
}
sub float2vv {
    my($n) = @_;
    my($rev) = int($n);
    $rev ||= 0;
    my($mantissa) = $n =~ /\.(\d{1,12})/; # limit to 12 digits to limit
                                          # architecture influence
    $mantissa ||= 0;
    $mantissa .= "0" while length($mantissa)%3;
    my $ret = "v" . $rev;
    while ($mantissa) {
        $mantissa =~ s/(\d{1,3})// or
            die "Panic: length>0 but not a digit? mantissa[$mantissa]";
        $ret .= ".".int($1);
    }
    # warn "n[$n]ret[$ret]";
    $ret =~ s/(\.0)+/.0/; # v1.0.0 => v1.0
    $ret;
}
sub vstring {
    my($n) = @_;
    $n =~ s/^v// or die "vstring() called with invalid arg [$n]";
    pack "U*", split /\./, $n;
}

sub wrap {
  my $text = shift;
  my $indent = shift || "";
  my @words = (split /(\s)/, $text);
  my $line=$indent;
  my $out="";
  while (@words) {
    my $word = shift @words;
    if ($word eq "\n") {
      $line=~s/\s+$//;
      $out.= $line.$word;
      $line="";
    } elsif (length($line)+length($word)<72) {
      $line.=$word;
    } else {
      $line=~s/\s+$//;
      $out.= $line."\n";
      $line=$indent;
      $line.=$word unless $word=~/\s/;
    }
  }
  if ($line ne "") {
    $out.= $line;
  }
  return $out;
}

