App-Bootstrap-Perl
view release on metacpan or search on metacpan
bin/bootstrap-perl view on Meta::CPAN
#! /usr/bin/perl
# -*- mode: cperl -*-
# PODNAME: bootstrap-perl
# ABSTRACT: Bootstrap Perl inclusive CPAN from git
$|++; # autoflush
use 5.006;
use strict;
use warnings;
use Getopt::Long ":config", "no_ignore_case", "bundling";
use Cwd "getcwd";
use Sys::Hostname;
my $timestamp = ~~localtime();
my $HOME = $ENV{HOME} || '/opt';
my $hostname = hostname();
our $build_path = "$HOME/.bootstrapperl/bootstrap-perl-build";
my $build_path_perl = "$build_path/perl";
my $logfile = "$build_path/bootstrap-perl.log";
my $logcommand = "$build_path/command.log";
my $giturl = "git://github.com/Perl/perl5.git";
my $cpucount = `cat /proc/cpuinfo | grep bogomips | wc -l`; chomp $cpucount;
my $threadcount = $cpucount + 1;
# getopt
my $dry;
my $prefix;
my $prefixbase = "$HOME/.bootstrapperl/$hostname";
my $version = "blead";
our $VERSION;
my $installdeps;
my $sourcetgz = "/var/tmp/perl.tar.gz";
my $blead = 0;
my $usethreads = 1;
my $bit64 = 1;
my $taintsupport = 1;
my $silentnotaint = 0;
my $help = 0;
my $test = 0;
my $cpan = 1;
my $cleancpansources = 0;
my $forcecpancfg = 0;
my $forcebuildperl = 0;
my $forcemoduleinstall = 0;
my $perlformance = 0;
my $perlformance_local = 0;
my $perlformance_report = 0;
my $jobs = $threadcount;
my @mirrors;
my @modules;
my @runscripts = ();
my @runargs = ();
my @confargs = ();
my @exesuffixes = ();
my $OLDSTDOUT;
my $OLDSTDERR;
my $LOGFILE;
my $COMMAND;
my $USER;
use constant {
NO_REINSTALL => 0,
REINSTALL => 1,
NO_FORCE => 0,
FORCE => 1,
};
sub caller_line {
my $i = 0;
my ($rc, $rc_prev, $func);
while (1) {
my @v = caller($i);
defined($v[0]) or last;
$rc_prev = $rc;
($rc, $func) = @v[2..3];
$i++;
};
$func ne "(eval)"? $rc: $rc_prev;
}
sub setup_user {
open($USER, ">&", \*STDOUT) || die;
}
sub setup_log {
open($OLDSTDOUT, ">&", \*STDOUT) || die;
open($OLDSTDERR, ">&", \*STDERR) || die;
open($LOGFILE, ">>" , $logfile) || die;
open($COMMAND, ">>", $logcommand) || die;
}
# Execute a command via system(). Output goes to log file.
sub print_and_system {
my ($cmd) = @_;
my $exitcode;
my $line = caller_line();
my @cmd = split / +/, $cmd;
my $bin = $cmd[0];
my $is_cpan = $bin eq bin_cpan();
open(STDOUT, ">>&", $LOGFILE) || die;
open(STDERR, ">>&", $LOGFILE) || die;
print $COMMAND $cmd, "\n";
print $LOGFILE $cmd, "\n";
$exitcode = system ($cmd);
open(STDOUT, ">&", $OLDSTDOUT) || die;
open(STDERR, ">&", $OLDSTDERR) || die;
$exitcode == 0 || warn "$0($line): $cmd ($exitcode>>".($exitcode>>8).")";
}
# Execute a command via system(). Output goes to normal stdout/stderr.
sub print_and_system_out {
my ($cmd) = @_;
my $line = caller_line();
print $COMMAND $cmd, "\n";
print $LOGFILE $cmd, "\n";
print $USER $cmd, "\n";
system ($cmd) == 0 || warn "$0($line): $cmd ($?>>".($?>>8).")";
}
# Execute a command via qx(). Output goes to log file.
sub print_and_qx {
my ($cmd) = @_;
my $line = caller_line();
my $exitcode;
my $out;
print $COMMAND $cmd, "\n";
print $LOGFILE $cmd, "\n";
$out = qx($cmd 2>&1);
$exitcode = $?;
print $LOGFILE $out, "\n";
$exitcode == 0 || warn "$0($line): $cmd ($exitcode>>".($exitcode>>8).")";
$out;
}
# Execute a command via qx(). Output goes to normal stdout/stderr.
sub print_and_qx_chomp {
my $out = print_and_qx @_;
chomp $out;
$out;
}
sub _perl_base_name {
my ($perl_revision, $perl_version, $perl_subversion, $usethreads, $bit64, $taintsupport, $silentnotaint, $gitdescribe, $gitchangeset, $exe_suffixes, $blead) = @_;
return join("-",
($blead ? "blead" : "$perl_revision.$perl_version"),
($usethreads ? "" : "no" )."thread",
($bit64 ? "" : "no" )."64bit",
($taintsupport ? "" : (($silentnotaint ? "silent" : "")."no"))."taint",
@$exe_suffixes,
);
}
sub _perl_exe_name {
my ($perl_revision, $perl_version, $perl_subversion, $usethreads, $bit64, $taintsupport, $silentnotaint, $gitdescribe, $gitchangeset, $exe_suffixes, $blead) = @_;
return join("-",
"perl",
_perl_base_name(@_),
);
}
sub _perl_pathprefix {
my ($perl_revision, $perl_version, $perl_subversion, $usethreads, $bit64, $taintsupport, $silentnotaint, $gitdescribe, $gitchangeset, $exe_suffixes, $blead) = @_;
return join("-",
$prefixbase."/perl",
_perl_base_name(@_),
$gitdescribe,
);
}
# ========== setup user log ==========
setup_user();
# ========== getopt ==========
my $ok = GetOptions (
"prefix=s" => \$prefix,
"prefixbase=s" => \$prefixbase,
"version|commit|c=s" => \$version,
"installdeps=s" => \$installdeps,
"sourcetgz=s" => \$sourcetgz,
"blead!" => \$blead,
"usethreads!" => \$usethreads,
"use64bit!" => \$bit64,
"taintsupport!" => \$taintsupport,
"silentnotaint!" => \$silentnotaint,
"help|h" => \$help,
"jobs|j=i" => \$jobs,
"test|t" => \$test,
"cpan!" => \$cpan,
"cleancpansources!" => \$cleancpansources,
"forcecpancfg!" => \$forcecpancfg,
"forcebuildperl!" => \$forcebuildperl,
"forcemoduleinstall!" => \$forcemoduleinstall,
'perlformance' => \$perlformance,
'perlformance-local' => \$perlformance_local,
'perlformance-report' => \$perlformance_report,
'dry|n' => \$dry,
'mirror|m=s@' => \@mirrors,
'module|M=s@' => \@modules,
'run|r=s@' => \@runscripts,
'runargs=s@' => \@runargs,
'confargs=s@' => \@confargs,
'exesuffixes=s@' => \@exesuffixes,
'giturl|g=s' => \$giturl,
);
if ($help) {
exec("perldoc", "bootstrap-perl") or do {
print $USER "\nPlease see 'perldoc bootstrap-perl' for help.\n";
exit 0;
}
}
my $DISTROPREFSREPO = "git://github.com/renormalist/cpanpm-distroprefs.git";
my $DISTROPREFSSRCBASE = "$build_path";
my $DISTROPREFSSRC1 = "$DISTROPREFSSRCBASE/cpanpm-distroprefs/cpanpm/distroprefs";
my $DISTROPREFSSRC2 = "$DISTROPREFSSRCBASE/cpanpm-distroprefs/renormalist/distroprefs";
my $DISTROPREFSDIR = $prefixbase."/cpan/prefs";
my $SOURCESDIR = $prefixbase."/cpan/sources";
my $CPANBUILDDIR = $prefixbase."/cpan/build";
# defaults
my @default_runscripts = ();
my @default_confargs = ();
my @default_runargs = ();
my @default_modules = ();
my @default_mirrors = (qw( http://search.cpan.org/CPAN/ ));
return if defined($::{"no_run"});
print $USER "\n";
print $USER "============================================================\n";
print $USER "Bootstrap Perl - $timestamp\n";
print $USER "============================================================\n";
# giturl "." means clone from local dir
if ($giturl eq '.') {
$giturl = getcwd;
my $firstcommit = qx!git log --oneline 8d063cd8450e59ea1c611a2f4f5a21059a2804f1 2> /dev/null!;
if ($firstcommit !~ /a "replacement" for awk and sed/) {
print $USER "Local git repo does not look like it is Perl's.\n";
exit 1;
}
}
# version "." is replaced with current repo's HEAD
bin/bootstrap-perl view on Meta::CPAN
print_and_system_out "cd $build_path && tar xzf $sourcetgz";
} else {
print_and_system_out "cd $build_path && git clone $giturl perl";
}
}
chdir "$build_path_perl"; # important, I don't remember for what below
print_and_system "cd $build_path_perl && rm -f $build_path_perl/.git/index.lock";
print_and_system "cd $build_path_perl && git reset --hard";
print_and_system "cd $build_path_perl && git clean -dxf";
print_and_system "cd $build_path_perl && git checkout blead";
print_and_system "cd $build_path_perl && git stash";
print_and_system "cd $build_path_perl && git pull";
print_and_system "cd $build_path_perl && git stash";
print_and_system "cd $build_path_perl && git checkout $version";
print_and_system "cd $build_path_perl && git pull";
# ========= version =========
my $gitdescribe;
$gitdescribe = print_and_qx_chomp qq!cd $build_path_perl && git describe --all!;
$gitdescribe = print_and_qx_chomp qq!cd $build_path_perl && git describe! if ($gitdescribe =~ m,/, && $gitdescribe !~ m,tags/,);
$gitdescribe =~ s|^tags/||g;
my $gitchangeset = print_and_qx_chomp qq!cd $build_path_perl && git rev-parse HEAD!;
my ($perl_revision, $perl_version, $perl_subversion) = $gitdescribe =~ m/^\D*(\d+)[._](\d+)(?:[._](\d+))?.*$/g;
$perl_version = sprintf("%02d", $perl_version);
$perl_subversion ||= 0;
$VERSION = eval("v$perl_revision.$perl_version.$perl_subversion");
# ========= cherry-picks =========
# cherry pick several bugfixes in 5.8/5.9 that break building
if (v5.8.0 le $VERSION && $VERSION le v5.9.4) {
print $LOGFILE "*** cherry-pick building fixes for older perl versions\n";
# FIX: <command-line>
print_and_system qq!cd $build_path_perl && git cherry-pick -n cf14425e2bd169ce935f76d359f3253b51e441a0!;
# FIX: rebuild / gcc -g
print_and_system qq!cd $build_path_perl && git cherry-pick -n a3d1be69db4c082c1b9cef14f51a6e7fe4a9dac9!;
# FIX: makedepend.SH
print_and_system qq!cd $build_path_perl && git cherry-pick -n 96a8704c85b1e14bb3d63195b0b720f2fd5b8182!;
# FIX: create missing link (5.8.8)
print_and_system q'sudo ln -sf `find /usr/src -name page.h | grep arch/x86/include/asm/page.h | tail -1` /usr/include/asm/' if ! -e "/usr/include/asm/page.h";
}
if ($VERSION lt v5.12.4) {
print $LOGFILE "*** cherry-pick: fix search libs in configure\n";
print_and_system qq!cd $build_path_perl && git cherry-pick -n 40f026236b9959b7ad3260fedc6c66cd30bb7abc!;
print_and_system qq!cd $build_path_perl && git cherry-pick -n bcab1245a0693be445ce018352f6fbe4abc26e88!;
}
# ========== prepare paths ==========
unless (defined $perl_revision && defined $perl_version)
{
no warnings 'uninitialized';
print $USER "# Bummer! Unrecognized version schema ($gitdescribe).\n";
print $USER "# ($perl_revision, $perl_version, $perl_subversion)\n";
exit 1;
}
my $codespeed_executable = _perl_exe_name ($perl_revision, $perl_version, $perl_subversion, $usethreads, $bit64, $taintsupport, $silentnotaint, $gitdescribe, $gitchangeset, \@EXESUFFIXES, $blead);
my $PREFIX = $prefix || _perl_pathprefix($perl_revision, $perl_version, $perl_subversion, $usethreads, $bit64, $taintsupport, $silentnotaint, $gitdescribe, $gitchangeset, \@EXESUFFIXES, $blead);
my $USETHREADS = $usethreads ? "-Dusethreads" : "";
my $BIT64 = $bit64 ? "-Duse64bitall" : "";
my $TAINTSUPPORT = $taintsupport ? "" : ($silentnotaint ? "-Accflags=-DSILENT_NO_TAINT_SUPPORT" : "-Accflags=-DNO_TAINT_SUPPORT");
print $USER "\n";
print $USER "============================================================\n\n";
print $USER " Bootstrap Perl\n";
print $USER " --------------\n\n";
print $USER " version: $version\n\n";
print $USER " git-describe: $gitdescribe\n\n";
print $USER " git-changeset: $gitchangeset\n\n";
print $USER " codespeed name: $codespeed_executable\n\n";
print $USER " PREFIX: $PREFIX\n\n";
print $USER " configureargs: ".join("\n ", @confargs)."\n\n";
print $USER " CPAN mirrors: ".join("\n ", @CPANMIRRORS)."\n\n";
print $USER " modules: ".join("\n ", @MODULES)."\n\n";
print $USER " scripts: ".join("\n ", map { $_ ." ".join(" ", @RUNARGS) } @RUNSCRIPTS)."\n\n";
print $USER "============================================================\n\n";
my @in_blead = grep { / blead$/ }
map { chomp ; $_ }
print_and_qx qq!git branch --contains $gitchangeset!;
if ($blead && !@in_blead) {
print $USER "# Bummer!\n";
print $USER "# Use '--blead' only with commits in blead branch.\n";
exit 1;
}
$ENV{PERL_AUTOINSTALL} = "--defaultdeps";
$ENV{TEST_JOBS} = $threadcount;
$ENV{TWMC_TEST_PORT} = "9876";
my $perl_exe = "$PREFIX/bin/perl";
my $THREADS = $jobs ? "-j $jobs" : "-j $threadcount";
my $DRY = $dry ? "-n" : "";
# ========== build ==========
if ($forcebuildperl or ! -x $perl_exe )
{
print $USER "*** BUILD perl\n";
# ========== metainfo in Perl Config ==========
# This (mis-)uses a built-in mechanism in Configure.
#
# Ultimately we want to inject meta information so it appears
# later in Perl's %Config.
#
# Instead of dumping metadata directly into "config.arch" it
# generates script code in there that in turn appends variable
# assignments to "UU/config.sh" which in turn pretend to
# later Configure steps that there would be configuration data
# of an earlier session which it then merges into %Config.
#
# It should work for any ancient or future Perl 5 version.
# Credits to Tux for help sorting this out.
my $config_arch;
print $LOGFILE "*** CONFIGURE meta information for Perl's \%Config via config.arch\n";
open ($config_arch, ">>", "$build_path_perl/config.arch") and do {
print $config_arch qq!\
cat >> UU/config.sh <<EOUUCONFIG
bin/bootstrap-perl view on Meta::CPAN
=head3 test
Run the perl test suite:
$ bootstrap-perl --test
$ bootstrap-perl -t
Default is B<not> to run the tests.
=head3 (re-)build Perl
You can specify whether you want to compile perl even when an
executable C<perl> already exists:
$ bootstrap-perl --forcebuildperl
$ bootstrap-perl --noforcebuildperl
Default is B<off>.
=head3 bootstrap CPAN
You can specify whether to bootstrap a full CPAN environment with
distroprefs and dependencies:
$ bootstrap-perl --cpan
$ bootstrap-perl --nocpan
Default is B<on>.
=head3 (re-)configure CPAN
You can specify whether to (re-)write a CPAN config
(C<CPAN/Config.pm>, C<CPAN/MyConfig.pm>) even when it already exists:
$ bootstrap-perl --forcecpancfg
$ bootstrap-perl --noforcecpancfg
Default is B<on>.
=head3 use threads
Build a threaded Perl (C<-Dusethreads>):
$ bootstrap-perl --usethreads
which is already the default. To build non-threaded Perl use:
$ bootstrap-perl --nousethreads
=head3 use 64bit
Build a 64bit enabled Perl (C<-Duse64bitall>):
$ bootstrap-perl --use64bit
which is already the default. To build Perl without 64bit use:
$ bootstrap-perl --nouse64bit
=head3 taint support
Perl can be built without taint support (C<-DNO_TAINT_SUPPORT>, C<-DSILENT_NO_TAINT_SUPPORT>).
To build a Perl with taint support:
$ bootstrap-perl --taintsupport
which is already the default.
To build Perl without taint support use:
$ bootstrap-perl --notaintsupport
To make the notaintsupport silent, combine it with --silentnotaint:
$ bootstrap-perl --notaintsupport --silentnotaint
(sic, both are needed).
=head3 version numbers vs. blead
By default the Perl version number is derived from git-describe and
kept for later reference (e.g., for codespeed exe name). However, if you
specify
$ bootstrap-perl --blead
then the version used in paths or codespeed executables is using
"blead" in order to generate a make it belong to to a common
development line besides the yearly segmented graphs.
It is only possible to use C<--blead> with development versions in
order to create consistent results from only the blead branch.
Note that C<--blead> does not set a version but only modifies the
formats of path prefix and how the version is reported to
Perl::Formance; it is B<not> the same as C<--version blead>.
=head3 configure CPAN and modules
Configure CPAN to use these mirrors:
$ bootstrap-perl --mirror file:///home/ss5/MINICPAN/
$ bootstrap-perl -m file:///home/ss5/MINICPAN/ -m ftp://ftp.rub.de/pub/CPAN/
$ bootstrap-perl -m file:///home/ss5/MINICPAN/,ftp://ftp.rub.de/pub/CPAN/
(Option can be repeated and allow comma separated lists.)
Install these modules from CPAN:
$ bootstrap-perl --module YAML::Syck
$ bootstrap-perl --module YAML::Syck --forcemoduleinstall
$ bootstrap-perl -M YAML::Syck -M Digest::SHA1 -M IO::Tty -M LWP
$ bootstrap-perl -M YAML::Syck,Digest::SHA1 -M IO::Tty,LWP
(Option can be repeated and allow comma separated lists.)
=head3 run scripts
Run these scripts relative to built <PREFIX>/bin/:
$ bootstrap-perl --run tapper-testsuite-benchmark-perlformance
$ bootstrap-perl --run tapper-testsuite-benchmark-perlformance --runargs="--plugins=Fib,FibOO;-vvv"
$ bootstrap-perl -r tapper-testsuite-benchmark-perlformance -r primes.pl
(Option C<--run> can be repeated and allows comma separated lists.)
(Option C<--runargs> can be repeated and allows semicolon[sic] separated lists.)
=head3 run Perl::Formance benchmarks
To do everything in one go needed for running
Benchmark::Perl::Formance do:
$ bootstrap-perl --perlformance
This sets defaults equivalent to C<-M Task::PerlFormance> C<-m
http://perlformance.net/PINTO/perlformance/> C<--run
benchmark-perlformance> C<--runargs=--plugins=ALL>.
You can override parts of that like this:
$ bootstrap-perl --perlformance --runargs="--plugins=Fib,FibOO"
to specify different set ob benchmarks.
To use current subdir as Perl's repo and run only
those benchmarks with no extra dependencies
$ bootstrap-perl --perlformance-local
$ bootstrap-perl --perlformance-local -c .
$ bootstrap-perl --perlformance-local -c .^
$ bootstrap-perl --perlformance-local -c .~5
=head2 Unified installation prefix schema
It uses a unified naming schema for it's installation PREFIX:
/$HOME/.bootstrapperl/$HOSTNAME/perl-5.<VERSION>-<(no)?thread>-<(no)?64bit>-<(no)?taint>-<git-describe>
which leads to paths like this:
/home/ss5/.bootstrapperl/zomtec/perl-5.10-thread-64bit-taint-v5.10.0
/home/ss5/.bootstrapperl/zomtec/perl-5.15-thread-64bit-taint-v5.15.5-258-ge7d0a3f
This naming schema consist of the major version, basic configuration
and I<git-describe>.
=head1 ABOUT
The script B<bootstrap-perl> bootstraps Perl installations with
complete CPAN environment, inclusive distroprefs, from git.
It was originally developed to be used by
L<Benchmark::Perl::Formance|Benchmark::Perl::Formance> and now lives
on its own.
It should work for Perl versions from 5.8.6 to blead. Occasionally it
cherry-picks a very few patches to fix some known build issues, like
for 5.8.x.
=head1 AUTHOR
Steffen Schwigon <ss5@renormalist.net>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2021 by Steffen Schwigon.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
__DATA__
# Have POD in it so perldoc -l can find its path.
=head1 ABOUT
B<AUTO-GENERATED> -- see L<https://metacpan.org/pod/bootstrap-perl>
This is CPAN.pm's systemwide configuration file. This file provides
defaults for users, and the values can be changed in a per-user
configuration file. The user-config file is being looked for as
~/.cpan/CPAN/MyConfig.pm.
=cut
$CPAN::Config = {
'applypatch' => q[/usr/bin/applypatch],
'auto_commit' => q[1],
'build_cache' => q[10],
'build_dir' => q[__PREFIXBASE__/cpan/build],
'build_dir_reuse' => q[0],
'build_requires_install_policy' => q[yes],
'bzip2' => q[/bin/bzip2],
'cache_metadata' => q[0],
'check_sigs' => q[0],
'colorize_debug' => q[bold cyan],
'colorize_output' => q[1],
'colorize_print' => q[bold blue],
'colorize_warn' => q[bold red],
'commandnumber_in_prompt' => q[0],
( run in 0.755 second using v1.01-cache-2.11-cpan-39bf76dae61 )