6 use Getopt::Long qw(:config bundling no_ignore_case);
18 my $builder = `hostname`;
20 GetOptions ("h|help" => \$help,
21 "binary!" => \$build_binary,
23 "o|output=s" => \$builddir,
24 "builder=s" => \$builder,
25 "build-number=i" => \$build_number);
29 die "$0: exactly one or two nonoption arguments are required\n"
30 if @ARGV != 1 && @ARGV != 2;
34 $tarball = File::Spec->rel2abs ($ARGV[0]);
36 # Tarball will be generated later.
39 # Select build number.
40 if (!defined ($build_number)) {
41 $build_number = POSIX::strftime("%Y%m%d%H%M%S", localtime);
44 my $topdir = dirname ($0);
45 $topdir = cwd () . "/$topdir" if $topdir !~ m%^/%;
47 # Create build directory.
48 if (!defined ($builddir)) {
49 $builddir = "builds/$build_number";
50 mkdir "builds" or die "builds: mkdir: $!\n" if ! -d "builds";
52 $builddir = File::Spec->rel2abs ($builddir);
53 -d $builddir or mkdir $builddir or die "$builddir: mkdir: $!\n";
54 chdir ($builddir) or die "$builddir: chdir: $!\n";
56 our $resultsdir = "$builddir/results";
57 mkdir $resultsdir or die "$resultsdir: mkdir: $!\n";
58 mkdir "$resultsdir/vars" or die "$resultsdir/vars: mkdir: $!\n";
60 my $varsfile = "$resultsdir/VARS";
61 open (VARS, '>', $varsfile) or die "creating $varsfile failed: $!\n";
64 our $logfile = "$resultsdir/LOG";
65 open (LOG, '>', $logfile) or die "creating $logfile failed: $!\n";
67 set_var ("builder", $builder);
68 set_var ("build_number", $build_number);
70 our $GIT = "git --git-dir=$topdir/.git";
74 print LOG "
\f\n$msg\n";
75 print "$msg\n" unless $batch;
79 my ($var, $value) = @_;
81 print VARS "$var=$value\n";
83 print "\t$var=$value\n" unless $batch;
85 open (VAR, '>', "$resultsdir/vars/$var")
86 or die "creating $resultsdir/$var failed: $!\n";
92 my ($name, $product) = @_;
93 start_step ("Saving $name: $product");
97 my ($name, $src, $rm_src) = @_;
98 my ($basename) = $src;
99 $basename =~ s(^.*/)();
100 my ($dst) = "$resultsdir/$basename";
102 saved_result ($name, $basename);
103 run ("cp -R $src $dst");
105 if (defined ($rm_src) && $rm_src) {
112 sub save_result_if_exists {
113 my ($name, $src, $rm_src) = @_;
117 start_step ("$src does not exist, cannot save");
123 my $commit = `$GIT rev-parse $ref`;
128 my $ssw = "spread-sheet-widget-0.1";
130 my ($repo, $branch) = @ARGV;
132 my $ssw_tar = "$topdir/$ssw.tar.gz";
133 start_step ("Extract spread-sheet-widget");
134 run ("tar xzf $ssw_tar");
136 start_step ("Configure spread-sheet-widget");
137 run ("cd $ssw && ./configure --prefix=''");
139 start_step ("Build spread-sheet-widget");
140 run ("cd $ssw && make -j10");
142 start_step ("Install spread-sheet-widget");
143 run ("cd $ssw && make -j10 install DESTDIR=\$PWD/inst");
145 start_step ("Fetch branch from Git");
146 set_var ("git_repo", $repo);
147 set_var ("git_branch", $branch);
148 run ("$GIT fetch $repo +$branch:refs/builds/$build_number/pspp");
150 # Get revision number.
151 set_var ("pspp_ref", "refs/builds/$build_number/pspp");
152 my $revision = ref_to_commit ("refs/builds/$build_number/pspp");
153 set_var ("pspp_commit", $revision);
154 my $abbrev_commit = substr ($revision, 0, 6);
157 start_step ("Extract branch into source directory");
158 run ("$GIT archive --format=tar --prefix=pspp/ refs/builds/$build_number/pspp | tar xf -");
160 # Extract version number.
161 start_step ("Extract repository version number");
162 my $trace = `cd pspp && autoconf -t AC_INIT`;
164 my ($file, $line, $macro, $package, $repo_version, @rest)
165 = split (':', $trace);
166 set_var ("repo_version", $repo_version);
168 # Is this a "gnits" mode tree?
169 start_step ("Checking Automake mode");
170 open (MAKEFILE_AM, '<', "pspp/Makefile.am");
172 while (<MAKEFILE_AM>) {
179 print LOG "Automake mode is $am_mode\n";
181 # Generate version number for build.
182 # We want to append -g012345, but if we're in Gnits mode and the
183 # version number already has a hyphen, we have to omit it.
184 start_step ("Generate build version number");
185 my $version = $repo_version;
186 $version .= '-' unless $version =~ /-/;
187 $version .= "g$abbrev_commit";
188 set_var ("version", $version);
190 # Append -g012345 to configure.ac version number.
191 start_step ("Updating version number in $file");
192 my $fullname = "pspp/$file";
193 open (OLDFILE, '<', $fullname)
194 or die "opening $fullname failed: $!\n";
195 open (NEWFILE, '>', "$fullname.new")
196 or die "creating $fullname.new failed: $!\n";
201 print NEWFILE "AC_INIT([$package], [$version]";
202 print NEWFILE ", [$_]" foreach @rest;
208 rename ("$fullname.new", $fullname)
209 or die "rename $fullname.new to $fullname failed: $!\n";
211 # Get Gnulib commit number.
212 start_step ("Reading README.Git to find Gnulib commit number");
214 $fullname = "pspp/README.Git";
215 open (README_GIT, '<', $fullname)
216 or die "opening $fullname failed: $!\n";
217 while (<README_GIT>) {
218 ($gnulib_commit) = /^\s+commit ([0-9a-fA-F]{8,})/ and last;
220 die "$fullname does not specify a Git commit number\n"
221 if !defined ($gnulib_commit);
222 set_var ("gnulib_commit", $gnulib_commit);
224 # Add note to beginning of NEWS (otherwise "make dist" fails).
225 start_step ("Updating NEWS");
226 $fullname = "pspp/NEWS";
227 open (OLDFILE, '<', $fullname)
228 or die "opening $fullname failed: $!\n";
229 open (NEWFILE, '>', "$fullname.new")
230 or die "creating $fullname.new failed: $!\n";
231 my $found_changes = 0;
233 if (!$found_changes && /^Changes/) {
236 Changes from $repo_version to $version:
238 * Built from PSPP commit $revision
239 in branch $branch on builder $builder.
241 * Built from Gnulib commit $gnulib_commit.
249 rename ("$fullname.new", $fullname)
250 or die "rename $fullname.new to $fullname failed: $!\n";
252 # If we don't already have that Gnulib commit, update Gnulib.
253 system ("$GIT rev-parse --verify --quiet $gnulib_commit^0 > /dev/null");
255 start_step ("Updating Gnulib to obtain commit");
256 run ("$GIT fetch gnulib");
258 run ("$GIT update-ref refs/builds/$build_number/gnulib $gnulib_commit");
259 set_var ("gnulib_ref", "refs/builds/$build_number/gnulib");
261 # Extract gnulib source.
262 start_step ("Extract Gnulib source");
263 run ("$GIT archive --format=tar --prefix=gnulib/ $gnulib_commit | tar xf -");
266 start_step ("Bootstrap (make -f Smake)");
267 run ("cd pspp && make -f Smake -j10", "bootstrap");
270 start_step ("Configure source");
271 run ("cd pspp && mkdir _build && cd _build && ../configure PKG_CONFIG_PATH=\$PWD/../../../source/$ssw/inst/lib/pkgconfig", "configure");
274 start_step ("Make source tarball");
275 run ("cd pspp/_build && make dist", "dist");
276 my $tarname = "pspp-$version.tar.gz";
277 $tarball = save_result ("source distribution", "pspp/_build/$tarname", 1);
279 # Save translation templates.
280 my $potfile = "pspp/_build/po/pspp.pot";
281 $potfile = "pspp/po/pspp.pot" if ! -e $potfile;
282 save_result ("translation templates", $potfile);
285 start_step ("Build user manual");
286 run ("cd pspp && GENDOCS_TEMPLATE_DIR=$topdir $topdir/gendocs.sh -s doc/pspp.texi -I doc -o $resultsdir/user-manual --email bug-gnu-pspp\@gnu.org pspp \"GNU PSPP User Manual\"", "user-manual");
287 saved_result ("User Manual", "user-manual");
289 # Build developer's guide
290 start_step ("Build developers guide");
291 run ("cd pspp && GENDOCS_TEMPLATE_DIR=$topdir $topdir/gendocs.sh -s doc/pspp-dev.texi -I doc -o $resultsdir/dev-guide --email bug-gnu-pspp\@gnu.org pspp-dev \"GNU PSPP Developers Guide\"", "dev-guide");
292 saved_result ("Developers Guide", "dev-guide");
294 start_step ("Starting from $tarball");
298 start_step ("Save tarball to Git");
299 run ("GIT_DIR=$topdir/.git $topdir/git-import-tar $tarball refs/builds/$build_number/dist", "git-dist");
300 set_var ("dist_ref", "refs/builds/$build_number/dist");
301 set_var ("dist_commit", ref_to_commit ("refs/builds/$build_number/dist"));
303 start_step ("Determining $tarball target directory");
304 my $sample_filename = `zcat $tarball | tar tf - | head -1`;
305 my ($tarball_dir) = $sample_filename =~ m%^(?:[./])*([^/]+)/%;
306 set_var ("dist_dir", $tarball_dir);
308 start_step ("Extracting source tarball");
309 run ("zcat $tarball | (cd $builddir && tar xf -)");
311 start_step ("Extracting tar version");
312 my ($version) = `cd $builddir/$tarball_dir && ./configure --version | head -1`
313 =~ /configure (\S+)$/;
314 set_var ("dist_version", $version);
315 my ($binary_version) = "$version-$builder-build$build_number";
316 set_var ("binary_version", $binary_version);
318 start_step ("Configuring");
319 run ("chmod -R a-w $builddir/$tarball_dir");
320 run ("chmod u+w $builddir/$tarball_dir");
321 run ("mkdir $builddir/$tarball_dir/_build");
322 run ("chmod a-w $builddir/$tarball_dir");
323 my $ok = try_run ("cd $builddir/$tarball_dir/_build && ../configure --enable-relocatable --prefix='' PKG_CONFIG_PATH=\$PWD/../../../source/$ssw/inst/lib/pkgconfig CPPFLAGS=\"-I\$PWD/../../../source/$ssw/inst/include\" LDFLAGS=\"-L\$PWD/../../../source/$ssw/inst/lib\"", "bin-configure");
324 for my $basename ("config.h", "config.log") {
325 save_result_if_exists ("build configuration",
326 "$builddir/$tarball_dir/_build/$basename");
330 start_step ("Build");
331 run ("cd $builddir/$tarball_dir/_build && make -j10", "build");
333 start_step ("Install");
334 run ("cd $builddir/$tarball_dir/_build && make install DESTDIR=\$PWD/pspp-$binary_version", "install");
335 run ("cd ../source/$ssw && make -j10 install DESTDIR=$builddir/$tarball_dir/_build/pspp-$binary_version");
337 start_step ("Make binary distribution");
338 run ("cd $builddir/$tarball_dir/_build && tar cfz pspp-$binary_version.tar.gz pspp-$binary_version");
339 save_result ("binary distribution", "$builddir/$tarball_dir/_build/pspp-$binary_version.tar.gz", 1);
341 start_step ("Check");
342 $ok = try_run ("cd $builddir/$tarball_dir/_build && make check", "check");
343 for my $basename ("tests/testsuite.log", "tests/testsuite.dir") {
344 save_result_if_exists ("test logs", "$builddir/$tarball_dir/_build/$basename");
348 start_step ("Uninstall");
349 run ("cd ../source/$ssw && make -j10 uninstall DESTDIR=$builddir/$tarball_dir/_build/pspp-$binary_version");
350 run ("cd $builddir/$tarball_dir/_build && make uninstall DESTDIR=\$PWD/pspp-$binary_version", "uninstall");
352 start_step ("Check uninstall");
353 run ("cd $builddir/$tarball_dir/_build && make distuninstallcheck distuninstallcheck_dir=\$PWD/pspp-$binary_version", "distuninstallcheck");
358 start_step ("Success");
362 $0, for building and testing PSPP
363 usage: $0 [OPTIONS] [TARBALL | REPO REFSPEC]
364 where TARBALL is the name of a tarball produced by "make dist"
365 or REPO and REFSPEC are a Git repo and refspec (e.g. branch) to clone.
368 --help Print this usage message and exit
369 --no-binary Build source tarballs but no binaries.
370 --batch Do not print progress to stdout.
376 fail () if !try_run (@_);
380 my ($command, $id) = @_;
382 print LOG "$command\n";
384 my $pid = open (COMMAND, '-|');
385 if (!defined ($pid)) {
386 die "fork failed: $!\n";
390 die "$command: exec failed: $!\n";
393 my ($start) = time ();
394 my ($est_time) = (defined ($id) ? read_timing ($id) : 0);
401 my $elapsed = time () - $start;
402 my $progress = sprintf "%d lines logged, %d s elapsed",
405 my $left = $est_time - $elapsed;
407 $progress .= sprintf ", ETA %d s", $left;
410 print "\r$progress", " " x (79 - length ($progress)), "\r"
414 print "\r", " " x 79, "\r" unless $batch;
416 write_timing ($id, time () - $start) if defined ($id);
421 printf STDERR "%s: child died with signal %d, %s coredump\n",
422 $command, ($? & 127), ($? & 128) ? 'with' : 'without';
424 printf STDERR "%s: child exited with value %d\n", $command, $? >> 8;
431 open (TIMINGS, "<", "$topdir/timings") or return 0;
434 my ($key, $value) = /^([^=]+)=(.*)/ or next;
435 return $value if $key eq $id;
442 my ($id, $time) = @_;
444 open (NEWTIMINGS, ">", "$topdir/timings.tmp$$") or return;
446 if (open (OLDTIMINGS, "<", "$topdir/timings")) {
447 while (<OLDTIMINGS>) {
448 if (my ($key, $value) = /^([^=]+)=(.*)/) {
456 print NEWTIMINGS "$id=$time\n";
459 rename ("$topdir/timings.tmp$$", "$topdir/timings");
463 die "Build failed, refer to:\n\t$logfile\nfor details.\n";