#!/usr/bin/env perl
use Getopt::Long;


sub usage {
    print "build_external --package_name=packagename\n" .
          "               --extern_config=/path/to/config/file\n" .
	  "                     (then either)\n" .
          "               --extern_src=/path/to/externals-src\n" .
          "               --extern_build=/path/to/externals-build\n" .
	  "                     (or)\n" .
          "               --log=/path/to/log-file\n" .
          "               --build_dir=/scratch/space\n" .
          "               --bundle_dir=/path/to/bundles\n" .
          "               --trigger_dir=/path/to/triggers\n" .
          "               --install_dir=/path/to/install\n" .
          "               --package_dir=/path/to/install/package_name\n";
    exit 1;
}

sub checkOpt {
    my( $ref, $base, $opt_name, $def_val ) = @_;
    if( ! $$ref ) {
        if( $$base ) {
            $$ref = "$$base/$def_val";
        } else {
            print "neither --extern_src/build nor --$opt_name specified\n"; 
            usage();
        }
    }
}


sub fetch_url {
	my ( $url ) = @_;
	my $prog;
	if ( $^O eq "darwin" ) {
		$prog = "curl -O";
	} else {
		$prog = "wget";
	}
	print LOG "** $prog $url\n";
	print LOG `$prog $url 2>&1`;
	my $rc=$?;
	if ($rc) {
		$status = $? >> 8;
		print LOG "ERROR: URL retrieval failed: $status\n";
		print LOG "ERROR: unable to download $url into $bundle_workspace\n";
		print LOG "ERROR: aborting the build of $package_name\n";
		return $rc;
	}
	return 0;
}

sub build_from_source {

    my( $package_name, $build_dir, $bundle_dir, $install_dir, 
        $ext_config ) = @_;

	print LOG "*****************************************\n";
	print LOG "** Building $package_name\n";
	print LOG "*******\n";

	my ($pname,$pversion) = split(/-/,$package_name,2);

	$bundle_workspace = "$build_dir/$package_name";
	$bundle_location  = "$bundle_dir/$pname/$pversion";
	$install_location = "$install_dir/$package_name";

	print LOG "** rm -rf $install_location\n";
	system ("rm -rf $install_location");

	print LOG "** rm -rf $bundle_workspace\n";
	system ("rm -rf $bundle_workspace");

	print LOG "** mkdir -p $bundle_workspace\n";

	# HACK - surround mkdir with quotes so Windows uses our GNU
	# mkdir instead of the mkdir shell builtin.
	print LOG `"mkdir" -p $bundle_workspace 2>&1`;
	if ($?) {
		$status = $? >> 8;
		print LOG "ERROR: mkdir returned: $status\n";
		print LOG "ERROR: unable to create dir $bundle_workspace\n";
		print LOG "ERROR: aborting the build of $package_name\n";
		return;
	}

	my $cwd_save = getcwd(); # pushd so we can go back when we're done.

	print LOG "** chdir $bundle_workspace\n";
	chdir $bundle_workspace;

	# copy the bundle
	print LOG "** cp -r $bundle_location/* .\n";
	print LOG `cp -r $bundle_location/* . 2>&1`;
	if ($?) {
		$status = $? >> 8;
		print LOG "ERROR: cp returned: $status\n";
		print LOG "ERROR: unable to copy contents of $bundle_location into $bundle_workspace\n";
		print LOG "ERROR: aborting the build of $package_name\n";
		return;
	}

	if ( -r "URLS" ) {
		my $url;
		if ( ! open( URLS, "<$bundle_workspace/URLS" ) ) {
			print LOG "ERROR: unable to open $bundle_workspace/URLS\n";
			print LOG "ERROR: aborting the build of $package_name\n";
			return;
		}
		while ( <URLS> ) {
			if ( /^#/ || /^\s+$/ ) {
				next;
			}
			$url = (split())[0];
			if ( fetch_url( $url ) != 0 ) {
				return;
			}
		}
		close URLS;
	}

	my $package_src_name = $package_name;
	if ( ! -r "$package_name.tar.gz" && $package_name =~ /-p\d+$/ ) {
		my $base_name = $package_name;
		$base_name =~ s/-p\d+$//;
		if ( -r "$base_name.tar.gz" ) {
			$package_src_name = $base_name;
		}
	}

	# untar the package itself
	$cmd = "gunzip -c $bundle_workspace/$package_src_name.tar.gz | tar xvf -";
	print LOG "** $cmd \n";
	if( ! open(CMD, "$cmd 2>&1 |") ) { 
	    print LOG "ERROR: unable to untar $bundle_workspace/$package_src_name.bundle/$package_name.tar.gz into $bundle_workspace\n";
	    print LOG "ERROR: aborting the build of $package_src_name\n";
	    return;
	}
	# turn autoflush on for the CMD file handle
	$old_fh = select(CMD);
	$| = 1;
	select( $old_fh);
	while( <CMD> ) {
		print LOG;
	}
	close CMD;
	if ($?) {
	    $status = $? >> 8;
	    print LOG "ERROR: tar returned: $status\n";
	    print LOG "ERROR: unable to untar $bundle_workspace/$package_name.bundle/$package_name.tar.gz into $bundle_workspace\n";
	    print LOG "ERROR: aborting the build of $package_name\n";
	    return;
	}

	# run the build script from the bundle.
	# the build script expects certain environment variables to be set

	$ENV{"PACKAGE_NAME"} = $package_name;
	$ENV{"PACKAGE_SRC_NAME"} = $package_src_name;
	$ENV{"PACKAGE_BUILD_DIR"} = "$bundle_workspace";
	$ENV{"PACKAGE_INSTALL_DIR"} = "$install_location";
	$ENV{"EXTERNALS_INSTALL_DIR"} = "$install_dir";
	if( $ext_config ) {
	    $ENV{"EXTERNALS_CONFIG"} = "$ext_config";
	}

	$cmd = "$bundle_workspace/build_$package_name";

	if ($^O eq "MSWin32") {
		$cmd = $cmd . ".bat";
		$cmd =~ s/\//\\/g;	# fix path to use backslashes
	}

	print LOG "** $cmd\n";
	if( ! open(CMD, "$cmd 2>&1 |") ) { 
	    print LOG "ERROR: unable to execute $cmd: $!\n";
	    print LOG "ERROR: aborting the build of $package_name\n";
	    return;
	}
	# turn autoflush on for the CMD file handle 
	$old_fh = select(CMD);
	$| = 1;
	select( $old_fh);
	while( <CMD> ) {
		print LOG;
	}
	close CMD;
	if ($?) {
	    $status = $? >> 8;
	    print LOG "ERROR: build_$package_name returned: $status\n";
	    print LOG "ERROR: unable to build $package_name\n";
	    print LOG "ERROR: aborting\n";
	    return;
	}

	# clean up

	# since we're still in the directory we're about to delete, let's
	# popd to our original path.
	chdir $cwd_save;

	system ("rm -rf $bundle_workspace");
	if ($?) {
	    $status = $? >> 8;
	    print LOG "WARNING: rm -rf returned: $status\n";
	    print LOG "WARNING: unable to remove directory $bundle_workspace\n";
	}

	print LOG "*******\n";
	print LOG "** Finished $package_name\n";
	print LOG "*****************************************\n\n";

	# success
	return 1;
}


sub make_symlink {
	my ($package_name, $install_dir, $package_dir) = @_;

	print LOG "*****************************************\n";
	print LOG "** Configuring $package_name\n";
	print LOG "*******\n";

	# make the install directory just in case
	print LOG "** mkdir -p $install_dir\n";

		# HACK - we surround the mkdir command with quotes
		# so Windows uses our GNU mkdir (that supports -p)
		# instead of the mkdir shell builtin.
	print LOG `"mkdir" -p $install_dir 2>&1`;
	if ($?) {
		$status = $? >> 8;
		print LOG "ERROR: mkdir returned: $status\n";
		print LOG "ERROR: unable to create dir $install_dir\n";
		print LOG "ERROR: aborting the setup of $package_name\n";
		return;
	}

	print LOG "** ln -s $package_dir $install_dir/$package_name\n";
	print LOG `ln -s $package_dir $install_dir/$package_name 2>&1`;
	if ($?) {
		$status = $? >> 8;
		print LOG "ERROR: ln -s returned: $status\n";
		print LOG "ERROR: unable to symlink $install_dir/$package_name to $package_dir\n";
		print LOG "ERROR: aborting the setup of $package_name\n";
		return;
	}

	print LOG "*******\n";
	print LOG "** Finished $package_name\n";
	print LOG "*****************************************\n\n";

	# success
	return 1;
}



use Cwd;
my $pwd = cwd;
my $trigger_dir = "";
my $bundle_dir = "";
my $install_dir = "";
my $build_dir = "";
my $ext_config = "";
my $log = "";
my $pacakge_name = "";
my $pacakge_dir = "";


$result = GetOptions( "package_name=s" => \$package_name,
                      "trigger_dir:s" => \$trigger_dir,
                      "bundle_dir:s" => \$bundle_dir,
                      "install_dir:s" => \$install_dir,
                      "build_dir:s" => \$build_dir,
                      "package_dir:s" => \$package_dir,
                      "log=s" => \$log,
                      "extern_config:s" => \$ext_config,
                      "extern_src:s" => \$ext_src_dir,
                      "extern_build:s" => \$ext_build_dir );

if( !$result ) {
    print "Error parsing command-line options\n";
    usage();
}

# first, make sure we've got the required options:
if( ! $package_name ) {
    print "Required option --package_name not defined\n";
    usage();
}

# now, get all the info we need, either with the explicit option or
# with --externals_src/build and the default value relative to that.
checkOpt( \$build_dir, \$ext_build_dir, "build_dir", "build" );
checkOpt( \$bundle_dir, \$ext_src_dir, "bundle_dir", "bundles" );
checkOpt( \$trigger_dir, \$ext_build_dir, "trigger_dir", "triggers" );
checkOpt( \$install_dir, \$ext_build_dir, "install_dir", "install" );
checkOpt( \$package_dir, \$ext_build_dir, "package_dir", "install/$package_name" );

# log is a special case, since we want it relative to $build_dir,
# which the user might have specified themselves, so we don't want to
# use the checkOpt() method.  also, if we got this far, we know we've
# got everything else we need, so we can just define it for them.
if( ! $log ) {
    $log = "$build_dir/log.$package_name";
}

# should add code to check for a / on the front of the paths to make sure
# they are absolute

######################################################################
# Make sure the directories we depend on exist, and create them if not
######################################################################
if ( ! -d $build_dir ) {
   mkdir $build_dir, 0777 || die "unable to create $build_dir: $!\n";
}
if ( ! -d $install_dir ) {
   mkdir $install_dir, 0777 || die "unable to create $install_dir: $!\n";
}
if ( ! -d $trigger_dir ) {
   mkdir $trigger_dir, 0777 || die "unable to create $trigger_dir: $!\n";
}

select(STDERR); $| = 1;     # make unbuffered
select(STDOUT); $| = 1;     # make unbuffered

## LOG is a global, make_symlink and build_from_source expect
#  it to be open for writing.
open(LOG, ">>$log") || die "unable to open log $log!\n";
# turn autoflush on for the LOG file handle
$old_fh = select(LOG);
$| = 1;
select($old_fh);

print LOG "\n";
print LOG "*****************************************\n";
print LOG "** " . scalar localtime;

print "Setting up $package_name ...\n";
print "(see $log)\n";

if ( "$install_dir/$package_name" ne "$package_dir" ) {
    $result = make_symlink( $package_name, $install_dir, $package_dir );
} else {
    $result = build_from_source( $package_name, $build_dir, $bundle_dir,
                                 $install_dir, $ext_config );
}

if ($result) {
	print "$package_name: SUCCESS!\n";
	system("touch $trigger_dir/$package_name");
	if ($^O ne "MSWin32") {
	    system("chmod a+rwX $trigger_dir/$package_name") &&
		print "WARNING: Failed to chmod $trigger_dir/$package_name";
	    system("chmod -R a+rwX $install_dir/$package_name") &&
		print "WARNING: Failed to chmod $install_dir/$package_name";
	}
	$exit_status = 0;
} else {
	print "$package_name: FAILED! (see $log)\n";
	system("tail -50 $log");
	$exit_status = 1;
}

# Just for good measure, set permissions on as much as we can, we
# really just need to make sure the install_dir, trigger_dir and
# whatever was created in the build_dir are a+rwX -- this should be
# fixed - matt
if ($^O ne "MSWin32") {
    system("chmod -f -R a+rwX $trigger_dir $install_dir $build_dir");
}

close LOG;

exit $exit_status;

