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