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