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