#! /usr/bin/env perl use Getopt::Long qw(:config bundling no_ignore_case); use POSIX; use Cwd; use strict; use warnings; my $help = 0; my $build_binary = 1; GetOptions ("h|help" => \$help, "binary!" => \$build_binary); usage () if $help; die "$0: exactly one or two nonoption arguments are required\n" if @ARGV != 1 && @ARGV != 2; my $builder = `hostname`; chomp $builder; # Select build number. my $build_number = POSIX::strftime("%Y%m%d%H%M%S", localtime); my $topdir = getcwd (); # Create build directory. my $builddir = "builds/$build_number"; mkdir "builds" or die "builds: mkdir: $!\n" if ! -d "builds"; mkdir $builddir or die "$builddir: mkdir: $!\n"; our $resultsdir = "$builddir/results"; mkdir $resultsdir or die "$resultsdir: mkdir: $!\n"; mkdir "$resultsdir/vars" or die "$resultsdir/vars: mkdir: $!\n"; my $logfile = "$resultsdir/LOG"; open (LOG, '>', $logfile) or die "creating $logfile failed: $!\n"; set_var ("builder", $builder); set_var ("build_number", $build_number); sub start_step { my ($msg) = @_; print LOG " \n$msg\n"; print "$msg\n"; } sub set_var { my ($var, $value) = @_; open (VAR, '>', "$resultsdir/vars/$var") or die "creating $resultsdir/$var failed: $!\n"; print VAR "$value\n"; close VAR; print LOG "$var=$value\n"; print "\t$var=$value\n"; } sub save_result { my ($src, $rm_src) = @_; my ($dst) = $src; $dst =~ s(^.*/)(); $dst = "$resultsdir/$dst"; start_step ("Saving $src"); run ("cp -R $src $dst"); if (defined ($rm_src) && $rm_src) { run ("rm $src"); } return $dst; } sub save_result_if_exists { my ($src, $rm_src) = @_; if (-e $src) { save_result (@_); } else { start_step ("$src does not exist, cannot save"); } } my $tarball; if (@ARGV == 2) { my ($repo, $branch) = @ARGV; # Fetch branch start_step ("Fetch $repo, branch $branch"); run ("git fetch $repo +$branch:buildtmp/$$/pspp"); # Get revision number. my $revision = `git rev-parse buildtmp/$$/pspp`; chomp $revision; set_var ("pspp_commit", $revision); my $abbrev_commit = substr ($revision, 0, 6); # Extract source. start_step ("Extract branch into $builddir/pspp$build_number"); run ("git archive --format=tar --prefix=pspp$build_number/ buildtmp/$$/pspp | (cd $builddir && tar xf -)"); # Extract version number. start_step ("Extract repository version number"); my $trace = `cd $builddir/pspp$build_number && autoconf -t AC_INIT`; chomp $trace; my ($file, $line, $macro, $package, $repo_version, @rest) = split (':', $trace); set_var ("repo_version", $repo_version); # Is this a "gnits" mode tree? start_step ("Checking Automake mode"); open (MAKEFILE_AM, '<', "$builddir/pspp$build_number/Makefile.am"); my $am_mode = "gnu"; while () { if (/gnits/) { $am_mode = "gnits"; last; } } set_var ("am_mode", $am_mode); # Generate version number for build. # We want to append -g012345, but if we're in Gnits mode and the # version number already has a hyphen, we have to omit it. start_step ("Generate build version number"); my $version = $repo_version; $version .= '-' unless $version =~ /-/; $version .= "g$abbrev_commit"; set_var ("version", $version); # Append -g012345 to configure.ac version number. start_step ("Updating version number in $file"); my $fullname = "$builddir/pspp$build_number/$file"; open (OLDFILE, '<', $fullname) or die "opening $fullname failed: $!\n"; open (NEWFILE, '>', "$fullname.new") or die "creating $fullname.new failed: $!\n"; while () { if ($. != $line) { print NEWFILE $_; } else { print NEWFILE "AC_INIT([$package], [$version]"; print NEWFILE ", [$_]" foreach @rest; print NEWFILE ")\n"; } } close (NEWFILE); close (OLDFILE); rename ("$fullname.new", $fullname) or die "rename $fullname.new to $fullname failed: $!\n"; # Add note to beginning of NEWS (otherwise "make dist" fails). start_step ("Updating NEWS"); $fullname = "$builddir/pspp$build_number/NEWS"; open (OLDFILE, '<', $fullname) or die "opening $fullname failed: $!\n"; open (NEWFILE, '>', "$fullname.new") or die "creating $fullname.new failed: $!\n"; my $found_changes = 0; while () { if (!$found_changes && /^Changes/) { $found_changes = 1; print NEWFILE <) { ($gnulib_commit) = /^\s+commit ([0-9a-fA-F]{8,})/ and last; } die "$fullname does not specify a Git commit number\n" if !defined ($gnulib_commit); set_var ("gnulib_commit", $gnulib_commit); # If we don't already have that Gnulib commit, update Gnulib. `git rev-parse $gnulib_commit`; if ($? != 0) { start_step ("Updating Gnulib to obtain commit"); run ("git fetch gnulib"); } # Extract gnulib source. start_step ("Extract Gnulib source"); run ("git archive --format=tar --prefix=gnulib/ $gnulib_commit | (cd $builddir && tar xf -)"); # Bootstrap. start_step ("Bootstrap (make -f Smake)"); run ("cd $builddir/pspp$build_number && make -f Smake"); # Configure. start_step ("Configure source"); run ("cd $builddir/pspp$build_number && mkdir _build && cd _build && ../configure"); # Distribute. start_step ("Make source tarball"); run ("cd $builddir/pspp$build_number/_build && make dist"); my $tarname = "pspp-$version.tar.gz"; $tarball = save_result ("$builddir/pspp$build_number/_build/$tarname", 1); # Build user manual start_step ("Build user manual"); run ("cd $builddir/pspp$build_number && cp _build/doc/*.texi doc/"); run ("cd $builddir/pspp$build_number && GENDOCS_TEMPLATE_DIR=$topdir $topdir/gendocs.sh -s doc/pspp.texinfo -o $topdir/$builddir/results/user-manual --email bug-gnu-pspp\@gnu.org pspp \"GNU PSPP User Manual\""); # Build developer's guide start_step ("Build developers guide"); run ("cd $builddir/pspp$build_number && GENDOCS_TEMPLATE_DIR=$topdir $topdir/gendocs.sh -s doc/pspp-dev.texinfo -o $topdir/$builddir/results/dev-guide --email bug-gnu-pspp\@gnu.org pspp \"GNU PSPP Developers Guide\""); } else { $tarball = $ARGV[0]; } if ($build_binary) { start_step ("Determining $tarball target directory"); my $sample_filename = `zcat $tarball | tar tf - | head -1`; my ($tarball_dir) = $sample_filename =~ m%^(?:[./])*([^/]+)/%; set_var ("dist_dir", $tarball_dir); start_step ("Extracting $tarball into $builddir/$tarball_dir"); run ("zcat $tarball | (cd $builddir && tar xf -)"); start_step ("Extracting tar version"); my ($version) = `cd $builddir/$tarball_dir && ./configure --version | head -1` =~ /configure (\S+)$/; set_var ("dist_version", $version); my ($binary_version) = "$version-$builder-build$build_number"; set_var ("binary_version", $binary_version); start_step ("Configuring"); run ("chmod -R a-w $builddir/$tarball_dir"); run ("chmod u+w $builddir/$tarball_dir"); run ("mkdir $builddir/$tarball_dir/_build"); run ("chmod a-w $builddir/$tarball_dir"); my $ok = try_run ("cd $builddir/$tarball_dir/_build && ../configure --enable-relocatable --prefix=''"); for my $basename ("config.h", "config.log") { save_result_if_exists ("$builddir/$tarball_dir/_build/$basename"); } exit 1 if !$ok; start_step ("Build"); run ("cd $builddir/$tarball_dir/_build && make"); start_step ("Install"); run ("cd $builddir/$tarball_dir/_build && make install DESTDIR=\$PWD/pspp-$binary_version"); start_step ("Make binary distribution"); run ("cd $builddir/$tarball_dir/_build && tar cfz pspp-$binary_version.tar.gz pspp-$binary_version"); save_result ("$builddir/$tarball_dir/_build/pspp-$binary_version.tar.gz", 1); start_step ("Check"); $ok = try_run ("cd $builddir/$tarball_dir/_build && make check"); for my $basename ("tests/testsuite.log", "tests/testsuite.dir") { save_result_if_exists ("$builddir/$tarball_dir/_build/$basename"); } exit 1 if !$ok; start_step ("Uninstall"); run ("cd $builddir/$tarball_dir/_build && make uninstall DESTDIR=\$PWD/pspp-$binary_version"); start_step ("Check uninstall"); run ("cd $builddir/$tarball_dir/_build && make distuninstallcheck distuninstallcheck_dir=\$PWD/pspp-$binary_version"); # distcleancheck } start_step ("Success"); sub usage { print <) { print LOG $_; print "\r", $i++; } close (COMMAND); print "\r \r"; return 1 if !$?; if ($? & 127) { printf "%s: child died with signal %d, %s coredump\n", $command, ($? & 127), ($? & 128) ? 'with' : 'without'; } else { printf "%s: child exited with value %d\n", $command, $? >> 8; } return 0; }