#!/usr/bin/perl -w

#
# Todo:
#   + download 
#

use IO::Handle;
use Cwd;
use List::Util qw(shuffle);

# -- Things to tweak --

$CONFIGURE_OPTIONS =
    '--enable-gnome-vfs --with-ant-home=/usr/share/ant '.
    '--with-java=gij --without-myspell-dicts ' .
    '--enable-libsn --enable-build-mozilla';

$COMMON_CONFIGURE_OPTIONS=
    '--disable-binfilter --disable-crashdump ' .
    '--enable-crashdump=no --enable-cups --enable-fontconfig ' .
    '--enable-openldap --with-lang="en_US" --without-fonts --without-gpc ' .
    '--with-system-freetype --with-system-gcc --with-system-jpeg ' .
    '--with-system-python --with-system-zlib';

$CONFIGURE_OPTIONS = "$COMMON_CONFIGURE_OPTIONS $CONFIGURE_OPTIONS";

$WORK_DIR = '/tmp/tinder';
$BUILD_DIR = $WORK_DIR.'/build';
$BUILD_NCPUS = 1;
$BUILDNAME = 'NLD9-gcc33(x86)'; # best if it includes the OS etc.
$MAINTAINER = 'Michael Meeks <michael.meeks@novell.com>';
$BUILD_OS = 'NLD 9';
$DEFAULT_CVSROOT = ':pserver:anoncvs@anoncvs.services.openoffice.org:/cvs';

# All for broken mozilla foo
$BUILD_SRC = $WORK_DIR.'/src';
$MOZILLA_SRC = 'mozilla-source-1.7.5.tar.gz';
$MOZILLA_URL = 'http://ftp.mozilla.org/pub/mozilla.org/mozilla/releases/mozilla1.7.5/source/';

# cvs compression level
$CVS_COMPRESS = '-z5';

# -- debug tweaks --
my $debug = 0;
my $shortcircuit_checkout = 0;
my $shortcircuit_pre_clean = 0;
my $shortcircuit_build = 0;

# -- Things not to tweak --
$BUILD_TAG_BASE = 'http://go-oo.org/tinderbox/tags/';
$BUILD_TAG_NAME = 'tag-list';
# $TINDER_DEST = 'ooo@go-oo.org';
$TINDER_DEST = 'ooo@ooo.ximian.com';
# My odd chroot jail setup:
$MTA = 'ssh localhost /usr/bin/mail';
# Something more sane:
# $MTA = '/usr/bin/mail';
$LOGDIR = $ENV{'HOME'} . "/build-logs";
`mkdir -p $LOGDIR`;

sub log_msg($@)
{
    my $log = shift;
    my $logf;

    print STDERR @_ if ($debug);

    open($logf, ">>$log") || die "Can't open log: $!";
    print $logf @_;
    close($logf);
}

# 'not_running', 'building', 'build_failed', 'test_failed', 'success'
sub start_mail($$$)
{
	my $status = shift;
	my $tree   = shift;
	my $starttime = shift;
	my $MAIL;
	my $MAIL_HEADER =
		"X-Tinder: cookie\n".
		"\n". # vital header / body separator
		"tinderbox: administrator: michael\@ximian.com\n".
		"tinderbox: builddate: deprecated\n".
		"tinderbox: starttime: $starttime\n".
		"tinderbox: buildname: $BUILDNAME\n".
		"tinderbox: errorparser: unix\n".
		"tinderbox: status: $status\n".
		"tinderbox: timenow: ". time(). "\n".
		"tinderbox: tree: $tree\n".
		"tinderbox: END\n".
		"\n"; # end of tinder header separator

	print "Mailing '$status'\n";

#	open ($MAIL, "|cat > $TINDER_DEST") || die "Can't log to file";
	if( $debug ) {
	    $MAIL = STDERR;
	} else {
	    open ($MAIL, "|$MTA -s 'tinder log' $TINDER_DEST") || die "Can't send mail";
	}
	print $MAIL $MAIL_HEADER;

	return $MAIL;
}

sub end_mail($)
{
    my $MAIL = shift;
    close ($MAIL) if (!$debug);
}

sub send_log($$$$)
{
    my $status = shift;
    my $tree   = shift;
    my $starttime = shift;
    my $buildlog = shift;

    $MAIL = start_mail ($status, $tree, $starttime);
    local $SIG{PIPE} = 'IGNORE';
    print $MAIL "Tinder build from $MAINTAINER, on $BUILD_OS\n"; 

    my $LOG;
    open ($LOG, "$buildlog") || warn "Can't open $buildlog: $!";
    while (<$LOG>) {
	print $MAIL $_;
    }
    close ($LOG);

    end_mail ($MAIL);
}

sub cvs_op($$$)
{
    my $op = shift;
    my $subdir = shift;
    my $log = shift;

    my $count = 0;
    while (1) {
	$count++;
	if (system ("cd $BUILD_DIR/$subdir ; cvs -z3 $op 2>&1 | tee -a $log")) {
	    if ($count < 5) {
		log_msg ($log, "WARNING: $op failed, retrying $count\n");
	    } else {
		log_msg ($log, "ERROR: $op failed, retry limit reached\n");
		return 0;
	    }
	} else {
	    log_msg ($log, "cvs $op succeeded\n");
	    return 1;
	}
    }
}

# the anoncvs server just dies if you try a complete checkout
sub checkout_complete($$)
{
    my $master_tag = shift;
    my $log = shift;

    log_msg ($log, "Start complete checkout of $master_tag\n");
    
    my $CvsRoot;
    my @modules = ();
    my $reading_modules = 0;
    open ($CvsRoot, "cvs $CVS_COMPRESS co -c 2>&1 | tee -a $log |") || return 0;
    while (<$CvsRoot>) {
	$reading_modules = 0 if (/^\S/);
	$reading_modules = '1' if (/^OpenOffice2\s+\-a\s+/);
	if ($reading_modules) {
	    chomp;
	    s/^.*\-a//;
	    for my $elem (split (/ +/, $_)) {
		push @modules, $elem if ($elem ne '');
	    }
	}
    }
    close ($CvsRoot) || return 0;

    for my $mod (@modules) {
	print "Module '$mod'\n";
	cvs_op ("checkout -r $master_tag $mod", '.', $log) || return 0;
    }

    log_msg ($log, "Completed checkout of $master_tag\n");

    return 1;
}

sub checkout_source($$)
{
    my $cws = shift;
    my $childws = $cws->{'name'};
    my $log = shift;

    log_msg ($log, "Checking out $childws ... \n");

    my @modules = split / /, $cws->{'modules'};

    log_msg ($log, "Modules:");
    foreach (@modules) {
       log_msg ($log, "\t$_\n");
    }

    my $cws_branch_tag = $cws->{'cws_tag'};
    my $master_milestone_tag = $cws->{'master_tag'};

    checkout_complete ($master_milestone_tag, $log) || return 0;

#    unfortunately this fails ...
#    cvs_op ("checkout -r $master_milestone_tag OpenOffice", '', $log) || return 0;

    log_msg ($log, "Start update of modules to $cws_branch_tag\n");

    for my $module (@modules) {
       if (! -d "$BUILD_DIR/$module") {
           log_msg ($log, "ERROR: missing module $module: no directory to update to $cws_branch_tag\n");
           return 0;
       }
       cvs_op ("update -r $cws_branch_tag", $module, $log) || return 0;
    }

    return 1;
}

sub clean_source($$)
{
    my $childws = shift;
    my $log = shift;

    log_msg ($log, "Cleaning tree ...\n");

    if (`cd $BUILD_DIR; rm -Rf */unx*.pro 2>&1 | tee $log`) {
	log_msg ($log, "ERROR: problem cleaning source");
	return 0;
    }

    log_msg ($log, "Linking moz source ...\n");
    my $dest = "$BUILD_DIR/moz/download/$MOZILLA_SRC";
    unlink ($dest);
    symlink ("$BUILD_SRC/$MOZILLA_SRC", $dest) || log_msg ($log, "Failed to link moz source: $!");

    log_msg ($log, "Successfully cleaned");
    return 1;
}

sub build_source($$)
{
    my $childws = shift;
    my $log = shift;

    # FIXME - safe / random name
    my $script = '/tmp/build-ooo-bash';
    my $sof;
    
    open ($sof, ">$script") || die "can't open $script: $!";
    # force autoconf regen
    print $sof <<"EOF"
#!/bin/bash

#  main configure
cd $BUILD_DIR/config_office
rm -f config.cache
echo "configuring ...";
autoconf || exit 1;

echo "Env for configure:"
set
echo "Env for configure ends"

./configure $CONFIGURE_OPTIONS --with-build-version="Tinder built $childws" || exit 1;

# Many Java files have 8bit char-set comments, javac barfs on them in utf8 locales
export LANG="C";

# Many Java's can't cope with the NPTL on Linux.
LD_ASSUME_KERNEL=2.2.5 /bin/true 2> /dev/null || LD_ASSUME_KERNEL=2.4.10 /bin/true 2> /dev/null || LD_ASSUME_KERNEL=2.6.0 2> /dev/null || unset LD_ASSUME_KERNEL
export LD_ASSUME_KERNEL

# Embedded python dies without Home set
if test "z\$HOME" = "z"; then
    export HOME="";
fi

cd $BUILD_DIR
. $BUILD_DIR/*.Set.sh

echo 'Verifying environment'
echo "Path:  \'\$PATH\'"
echo "Shell: \'\$SHELL\'"
echo "Lang:  \'\$OOO_LANGS\'"
echo "Gcc: "
gcc -dumpversion
bison --version
flex --version
echo 'Bootstrapping'
./bootstrap || ./bootstrap || ./bootstrap || exit 1;

# parallel build setting
EXTRA_BUILD_FLAGS=
if test "$BUILD_NCPUS" -gt 1; then
    EXTRA_BUILD_FLAGS="-P$BUILD_NCPUS"
fi
# not for Win32 ...
EXTRA_BUILD_FLAGS="--dlv_switch link \$EXTRA_BUILD_FLAGS"

EXTRA_DMAKE_FLAGS=

# tcsh sucks great rocks, and refuses to re-direct it's output otherwise
export TERM=

# Automake exports MAKEFLAGS= --unix or something
# similar that gives child nmake's pain.
unset MAKEFLAGS

# Accelerate straight-through compiles by not building / using dependencies
# export nodep=1 - FIXME: make conditional on shortcircuit_pre_clean setting
echo "Env:"
set

echo 'Commencing main build'
cd $BUILD_DIR/instsetoo_native || exit 1;
perl \$SOLARENV/bin/build.pl --all \$EXTRA_BUILD_FLAGS \$EXTRA_DMAKE_FLAGS || exit 1;
EOF
    ;
    close ($sof);
    chmod 0755, $script;

    return !`$script 2>&1 | tee $log`;
}

sub build_one {
    my $cws = shift;
    my $tree = $cws->{'name'};
    my $starttime = time();
    my $buildlog = "$LOGDIR/log-$tree-$starttime";
    my $ret;
    my $MAIL;

    print "Building $tree: ".$starttime." log $buildlog\n";

    $MAIL = start_mail ('building', $tree, $starttime);
    end_mail ($MAIL);

    if (($shortcircuit_checkout  || checkout_source ($cws, $buildlog)) &&
	($shortcircuit_pre_clean || clean_source ($tree, $buildlog)) &&
	($shortcircuit_build     || build_source ($tree, $buildlog)) ) {
	$status = 'success';
    } else {
	$status = 'build_failed';
    }

    # autoconf / cvs conflict zone
    unlink "$BUILD_DIR/config_office/configure";

    sleep 1; # time to flush that log.
    
    send_log ($status, $tree, $starttime, $buildlog);
}

if ($BUILDNAME =~ m/[\/!|~\#\?\>\<]/) {
    print "Invalid build name '$BUILDNAME'\n";
}

if (! -f "$BUILD_SRC/$MOZILLA_SRC") {
    system( "cd $BUILD_SRC ; wget $MOZILLA_URL/$MOZILLA_SRC" ) && die "Failed to download $MOZILLA_SRC";
}

unlink ($BUILD_TAG_NAME);
system( "wget $BUILD_TAG_BASE/$BUILD_TAG_NAME" ) && die "Failed to download: $BUILD_TAG_BASE/$BUILD_TAG_NAME";

my $TagList;
my %cws_table;
open ($TagList, $BUILD_TAG_NAME) || die "Can't open $BUILD_TAG_NAME: $!"; 
while (<$TagList>) {
    /^\s*\#/ && next;
    if (/\s*([^\s]+)\s*:\s*([^\s]+)\s*:\s*([^\s]+)\s*:\s*(.*)\s*/) {
	my %values = ( 'name', $1, 'master_tag', $2, 'cws_tag', $3, 'modules', $4 );
	$cws_table{$1} = \%values;
#	print "Module '$1' '$2' '$3' '$4'\n";
    } else {
	print STDERR "invalid line '$_'\n";
    }
}
close ($TagList);

`mkdir -p $BUILD_DIR`;
chdir ($BUILD_DIR) || die "Can't enter $BUILD_DIR: $!";
$ENV{CVSROOT} = $DEFAULT_CVSROOT;

for my $tag (shuffle(keys %cws_table)) {
    print "Build $tag\n";
    build_one ($cws_table{$tag});
}
