LT 79: Rename auto_build.pl to auto-build

Index: ChangeLog from Clement Vasseur <clement.vasseur@lrde.epita.fr> * auto-build/auto_build.pl: Rename to... * auto-build/auto-build: ...this. 2004-07-13 Clement Vasseur <clement.vasseur@lrde.epita.fr> Index: auto-build/auto-build --- auto-build/auto-build (revision 0) +++ auto-build/auto-build (revision 79) @@ -0,0 +1,643 @@ +#!/usr/bin/perl -w +# +# Auto Build System - auto-build +# Copyright (C) 2004 LRDE - EPITA Research and Development Laboratory +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +# Authors: +# Clement Vasseur <clement.vasseur@lrde.epita.fr> +# Nicolas Pouillard <nicolas.pouillard@lrde.epita.fr> + +# Any contribution should follow the Perl style guide: perldoc perlstyle +# This script should remain self-contained and it should not depend on any +# module besides the ones in the standard Perl 5.8 installation. + +my $VERSION = 0.1; + +=pod + +=head1 NAME + +auto-build + +=head1 SYNOPSIS + +auto-build [options] packages.list + + -m, --man display the manual + -h, --help print this help and exit + -V, --version print version information and exit + -d, --delay=DELAY redo a build cycle after DELAY minutes + +=head1 DESCRIPTION + +=cut + +use strict; +use Getopt::Long; +use Pod::Usage; + +my $report_dir; +my $build_dir; +my $inst_dir; +my @packages; + +my %variables; +my @colors; +my %deps; + +my $packages; +my $man = 0; +my $help = 0; +my $delay = 0; + +GetOptions( + 'man|m' => \$man, + 'help|h|?' => \$help, + 'version|V' => \&version, + 'delay|d=i' => \$delay, + '<>' => sub { $packages = $_[0] }, +) or pod2usage(-verbose => 0); + +pod2usage(-verbose => 1) if $help; +pod2usage(-verbose => 2) if $man; +pod2usage(-verbose => 0) unless defined $packages; + +&read_pkg_list($packages); +&create_dir($report_dir); +&create_dir($build_dir); +&create_dir($inst_dir); +&prepare_pkgs; + +while (1) { + &auto_build; + last if $delay == 0; + sleep $delay * 60; +} + +sub auto_build { + &gen_summary; + + foreach my $pkg (@packages) { + print "$pkg->{'name'}\n"; + + next if &check_revision($pkg); + &gen_pkg_report($pkg); + + my $ok = 1; + foreach my $dep (@{$deps{$pkg->{'name'}}}) { + foreach (@packages) { + $ok = 0 if $_->{'name'} eq $dep + and ($_->{'status'} eq 'failed' or $_->{'status'} eq 'skipped'); + } + unless ($ok) { + &set_status($pkg, 'skipped'); + last; + } + } + + &build_pkg($pkg) if $ok; + } +} + +sub xopen { + my $filename = shift; + my $name = $filename; + my $FILE; + + $name =~ s/^>//; + open $FILE, $filename + or die "$0: unable to open `$name': $!\n"; + return $FILE; +} + +sub xclose { + my $FILE = shift; + close $FILE or die "$0: unable to close `$FILE': $!\n"; +} + +sub header { + my ($filename, $title) = @_; + my $FILE = &xopen(">$filename"); + select $FILE; + + print <<HTML; +<html> + <head> + <title>$title</title> + </head> + <body> +HTML + + return $FILE; +} + +sub footer { + print <<HTML; + <hr/> + <h4>Generated by <i>auto-build</i> version $VERSION</h4> + </body> +</html> +HTML + + select STDOUT; + my $FILE = shift; + &xclose($FILE); +} + +sub read_pkg_list { + my $filename = shift; + my $LIST = &xopen($filename); + my $section; + + while (<$LIST>) { + s/\s*\#.*$//; + next if m/^\s*$/; + chomp; + + while (s/\s*\\\s*$/ /) { + my $l = <$LIST>; + $l =~ s/^\s*//; + $l =~ s/\s*\#.*$//; + $_ .= $l; + chomp; + } + + unless (defined $report_dir) { + $report_dir = $_; + next; + } + + unless (defined $build_dir) { + $build_dir = $_; + next; + } + + unless (defined $inst_dir) { + $inst_dir = $_; + next; + } + + if (m/^\s*color\s+(\w+)\s+\"(.*?)\"\s*$/) { + push @colors, [ $2, $1 ]; + + } elsif (m/^\s*section\s+\"(.*)\"\s*$/) { + $section = $1; + + } elsif (m/^([\w\-]+)\s*=\s*(.*?)\s*$/) { + $variables{$1} = $2; + + } elsif (m/^([\w\-]+)\s*$/) { + push @packages, { + 'name' => $1, + 'section' => $section, + 'opt' => '', + 'cfg' => '', + 'rev' => '0' + }; + + } elsif (m/^\s*(\w\w\w)\s*:\s*(.*?)\s*$/) { + my $field = $1; + my $value = $2; + + my $err = 1; + foreach ('url', 'cfg', 'opt', 'env', 'dif') { + if ($field eq $_) { + $err = 0; + last; + } + } + + die "$filename:$.: invalid field `$field'\n" if $err; + while ($value =~ m/\$\{([\w\-]+)\}/) { + if (defined $variables{$1}) { + $value =~ s/\$\{([\w\-]+)\}/$variables{$1}/; + } else { + $value =~ s/\$\{([\w\-]+)\}/$inst_dir\/$1/; + push @{$deps{$packages[-1]->{'name'}}}, $1; + } + } + $packages[-1]->{$field} = $value; + + } else { + die "$filename:$.: syntax error\n"; + } + } + + &xclose($LIST); + (defined $report_dir and defined $build_dir and $inst_dir) + or die "$0: `report', `build' and `inst' directories must be defined\n"; +} + +sub create_dir { + my $dir = shift; + return if -d $dir; + system("/bin/mkdir -p \"$dir\"") + and die "$0: unable to mkdir `$dir'\n"; +} + +sub delete_dir { + my $dir = shift; + return unless -d $dir; + system("/bin/chmod -fR u+w \"$dir\"; /bin/rm -rf \"$dir\"") + and die "$0: unable to remove `$dir'\n"; +} + +sub ch_dir { + my $dir = shift; + chdir $dir or die "$0: unable to chdir `$dir': $!\n"; +} + +sub time_to_str { + $_ = shift; + my $t = ''; + + if (defined $_ and $_ ne '') { + $t = ($_ % 60) . "s"; + $t = (($_/60) % 60) . "m$t" if $_ >= 60; + $t = (($_/(60 * 60)) % 60) . "h$t" if $_ >= 60 * 60; + } + + return $t; +} + +sub gen_summary { + my $SUMMARY = &header("$report_dir/index.html", "Auto Build System"); + my $section = ''; + + print <<HTML; + <h1>Auto Build System</h1> + <hr/> + <table> +HTML + + foreach my $pkg (@packages) { + if ($pkg->{'section'} ne $section) { + if ($section ne '') { + print <<HTML; + <tr> + <td> + + </td> + </tr> +HTML + } + $section = $pkg->{'section'}; + print <<HTML; + <tr> + <td> + <h3>$section</h3> + </td> + </tr> +HTML + } + + $pkg->{'status'} = '' unless defined $pkg->{'status'}; + + my $color = "black"; + my $time = &time_to_str($pkg->{'time'}); + + $color = "red" if $pkg->{'status'} eq "failed"; + $color = "green" if $pkg->{'status'} eq "succeeded"; + $color = "gray" if $pkg->{'status'} eq "skipped"; + + print <<HTML; + <tr> + <td><a href=\"$pkg->{'name'}/$pkg->{'name'}.html\">$pkg->{'name'}</a></td> + <td> </td> + <td><font color=\"$color\">$pkg->{'status'}</font></td> + <td> </td> + <td><font color=\"gray\">$time</font></td> + </tr> +HTML + } + + print " </table>\n"; + &footer($SUMMARY); +} + +sub gen_pkg_report { + my $pkg = shift; + my $filename = "$report_dir/$pkg->{'name'}/$pkg->{'name'}.html"; + my $REPORT = &header("$filename", "$pkg->{'name'} report"); + + my ($ts, $tf) = ('', ''); + $ts = localtime $pkg->{'time_started'} if defined $pkg->{'time_started'}; + $tf = localtime $pkg->{'time_finished'} if defined $pkg->{'time_finished'}; + + print <<HTML; + <h1>$pkg->{'name'}</h1> + <hr/> + <h3>Build started: $ts</h3> + <table cellpadding=\"8\"> +HTML + + foreach my $step (@{$pkg->{'steps'}}) { + next unless defined $step->{'name'}; + if (defined $step->{'status'}) { + my $color = "black"; + $color = "green" if $step->{'status'} eq "succeeded"; + $color = "red" if $step->{'status'} eq "failed"; + + my $time = &time_to_str($step->{'time'}); + + print <<HTML; + <tr> + <td><a href=\"$step->{'name'}.html\">$step->{'name'}</a></td> + <td><font color=\"$color\">$step->{'status'}</font></td> + <td><font color=\"gray\">$time</font></td> + </tr> +HTML + } else { + print <<HTML; + <tr> + <td>$step->{'name'}</td> + </tr> +HTML + } + } + + print <<HTML; + </table> + <h3>Build finished: $tf</h3> +HTML + + if (defined $pkg->{'archives'}) { + foreach (@{$pkg->{'archives'}}) { + print " <a href=\"$_\">$_</a><br/>\n" if -f $_; + } + } + + &footer($REPORT); +} + +sub set_status { + $_[0]->{'status'} = $_[1]; + print " $_[1]\n"; + &gen_summary; +} + +sub gen_log_page { + my $pkg = shift; + my $step = shift; + my $filename = "$report_dir/$pkg->{'name'}/$step->{'name'}.html"; + my $REPORT = &header("$filename", "$pkg->{'name'} - $step->{'name'}"); + + print <<HTML; + <h1>$step->{'name'}</h1> + <h4>$step->{'cmd'}</h4> + <hr/> + <a href="$step->{'name'}.log">Log</a> +HTML + + &footer($REPORT); +} + +sub run { + my $pkg = shift; + my $step = shift; + my $status = $step->{'name'}; + my $out = "$report_dir/$pkg->{'name'}/$status"; + + &set_status($pkg, $status); + &gen_log_page($pkg, $step); + + my $t = time; + my $exit_code = system("$step->{'cmd'} >$out.log 2>&1"); + $step->{'time'} = time - $t; + + my $REPORT = &header("$out.html", "$pkg->{'name'} - $status"); + + print <<HTML; + <h1>$step->{'name'}</h1> + <h4>$step->{'cmd'}</h4> + <hr/> + <pre> +HTML + + my $LOG = &xopen("$out.log"); + my $archives_ready; + + while (<$LOG>) { + my $color; + foreach my $c (@colors) { + $color = $c->[1] if m/^$c->[0]/; + last if defined $color; + } + if ($status eq 'check') { + if (($archives_ready and m/^($archives_ready\.tar\.(gz|bz2))$/) + or m/^(.*\.tar\.(gz|bz2)) is ready for distribution$/) { + push @{$pkg->{'archives'}}, $1; + system("/bin/cp \"$1\" \"$report_dir/$pkg->{'name'}\"") + and die "$0: unable to cp `$1' to `$report_dir/$pkg->{'name'}': $!\n" ; + } + $archives_ready = $1 if m/^(.*) archives ready for distribution: $/; + } + print "<font color=\"$color\">" if $color; + print; + print "</font>" if $color; + } + + print " </pre>\n"; + &xclose($LOG); + &footer($REPORT); + return undef if $exit_code; + return 1; +} + +sub check_revision { + my $pkg = shift; + my $revfile = "$inst_dir/$pkg->{'name'}/.rev"; + my $rev; + + if ($pkg->{'url'} =~ m|^svn:(.*/([^/]+))$|) { + chomp($rev = `svn log -q \"https:$1\" 2>/dev/null | grep '^r' | head -n 1`) ; + $rev =~ s/^r(\d+).*$/$1/; + } elsif ($pkg->{'url'} =~ m/^((ht|f)tp:.*)$/) { + chomp($rev = `wget -qnv -O - \"$1\" | md5sum`); + } + $pkg->{'rev'} = $rev; + + if (-f $revfile) { + chomp($rev = `cat \"$revfile\"`); + unlink $revfile if $pkg->{'rev'} ne $rev; + } + if (-f $revfile) { + chomp($pkg->{'time'} = `cat \"$inst_dir/$pkg->{'name'}/.time\"`); + &set_status($pkg, 'succeeded'); + return 1; + } + return undef; +} + +sub get_package { + my $pkg = shift; + my @steps; + $_ = $pkg->{'url'}; + + if (m|^http://.*/([^/]+)/?$| + or m|^ftp://.*/([^/]+)/?$|) { + $_ = "file://$1"; + push @steps, { + 'name' => 'download', + 'cmd' => "wget -nv \"$pkg->{'url'}\"" + }; + } + + if (m|^file://(.*)$|) { + my $file = $_ = $1; + my $c; + + $c = "z" if m/\.tar\.gz$/ or m/\.tgz$/; + $c = "j" if m/\.tar\.bz2$/ or m/\.tbz2$/; + $pkg->{'dir'} = $file; + + if (defined $c) { + push @steps, { + 'name' => 'unpack', + 'cmd' => "tar -x${c}vf \"$file\"" + }, { + 'dir' => "tar -t${c}f \"$file\" | head -n 1 | cut -d '/' -f 0-1", + }; + } + + } elsif (m|^svn://(.*/([^/]+))/?$|) { + my $repository = "https://$1"; + $pkg->{'dir'} = $2; + + push @steps, { + 'name' => 'checkout', + 'cmd' => "svn checkout \"$repository\"" + }, { + 'dir' => "echo \"$pkg->{'dir'}\"", + 'name' => 'bootstrap', + 'cmd' => '(./bootstrap || ./bootstrap.sh || autoreconf -fvi)' + }; + + } elsif (m|^prcs://(.*)$|) { + $pkg->{'dir'} = $1; + + push @steps, { + 'dir' => "echo \"$1\"", + 'name' => 'checkout', + 'cmd' => "prcs checkout \"$1\"" + }, { + 'name' => 'bootstrap', + 'cmd' => '(./bootstrap || ./bootstrap.sh || autoreconf -fvi)' + }; + + } else { + die "$0: invalid URL `$_': unknown protocol\n"; + } + + return @steps; +} + +sub prepare_pkgs { + foreach my $pkg (@packages) { + my @steps = &get_package($pkg); + my $dir = "$report_dir/$pkg->{'name'}"; + &create_dir($dir); + + push @steps, { + 'name' => 'patch', + 'cmd' => "patch -p0 -t -i \"$pkg->{'dif'}\"" + } if defined $pkg->{'dif'}; + push @steps, { + 'name' => 'configure', + 'cmd' => "./configure --prefix=$inst_dir/$pkg->{'name'} $pkg->{'cfg'}" + }, { + 'name' => 'build', 'cmd' => 'make' + }; + my $dcf = ''; + defined $pkg->{'cfg'} and $pkg->{'cfg'} ne '' + and $dcf = "DISTCHECK_CONFIGURE_FLAGS=\"$pkg->{'cfg'}\" "; + push @steps, { + 'name' => 'check', + 'cmd' => "make ${dcf}distcheck" + } unless $pkg->{'opt'} =~ m/\bno-check\b/; + push @steps, { + 'rm' => "$inst_dir/$pkg->{'name'}" + },{ + 'name' => 'install', 'cmd' => 'make install' + }; + $pkg->{'steps'} = \@steps; + next if &check_revision($pkg); + &gen_pkg_report($pkg) unless -f "$dir/$pkg->{'name'}.html"; + } +} + +sub build_pkg { + my $pkg = shift; + my %env = %ENV; + + if (defined $pkg->{'env'}) { + foreach (split /\s*,\s*/, $pkg->{'env'}) { + if (m/^\s*(\w+)\s*=\s*\"(.*)\"\s*$/) { + my ($v, $w) = ($1, $2); + $w =~ s/\$(\w+)/$ENV{$1}/; + $ENV{$v} = $w; + } + } + } + + my $st; + my $dir = "$build_dir/$pkg->{'name'}"; + &delete_dir($dir); + &create_dir($dir); + &ch_dir($dir); + $pkg->{'time_started'} = time; + foreach my $step (@{$pkg->{'steps'}}) { + $step->{'status'} = "running"; + &gen_pkg_report($pkg); + &delete_dir($step->{'rm'}) if defined $step->{'rm'}; + if (defined $step->{'dir'}) { + chomp($pkg->{'dir'} = `$step->{'dir'}`); + &create_dir($pkg->{'dir'}); + &ch_dir($pkg->{'dir'}); + } + if (not defined $step->{'cmd'} + or &run($pkg, $step)) { + $step->{'status'} = $st = "succeeded"; + next; + } + $step->{'status'} = $st = "failed"; + last; + } + + $pkg->{'time_finished'} = time; + $pkg->{'time'} = $pkg->{'time_finished'} - $pkg->{'time_started'}; + %ENV = %env; + &gen_pkg_report($pkg); + &ch_dir($build_dir); + &set_status($pkg, $st); + if ($st eq "succeeded") { + system("echo \"$pkg->{'rev'}\" > \"$inst_dir/$pkg->{'name'}/.rev\""); + system("echo \"$pkg->{'time'}\" > \"$inst_dir/$pkg->{'name'}/.time\""); + &delete_dir($pkg->{'name'}); + } +} + +sub version { + print "auto-build (Auto Build System) $VERSION\n", + "Written by Clement Vasseur and Nicolas Pouillard.\n\n", + "Copyright (C) 2004 LRDE - EPITA Research and Development Laboratory.\n", + "This is free software; see the source for copying conditions. ", + "There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A ", + "PARTICULAR PURPOSE.\n"; + exit 0; +} Property changes on: auto-build/auto-build ___________________________________________________________________ Name: svn:executable + * Index: auto-build/auto_build.pl --- auto-build/auto_build.pl (revision 78) +++ auto-build/auto_build.pl (revision 79) @@ -1,643 +0,0 @@ -#!/usr/bin/perl -w -# -# Auto Build System - auto-build -# Copyright (C) 2004 LRDE - EPITA Research and Development Laboratory -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# - -# Authors: -# Clement Vasseur <clement.vasseur@lrde.epita.fr> -# Nicolas Pouillard <nicolas.pouillard@lrde.epita.fr> - -# Any contribution should follow the Perl style guide: perldoc perlstyle -# This script should remain self-contained and it should not depend on any -# module besides the ones in the standard Perl 5.8 installation. - -my $VERSION = 0.1; - -=pod - -=head1 NAME - -auto-build - -=head1 SYNOPSIS - -auto-build [options] packages.list - - -m, --man display the manual - -h, --help print this help and exit - -V, --version print version information and exit - -d, --delay=DELAY redo a build cycle after DELAY minutes - -=head1 DESCRIPTION - -=cut - -use strict; -use Getopt::Long; -use Pod::Usage; - -my $report_dir; -my $build_dir; -my $inst_dir; -my @packages; - -my %variables; -my @colors; -my %deps; - -my $packages; -my $man = 0; -my $help = 0; -my $delay = 0; - -GetOptions( - 'man|m' => \$man, - 'help|h|?' => \$help, - 'version|V' => \&version, - 'delay|d=i' => \$delay, - '<>' => sub { $packages = $_[0] }, -) or pod2usage(-verbose => 0); - -pod2usage(-verbose => 1) if $help; -pod2usage(-verbose => 2) if $man; -pod2usage(-verbose => 0) unless defined $packages; - -&read_pkg_list($packages); -&create_dir($report_dir); -&create_dir($build_dir); -&create_dir($inst_dir); -&prepare_pkgs; - -while (1) { - &auto_build; - last if $delay == 0; - sleep $delay * 60; -} - -sub auto_build { - &gen_summary; - - foreach my $pkg (@packages) { - print "$pkg->{'name'}\n"; - - next if &check_revision($pkg); - &gen_pkg_report($pkg); - - my $ok = 1; - foreach my $dep (@{$deps{$pkg->{'name'}}}) { - foreach (@packages) { - $ok = 0 if $_->{'name'} eq $dep - and ($_->{'status'} eq 'failed' or $_->{'status'} eq 'skipped'); - } - unless ($ok) { - &set_status($pkg, 'skipped'); - last; - } - } - - &build_pkg($pkg) if $ok; - } -} - -sub xopen { - my $filename = shift; - my $name = $filename; - my $FILE; - - $name =~ s/^>//; - open $FILE, $filename - or die "$0: unable to open `$name': $!\n"; - return $FILE; -} - -sub xclose { - my $FILE = shift; - close $FILE or die "$0: unable to close `$FILE': $!\n"; -} - -sub header { - my ($filename, $title) = @_; - my $FILE = &xopen(">$filename"); - select $FILE; - - print <<HTML; -<html> - <head> - <title>$title</title> - </head> - <body> -HTML - - return $FILE; -} - -sub footer { - print <<HTML; - <hr/> - <h4>Generated by <i>auto-build</i> version $VERSION</h4> - </body> -</html> -HTML - - select STDOUT; - my $FILE = shift; - &xclose($FILE); -} - -sub read_pkg_list { - my $filename = shift; - my $LIST = &xopen($filename); - my $section; - - while (<$LIST>) { - s/\s*\#.*$//; - next if m/^\s*$/; - chomp; - - while (s/\s*\\\s*$/ /) { - my $l = <$LIST>; - $l =~ s/^\s*//; - $l =~ s/\s*\#.*$//; - $_ .= $l; - chomp; - } - - unless (defined $report_dir) { - $report_dir = $_; - next; - } - - unless (defined $build_dir) { - $build_dir = $_; - next; - } - - unless (defined $inst_dir) { - $inst_dir = $_; - next; - } - - if (m/^\s*color\s+(\w+)\s+\"(.*?)\"\s*$/) { - push @colors, [ $2, $1 ]; - - } elsif (m/^\s*section\s+\"(.*)\"\s*$/) { - $section = $1; - - } elsif (m/^([\w\-]+)\s*=\s*(.*?)\s*$/) { - $variables{$1} = $2; - - } elsif (m/^([\w\-]+)\s*$/) { - push @packages, { - 'name' => $1, - 'section' => $section, - 'opt' => '', - 'cfg' => '', - 'rev' => '0' - }; - - } elsif (m/^\s*(\w\w\w)\s*:\s*(.*?)\s*$/) { - my $field = $1; - my $value = $2; - - my $err = 1; - foreach ('url', 'cfg', 'opt', 'env', 'dif') { - if ($field eq $_) { - $err = 0; - last; - } - } - - die "$filename:$.: invalid field `$field'\n" if $err; - while ($value =~ m/\$\{([\w\-]+)\}/) { - if (defined $variables{$1}) { - $value =~ s/\$\{([\w\-]+)\}/$variables{$1}/; - } else { - $value =~ s/\$\{([\w\-]+)\}/$inst_dir\/$1/; - push @{$deps{$packages[-1]->{'name'}}}, $1; - } - } - $packages[-1]->{$field} = $value; - - } else { - die "$filename:$.: syntax error\n"; - } - } - - &xclose($LIST); - (defined $report_dir and defined $build_dir and $inst_dir) - or die "$0: `report', `build' and `inst' directories must be defined\n"; -} - -sub create_dir { - my $dir = shift; - return if -d $dir; - system("/bin/mkdir -p \"$dir\"") - and die "$0: unable to mkdir `$dir'\n"; -} - -sub delete_dir { - my $dir = shift; - return unless -d $dir; - system("/bin/chmod -fR u+w \"$dir\"; /bin/rm -rf \"$dir\"") - and die "$0: unable to remove `$dir'\n"; -} - -sub ch_dir { - my $dir = shift; - chdir $dir or die "$0: unable to chdir `$dir': $!\n"; -} - -sub time_to_str { - $_ = shift; - my $t = ''; - - if (defined $_ and $_ ne '') { - $t = ($_ % 60) . "s"; - $t = (($_/60) % 60) . "m$t" if $_ >= 60; - $t = (($_/(60 * 60)) % 60) . "h$t" if $_ >= 60 * 60; - } - - return $t; -} - -sub gen_summary { - my $SUMMARY = &header("$report_dir/index.html", "Auto Build System"); - my $section = ''; - - print <<HTML; - <h1>Auto Build System</h1> - <hr/> - <table> -HTML - - foreach my $pkg (@packages) { - if ($pkg->{'section'} ne $section) { - if ($section ne '') { - print <<HTML; - <tr> - <td> - - </td> - </tr> -HTML - } - $section = $pkg->{'section'}; - print <<HTML; - <tr> - <td> - <h3>$section</h3> - </td> - </tr> -HTML - } - - $pkg->{'status'} = '' unless defined $pkg->{'status'}; - - my $color = "black"; - my $time = &time_to_str($pkg->{'time'}); - - $color = "red" if $pkg->{'status'} eq "failed"; - $color = "green" if $pkg->{'status'} eq "succeeded"; - $color = "gray" if $pkg->{'status'} eq "skipped"; - - print <<HTML; - <tr> - <td><a href=\"$pkg->{'name'}/$pkg->{'name'}.html\">$pkg->{'name'}</a></td> - <td> </td> - <td><font color=\"$color\">$pkg->{'status'}</font></td> - <td> </td> - <td><font color=\"gray\">$time</font></td> - </tr> -HTML - } - - print " </table>\n"; - &footer($SUMMARY); -} - -sub gen_pkg_report { - my $pkg = shift; - my $filename = "$report_dir/$pkg->{'name'}/$pkg->{'name'}.html"; - my $REPORT = &header("$filename", "$pkg->{'name'} report"); - - my ($ts, $tf) = ('', ''); - $ts = localtime $pkg->{'time_started'} if defined $pkg->{'time_started'}; - $tf = localtime $pkg->{'time_finished'} if defined $pkg->{'time_finished'}; - - print <<HTML; - <h1>$pkg->{'name'}</h1> - <hr/> - <h3>Build started: $ts</h3> - <table cellpadding=\"8\"> -HTML - - foreach my $step (@{$pkg->{'steps'}}) { - next unless defined $step->{'name'}; - if (defined $step->{'status'}) { - my $color = "black"; - $color = "green" if $step->{'status'} eq "succeeded"; - $color = "red" if $step->{'status'} eq "failed"; - - my $time = &time_to_str($step->{'time'}); - - print <<HTML; - <tr> - <td><a href=\"$step->{'name'}.html\">$step->{'name'}</a></td> - <td><font color=\"$color\">$step->{'status'}</font></td> - <td><font color=\"gray\">$time</font></td> - </tr> -HTML - } else { - print <<HTML; - <tr> - <td>$step->{'name'}</td> - </tr> -HTML - } - } - - print <<HTML; - </table> - <h3>Build finished: $tf</h3> -HTML - - if (defined $pkg->{'archives'}) { - foreach (@{$pkg->{'archives'}}) { - print " <a href=\"$_\">$_</a><br/>\n" if -f $_; - } - } - - &footer($REPORT); -} - -sub set_status { - $_[0]->{'status'} = $_[1]; - print " $_[1]\n"; - &gen_summary; -} - -sub gen_log_page { - my $pkg = shift; - my $step = shift; - my $filename = "$report_dir/$pkg->{'name'}/$step->{'name'}.html"; - my $REPORT = &header("$filename", "$pkg->{'name'} - $step->{'name'}"); - - print <<HTML; - <h1>$step->{'name'}</h1> - <h4>$step->{'cmd'}</h4> - <hr/> - <a href="$step->{'name'}.log">Log</a> -HTML - - &footer($REPORT); -} - -sub run { - my $pkg = shift; - my $step = shift; - my $status = $step->{'name'}; - my $out = "$report_dir/$pkg->{'name'}/$status"; - - &set_status($pkg, $status); - &gen_log_page($pkg, $step); - - my $t = time; - my $exit_code = system("$step->{'cmd'} >$out.log 2>&1"); - $step->{'time'} = time - $t; - - my $REPORT = &header("$out.html", "$pkg->{'name'} - $status"); - - print <<HTML; - <h1>$step->{'name'}</h1> - <h4>$step->{'cmd'}</h4> - <hr/> - <pre> -HTML - - my $LOG = &xopen("$out.log"); - my $archives_ready; - - while (<$LOG>) { - my $color; - foreach my $c (@colors) { - $color = $c->[1] if m/^$c->[0]/; - last if defined $color; - } - if ($status eq 'check') { - if (($archives_ready and m/^($archives_ready\.tar\.(gz|bz2))$/) - or m/^(.*\.tar\.(gz|bz2)) is ready for distribution$/) { - push @{$pkg->{'archives'}}, $1; - system("/bin/cp \"$1\" \"$report_dir/$pkg->{'name'}\"") - and die "$0: unable to cp `$1' to `$report_dir/$pkg->{'name'}': $!\n" ; - } - $archives_ready = $1 if m/^(.*) archives ready for distribution: $/; - } - print "<font color=\"$color\">" if $color; - print; - print "</font>" if $color; - } - - print " </pre>\n"; - &xclose($LOG); - &footer($REPORT); - return undef if $exit_code; - return 1; -} - -sub check_revision { - my $pkg = shift; - my $revfile = "$inst_dir/$pkg->{'name'}/.rev"; - my $rev; - - if ($pkg->{'url'} =~ m|^svn:(.*/([^/]+))$|) { - chomp($rev = `svn log -q \"https:$1\" 2>/dev/null | grep '^r' | head -n 1`) ; - $rev =~ s/^r(\d+).*$/$1/; - } elsif ($pkg->{'url'} =~ m/^((ht|f)tp:.*)$/) { - chomp($rev = `wget -qnv -O - \"$1\" | md5sum`); - } - $pkg->{'rev'} = $rev; - - if (-f $revfile) { - chomp($rev = `cat \"$revfile\"`); - unlink $revfile if $pkg->{'rev'} ne $rev; - } - if (-f $revfile) { - chomp($pkg->{'time'} = `cat \"$inst_dir/$pkg->{'name'}/.time\"`); - &set_status($pkg, 'succeeded'); - return 1; - } - return undef; -} - -sub get_package { - my $pkg = shift; - my @steps; - $_ = $pkg->{'url'}; - - if (m|^http://.*/([^/]+)/?$| - or m|^ftp://.*/([^/]+)/?$|) { - $_ = "file://$1"; - push @steps, { - 'name' => 'download', - 'cmd' => "wget -nv \"$pkg->{'url'}\"" - }; - } - - if (m|^file://(.*)$|) { - my $file = $_ = $1; - my $c; - - $c = "z" if m/\.tar\.gz$/ or m/\.tgz$/; - $c = "j" if m/\.tar\.bz2$/ or m/\.tbz2$/; - $pkg->{'dir'} = $file; - - if (defined $c) { - push @steps, { - 'name' => 'unpack', - 'cmd' => "tar -x${c}vf \"$file\"" - }, { - 'dir' => "tar -t${c}f \"$file\" | head -n 1 | cut -d '/' -f 0-1", - }; - } - - } elsif (m|^svn://(.*/([^/]+))/?$|) { - my $repository = "https://$1"; - $pkg->{'dir'} = $2; - - push @steps, { - 'name' => 'checkout', - 'cmd' => "svn checkout \"$repository\"" - }, { - 'dir' => "echo \"$pkg->{'dir'}\"", - 'name' => 'bootstrap', - 'cmd' => '(./bootstrap || ./bootstrap.sh || autoreconf -fvi)' - }; - - } elsif (m|^prcs://(.*)$|) { - $pkg->{'dir'} = $1; - - push @steps, { - 'dir' => "echo \"$1\"", - 'name' => 'checkout', - 'cmd' => "prcs checkout \"$1\"" - }, { - 'name' => 'bootstrap', - 'cmd' => '(./bootstrap || ./bootstrap.sh || autoreconf -fvi)' - }; - - } else { - die "$0: invalid URL `$_': unknown protocol\n"; - } - - return @steps; -} - -sub prepare_pkgs { - foreach my $pkg (@packages) { - my @steps = &get_package($pkg); - my $dir = "$report_dir/$pkg->{'name'}"; - &create_dir($dir); - - push @steps, { - 'name' => 'patch', - 'cmd' => "patch -p0 -t -i \"$pkg->{'dif'}\"" - } if defined $pkg->{'dif'}; - push @steps, { - 'name' => 'configure', - 'cmd' => "./configure --prefix=$inst_dir/$pkg->{'name'} $pkg->{'cfg'}" - }, { - 'name' => 'build', 'cmd' => 'make' - }; - my $dcf = ''; - defined $pkg->{'cfg'} and $pkg->{'cfg'} ne '' - and $dcf = "DISTCHECK_CONFIGURE_FLAGS=\"$pkg->{'cfg'}\" "; - push @steps, { - 'name' => 'check', - 'cmd' => "make ${dcf}distcheck" - } unless $pkg->{'opt'} =~ m/\bno-check\b/; - push @steps, { - 'rm' => "$inst_dir/$pkg->{'name'}" - },{ - 'name' => 'install', 'cmd' => 'make install' - }; - $pkg->{'steps'} = \@steps; - next if &check_revision($pkg); - &gen_pkg_report($pkg) unless -f "$dir/$pkg->{'name'}.html"; - } -} - -sub build_pkg { - my $pkg = shift; - my %env = %ENV; - - if (defined $pkg->{'env'}) { - foreach (split /\s*,\s*/, $pkg->{'env'}) { - if (m/^\s*(\w+)\s*=\s*\"(.*)\"\s*$/) { - my ($v, $w) = ($1, $2); - $w =~ s/\$(\w+)/$ENV{$1}/; - $ENV{$v} = $w; - } - } - } - - my $st; - my $dir = "$build_dir/$pkg->{'name'}"; - &delete_dir($dir); - &create_dir($dir); - &ch_dir($dir); - $pkg->{'time_started'} = time; - foreach my $step (@{$pkg->{'steps'}}) { - $step->{'status'} = "running"; - &gen_pkg_report($pkg); - &delete_dir($step->{'rm'}) if defined $step->{'rm'}; - if (defined $step->{'dir'}) { - chomp($pkg->{'dir'} = `$step->{'dir'}`); - &create_dir($pkg->{'dir'}); - &ch_dir($pkg->{'dir'}); - } - if (not defined $step->{'cmd'} - or &run($pkg, $step)) { - $step->{'status'} = $st = "succeeded"; - next; - } - $step->{'status'} = $st = "failed"; - last; - } - - $pkg->{'time_finished'} = time; - $pkg->{'time'} = $pkg->{'time_finished'} - $pkg->{'time_started'}; - %ENV = %env; - &gen_pkg_report($pkg); - &ch_dir($build_dir); - &set_status($pkg, $st); - if ($st eq "succeeded") { - system("echo \"$pkg->{'rev'}\" > \"$inst_dir/$pkg->{'name'}/.rev\""); - system("echo \"$pkg->{'time'}\" > \"$inst_dir/$pkg->{'name'}/.time\""); - &delete_dir($pkg->{'name'}); - } -} - -sub version { - print "auto-build (Auto Build System) $VERSION\n", - "Written by Clement Vasseur and Nicolas Pouillard.\n\n", - "Copyright (C) 2004 LRDE - EPITA Research and Development Laboratory.\n", - "This is free software; see the source for copying conditions. ", - "There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A ", - "PARTICULAR PURPOSE.\n"; - exit 0; -}
participants (1)
-
Clement Vasseur