#!/usr/bin/perl

# Copyright (C) 2004 Simon Josefsson.
#
# This file is part of Autobuild.
#
# Autobuild 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, or (at your option)
# any later version.
#
# Autobuild 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 Autobuild; see the file COPYING.  If not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
# MA 02111-1307, USA.

use strict;
use Getopt::Long;

# Parse command line parameters.
my $Verbose;
my ($PrintHelp, $PrintVersion);
my ($Abort, $DryRun);
my ($Project, $Revision, $Mode, $Hostname, $Hosttype,
    $Buildtype, $Timestamp, $Status);
GetOptions ('help|usage|h' => \$PrintHelp,
	    'version'      => \$PrintVersion,
	    'verbose|v'    => \$Verbose,
	    'abort|a'      => \$Abort,
	    'dry-run|n'    => \$DryRun,
	    'p|project=s'  => \$Project,
	    'revision=s'   => \$Revision,
	    'mode=s'       => \$Mode,
	    'Hosttype=s'   => \$Hosttype,
	    'Buildtype=s'  => \$Buildtype,
	    'Hostname=s'   => \$Hostname,
	    'Timestamp=s'  => \$Timestamp,
	    'Status=s'     => \$Status);

# Handle --help.
if ($PrintHelp || (!$PrintVersion && $#ARGV == -1)) {
    print "Usage: $0 [OPTION]... FILE...\n";
    print "\n";
    print "Read and parse build logs to find project name, revision, build mode,\n";
    print "build host type, build type (for cross compile builds), hostname, and\n";
    print "timestamp, then print a HTML page with information and links to the logs.\n";
    print "\n";
    print "Mandatory arguments to long options are mandatory for short options too.\n";
    print "\n";
    print "      --abort             Abort if any of the following values\n";
    print "                          cannot be guessed: project name, revision,\n";
    print "                          host type, and build type.\n";
    print "  -n, --dry-run           Just parse, don't print output.\n";
    print "  -v, --verbose           Explain what is being done.\n";
    print "\n";
    print "For use when autobuild fail to guess the values properly:\n";
    print "\n";
    print "  -p, --project=STRING    Specify project name.\n";
    print "      --revision=STRING   Specify project revision.\n";
    print "      --mode=STRING       Specify build mode (typically 'default').\n";
    print "      --hosttype=STRING   Specify host type (e.g., i686-pc-linux-gnu).\n";
    print "      --buildtype=STRING  Specify build type (e.g., m68k-uclinux-elf).\n";
    print "                          Different from hosttype for cross compiles.\n";
    print "      --hostname=STRING   Specify name of host log was created on.\n";
    print "      --timestamp=STRING  Specify when build was made.\n";
    print "                          Any date format will work, but\n";
    print "                          'YYYY-MM-DDTHH:MM:SS' is recommended.\n";
    print "      --status=STRING     Outcome of build.\n";
    print "                          Any string will work, but \"ok\" and\n";
    print "                          \"fail\" are recommended for the two\n";
    print "                          basic outcomes.\n";
    print "\n";
    print "Other options:\n";
    print "\n";
    print "      --help              Display this help and exit.\n";
    print "      --version           Output version information and exit.\n";
    print "\n";
    print "Report bugs to <bug-autobuild\@josefsson.org>.\n";
    exit 0;
}

# Handle --verison.
if ($PrintVersion) {
    my ($rev) = '$Revision: 1.54 $';
    $rev = $1 if $rev =~ m/Revision: (\S+) /;
    print "autobuild (autobuild) $rev\n";
    print "\n";
    print "Copyright (C) 2004 Simon Josefsson\n";
    print "This is free software; see the source for copying conditions.  There is NO\n";
    print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
    exit 0;
}

# Core.

my (@Files, $arg, $file);

my (%Project);
my (%Revision);
my (%Mode);
my (%Hosttype);
my (%Buildtype);
my (%Hostname);
my (%Timestamp);
my (%Status);

my (%Projects);
my (%Revisions);
my (%Hosttypes);
my (%Hostnames);

foreach $arg (@ARGV) {
    my (@files);

    if (-d $arg) {
	if (!opendir DIRH, $arg) {
	    warn "Cannot open directory: $arg";
	    next;
	}
	@files = grep { /^[^.]/ && -f "$arg/$_" } readdir(DIRH);
	closedir DIRH;
    } elsif (-f $arg) {
	push @files, $arg;
    } else {
	warn "Skipping unknown file: $arg";
	next;
    }

    foreach $file (@files) {
	my ($project, $revision, $mode, $hostname, $hosttype,
	    $buildtype, $timestamp, $status);

	# Initialize variables.
	$project = $Project;
	$revision = $Revision;
	$mode = $Mode;
	$hostname = $Hostname;
	$hosttype = $Hosttype;
	$buildtype = $Buildtype;
	$timestamp = $Timestamp;
	$status = $Status;

	open FH, $file or die "Cannot open file: $file";

	while (<FH>) {
	    if (m,\r\n$,) {
		chop; chop;
		$_ = $_ . "\n";
	    }

	    # These override everything (except command line parameters).
	    $project = $1 if !$Project && m,autobuild project... (.+),;
	    $revision = $1 if !$Revision && m,autobuild revision... (.+),;
	    $mode = $1 if !$Mode && m,autobuild mode... (.+),;
	    $hostname = $1 if !$Hostname && m,autobuild hostname... (.+),;
	    $hosttype = $1 if !$Hosttype && m,autobuild hosttype... (.+),;
	    $buildtype = $1 if !$Buildtype && m,autobuild buildtype... (.+),;
	    $timestamp = $1 if !$Timestamp && m,autobuild timestamp... (.+),;

	    # GNU Autoconf host/build output.
	    $hosttype = $3 if !$hosttype && m,host system type(<[^>]+>)?\.\.\. (<[^>]+>)?([a-z0-9_.-]+),;
	    $buildtype = $3 if !$buildtype && m,build system type(<[^>]+>)?\.\.\. (<[^>]+>)?([a-z0-9_.-]+),;

	    # Automake self test output.
	    $status = "ok" if !$status && m,All [0-9]+ tests passed,;
	    $status = "almost" if !$status && m,[0-9]+ of [0-9]+ tests failed,;

	    # Works for GNU make.
	    $project = $2 if !$project && m,Entering directory `(<[^>]+>)?.*/([a-z-]+)-[0-9.a-z-]+/.*(<[^>]+>)?',;
	    $revision = $2 if !$revision && m,Entering directory `(<[^>]+>)?.*/[a-z-]+-([0-9.a-z-]+)/.*(<[^>]+>)?',;

	    # Telnet string.
	    $hostname = $1 if !$hostname && m,Trying (.*)\.\.\.,;
	    $hostname = $1 if !$hostname && m,Connected to (.*)\.,;

	    # SSH string.
	    $hostname = $1 if !$hostname && m,Connection to (.*) closed.,;

	    # If output mention a tar.gz archive.
	    $project = $1 if !$project && m,/([a-z-]+)-[0-9.a-z-]+.tar.gz,;
	    $revision = $1 if !$revision && m,/[a-z-]+-([0-9.a-z-]+).tar.gz,;

	    # If you invoke set standing in the directory of the project...
	    # Happens to work for 'abbuild-sourceforge', but looks error prone.
	    $project = $2 if !$project && m,PWD=(<[^>]+>)?.*/([a-z0-9-]+)-[0-9.a-z-]+,;
	    $revision = $2 if !$revision && m,PWD=(<[^>]+>)?.*/[a-z0-9-]+-([0-9.a-z-]+),;

	    # Errors from tar...
	    $project = $2 if !$project && m,tar(<[^>]+>)?: ([a-z0-9-]+)-[0-9.a-z-]+/,;
	    $revision = $2 if !$revision && m,tar(<[^>]+>)?: [a-z0-9-]+-([0-9.a-z-]+)/,;

	    # File name .../*.tar.gz...
	    $project = $1 if !$project && m,/([a-z0-9-]+)-[0-9.a-z-]+.tar.gz,;
	    $revision = $1 if !$revision && m,/[a-z0-9-]+-([0-9.a-z-]+).tar.gz,;
	}

	close FH;

	if (!$Abort) {
	    $project = "unknown" if !$project;
	    $revision = "0" if !$revision || $revision eq "";
	    $hosttype = "unknown" if !$hosttype;
	    $buildtype = "unknown" if !$buildtype;
	}
	$hostname = "unknown" if !$hostname;
	$mode = "default" if !$mode;
	$status = "fail" if !$status;
	$timestamp = "unknown" if !$timestamp;

	if ($Verbose) {
	    print STDERR "`$file':\n";
	    print STDERR " Project: $project\n";
	    print STDERR " Revision: $revision\n";
	    print STDERR " Mode: $mode\n";
	    print STDERR " Hosttype: $hosttype\n";
	    print STDERR " Buildtype: $buildtype\n";
	    print STDERR " Hostname: $hostname\n";
	    print STDERR " Timestamp: $timestamp\n";
	    print STDERR " Status: $status\n";
	}

	die "Could not guess all required values" .
	    ($Verbose ? " for file\n" :
	     " (use -v to print values) while reading:\n$file\n")
	    if $Abort && (!$project || $revision eq "" ||
			  !$hosttype || !$buildtype || !$status);

	push @Files, $file;

	$Project{$file} = $project;
	$Revision{$file} = $revision;
	$Mode{$file} = $mode;
	$Hostname{$file} = $hostname;
	$Hosttype{$file} = $hosttype;
	$Buildtype{$file} = $buildtype;
	$Timestamp{$file} = $timestamp;
	$Status{$file} = $status;

	$Projects{$project} = 0;
	${$Revisions{$project}}{$revision} = 0;
	${$Hosttypes{$project}}{$hosttype} = 0;
	${$Hostnames{$project}}{$hostname} = 0;
    }
}

exit 0 if $DryRun;

my ($project, $revision, $hosttype, $hostname);

print "<html>\n";
print "<head>\n";
print "<title>Index to Autobuild logs</title>\n";
print "</head>\n";
print "\n";
print "<body>\n";
print "<h1>Index to Autobuild logs</h1>\n";
print "\n";
print "<p>This page was created on " . (scalar localtime) . " using <a href=\"http://josefsson.org/autobuild/\">Autobuild</a> written by Simon Josefsson.\n";
print "\n";

if (scalar keys %Projects > 1) {
    print "<hr>\n";
    print "<h2>Quick links to each project</h2>\n";
    print "\n";
    print "<p><ul>\n";
    foreach $project (sort keys %Projects) {
	print "<li><a href=\"#$project\">$project</a>\n";
    }
    print "</ul>\n";
}

foreach $project (sort keys %Projects) {
    print "<hr>\n";
    print "<h2><a name=\"$project\">Project '$project'</a></h2>\n";
    print "\n";
    print "<p>Revisions (" . keys(%{$Revisions{$project}}) . "): ";
    foreach $revision (reverse sort { if ($a eq $b) { return 0; } else { my $i = 0; do { my $j = substr ($a, $i, 1); my $k = substr ($b, $i, 1); if ($j != $k) { $j = substr ($a, $i); $k = substr ($b, $i); return $j <=> $k; } $i++; } while ($i < length($a)); } } keys %{$Revisions{$project}}) {
	print "<a href=\"#$project-$revision\">$revision</a>, ";
    }
    print "\n";
    print "<p>Hosttypes (" . keys(%{$Hosttypes{$project}}) . "): ";
    foreach $hosttype (sort keys %{$Hosttypes{$project}}) {
	print "<a href=\"#$project-$hosttype\">$hosttype</a>, \n";
    }
    print "\n";
    print "<p>Build hosts (" . keys(%{$Hostnames{$project}}) . "): ";
    foreach $hostname (sort keys %{$Hostnames{$project}}) {
	print "<a href=\"#$project-$hostname\">$hostname</a>, \n";
    }
    print "\n";

    foreach $revision (reverse sort { if ($a eq $b) { return 0; } else { my $i = 0; do { my $j = substr ($a, $i, 1); my $k = substr ($b, $i, 1); if ($j != $k) { $j = substr ($a, $i); $k = substr ($b, $i); return $j <=> $k; } $i++; } while ($i < length($a)); } } keys %{$Revisions{$project}}) {
	print "<hr>\n";
	print "<a name=\"$project-$revision\"><h3>Summary for $project $revision</h3></a>\n";
	print "\n";
	print "<table border=1>\n";
	print "<tr>\n";
	print "<td>System</td>\n";
	print "<td>Cross compile?</td>\n";
	print "<td>Build host</td>\n";
	print "<td>Build date</td>\n";
	print "<td>Results</td>\n";
	print "<td>Log</td>\n";
	print "</tr>\n";
	print "\n";
	foreach $file (@Files) {
	    next unless $Project{$file} eq $project;
	    next unless $Revision{$file} eq $revision;

	    my ($Project);
	    my ($Revision);
	    my ($Mode);
	    my ($Hosttype);
	    my ($Buildtype);
	    my ($Hostname);
	    my ($Timestamp);
	    my ($Status);

	    $Mode = $Mode{$file};
	    $Hosttype = $Hosttype{$file};
	    $Buildtype = $Buildtype{$file};
	    $Hostname = $Hostname{$file};
	    $Timestamp = $Timestamp{$file};
	    $Status = $Status{$file};

	    print "<tr>\n";
	    print "<td>$Hosttype</td>\n";
	    if ($Hosttype eq $Buildtype) {
		print "<td>no</td>\n";
	    } else {
		print "<td>yes, from $Buildtype</td>\n";
	    }
	    print "<td>$Hostname</td>\n";
	    print "<td>$Timestamp</td>\n";
	    if ($Status eq "ok") {
		print "<td><font color=green>Success</font></td>\n";
	    } elsif ($Status eq "almost") {
		print "<td><font color=orange>Almost</font></td>\n";
	    } else {
		print "<td><font color=red>Failure</font></td>\n";
	    }
	    print "<td><a href=\"$file\">log</a></td>\n";
	    print "</tr>\n";
	    print "\n";
	}
	print "</table>\n";
	print "\n";
    }
    print "\n";

    foreach $hosttype (sort keys %{$Hosttypes{$project}}) {
	print "<hr>\n";
	print "<a name=\"$project-$hosttype\"><h3>Summary for $project on $hosttype</h3></a>\n";
	print "\n";
	print "<table border=1>\n";
	print "<tr>\n";
	print "<td>Revision</td>\n";
	print "<td>Build host type</td>\n";
	print "<td>Build host</td>\n";
	print "<td>Build date</td>\n";
	print "<td>Results</td>\n";
	print "<td>Log</td>\n";
	print "</tr>\n";
	print "\n";
	foreach $file (@Files) {
	    next unless $Project{$file} eq $project;
	    next unless $Hosttype{$file} eq $hosttype;

	    my ($Project);
	    my ($Revision);
	    my ($Mode);
	    my ($Hosttype);
	    my ($Buildtype);
	    my ($Hostname);
	    my ($Timestamp);
	    my ($Status);

	    $Revision = $Revision{$file};
	    $Mode = $Mode{$file};
	    $Buildtype = $Buildtype{$file};
	    $Hostname = $Hostname{$file};
	    $Timestamp = $Timestamp{$file};
	    $Status = $Status{$file};

	    print "<tr>\n";
	    print "<td>$Revision</td>\n";
	    print "<td>$Buildtype</td>\n";
	    print "<td>$Hostname</td>\n";
	    print "<td>$Timestamp</td>\n";
	    if ($Status eq "ok") {
		print "<td><font color=green>Success</font></td>\n";
	    } elsif ($Status eq "almost") {
		print "<td><font color=orange>Almost</font></td>\n";
	    } else {
		print "<td><font color=red>Failure</font></td>\n";
	    }
	    print "<td><a href=\"$file\">log</a></td>\n";
	    print "</tr>\n";
	    print "\n";
	}
	print "</table>\n";
	print "\n";
    }
    print "\n";

    foreach $hostname (sort keys %{$Hostnames{$project}}) {
	print "<hr>\n";
	print "<a name=\"$project-$hostname\"><h3>Summary for $project built on $hostname</h3></a>\n";
	print "\n";
	print "<table border=1>\n";
	print "<tr>\n";
	print "<td>Version</td>\n";
	print "<td>System</td>\n";
	print "<td>Cross compile?</td>\n";
	print "<td>Build date</td>\n";
	print "<td>Results</td>\n";
	print "<td>Log</td>\n";
	print "</tr>\n";
	print "\n";
	foreach $file (@Files) {
	    next unless $Project{$file} eq $project;
	    next unless $Hostname{$file} eq $hostname;

	    my ($Project);
	    my ($Revision);
	    my ($Mode);
	    my ($Hosttype);
	    my ($Buildtype);
	    my ($Hostname);
	    my ($Timestamp);
	    my ($Status);

	    $Revision = $Revision{$file};
	    $Mode = $Mode{$file};
	    $Hosttype = $Hosttype{$file};
	    $Buildtype = $Buildtype{$file};
	    $Timestamp = $Timestamp{$file};
	    $Status = $Status{$file};

	    print "<tr>\n";
	    print "<td>$Revision</td>\n";
	    print "<td>$Hosttype</td>\n";
	    if ($Hosttype eq $Buildtype) {
		print "<td>no</td>\n";
	    } else {
		print "<td>yes, from $Buildtype</td>\n";
	    }
	    print "<td>$Timestamp</td>\n";
	    if ($Status eq "ok") {
		print "<td><font color=green>Success</font></td>\n";
	    } elsif ($Status eq "almost") {
		print "<td><font color=orange>Almost</font></td>\n";
	    } else {
		print "<td><font color=red>Failure</font></td>\n";
	    }
	    print "<td><a href=\"$file\">log</a></td>\n";
	    print "</tr>\n";
	    print "\n";
	}
	print "</table>\n";
	print "\n";
    }
    print "\n";

}

print "\n";
print "<hr>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<br>\n";
print "<hr>\n";
print "\n";
print "</body>\n";
print "</html>\n";
