Upgrade to Devel::PPPort 3.10_01
[p5sagit/p5-mst-13.2.git] / ext / 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 #  $Revision: 10 $
9 #  $Author: mhx $
10 #  $Date: 2006/12/02 09:58:34 +0100 $
11 #
12 ################################################################################
13 #
14 #  Version 3.x, Copyright (C) 2004-2006, Marcus Holland-Moritz.
15 #  Version 2.x, Copyright (C) 2001, Paul Marquess.
16 #  Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
17 #
18 #  This program is free software; you can redistribute it and/or
19 #  modify it under the same terms as Perl itself.
20 #
21 ################################################################################
22
23 use strict;
24 use Getopt::Long;
25 use Pod::Usage;
26 use File::Find;
27 use File::Path;
28 use Data::Dumper;
29 use IO::File;
30 use Archive::Tar;
31 use Cwd;
32
33 # TODO: - extra arguments to Configure
34
35 my %opt = (
36   prefix  => '/tmp/perl/install/<config>/<perl>',
37   build   => '/tmp/perl/build/<config>',
38   source  => '/tmp/perl/source',
39   force   => 0,
40   test    => 0,
41   install => 1,
42   'test-archives' => 0,
43 );
44
45 my %config = (
46   default     => {
47                    config_args => '-des',
48                  },
49   thread      => {
50                    config_args     => '-des -Dusethreads',
51                    masked_versions => [ qr/^5\.00[01234]/ ],
52                  },
53   thread5005  => {
54                    config_args     => '-des -Duse5005threads',
55                    masked_versions => [ qr/^5\.00[012345]|^5.(9|\d\d)/ ],
56                  },
57   debug       => {
58                    config_args => '-des -Doptimize=-g',
59                  },
60 );
61
62 my @patch = (
63   {
64     perl => [
65               qr/^5\.00[01234]/,
66               qw/
67                 5.005
68                 5.005_01
69                 5.005_02
70                 5.005_03
71               /,
72             ],
73     subs => [
74               [ \&patch_db, 1 ],
75             ],
76   },
77   {
78     perl => [
79               qw/
80                 5.6.0
81                 5.6.1
82                 5.7.0
83                 5.7.1
84                 5.7.2
85                 5.7.3
86                 5.8.0
87               /,
88             ],
89     subs => [
90               [ \&patch_db, 3 ],
91             ],
92   },
93   {
94     perl => [
95               qr/^5\.004_0[1234]/,
96             ],
97     subs => [
98               [ \&patch_doio ],
99             ],
100   },
101 );
102
103 my(%perl, @perls);
104
105 GetOptions(\%opt, qw(
106   config=s@
107   prefix=s
108   build=s
109   source=s
110   perl=s@
111   force
112   test
113   install!
114   test-archives+
115 )) or pod2usage(2);
116
117 if (exists $opt{config}) {
118   for my $cfg (@{$opt{config}}) {
119     exists $config{$cfg} or die "Unknown configuration: $cfg\n";
120   }
121 }
122 else {
123   $opt{config} = [sort keys %config];
124 }
125
126 find(sub {
127   /^(perl-?(5\..*))\.tar\.(gz|bz2)$/ or return;
128   $perl{$1} = { version => $2, source => $File::Find::name, compress => $3 };
129 }, $opt{source});
130
131 if (exists $opt{perl}) {
132   for my $perl (@{$opt{perl}}) {
133     my $p = $perl;
134     exists $perl{$p} or $p = "perl$perl";
135     exists $perl{$p} or $p = "perl-$perl";
136     exists $perl{$p} or die "Cannot find perl: $perl\n";
137     push @perls, $p;
138   }
139 }
140 else {
141   @perls = sort keys %perl;
142 }
143
144 if ($opt{'test-archives'}) {
145   my $test = 'test';
146   my $cwd = cwd;
147   -d $test or mkpath($test);
148   chdir $test or die "chdir $test: $!\n";
149   for my $perl (@perls) {
150     eval {
151       my $d = extract_source($perl{$perl});
152       rmtree($d) if -e $d;
153     };
154     warn $@ if $@;
155   }
156   chdir $cwd or die "chdir $cwd: $!\n";
157   print STDERR "cleaning up\n";
158   rmtree($test);
159   exit 0;
160 }
161
162 my %current;
163
164 for my $cfg (@{$opt{config}}) {
165   for my $perl (@perls) {
166     my $config = $config{$cfg};
167     %current = (config => $cfg, perl => $perl, version => $perl{$perl}{version});
168
169     if (is($config->{masked_versions}, $current{version})) {
170       print STDERR "skipping $perl for configuration $cfg (masked)\n";
171       next;
172     }
173
174     if (-d expand($opt{prefix}) and !$opt{force}) {
175       print STDERR "skipping $perl for configuration $cfg (already installed)\n";
176       next;
177     }
178
179     my $cwd = cwd;
180
181     my $build = expand($opt{build});
182     -d $build or mkpath($build);
183     chdir $build or die "chdir $build: $!\n";
184
185     print STDERR "building $perl with configuration $cfg\n";
186     buildperl($perl, $config);
187
188     chdir $cwd or die "chdir $cwd: $!\n";
189   }
190 }
191
192 sub expand
193 {
194   my $in = shift;
195   $in =~ s/(<(\w+)>)/exists $current{$2} ? $current{$2} : $1/eg;
196   return $in;
197 }
198
199 sub is
200 {
201   my($s1, $s2) = @_;
202
203   defined $s1 != defined $s2 and return 0;
204
205   ref $s2 and ($s1, $s2) = ($s2, $s1);
206
207   if (ref $s1) {
208     if (ref $s1 eq 'ARRAY') {
209       is($_, $s2) and return 1 for @$s1;
210       return 0;
211     }
212     return $s2 =~ $s1;
213   }
214
215   return $s1 eq $s2;
216 }
217
218 sub buildperl
219 {
220   my($perl, $cfg) = @_;
221
222   my $d = extract_source($perl{$perl});
223   chdir $d or die "chdir $d: $!\n";
224
225   patch_source($perl{$perl}{version});
226
227   build_and_install($perl{$perl});
228 }
229
230 sub extract_source
231 {
232   my $perl = shift;
233
234   my $what = $opt{'test-archives'} ? 'test' : 'read';
235   print "${what}ing $perl->{source}\n";
236
237   my $target;
238
239   for my $f (Archive::Tar->list_archive($perl->{source})) {
240     my($t) = $f =~ /^([^\\\/]+)/ or die "ooops, should always match...\n";
241     die "refusing to extract $perl->{source}, as it would not extract to a single directory\n"
242         if defined $target and $target ne $t;
243     $target = $t;
244   }
245
246   if ($opt{'test-archives'} == 0 || $opt{'test-archives'} > 1) {
247     if (-d $target) {
248       print "removing old build directory $target\n";
249       rmtree($target);
250     }
251
252     print "extracting $perl->{source}\n";
253
254     Archive::Tar->extract_archive($perl->{source})
255         or die "extract failed: " . Archive::Tar->error() . "\n";
256
257     -d $target or die "oooops, $target not found\n";
258   }
259
260   return $target;
261 }
262
263 sub patch_source
264 {
265   my $version = shift;
266
267   for my $p (@patch) {
268     if (is($p->{perl}, $version)) {
269       for my $s (@{$p->{subs}}) {
270         my($sub, @args) = @$s;
271         $sub->(@args);
272       }
273     }
274   }
275 }
276
277 sub build_and_install
278 {
279   my $perl = shift;
280   my $prefix = expand($opt{prefix});
281
282   print "building perl $perl->{version} ($current{config})\n";
283
284   run_or_die("./Configure $config{$current{config}}{config_args} -Dusedevel -Uinstallusrbinperl -Dprefix=$prefix");
285   run_or_die("sed -i -e '/^.*<built-in>/d' -e '/^.*<command line>/d' makefile x2p/makefile");
286   run_or_die("make all");
287   run("make test") if $opt{test};
288   if ($opt{install}) {
289     run_or_die("make install");
290   }
291   else {
292     print "\n*** NOT INSTALLING PERL ***\n\n";
293   }
294 }
295
296 sub patch_db
297 {
298   my $ver = shift;
299   print "patching DB_File\n";
300   run_or_die("sed -i -e 's/<db.h>/<db$ver\\/db.h>/' ext/DB_File/DB_File.xs");
301 }
302
303 sub patch_doio
304 {
305   patch('doio.c', <<'END');
306 --- doio.c.org  2004-06-07 23:14:45.000000000 +0200
307 +++ doio.c      2003-11-04 08:03:03.000000000 +0100
308 @@ -75,6 +75,16 @@
309  #  endif
310  #endif
311
312 +#if _SEM_SEMUN_UNDEFINED
313 +union semun
314 +{
315 +  int val;
316 +  struct semid_ds *buf;
317 +  unsigned short int *array;
318 +  struct seminfo *__buf;
319 +};
320 +#endif
321 +
322  bool
323  do_open(gv,name,len,as_raw,rawmode,rawperm,supplied_fp)
324  GV *gv;
325 END
326 }
327
328 sub patch
329 {
330   my($file, $patch) = @_;
331   print "patching $file\n";
332   my $diff = "$file.diff";
333   write_or_die($diff, $patch);
334   run_or_die("patch -s -p0 <$diff");
335   unlink $diff or die "unlink $diff: $!\n";
336 }
337
338 sub write_or_die
339 {
340   my($file, $data) = @_;
341   my $fh = new IO::File ">$file" or die "$file: $!\n";
342   $fh->print($data);
343 }
344
345 sub run_or_die
346 {
347   # print "[running @_]\n";
348   system "@_" and die "@_: $?\n";
349 }
350
351 sub run
352 {
353   # print "[running @_]\n";
354   system "@_" and warn "@_: $?\n";
355 }
356
357 __END__
358
359 =head1 NAME
360
361 buildperl.pl - build/install perl distributions
362
363 =head1 SYNOPSIS
364
365   perl buildperl.pl [options]
366
367   --help                      show this help
368
369   --source=directory          directory containing source tarballs
370                               [default: /tmp/perl/source]
371
372   --build=directory           directory used for building perls [EXPAND]
373                               [default: /tmp/perl/build/<config>]
374
375   --prefix=directory          use this installation prefix [EXPAND]
376                               [default: /tmp/perl/install/<config>/<perl>]
377
378   --config=configuration      build this configuration [MULTI]
379                               [default: all possible configurations]
380
381   --perl=version              build this version of perl [MULTI]
382                               [default: all possible versions]
383
384   --force                     rebuild and install already installed versions
385
386   --test                      run test suite after building
387
388   --noinstall                 don't install after building
389
390   options tagged with [MULTI] can be given multiple times
391
392   options tagged with [EXPAND] expand the following items
393
394     <perl>      versioned perl directory  (e.g. 'perl-5.6.1')
395     <version>   perl version              (e.g. '5.6.1')
396     <config>    name of the configuration (e.g. 'default')
397
398 =head1 EXAMPLES
399
400 The following examples assume that your Perl source tarballs are
401 in F</tmp/perl/source>. If they are somewhere else, use the C<--source>
402 option to specify a different source directory.
403
404 To build a default configuration of perl5.004_05 and install it
405 to F</opt/perl5.004_05>, you would say:
406
407   buildperl.pl --prefix='/opt/<perl>' --perl=5.004_05 --config=default
408
409 To build debugging configurations of all perls in the source directory
410 and install them to F</opt>, use:
411
412   buildperl.pl --prefix='/opt/<perl>' --config=debug
413
414 To build all configurations for perl-5.8.5 and perl-5.8.6, test them
415 and don't install them, run:
416
417   buildperl.pl --perl=5.8.5 --perl=5.8.6 --test --noinstall
418
419 =head1 COPYRIGHT
420
421 Copyright (c) 2004-2006, Marcus Holland-Moritz.
422
423 This program is free software; you can redistribute it and/or
424 modify it under the same terms as Perl itself.
425
426 =head1 SEE ALSO
427
428 See L<Devel::PPPort> and L<HACKERS>.
429