#!/usr/bin/perl
    eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
	if $running_under_some_shell;
#!/usr/bin/perl

use strict;
use File::Copy;
use File::Temp qw/ tempfile tempdir /;

my $default_svn_tree='trunk';
my $svn_root='svn://svn.gnome.org/svn/ooo-build';

# get ooo-build version from the given ooo-build sources
sub get_config_version($)
{
    my ($ooo_build_dir) = @_;
    my $version;

    open (CONFIGURE, "$ooo_build_dir/configure.in") ||
	die "can't open \"$ooo_build_dir/configure.in\" for reading: $!\n";

    while (my $line = <CONFIGURE>) {
        chomp $line;
    
	if ($line =~ /AC_INIT\s*\(\s*ooo-build\s*,\s*([\w\.]*)\)/) {
	    $version="$1";
	}
    }
    close (CONFIGURE);
    return $version;
}

# set ooo-build version in the given ooo-build sources
sub set_config_version($$)
{
    my ($ooo_build_dir, $version) = @_;
    my $configure = "$ooo_build_dir/configure.in";

    open (CONFIGURE, "$configure") ||
	die "can't open \"$configure\" for reading: $!\n";

    my ( $tmp_fh, $tmp_filename ) = tempfile( "$configure.XXXXXX" );
    if ( !defined $tmp_fh ) {
	close (CONFIGURE);
	die "Error: can't create temporary file: \"$configure.XXXXXX\"\n";
    }
    
    while (my $line = <CONFIGURE>) {
        chomp $line;
    
	if ($line =~ /^(\s*AC_INIT\s*\(\s*ooo-build\s*,\s*)([\w\.]*)(\s*\)\s*)$/) {
	    print ${tmp_fh} "$1$version$3\n";
	} else {
	    print ${tmp_fh} "$line\n";
	}
    }
    close (CONFIGURE);
    close (${tmp_fh});
    
    # preserve permissions on target file by applying them to temp file
    my ( $mode, $uid, $gid ) = ( stat($configure) )[ 2, 4, 5 ];
    $mode = $mode & 07777;

    chmod $mode, $tmp_filename;
    chown $uid, $gid, $tmp_filename;

    rename ($tmp_filename, $configure) ||
	die "Can't rename \"$tmp_filename\" to \"$configure\": $!\n";
}

# increment the version for a test build:
#	+ add 'a' if the version ended with a number
#       + bump the letter otherwise
sub inc_test_version($)
{
    my ($version) = @_;

    my $lastchar = chop $version;
    my $new_version;

    if ($lastchar =~ /\d/) {
	return "$version" . "$lastchar" . "a";
    } elsif ($lastchar =~ /\w/) {
	# select next letter alhabeticaly: a->b, b->c, ...
	$lastchar =~ tr/0a-zA-Z/a-zA-Z0/;
	return "$version" . "$lastchar";
    } else {
	die "Can't generate test version from \"$version$lastchar\n";
    }
}

# copy the local version of ooo-build into a tmp directory
# omit the .svn subdirectories
sub copy_to_tempdir($)
{
    my ($ooo_build_dir) = @_;
    
    my $tempdir = tempdir( '/tmp/ooo-build-XXXXXX' );
    my $blacklist = "$tempdir/ooo-build.copy.blacklist";
    
    print "Copying \"$ooo_build_dir\" -> \"$tempdir\"...";
    # FIXME: crazy hacks to copy ooo-build without .svn subdirectories and to show a progress
    system ("find $ooo_build_dir -wholename \"*/.svn\" -printf \"\%P\\n\" >$blacklist ") &&
	die "Error: failed to find .svn subdirectories: $!\n";
    system ("tar -cf - -C $ooo_build_dir -X $blacklist \.\/ | " .
            "tar -xf - -C $tempdir --checkpoint 2>&1 | " . 
	    "awk '{ ORS=\"\" ; if (++nlines\%10 == 0) printf \".\"; fflush() }'") &&
	die "Error: copying failed: $!\n";
    print "\n";
    unlink $blacklist;
    
    return $tempdir;
}

sub check_out_to_tempdir($)
{
    my ($svn_path) = @_;

    my $tempdir = tempdir( '/tmp/ooo-build-XXXXXX' );

    print "Checking out from $svn_path...";
    system ("svn co \"$svn_path\" \"$tempdir\"") &&
	die "Check out failed\n";

    return $tempdir;
}

# get ooo-build version from the given ooo-build sources
sub get_svn_config_version($)
{
    my ($svn_path) = @_;
    my $tempdir = tempdir( '/tmp/ooo-build-XXXXXX' );
    my $version;

    system ("svn co -N \"$svn_path\" \"$tempdir\" >/dev/null 2>&1") &&
	die "Check out failed\n";

    my $version = get_config_version("$tempdir");

    remove_tempdir($tempdir);
    
    return $version;
}

sub release_tarball($)
{
    my ($ooo_build_dir) = @_;

    print "Creating ooo-build tarball...\n";
    system ("cd $ooo_build_dir && " .
            "./autogen.sh --with-distro=GoOoLinux && " .
	    "make dist && " .
	    "cd -") && die "Error: releasing failed: $!\n";
}

sub generate_md5($$)
{
    my ($ooo_build_dir, $release_tarball) = @_;

    print "Generating MD5...\n";
    system ("cd $ooo_build_dir && " .
            "md5sum $release_tarball >$release_tarball.md5 && " .
	    "cd -") && die "Error: releasing failed: $!\n";
}

sub default_releases_state_file($)
{
    my ($ooo_build_dir) = @_;

    my $rootdir = $ooo_build_dir;
    $rootdir =~ s/^(.*?)\/?[^\/]+\/?$/$1/;

    my $releases_state_file;
    if ($rootdir) {
	$releases_state_file = "$rootdir/.releases";
    } else {
	$releases_state_file = ".releases";
    }

    return "$releases_state_file";
}

sub default_releases_archive($)
{
    my ($ooo_build_dir) = @_;

    my $rootdir = $ooo_build_dir;
    $rootdir =~ s/^(.*?)\/?[^\/]+\/?$/$1/;

    my $releases_archive_dir;
    if ($rootdir) {
	$releases_archive_dir = "$rootdir/archive";
    } else {
	$releases_archive_dir = "archive";
    }

    return "$releases_archive_dir";
}

sub load_releases_state($)
{
    my ($releases_state_file) = @_;
    
    my $state_config_version;
    my $state_release_version;
    
    if (open (STATE, "$releases_state_file")) {
	
	while (my $line = <STATE>) {
	    chomp $line;

	    if ($line =~ /^\s*configure_version\s*=\s*(.*)$/) {
		$state_config_version = "$1";
	    } elsif ($line =~ /^\s*released_version\s*=\s*(.*)$/) {
		$state_release_version = "$1";
	    }
	}
    	close (STATE);
    }

    return $state_config_version, $state_release_version;
}

sub save_releases_state($$$)
{
    my ($releases_state_file, $config_version, $release_version) = @_;
    
    open (STATE, '>', "$releases_state_file") ||
	die "Can't open \"$releases_state_file\" for writing: $!\n";

    print STATE "configure_version = $config_version\n";
    print STATE "released_version  = $release_version\n";

    close (STATE);
}

sub remove_tempdir($)
{
    my ($tempdir) = @_;
    
#    print "Cleaning $tempdir...\n";
    system ("rm -rf $tempdir") && die "Error: rm failed: $!\n";
}

sub copy_to_archive($$$)
{
    my ($ooo_build_dir, $releases_archive_dir, $release_tarball) = @_;
    
    unless ( -d "$releases_archive_dir" ) {
	mkdir ("$releases_archive_dir") ||
	    die "Can't create directory $releases_archive_dir: $!\n";
    }
    
    if ( -f "$releases_archive_dir/$release_tarball" ) {
	print "Warning: $releases_archive_dir/$release_tarball already exists and will be replaced\n";
	unlink ("$releases_archive_dir/$release_tarball");
    }
    
    print "Copying into archive: $releases_archive_dir/$release_tarball ...\n";
    copy ("$ooo_build_dir/$release_tarball", "$releases_archive_dir/$release_tarball") ||
	die "Error: Can't copy $ooo_build_dir/$release_tarball to $releases_archive_dir/$release_tarball: $!\n";
}

sub copy_to_cwd($$)
{
    my ($ooo_build_dir, $release_tarball) = @_;
    
    if ( -f "$release_tarball" ) {
	print "Warning: $release_tarball already exists and will be replaced\n";
	unlink ("$release_tarball");
    }
    
    print "Copying $release_tarball to the working direcotry ...\n";
    copy ("$ooo_build_dir/$release_tarball", "$release_tarball") ||
	die "Error: Can't copy $ooo_build_dir/$release_tarball to $release_tarball: $!\n";
}

sub check_if_file_exists($$)
{
    my ($file, $force) = @_;
    
    if (-e $file) {
	if (defined $force) {
	    print "Warning: $file already exists and will be replaced!\n";
	} else {
	    die "Error: $file alrady exists.\n".
	        "       Use --force if you want to replace it.\n";
	}
    }
}

sub check_if_already_released($$$)
{
    my ($release_tarball, $releases_archive_dir, $force) = @_;

    check_if_file_exists($release_tarball, $force);
    check_if_file_exists("$releases_archive_dir/$release_tarball", $force) if (defined $releases_archive_dir);
}

sub usage()
{
    print "This tool helps with ooo-build releasing\n\n" .
    
          "Usage:\n".
	  "\tooo-build-release [--help] [--force] [--trunk] [--branch=<name>]\n" .
	  "\t[--tag=<name>] [--version] [--set-version=<ver>] [--inc-version]\n" .
	  "\t[--ptf=<bugid>] [dir]\n\n" .
	
	  "Options:\n\n" .
	  "\t--help: print this help\n" .
	  "\t--force: replace an already existing release of the same version\n" .
	  "\t--trunk: release ooo-build from the SVN trunk\n" .
	  "\t--branch: release ooo-build from the given SVN branch\n" .
	  "\t--tag: release ooo-build from the given SVN tag\n" .
	  "\t--version: just print version of the released package but do not\n" .
	  "\t\trelease it; the version is affected by the other options, e.g.\n" .
	  "\t\t--inc-version\n" .
	  "\t--set-version: force another version\n" .
	  "\t--inc-version: increment the latest version; there is a difference\n" .
	  "\t\tbetween test release (default) and final (not yet supported)\n" .
	  "\t--ptf: release ptf-specific tarball for given bugzilla number\n" .
	  "\t--md5: generate md5 sum for the final tarball\n" .
	  "\tdir: path of the local SVN repository copy\n";

#	  "\t--daily:\n" .
#	  "\t--final:\n" .
#	  "\t--get-last:\n" .
#	  "\t--upload=<milestone>\n" .
}


my $ptf;
my $md5;
my $final;
my $daily;
my $svn_tree;
my $bugid;
my $inc_version;
my $config_version;
my $set_version;
my $get_config_version;
my $release_version;
my $ooo_build_dir;
my $releases_archive_dir;
my $releases_state_file;
my $state_config_version;
my $state_release_version;
my $release_tarball;
my $ooo_build_tempdir;
my $force;
my $verbose=1;

###################
# Arguments parsing
###################

for my $arg (@ARGV) {
    if ($arg eq '--help' || $arg eq '-h') {
	usage;
	exit 0;
    } elsif ($arg eq '--force') {
	$force=1;
    } elsif ($arg eq '--md5') {
	$md5=1;
    } elsif ($arg eq '--final') {
	$final=1;
    } elsif ($arg eq '--version') {
	$get_config_version=1;
	$verbose = undef;
    } elsif ($arg eq '--inc-version') {
	$inc_version=1
    } elsif ($arg =~ m/--set-version=(.*)/) {
	  $set_version="$1";
    } elsif ($arg =~ m/--ptf=(.*)/) {
	$bugid=$1;
	$ptf=1;
    } elsif ($arg =~ m/--trunk/) {
	$svn_tree="trunk";
    } elsif ($arg =~ m/--branch=(.*)/) {
	$svn_tree="branches/$1";
    } elsif ($arg =~ m/--tag=(.*)/) {
	$svn_tree="tags/$1";
    } else {
	if (! defined $ooo_build_dir) {
	    $ooo_build_dir = $arg;
	} else {
	    die "Too many arguments $arg\n";
	}
    }
}

###################
# Initial checks
###################

#print "ooo_build_dir=$ooo_build_dir\n";

if ( defined $ptf && ! defined $svn_tree ) {
    print "Warning: Neither --branch nor --tag is used!\n" .
          "         Using \"$default_svn_tree\"\n\n";
    $svn_tree = $default_svn_tree;
}

unless ( defined $ooo_build_dir || defined $svn_tree ) {
    die "Error: Neither ooo-build source direcotry nor svn branch nor\n" .
        "       svn tag is defined\n";
}

if ( defined $ooo_build_dir && defined $svn_tree ) {
    die "Error: Source mismatch. Both svn tree and local copy are defined\n";
}

if ( defined $ooo_build_dir && ! -e "$ooo_build_dir/autogen.sh" ) {
    die "Error: \"$ooo_build_dir\" is not a valid directory\n";
}


###################
# Main logic
###################

if ($verbose) {
    if (defined $ooo_build_dir) {
	print "Source: $ooo_build_dir\n";
    } else {
	print "Source: $svn_root/$svn_tree\n";
    }
}

if (defined $ooo_build_dir) {
    $releases_state_file = default_releases_state_file($ooo_build_dir) unless (defined $releases_state_file);
    $releases_archive_dir = default_releases_archive($ooo_build_dir) unless (defined $releases_archive_dir);
}

# FIXME: this is not optimal in case of svn check but the svn check out to
#        get the version is optimized and it helps to keep the logic "simple"
if (defined $set_version) {
    $release_version = "$set_version";
    print "Forced version   : $set_version\n" if ($verbose);
} else {
    if (defined $ooo_build_dir) {
	$config_version = get_config_version($ooo_build_dir);
	print "Original version : $config_version\n" if ($verbose);
	($state_config_version, $state_release_version) = load_releases_state($releases_state_file);
	if (defined $state_config_version &&
	    defined $state_release_version &&
	    "$state_config_version" eq "$config_version") {
	    $release_version = "$state_release_version";
	    print ("Last used version: $state_release_version\n") if ($verbose);
	} else {
	    $release_version = "$config_version";
	}
    } else {
	$config_version = get_svn_config_version("$svn_root/$svn_tree");
	$release_version = "$config_version";
	print "Original version : $config_version\n" if ($verbose);
    }
}

if ( defined $inc_version ) {
    if (defined $final ) {
	die "FIXME: --inc-version toegeter with --final has not impemented yet\n";
    } else {
	$release_version = inc_test_version($release_version);
    }
    print "Bumped version   : $release_version\n" if ($verbose);
} elsif ( defined $ptf ) {
    $release_version .= ".$bugid";
    print "PTF version      : $release_version\n" if ($verbose);
}

if ( defined $get_config_version ) {
    print "$release_version\n";
} else {
    $release_tarball = "ooo-build-$release_version.tar.gz";
    check_if_already_released($release_tarball, $releases_archive_dir, $force);
    # give a chance to stop the process
    print ("\nWaiting 3 seconds...\n");
    sleep 3;
    # going to release
    if (defined $ooo_build_dir) {
	$ooo_build_tempdir = copy_to_tempdir("$ooo_build_dir");
    } else {
	$ooo_build_tempdir = check_out_to_tempdir("$svn_root/$svn_tree");
    }
    set_config_version($ooo_build_tempdir, $release_version);
    release_tarball($ooo_build_tempdir);
    generate_md5($ooo_build_tempdir, $release_tarball) if (defined $md5);
    copy_to_cwd($ooo_build_tempdir, $release_tarball);
    copy_to_cwd($ooo_build_tempdir, "$release_tarball.md5") if (defined $md5);
    if ( defined $releases_archive_dir ) {
	copy_to_archive($ooo_build_tempdir, $releases_archive_dir, $release_tarball);
	copy_to_archive($ooo_build_tempdir, $releases_archive_dir, "$release_tarball.md5") if (defined $md5);
    }
    if ( defined $releases_state_file ) {
	save_releases_state($releases_state_file, $config_version, $release_version);
    }
    remove_tempdir($ooo_build_tempdir);
}
