use strict;
use warnings;
use IO::Handle;
use File::Basename qw(basename dirname);
use Getopt::Long;
use Cwd;
my $WORKDIR;
my $SIXGILL_BIN;
my $poll_file;
my $build_dir;
my $wrap_dir;
my $ann_file = "";
my $old_dir = "";
my $foreground;
my $builder = "make -j4";
my $suppress_logs;
GetOptions("build-root|b=s" => \$build_dir,
"poll-file=s" => \$poll_file,
"no-logs!" => \$suppress_logs,
"work-dir=s" => \$WORKDIR,
"sixgill-binaries|binaries|b=s" => \$SIXGILL_BIN,
"wrap-dir=s" => \$wrap_dir,
"annotations-file|annotations|a=s" => \$ann_file,
"old-dir|old=s" => \$old_dir,
"foreground!" => \$foreground,
"buildcommand=s" => \$builder,
)
or die;
if (not -d $build_dir) {
mkdir($build_dir);
}
if ($old_dir ne "" && not -d $old_dir) {
die "Old directory '$old_dir' does not exist\n";
}
$WORKDIR ||= "sixgill-work";
mkdir($WORKDIR, 0755) if ! -d $WORKDIR;
$poll_file ||= "$WORKDIR/poll.file";
$build_dir ||= "$WORKDIR/js-inbound-xgill";
if (!defined $SIXGILL_BIN) {
chomp(my $path = `which xmanager`);
if ($path) {
use File::Basename qw(dirname);
$SIXGILL_BIN = dirname($path);
} else {
die "Cannot find sixgill binaries. Use the -b option.";
}
}
$wrap_dir ||= "$WORKDIR/xgill-inbound/wrap_gcc";
$wrap_dir = "$SIXGILL_BIN/../scripts/wrap_gcc" if not (-e "$wrap_dir/basecc");
die "Bad wrapper directory: $wrap_dir" if not (-e "$wrap_dir/basecc");
sub clean_project {
system("make clean");
}
sub build_project {
return system($builder) >> 8;
}
our %kill_on_exit;
END {
for my $pid (keys %kill_on_exit) {
kill($pid);
}
}
my $xmanager = "$SIXGILL_BIN/xmanager";
my $xsource = "$SIXGILL_BIN/xsource";
my $xmemlocal = "$SIXGILL_BIN/xmemlocal -timeout=20";
my $xinfer = "$SIXGILL_BIN/xinfer -timeout=60";
my $xcheck = "$SIXGILL_BIN/xcheck -timeout=30";
my $prefix_dir = $build_dir;
$ENV{CCACHE_COMPILERCHECK} = 'date +%s.%N';
delete $ENV{CCACHE_PREFIX};
my $usage = "USAGE: run_complete result-dir\n";
my $result_dir = shift or die $usage;
if (not $foreground) {
my $pid = fork();
if ($pid != 0) {
print "Forked, exiting...\n";
exit(0);
}
}
my $do_clean = 0;
if (not (-d $result_dir)) {
$do_clean = 1;
mkdir $result_dir;
}
if (!$suppress_logs) {
my $log_file = "$result_dir/complete.log";
open(OUT, ">>", $log_file) or die "append to $log_file: $!";
OUT->autoflush(1);
STDOUT->fdopen(\*OUT, "w");
STDERR->fdopen(\*OUT, "w");
}
my @waitpids;
chdir $result_dir;
my $status = run_build();
for my $pid (@waitpids) {
waitpid($pid, 0);
$status ||= $? >> 8;
}
print "Exiting run_complete with status $status\n";
exit $status;
sub get_manager_address
{
my $log_file = shift or die;
sleep(1);
my $log_data = `cat $log_file`;
my ($port) = $log_data =~ /Listening on ([\.\:0-9]*)/
or die "no manager found";
print OUT "Connecting to manager on port $port\n" unless $suppress_logs;
print "Connecting to manager on port $port.\n";
return $1;
}
sub logging_suffix {
my ($show_logs, $log_file) = @_;
return $show_logs ? "2>&1 | tee $log_file"
: "> $log_file 2>&1";
}
sub run_build
{
print "build started: ";
print scalar(localtime());
print "\n";
defined(my $pid = fork) or die;
my $manager_log_file = "$result_dir/build_manager.log";
if (!$pid) {
defined(my $pid = fork) or die;
my $logging = logging_suffix($suppress_logs, $manager_log_file);
exec("$xmanager -terminate-on-assert $logging") if (!$pid);
$kill_on_exit{$pid} = 1;
if (!$suppress_logs) {
open(LOGOUT, "> $result_dir/build.log");
open(LOGERR, "> $result_dir/build_err.log");
STDOUT->fdopen(\*LOGOUT, "w");
STDERR->fdopen(\*LOGERR, "w");
}
my $address = get_manager_address($manager_log_file);
my $config_file = "$WORKDIR/xgill.config";
open(CONFIG, ">", $config_file) or die "create $config_file: $!";
print CONFIG "$prefix_dir\n";
print CONFIG Cwd::abs_path("$result_dir/build_xgill.log")."\n";
print CONFIG "$address\n";
my @extra = ("-fplugin-arg-xgill-mangle=1");
push(@extra, "-fplugin-arg-xgill-annfile=$ann_file")
if ($ann_file ne "" && -e $ann_file);
print CONFIG join(" ", @extra) . "\n";
close(CONFIG);
$ENV{"XGILL_CONFIG"} = Cwd::abs_path($config_file);
if (exists $ENV{CC}) {
$ENV{GCCDIR} = dirname($ENV{CC});
}
$ENV{CC} = "$wrap_dir/" . basename($ENV{CC} // "gcc");
$ENV{CXX} = "$wrap_dir/" . basename($ENV{CXX} // "g++");
chdir $build_dir;
clean_project() if ($do_clean);
my $exit_status = build_project();
system("$xsource -remote=$address -end-manager");
print "Waiting for manager to finish (build status $exit_status)...\n";
waitpid($pid, 0);
my $manager_status = $?;
delete $kill_on_exit{$pid};
print "Exiting with status " . ($manager_status || $exit_status) . "\n";
exit($manager_status || $exit_status);
}
waitpid($pid, 0);
my $status = $? >> 8;
print "build finished (status $status): ";
print scalar(localtime());
print "\n";
return $status;
}
sub run_pass
{
my ($name, $command) = @_;
my $log_file = "$result_dir/manager.$name.log";
my $manager_extra = "";
$manager_extra .= "-modset-wait=10" if ($name eq "xmemlocal");
defined(my $pid = fork) or die;
my $logging = logging_suffix($suppress_logs, $log_file);
exec("$xmanager $manager_extra $logging") if (!$pid);
my $address = get_manager_address($log_file);
if (! -d dirname($poll_file)) {
system("mkdir", "-p", dirname($poll_file));
}
open(POLL, "> $poll_file");
print POLL "$command\n";
print POLL "$result_dir/$name\n";
print POLL "$address\n";
close(POLL);
print "$name started: ";
print scalar(localtime());
print "\n";
waitpid($pid, 0);
unlink($poll_file);
print "$name finished: ";
print scalar(localtime());
print "\n";
defined($pid = fork) or die;
if (!$pid) {
sleep(20);
exec("cat $name.*.log > $name.log");
}
push(@waitpids, $pid);
}
my $indexes;
sub run_index
{
my ($name, $kind) = @_;
return if (not (-e "report_$kind.xdb"));
print "$name started: ";
print scalar(localtime());
print "\n";
if ($old_dir ne "") {
system("make_index $kind $old_dir > $name.diff.log");
system("mv $kind diff_$kind");
$indexes .= " diff_$kind";
}
system("make_index $kind > $name.log");
$indexes .= " $kind";
print "$name finished: ";
print scalar(localtime());
print "\n";
}
sub archive_indexes
{
print "archive started: ";
print scalar(localtime());
print "\n";
system("tar -czf reports.tgz $indexes");
system("rm -rf $indexes");
print "archive finished: ";
print scalar(localtime());
print "\n";
}