#!/usr/local/bin/perl
#
# Copyright (c) 2002-2004 Eric Wallengren
# This file is part of the Continuous Automated Build and Integration
# Environment (CABIE)
#
# CABIE is distributed under the terms of the GNU General Public
# License version 2 or any later version. See the file COPYING for copying
# permission or http://www.gnu.org.
#
# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED OR
# IMPLIED, without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. ANY USE IS AT YOUR OWN RISK.
#
# Permission to modify the code and to distribute modified code is granted,
# provided the above notices are retained, and a notice that the code was
# modified is included with the above copyright notice.
#
use Getopt::Long;
use File::Basename;
use English;
use Sys::Hostname;
my $hostname = hostname();
BEGIN {push @INC, "./lib";}
require "$hostname.pm";
my $config = new $hostname;
my $logo = $config->LOGOICON;
my $prog = basename( $PROGRAM_NAME );
use strict;
use vars qw(
$opt_help
$opt_infile
$opt_outfile
$opt_errorfile
$opt_refresh
$opt_binary
$opt_server
$opt_port
$opt_job );
# ------------------------------------------------------------------
my( @whenlist ) = ( "build", "package" );
my( $output ) = "";
my( $num_sections ) = 0;
my( $num_warnings ) = 0;
my( $num_errors ) = 0;
my( @error_list );
my( %section_link );
my( %warn_link );
my( %error_link );
my( $module_order );
my( %elapsed_time );
my( %products );
my( %ignored );
my( $image_platform );
my( $image_product );
# ------------------------------------------------------------------
# Load the list of error strings from a file.
sub loadErrorList
{
my( $error_file ) = @_;
open( ERRORFILE, "<$error_file" )
or die "Can't open $error_file";
@error_list = <ERRORFILE>;
close( ERRORFILE );
chomp @error_list;
}
# ------------------------------------------------------------------
# Return 1 if an error string is detected in the input line.
sub hasError
{
my( $line ) = @_;
my( $err );
foreach $err ( @error_list )
{
if ( index( $line, $err ) > -1 )
{
return 1;
}
}
return 0;
}
# ------------------------------------------------------------------
# Escape HTML characters. "<" -> < ">" -> > "&" -> &
sub esc
{
my( $text ) = @_;
$text =~ s/&/&/g;
$text =~ s/</</g;
$text =~ s/>/>/g;
return $text;
}
# ------------------------------------------------------------------
sub processLog
{
my( $in_header ) = 0;
my( $when );
my( %seen );
my( %seen_product );
my( $who );
my( @contents );
my( @tmpcontents );
my( $line );
while ( <BUILDLOG> )
{
if ( /^\s*(\[echo\])?\s*#-----------------/ )
{
if ( ! $in_header )
{
$output = $output .
"</pre>\n" .
"<h4 class='section'>\n" .
"<pre>\n" .
esc( $_ );
$in_header = 1;
}
else
{
$in_header = 0;
$output = $output .
esc( $_ ) .
"</pre>\n" .
"</h4>\n" .
"<pre>\n";
}
}
elsif ( /^\s*\[create-distribution\]\s*creating.*image/ )
{
my( @words ) = split( " " );
$image_platform = $words[ $#words - 2 ] eq "creating"
? "all"
: $words[ $#words - 2 ];
$image_product = $words[ $#words - 1 ];
$num_sections++;
$output = $output .
"</pre>\n" .
"<h4 class='section'>\n" .
"<a name='section_$num_sections'><pre>$_</pre></a>\n" .
"</h4>\n" .
"<pre>\n";
$who = "$image_product-$image_platform";
$section_link{ $who, "image" } = $num_sections;
if ( ! $seen_product{ $who } )
{
$seen_product{ $who } = 1;
$products{ $image_product } .= " " . $image_platform;
}
}
elsif ( /^\s*\[create-distribution\]\s*elapsed staging time:/ )
{
my( @words ) = split( " " );
$output = $output . esc( $_ );
$elapsed_time{ $who, "image" } = $words[ $#words - 1 ];
}
elsif ( /^\s*\[create-distribution\]\s*elapsed iso time:/ )
{
my( @words ) = split( " " );
$output = $output . esc( $_ );
$elapsed_time{ $who, "iso" } = $words[ $#words - 1 ];
}
elsif ( $in_header eq 1 )
{
my( $ignore );
my( @words ) = split( " " );
chomp;
$num_sections++;
$output = $output . "<a name='section_$num_sections'>" . esc( $_ ) .
"</a>\n";
$ignore = $words[ $#words - 2 ] eq "Ignore";
if ( $words[ $#words ] eq "module" )
{
$when = "build";
}
elsif ( $words[ $#words ] eq "package" )
{
$when = "package";
}
else
{
$when = "?";
}
if ( $when ne "?" )
{
my( $module ) = $words[ $#words - 1 ];
$who = $module;
$section_link{ $who, $when } = $num_sections;
$ignored{ $who, $when } = $ignore;
if ( ! $seen{ $module } )
{
$seen{ $module } = 1;
$module_order = "$module_order $module";
}
}
}
else
{
if ( hasError( $_ ) )
{
$num_errors++;
chomp;
$output = $output .
"</pre>\n" .
"<pre class='error'>\n" .
"\n" .
"<a name='error_$num_errors'>" . esc( $_ ) .
"</a>\n" .
"\n" .
"</pre>\n" .
"<pre>\n";
$error_link{ $who, $when } .= " $num_errors";
}
elsif ( /warning/i )
{
$num_warnings++;
chomp;
$output = $output .
"</pre>\n" .
"<pre class='warn'>\n" .
"\n" .
"<a name='warning_$num_warnings'>" . esc( $_ ) .
"</a>\n" .
"\n" .
"</pre>\n" .
"<pre>\n";
$warn_link{ $who, $when } .= " $num_warnings";
}
elsif ( /^\s*(\[echo\])?\s*# .* time: [0-9.:]+ sec/ )
{
my( @words ) = split( " " );
my( $secs ) = $words[ $#words - 1 ];
my( @t ) = split( ":", $secs );
if ( $#t > 0 )
{
$secs = $t[ 0 ] * 60 + $t[ 1 ];
}
$output = $output . esc( $_ );
$elapsed_time{ $who, $when } = $secs;
}
else
{
$output = $output . esc( $_ );
}
}
}
close( BUILDLOG );
}
# ------------------------------------------------------------------
sub outputHtml
{
print HTML "<html>\n";
print HTML "<head>\n";
if ( $opt_refresh > 0 )
{
print HTML " <meta http-equiv='refresh' content='$opt_refresh'/>\n";
}
print HTML " <style>\n";
print HTML " th.title { font-size: 125%; background: #ccccff; }\n";
print HTML " td { padding-left: 4; padding-right: 4; }\n";
print HTML "\n";
print HTML " .working { background: #ffff99; }\n";
print HTML " .section { background: #ccccff; }\n";
print HTML " .success { background: #b9ffb9; }\n";
print HTML " .error { background: #ff9999; }\n";
print HTML " .warn { background: #ffcc99; }\n";
print HTML "\n";
print HTML " .time { text-align: right; background: #ddddff; font-family: monospace; }\n";
print HTML " .name { background: #ddddff; }\n";
print HTML " ul.nav li { display: inline;\n";
print HTML " overflow: hidden;\n";
print HTML " list-style-type: none;\n";
print HTML " }\n";
print HTML " </style>\n";
print HTML "</head>\n";
print HTML "<body>\n";
print HTML "<a name='top'/>";
print HTML "<img src=\"$logo\" alt=\"LOGO\">\n";
print HTML "<br>";
print HTML "<hr>";
outputNavigation();
outputModuleTable();
print HTML "<p/>\n";
outputCdTable();
print HTML "<pre>\n";
print HTML $output;
print HTML "</pre>\n";
outputNavigation();
print HTML "<a name='bottom'/>";
print HTML "</body>\n";
close( HTML );
}
# ------------------------------------------------------------------
sub outputNavigation
{
print HTML "<ul class='nav'>\n";
print HTML " <li>Go to:</li>";
print HTML " <li><a href='#top'>[top]</a></li>\n";
print HTML " <li><a href='#bottom'>[bottom]</a></li>\n";
print HTML "</ul>\n";
}
# ------------------------------------------------------------------
sub outputModuleTable
{
my( $when );
my( $module );
print HTML "<table>\n";
print HTML "<thead>\n";
print HTML "<tr>\n";
print HTML " <th class='title'>module</th>";
foreach $when ( @whenlist )
{
print HTML " <th/>\n";
print HTML " <th class='title' colspan='2'>$when</th>\n";
}
print HTML "</tr>\n";
print HTML "</thead>\n";
foreach $module ( split( " ", $module_order ) )
{
print HTML "<tr>\n";
print HTML " <td class='name'>$module</td>\n";
foreach $when ( @whenlist )
{
my( $status );
if ( $elapsed_time{ $module, $when } )
{
$status = "success";
}
else
{
$status = $ignored{ $module, $when } ? "n/a" : "working";
}
print HTML " <td/>";
outputStatusCell( $module, $when, $status );
outputTimeCell( $module, $when );
}
print HTML "</tr>\n";
}
print HTML "</table>\n";
}
# ------------------------------------------------------------------
sub outputCdTable
{
my( $when );
my( $key );
my( $platform );
if ( %products )
{
print HTML "<table>\n";
print HTML "<thead>\n";
print HTML "<tr>\n";
print HTML " <th class='title'>product</th>";
print HTML " <th class='title'>platform</th>";
print HTML " <th/>\n";
print HTML " <th class='title'>status</th>\n";
print HTML " <th class='title'>image</th>\n";
print HTML " <th class='title'>iso</th>\n";
print HTML "</tr>\n";
print HTML "</thead>\n";
for $key ( sort ( keys %products ) )
{
my( @platforms ) = split( " ", $products{ $key } );
my( $first ) = 1;
my( $span ) = $#platforms + 1;
print HTML " <tr/>\n";
print HTML " <tr>\n";
print HTML " <td class='name' rowspan='$span'>$key</td>\n";
foreach $platform ( sort @platforms )
{
my( $who ) = "$key-$platform";
if ( ! $first )
{
print HTML " <tr>\n";
}
print HTML " <td class='name'>$platform</td>\n";
print HTML " <td/>";
outputStatusCell( $who, "image", "success" );
outputTimeCell( $who, "image" );
outputTimeCell( $who, "iso" );
print HTML " </tr>\n";
$first = 0;
}
}
print HTML "</table>\n";
}
}
# ------------------------------------------------------------------
sub outputStatusCell
{
my( $who, $when, $status ) = @_;
my( $section ) = $section_link{ $who, $when };
my( $warn ) = $warn_link{ $who, $when };
my( $error ) = $error_link{ $who, $when };
my( $num );
# Add all error links
if ( $error )
{
print HTML " <td class='error'>";
print HTML "\n <a href='#section_$section'>errors:</a>\n";
foreach $num ( split( " ", $error ) )
{
print HTML " <a href='#error_$num'>$num</a>\n";
}
print HTML " ";
}
# Add all warning links
if ( $warn )
{
if ( $error )
{
print HTML "<br/>\n";
}
else
{
print HTML " <td class='warn'>";
}
print HTML "\n <a href='#section_$section'>warnings:</a>\n";
foreach $num ( split( " ", $warn ) )
{
print HTML " <a href='#warning_$num'>$num</a>\n";
}
print HTML " ";
}
# If no error or warnings, add section link if applicable
if ( ! $error && ! $warn )
{
if ( $section )
{
if ( $status eq "working" )
{
print HTML " <td class='working'>";
}
else
{
print HTML " <td class='success'>";
}
print HTML "<a href='#section_$section'>$status</a>";
}
else
{
print HTML " <td class='success'>";
print HTML " " .
" ";
}
}
print HTML "</td>\n";
}
# ------------------------------------------------------------------
sub outputTimeCell
{
my( $who, $when ) = @_;
my( $time ) = $elapsed_time{ $who, $when };
if ( $time )
{
my( $min ) = 0;
my( $sec ) = 0;
if ( $time >= 60 )
{
$min = int( $time / 60 );
$sec = $time % 60;
}
else
{
$sec = $time;
}
$time = sprintf( "%d' %02d\"", $min, $sec );
}
if ( ! $time )
{
$time = " ";
}
print HTML " <td class='time'>$time</td>\n";
}
# ------------------------------------------------------------------
sub help
{
print STDERR "Usage: $prog -e error_file [options]\n";
print STDERR "\n";
print STDERR " options:\n";
print STDERR " -i log_file\n";
print STDERR " -o html_file\n";
print STDERR " -e error_file\n";
print STDERR " -S buildserver\n";
print STDERR " -P port\n";
print STDERR " -j jobname\n";
print STDERR " -b path-to-build-command\n";
print STDERR " -r meta-refresh-in-secs\n";
print STDERR "\n";
print STDERR " If neither -i nor -j is specified, stdin is used.\n";
print STDERR " If -o is not specified, stdout is used.\n";
}
# ------------------------------------------------------------------
if ( ! GetOptions( "infile|i=s",
"outfile|o=s",
"errorfile|e=s",
"refresh|r=i",
"port|p=s",
"server|s=s",
"binary|b=s",
"job|j=s",
"help|h" ) )
{
help();
exit 1;
}
if ( $opt_help )
{
help();
exit 0;
}
#-----------------------------------------------------------------------
if ( $opt_job )
{
open( BUILDLOG, "$opt_binary buildlog -n $opt_job -t retail|" )
or die "Can't open $opt_job";
}
elsif ( $opt_infile )
{
if ( ! -f $opt_infile )
{
print "Error: $prog: no such file: $opt_infile\n";
exit 1;
}
open( BUILDLOG, "<$opt_infile" )
or die "Can't open $opt_infile";
}
else
{
open( BUILDLOG, "<&=STDIN" );
}
#-----------------------------------------------------------------------
if ( ! $opt_errorfile )
{
print "Error: $prog: '-e error_file' is required\n";
help();
exit 1;
}
elsif ( ! -f $opt_errorfile )
{
print "Error: $prog: no such file: $opt_errorfile\n";
exit 1;
}
else
{
loadErrorList( $opt_errorfile );
}
#-----------------------------------------------------------------------
if ( $opt_outfile )
{
open( HTML, ">$opt_outfile" )
or die "Can't open $opt_outfile";
}
else
{
open( HTML, ">&=STDOUT" );
$| = 1;
}
$ENV{ BLDSERVER } = "$opt_server:$opt_port";
processLog();
outputHtml();