#!/usr/bin/env python

# This script builds the executables needed for testing libstdc++
# without a compiler, and then runs the tests to generate a baseline.
# You should ensure that a canonical version of g++ is in your PATH, and
# a canonical version of libstdc++ in your LD_LIBRARY_PATH, before
# running this script; they will be taken as the gold standard against
# which tested versions will be compared.
#
# It must be run from the directory that contains the standalone V3
# distribution.

usage = """\
Usage:
    %(progname)s [executable-output-directory] [g++ to use] \\
       [directory containing libstdc++ to use]
If the first argument is not given, it defaults to "qm-executables".  If
the last two arguments are not given, defaults will be found in
PATH/LD_LIBRARY_PATH.
"""

import sys
import os
import os.path
import tempfile
import shutil
import atexit
import glob

def error(*msgs):
    sys.stderr.write("ERROR: " + "".join(msgs) + "\n")
    sys.stderr.flush()

def log(*msgs):
    prefix = "%s: " % progname
    sys.stdout.write(prefix  + "".join(msgs) + "\n")
    sys.stdout.flush()

def run_and_log(cmdline, failure_ok=False):
    log("Running command: %s" % cmdline)
    log("Output:")
    status = os.system(cmdline)
    if status != 0 and not failure_ok:
        error("Command did not complete successfully.")
        error("Exit status: %i" % status)
        sys.exit(1)
    log("Execution complete, status = %i." % status)
    return status

def resolve_executable(name):
    if os.path.isabs(name):
        return name
    if os.sep in name:
        return os.path.abspath(name)
    log("Searching PATH for %s." % name)
    path = os.environ.get("PATH", "").split(os.pathsep)
    for dir in path:
        candidate = os.path.join(dir, name)
        if os.path.exists(candidate):
            return os.path.abspath(candidate)
    error("Cannot find executable %s." % name)
    sys.exit(1)

if not os.path.exists("THIS-IS-STANDALONE-V3"):
    error("must run from root of standalone libstdc++ test "
          "distribution.")
    sys.exit(2)

# This global variable is used directly by log().
full_progname = sys.argv[0]
progname = os.path.basename(full_progname)
args = sys.argv[1:]

log("Called as: %s %s" % (full_progname, " ".join(args)))

## Process arguments.
if not 0 <= len(args) <= 3:
    error("bad command line.")
    sys.stderr.write(usage % {"progname": progname})
    sys.exit(2)

## Find compiler output directory.
if args:
    compiler_output_dir = args.pop(0)
else:
    compiler_output_dir = "qm-executables"

## Find g++.
if args:
    gpp_path = args.pop(0)
else:
    gpp_path = "g++"
gpp_path = resolve_executable(gpp_path)

log("Using g++: %s" % gpp_path)
run_and_log("%s --version" % gpp_path)
log()

## Find libstdc++.
if args:
    libstdcpp_path = args.pop(0)
    curr = os.environ.get("LD_LIBRARY_PATH", "")
    new = "%s:%s" % (libstdcpp_path, curr)
    os.environ["LD_LIBRARY_PATH"] = new

log('Using LD_LIBRARY_PATH="%s".'
    % os.environ.get("LD_LIBRARY_PATH", ""))
log()

## Find qmtest.
qmtest_path = resolve_executable("qmtest")
log("Using qmtest: %s" % qmtest_path)
run_and_log("%s --version" % qmtest_path)
log()

## Set up the compiler output directory.
if os.path.exists(compiler_output_dir):
    error("output directory %s already exists." % compiler_output_dir)
    sys.exit(1)

os.mkdir(compiler_output_dir)

## Create the temporary scratch directory.
if hasattr(tempfile, "mkdtemp"):
    tmpdir = tempfile.mkdtemp()
else:
    tmpdir = tempfile.mktemp()
    os.mkdir(tmpdir)
atexit.register(shutil.rmtree, tmpdir)

## Find the target triplet.
(config_guess_in, config_guess_out) = os.popen4("./config.guess")
config_guess_in.close()
target_triplet = config_guess_out.read()
target_triplet = target_triplet.strip()
assert "-" in target_triplet, "Bad target triplet"
log("Using target triplet: %s" % target_triplet)
log()

## Create the basic context to use.
log("Creating V3 context file.")
context_path = os.path.join(tmpdir, "__v3_context__")
f = open(context_path, "w")
f.write("""\
CompilerTable.languages=cplusplus
CompilerTable.cplusplus_kind=GCC
CompilerTable.cplusplus_options=
CompilerTable.cplusplus_path=%(gpp_path)s
DejaGNUTest.target=%(target_triplet)s
V3Test.scratch_dir=%(tmpdir)s
V3Test.compiler_output_dir=%(compiler_output_dir)s
""" % locals())
f.close()

## Set up QMTest environment variables.
class_paths = [os.path.abspath(os.path.join("qm-classes", pkg))
               for pkg in "qmtc", "qmtest_gcc"]
qmtest_class_path = os.pathsep.join(class_paths)
os.environ["QMTEST_CLASS_PATH"] = qmtest_class_path
log('Using QMTEST_CLASS_PATH="%s"' % qmtest_class_path)
log()

## Create the test database we use.
log("Creating V3 test database.")
dbpath = os.path.join(tmpdir, "__v3_db__")
srcdir = os.path.abspath("testsuite")
run_and_log("qmtest -D %(dbpath)s create-tdb "
                     "-c v3_database.V3Database "
                     "-a srcdir=%(srcdir)s" % locals())
log()

## Okay, we're ready to run the tests for the first time.
log("Running QMTest to generate executables.")
log("Results stored in executable-gen.qmr")
status = run_and_log("qmtest -D %(dbpath)s run "
                     "-C %(context_path)s --format=brief "
                     "-o executable-gen.qmr"
                     % locals(),
                     failure_ok=True)
if status == 0 or (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 1):
    log("Acceptable output status.")
else:
    error("qmtest exited unsuccessfully.")
    sys.exit(1)

## Clean the irrelevant non-executable output files; they take up a lot of
## space.
log("Cleaning up executable directory.")
for junk in glob.glob(os.path.join(compiler_output_dir, "*.[sio]")):
    os.unlink(junk)
    log("    Deleted: %s" % junk)

## We have the executables; all is well.  Now we'll run it again to
## generate the baseline result file.
log("Running QMTest again to generate baseline results.")
baseline_basename = "%s.qmr" % target_triplet
baseline = os.path.abspath(os.path.join("qm-baselines",
                                        baseline_basename))

## If there is no qm-baselines directory, create it
if not os.path.exists("qm-baselines"):
    os.mkdir("qm-baselines")

log("Results stored in %s" % baseline)
run_and_log("qmtest -D %(dbpath)s run "
            "-C %(context_path)s --format=brief "
            "-c V3Test.have_compiler=no "
            "-o %(baseline)s"
            % locals(),
            failure_ok=True)
if status == 0 or (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 1):
    log("Acceptable output status.")
else:
    error("qmtest exited unsuccessfully.")
    sys.exit(1)

## All done.
log("All done.")
