1#!/usr/bin/perl -w 2################################################################################ 3# 4# buildperl.pl -- build various versions of perl automatically 5# 6################################################################################ 7# 8# Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz. 9# Version 2.x, Copyright (C) 2001, Paul Marquess. 10# Version 1.x, Copyright (C) 1999, Kenneth Albanowski. 11# 12# This program is free software; you can redistribute it and/or 13# modify it under the same terms as Perl itself. 14# 15################################################################################ 16 17use strict; 18use Getopt::Long; 19use Pod::Usage; 20use File::Find; 21use File::Path; 22use Data::Dumper; 23use IO::File; 24use Cwd; 25 26# TODO: - extra arguments to Configure 27 28# 29# --test-archives=1 check if archives can be read 30# --test-archives=2 like 1, but also extract archives 31# --test-archives=3 like 2, but also apply patches 32# 33 34my %opt = ( 35 prefix => '/tmp/perl/install/<config>/<perl>', 36 build => '/tmp/perl/build/<config>', 37 source => '/tmp/perl/source', 38 force => 0, 39 test => 0, 40 install => 1, 41 oneshot => 0, 42 configure => 0, 43 jobs => 1, 44 'test-archives' => 0, 45); 46 47my $Configure_extra = '-Dman1dir="none" -Dman3dir="none"'; 48 49 50my %config = ( 51 default => { 52 config_args => "-des $Configure_extra" , 53 }, 54 thread => { 55 config_args => "-des -Dusethreads $Configure_extra", 56 masked_versions => [ qr/^5\.00[01234]/ ], 57 }, 58 thread5005 => { 59 config_args => "-des -Duse5005threads $Configure_extra", 60 masked_versions => [ qr/^5\.00[012345]|^5\.(9|\d\d)|^5\.8\.9/ ], 61 }, 62 debug => { 63 config_args => "-des -Doptimize=-g $Configure_extra", 64 }, 65); 66 67my @patch = ( 68 { 69 perl => [ 70 qr/^5\.00[01234]/, 71 qw/ 72 5.005 73 5.005_01 74 5.005_02 75 5.005_03 76 /, 77 ], 78 subs => [ 79 [ \&patch_db, 1 ], 80 ], 81 }, 82 { 83 perl => [ 84 qw/ 85 5.6.0 86 5.6.1 87 5.7.0 88 5.7.1 89 5.7.2 90 5.7.3 91 5.8.0 92 /, 93 ], 94 subs => [ 95 [ \&patch_db, 3 ], 96 ], 97 }, 98 { 99 perl => [ 100 qr/^5\.004_0[1234]$/, 101 ], 102 subs => [ 103 [ \&patch_doio ], 104 ], 105 }, 106 { 107 perl => [ 108 qw/ 109 5.005 110 5.005_01 111 5.005_02 112 /, 113 ], 114 subs => [ 115 [ \&patch_sysv, old_format => 1 ], 116 ], 117 }, 118 { 119 perl => [ 120 qw/ 121 5.005_03 122 5.005_04 123 /, 124 qr/^5\.6\.[0-2]$/, 125 qr/^5\.7\.[0-3]$/, 126 qr/^5\.8\.[0-8]$/, 127 qr/^5\.9\.[0-5]$/ 128 ], 129 subs => [ 130 [ \&patch_sysv ], 131 ], 132 }, 133 { 134 perl => [ 135 qr/^5\.004_05$/, 136 qr/^5\.005(?:_0[1-4])?$/, 137 qr/^5\.6\.[01]$/, 138 ], 139 subs => [ 140 [ \&patch_configure ], 141 [ \&patch_makedepend_lc ], 142 ], 143 }, 144 { 145 perl => [ 146 '5.8.0', 147 ], 148 subs => [ 149 [ \&patch_makedepend_lc ], 150 ], 151 }, 152); 153 154my(%perl, @perls); 155 156GetOptions(\%opt, qw( 157 config=s@ 158 prefix=s 159 build=s 160 source=s 161 perl=s@ 162 force 163 test 164 install! 165 test-archives=i 166 patch! 167 oneshot 168 jobs=i 169)) or pod2usage(2); 170 171my %current; 172 173my $job_string = ""; 174$job_string = "-j$opt{jobs}" if $opt{jobs} != 1; 175 176if ($opt{patch} || $opt{oneshot}) { 177 @{$opt{perl}} == 1 or die "Exactly one --perl must be given with --patch or --oneshot\n"; 178 my $perl = $opt{perl}[0]; 179 patch_source($perl) if !exists $opt{patch} || $opt{patch}; 180 if (exists $opt{oneshot}) { 181 eval { require String::ShellQuote }; 182 die "--oneshot requires String::ShellQuote to be installed\n" if $@; 183 %current = (config => 'oneshot', version => $perl); 184 $config{oneshot} = { config_args => String::ShellQuote::shell_quote(@ARGV) }; 185 build_and_install($perl{$perl}); 186 } 187 exit 0; 188} 189 190if (exists $opt{config}) { 191 for my $cfg (@{$opt{config}}) { 192 exists $config{$cfg} or die "Unknown configuration: $cfg\n"; 193 } 194} 195else { 196 $opt{config} = [sort keys %config]; 197} 198 199find(sub { 200 /^(perl-?(5\..*))\.tar\.(gz|bz2|lzma)$/ or return; 201 $perl{$1} = { version => $2, source => $File::Find::name, compress => $3 }; 202}, $opt{source}); 203 204if (exists $opt{perl}) { 205 for my $perl (@{$opt{perl}}) { 206 my $p = $perl; 207 exists $perl{$p} or $p = "perl$perl"; 208 exists $perl{$p} or $p = "perl-$perl"; 209 exists $perl{$p} or die "Cannot find perl: $perl\n"; 210 push @perls, $p; 211 } 212} 213else { 214 @perls = sort keys %perl; 215} 216 217if ($opt{'test-archives'}) { 218 my $test = 'test'; 219 my $cwd = cwd; 220 -d $test or mkpath($test); 221 chdir $test or die "chdir $test: $!\n"; 222 for my $perl (@perls) { 223 eval { 224 my $d = extract_source($perl{$perl}); 225 if ($opt{'test-archives'} > 2) { 226 my $cwd2 = cwd; 227 chdir $d or die "chdir $d: $!\n"; 228 patch_source($perl{$perl}{version}); 229 chdir $cwd2 or die "chdir $cwd2:$!\n" 230 } 231 rmtree($d) if -e $d; 232 }; 233 warn $@ if $@; 234 } 235 chdir $cwd or die "chdir $cwd: $!\n"; 236 print STDERR "cleaning up\n"; 237 rmtree($test); 238 exit 0; 239} 240 241for my $cfg (@{$opt{config}}) { 242 for my $perl (@perls) { 243 my $config = $config{$cfg}; 244 %current = (config => $cfg, perl => $perl, version => $perl{$perl}{version}); 245 246 if (is($config->{masked_versions}, $current{version})) { 247 print STDERR "skipping $perl for configuration $cfg (masked)\n"; 248 next; 249 } 250 251 if (-d expand($opt{prefix}) and !$opt{force}) { 252 print STDERR "skipping $perl for configuration $cfg (already installed)\n"; 253 next; 254 } 255 256 my $cwd = cwd; 257 258 my $build = expand($opt{build}); 259 -d $build or mkpath($build); 260 chdir $build or die "chdir $build: $!\n"; 261 262 print STDERR "building $perl with configuration $cfg\n"; 263 buildperl($perl, $config); 264 265 chdir $cwd or die "chdir $cwd: $!\n"; 266 } 267} 268 269sub expand 270{ 271 my $in = shift; 272 $in =~ s/(<(\w+)>)/exists $current{$2} ? $current{$2} : $1/eg; 273 return $in; 274} 275 276sub is 277{ 278 my($s1, $s2) = @_; 279 280 defined $s1 != defined $s2 and return 0; 281 282 ref $s2 and ($s1, $s2) = ($s2, $s1); 283 284 if (ref $s1) { 285 if (ref $s1 eq 'ARRAY') { 286 is($_, $s2) and return 1 for @$s1; 287 return 0; 288 } 289 return $s2 =~ $s1; 290 } 291 292 return $s1 eq $s2; 293} 294 295sub buildperl 296{ 297 my($perl, $cfg) = @_; 298 299 my $d = extract_source($perl{$perl}); 300 chdir $d or die "chdir $d: $!\n"; 301 302 patch_source($perl{$perl}{version}); 303 304 build_and_install($perl{$perl}); 305} 306 307sub extract_source 308{ 309 eval { require Archive::Tar }; 310 die "Archive processing requires Archive::Tar to be installed\n" if $@; 311 312 my $perl = shift; 313 314 my $what = $opt{'test-archives'} ? 'test' : 'read'; 315 print "${what}ing $perl->{source}\n"; 316 317 my $target; 318 319 for my $f (Archive::Tar->list_archive($perl->{source})) { 320 my($t) = $f =~ /^([^\\\/]+)/ or die "ooops, should always match...\n"; 321 die "refusing to extract $perl->{source}, as it would not extract to a single directory\n" 322 if defined $target and $target ne $t; 323 $target = $t; 324 } 325 326 if ($opt{'test-archives'} == 0 || $opt{'test-archives'} > 1) { 327 if (-d $target) { 328 print "removing old build directory $target\n"; 329 rmtree($target); 330 } 331 332 print "extracting $perl->{source}\n"; 333 334 Archive::Tar->extract_archive($perl->{source}) 335 or die "extract failed: " . Archive::Tar->error() . "\n"; 336 337 -d $target or die "oooops, $target not found\n"; 338 } 339 340 return $target; 341} 342 343sub patch_source 344{ 345 my $version = shift; 346 347 for my $p (@patch) { 348 if (is($p->{perl}, $version)) { 349 for my $s (@{$p->{subs}}) { 350 my($sub, @args) = @$s; 351 $sub->(@args); 352 } 353 } 354 } 355} 356 357sub build_and_install 358{ 359 my $perl = shift; 360 my $prefix = expand($opt{prefix}); 361 362 run_or_die(q{sed -i -e "s:\\*/\\*) finc=\\"-I\\`echo \\$file | sed 's#/\\[^/\\]\\*\\$##\\`\\" ;;:*/*) finc=\\"-I\\`echo \\$file | sed 's#/[^/]\\*\\$##'\\`\\" ;;:" makedepend.SH}); 363 364 print "building perl $perl->{version} ($current{config})\n"; 365 366 run_or_die("./Configure $config{$current{config}}{config_args} -Dusedevel -Uinstallusrbinperl -Dprefix=$prefix"); 367 if (-f "x2p/makefile") { 368 run_or_die("sed -i -e '/^.*<builtin>/d' -e '/^.*<built-in>/d' -e '/^.*<command line>/d' -e '/^.*<command-line>/d' makefile x2p/makefile"); 369 } 370 run_or_die("make $job_string all"); 371 run("TEST_JOBS=$opt{jobs} make $job_string test") if $opt{test}; 372 if ($opt{install}) { 373 run("make $job_string install"); 374 } 375 else { 376 print "\n*** NOT INSTALLING PERL ***\n\n"; 377 } 378} 379 380sub patch_db 381{ 382 my $ver = shift; 383 print "patching ext/DB_File/DB_File.xs\n"; 384 run_or_die("sed -i -e 's/<db.h>/<db$ver\\/db.h>/' ext/DB_File/DB_File.xs"); 385} 386 387sub patch_doio 388{ 389 patch(<<'END'); 390--- doio.c.org 2004-06-07 23:14:45.000000000 +0200 391+++ doio.c 2003-11-04 08:03:03.000000000 +0100 392@@ -75,6 +75,16 @@ 393 # endif 394 #endif 395 396+#if _SEM_SEMUN_UNDEFINED 397+union semun 398+{ 399+ int val; 400+ struct semid_ds *buf; 401+ unsigned short int *array; 402+ struct seminfo *__buf; 403+}; 404+#endif 405+ 406 bool 407 do_open(gv,name,len,as_raw,rawmode,rawperm,supplied_fp) 408 GV *gv; 409END 410} 411 412sub patch_sysv 413{ 414 my %opt = @_; 415 416 # check if patching is required 417 return if $^O ne 'linux' or -f '/usr/include/asm/page.h'; 418 419 if ($opt{old_format}) { 420 patch(<<'END'); 421--- ext/IPC/SysV/SysV.xs.org 1998-07-20 10:20:07.000000000 +0200 422+++ ext/IPC/SysV/SysV.xs 2007-08-12 10:51:06.000000000 +0200 423@@ -3,9 +3,6 @@ 424 #include "XSUB.h" 425 426 #include <sys/types.h> 427-#ifdef __linux__ 428-#include <asm/page.h> 429-#endif 430 #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM) 431 #include <sys/ipc.h> 432 #ifdef HAS_MSG 433END 434 } 435 else { 436 patch(<<'END'); 437--- ext/IPC/SysV/SysV.xs.org 2007-08-11 00:12:46.000000000 +0200 438+++ ext/IPC/SysV/SysV.xs 2007-08-11 00:10:51.000000000 +0200 439@@ -3,9 +3,6 @@ 440 #include "XSUB.h" 441 442 #include <sys/types.h> 443-#ifdef __linux__ 444-# include <asm/page.h> 445-#endif 446 #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM) 447 #ifndef HAS_SEM 448 # include <sys/ipc.h> 449END 450 } 451} 452 453sub patch_configure 454{ 455 patch(<<'END'); 456--- Configure 457+++ Configure 458@@ -3380,6 +3380,18 @@ 459 test "X$gfpthkeep" != Xy && gfpth="" 460 EOSC 461 462+# gcc 3.1 complains about adding -Idirectories that it already knows about, 463+# so we will take those off from locincpth. 464+case "$gccversion" in 465+3*) 466+ echo "main(){}">try.c 467+ for incdir in `$cc -v -c try.c 2>&1 | \ 468+ sed '1,/^#include <\.\.\.>/d;/^End of search list/,$d;s/^ //'` ; do 469+ locincpth=`echo $locincpth | sed s!$incdir!!` 470+ done 471+ $rm -f try try.* 472+esac 473+ 474 : What should the include directory be ? 475 echo " " 476 $echo $n "Hmm... $c" 477END 478} 479 480sub patch_makedepend_lc 481{ 482 patch(<<'END'); 483--- makedepend.SH 484+++ makedepend.SH 485@@ -58,6 +58,10 @@ case $PERL_CONFIG_SH in 486 ;; 487 esac 488 489+# Avoid localized gcc/cc messages 490+LC_ALL=C 491+export LC_ALL 492+ 493 # We need .. when we are in the x2p directory if we are using the 494 # cppstdin wrapper script. 495 # Put .. and . first so that we pick up the present cppstdin, not 496END 497} 498 499sub patch 500{ 501 my($patch) = @_; 502 print "patching $_\n" for $patch =~ /^\+{3}\s+(\S+)/gm; 503 my $diff = 'tmp.diff'; 504 write_or_die($diff, $patch); 505 run_or_die("patch -s -p0 <$diff"); 506 unlink $diff or die "unlink $diff: $!\n"; 507} 508 509sub write_or_die 510{ 511 my($file, $data) = @_; 512 my $fh = new IO::File ">$file" or die "$file: $!\n"; 513 $fh->print($data); 514} 515 516sub run_or_die 517{ 518 # print "[running @_]\n"; 519 system "@_" and die "@_: $?\n"; 520} 521 522sub run 523{ 524 # print "[running @_]\n"; 525 system "@_" and warn "@_: $?\n"; 526} 527 528__END__ 529 530=head1 NAME 531 532buildperl.pl - build/install perl distributions 533 534=head1 SYNOPSIS 535 536 perl buildperl.pl [options] 537 538 --help show this help 539 540 --source=directory directory containing source tarballs 541 [default: /tmp/perl/source] 542 543 --build=directory directory used for building perls [EXPAND] 544 [default: /tmp/perl/build/<config>] 545 546 --prefix=directory use this installation prefix [EXPAND] 547 [default: 548 /tmp/perl/install/<config>/<perl>] 549 550 --config=configuration build this configuration [MULTI] 551 The possibilities for this parameter are: 552 'thread', 'thread5005', 'debug'; 553 and 'default', 554 which means none of the others. 555 [default: all possible configurations] 556 557 --perl=version build this version of perl [MULTI] 558 [default: all possible versions] 559 560 --force rebuild and install already installed 561 versions 562 563 --test run test suite after building 564 565 --noinstall don't install after building 566 567 --patch only patch the perl source in the current 568 directory 569 570 --oneshot build from the perl source in the current 571 directory (extra arguments are passed to 572 Configure) 573 574 -j N Build and test with N parallel jobs 575 [default: 1] 576 577 options tagged with [MULTI] can be given multiple times 578 579 options tagged with [EXPAND] expand the following items 580 581 <perl> versioned perl directory (e.g. 'perl-5.6.1') 582 <version> perl version (e.g. '5.6.1') 583 <config> name of the configuration (e.g. 'default') 584 585=head1 EXAMPLES 586 587The following examples assume that your Perl source tarballs are 588in F</tmp/perl/source>. If they are somewhere else, use the C<--source> 589option to specify a different source directory. 590 591To build a default configuration of perl5.004_05 and install it 592to F</opt/perl5.004_05>, you would say: 593 594 buildperl.pl --prefix='/opt/<perl>' --perl=5.004_05 --config=default 595 596To build debugging configurations of all perls in the source directory 597and install them to F</opt>, use: 598 599 buildperl.pl --prefix='/opt/<perl>' --config=debug 600 601To build all configurations for perl-5.8.5 and perl-5.8.6, test them 602and don't install them, run: 603 604 buildperl.pl --perl=5.8.5 --perl=5.8.6 --test --noinstall 605 606To build and install a single version of perl with special configuration 607options, use: 608 609 buildperl.pl --perl=5.6.0 --prefix=/opt/p560ld --oneshot -- -des \ 610 -Duselongdouble 611 612=head1 COPYRIGHT 613 614Copyright (c) 2004-2013, Marcus Holland-Moritz. 615 616This program is free software; you can redistribute it and/or 617modify it under the same terms as Perl itself. 618 619=head1 SEE ALSO 620 621See L<Devel::PPPort> and L<HACKERS>. 622