#!/usr/bin/env python3

import sys
import os
import subprocess
import glob

uniqID = 1

# TAU_SET_NODE=0 allows serial tests to run with MPI builds
serial_runner = "TAU_SET_NODE=0 ./simple"
parallel_runner = "mpirun -np 2 ./simple"


def outputHeader(message):
    if verbose:
        if html:
            print(
                "</pre><h2><font color=blue>\n" +
                message +
                "\n</font></h2><pre>")
        else:
            print(message + "\n")


def output(message):
    if verbose:
        if html:
            print(
                "</pre><h3><font color=green>\n" +
                message +
                "\n</font></h3><pre>")
        else:
            print(message + "\n")


def begin():
    if html:
        print("<html><body><pre>\n")
    else:
        print("------------------------------------------------------")
        print("build/run : %-20s : %s" % ("test name", "stub makefile"))
        print("------------------------------------------------------")


def end():
    if (errorsFound == 0):
        outputHeader("No Errors!")
        code = 0
        cleanup()
    else:
        print("Encountered %d errors" % errorsFound)
        code = errorsFound
    if (html):
        print("</pre><body></html>\n")
    sys.exit(code)


def usesOption(makefile, option):
    opts = makefile.split('-')
    for opt in opts:
        if opt == option:
            return True
    return False


def shortMakefile(makefile):
    return makefile[makefile.find("/lib/") + 5:]


def system(command, timeout_sec=None):
    if verbose:
        if html:
            print("<b>" + os.getcwd() + "> " + command + "</b>")
        else:
            print(os.getcwd() + "> " + command)
        sys.stdout.flush()

    proc = None
    try:
        if verbose:
            fdout = subprocess.PIPE
        else:
            if sys.version_info >= (3, 3):
                fdout = subprocess.DEVNULL
            else:
                fdout = None
        proc = subprocess.Popen([command], shell=True,
                                stdout=fdout, stderr=fdout)
        stdout_data, stderr_data = proc.communicate(timeout=timeout_sec)
        ##The following produces unhelpful lines like "b''". Consider scrubbing these and reactivating.
        #if verbose:
        #    print(stdout_data, file=sys.stdout)
        #    print(stderr_data, file=sys.stderr)
        return proc.returncode
    except BaseException:
        # Many different exceptions can be thrown by subprocess,
        # but all of them indicate an unrecoverable failure, so just
        # swallow them all and return a generic error
        if proc is not None:
            proc.kill()

        if verbose and proc is not None:
            # Empty the pipes
            proc.communicate()
        return -1


def chdir(directory):
    if verbose:
        if html:
            print("<b>" + os.getcwd() + "> cd " + directory + "</b>")
        else:
            print(os.getcwd() + "> cd " + directory)
    if os.path.exists(directory) and os.path.isdir(directory):
        os.chdir(directory)


def prependPath(directory):
    print("<b>" + os.getcwd() + "> PATH=" + directory + ":$PATH</b>")
    os.environ['PATH'] = directory + os.pathsep + os.environ['PATH']


def setEnviron(variable, value):
    print("<b>" + os.getcwd() + "> export " + variable + "=" + value + "</b>")
    os.environ[variable] = value


def unsetEnviron(variable):
    print("<b>" + os.getcwd() + "> unset " + variable + "</b>")
    del os.environ[variable]


def openTable(tests):
    if html:
        print("<table border=1>")
        print("<tr><td rowspan=2>Stub Makefile</td>")
        for test in tests:
            print("<td colspan=2>" + test.name + "</td>")
        print("</tr>")

        print("<tr>")
        for test in tests:
            print("<td colspan>build</td>")
            print("<td colspan>run</td>")
        print("</tr>")


def closeTable():
    if html:
        print("</tr>")
        print("</table>")


def outputSingle(tests, makefile):
    if verbose and not html:
        for test in tests:
            print(
                "%4s : %40s : %s %s" %
                (test.buildresult,
                 test.name,
                 makefile,
                 test.message))
        return

    if html:
        print("<tr><td>" + makefile + "</td>")
        for test in tests:
            if test.buildresult == "pass":
                print("<td bgcolor=#64FF64>pass</td>")
            elif test.buildresult == "fail":
                print(
                    "<td bgcolor=#FF6464><a href=#%d>fail</a></td>" %
                    (test.errorID))
            elif test.buildresult == "timeout":
                print(
                    "<td bgcolor=#FF6464><a href=#%d>timeout</a></td>" %
                    (test.errorID))
            else:
                print("<td bgcolor=#CCCCCC>N/A</td>")

            if test.runresult == "pass":
                print("<td bgcolor=#64FF64>pass</td>")
            elif test.runresult == "fail":
                print(
                    "<td bgcolor=#FF6464><a href=#%d>fail</a></td>" %
                    (test.errorID))
            elif test.runresult == "timeout":
                print(
                    "<td bgcolor=#FF6464><a href=#%d>timeout</a></td>" %
                    (test.errorID))
            else:
                print("<td bgcolor=#CCCCCC>N/A</td>")


def outputSummary(testgrid):
    first = 1
    for makefile in list(testgrid.keys()):
        tests = testgrid[makefile]

        if first == 1:
            first = 0
            openTable(tests)

        outputSingle(tests, makefile)

    closeTable()


class Test:
    def __init__(self, name, makefile):
        self.name = name
        self.fullmakefile = makefile
        self.buildresult = "na"
        self.runresult = "na"
        self.message = ""
        self.makefile = shortMakefile(makefile)
        self.buildpath = "build"
        self.errorID = 0

    def checkApplicable(self):
        return True

    def runTest(self, timeout):
        self.error("Base class runTest called")

    def buildTest(self):
        self.error("Base class buildTest called")

    def checkResults(self):
        if os.path.exists("profile.0.0.0"):
            retval = system("pprof")
            self.runresult = "pass"
        elif os.path.exists("tautrace.0.0.0.trc"):
            self.runresult = "pass"
        elif os.path.exists("MULTI__GET_TIME_OF_DAY/profile.0.0.0"):
            self.runresult = "pass"
        elif usesOption(self.makefile, "epilog"):
            self.runresult = "pass"
        elif usesOption(self.makefile, "vampirtrace"):
            if os.path.exists("simple.otf"):
                self.runresult = "pass"
            else:
                self.error("Error: run succeeded, but no trace found")
                self.runresult = "fail"
        elif usesOption(self.makefile, "scorep"):
            self.runresult = "pass"
        else:
            self.error("Error: run succeeded, but no profiles found")
            self.runresult = "fail"

    def error(self, message):
        global errorsFound
        global uniqID
        errorsFound = errorsFound + 1
        if verbose:
            if html:
                print(("</pre><h1><font color=red><a name=%d>\n" +
                       message + "\n</a></font></h1><pre>") % (uniqID))
                self.errorID = uniqID
                uniqID = uniqID + 1
            else:
                print(message + "\n")


class SimpleTest(Test):
    def __init__(self, name, makefile):
        Test.__init__(self, name, makefile)

    def getActualTest(self):
        return "simple"

    def buildTest(self):
        outputHeader(
            "SimpleTest(" +
            self.getActualTest() +
            "), build (" +
            self.fullmakefile +
            ")")
        if not self.checkApplicable():
            return
        chdir(TEST_ROOT + "/" + self.getActualTest())
        system("rm -rf " + self.buildpath)
        system(
            "TAU_MAKEFILE=" +
            self.fullmakefile +
            " TAU_TEST_MAKEFILE=" +
            self.fullmakefile +
            " make clean")
        retval = system(
            "TAU_MAKEFILE=" +
            self.fullmakefile +
            " TAU_TEST_MAKEFILE=" +
            self.fullmakefile +
            " make")
        if retval != 0 or not os.path.exists("simple"):
            self.error("Error: failed to build")
            self.buildresult = "fail"
            return
        self.buildresult = "pass"
        system("mkdir " + self.buildpath)
        system("cp simple " + self.buildpath)

    def runTest(self, timeout):
        outputHeader("SimpleTest(" + self.getActualTest() +
                     ", run (" + self.fullmakefile + ")")
        if not self.checkApplicable():
            return

        chdir(TEST_ROOT + "/" + self.getActualTest())
        if not os.path.exists(self.buildpath):
            output("Nothing to run")
            return
        chdir(self.buildpath)
        os.environ['TAU_METRICS'] = 'GET_TIME_OF_DAY'
        retval = system(serial_runner, timeout)
        if retval > 0:
            self.error("Error: failed to run")
            self.runresult = "fail"
        elif retval < 0:
            self.error("Error: timeout in run")
            self.runresult = "timeout"
        else:
            self.checkResults()

    def runMPITest(self, timeout):
        outputHeader("MPITest(" + self.getActualTest() +
                     ", run (" + self.fullmakefile + ")")
        if parallel_runner == "":
            output(
                "Warning: Using default mpi launch: \"mpirun -np 2\". Set TAU_VALIDATE_PARALLEL to change")
        if not self.checkApplicable():
            return

        chdir(TEST_ROOT + "/" + self.getActualTest())
        if not os.path.exists(self.buildpath):
            output("Nothing to run")
            return
        chdir(self.buildpath)
        os.environ['TAU_METRICS'] = 'GET_TIME_OF_DAY'
        retval = system(parallel_runner, timeout)
        if retval > 0:
            self.error("Error: failed to run")
            self.runresult = "fail"
        elif retval < 0:
            self.error("Error: timeout in run")
            self.runresult = "timeout"
        else:
            self.checkResults()


class CTest(SimpleTest):
    def __init__(self, makefile):
        SimpleTest.__init__(self, "C", makefile)

    def getActualTest(self):
        return "c"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        return True


class CompInstCTest(SimpleTest):
    def __init__(self, makefile):
        SimpleTest.__init__(self, "CompInst (C)", makefile)

    def getActualTest(self):
        return "compc"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        if system("grep \"^TAU_COMPINST_OPTION\" " + self.fullmakefile) != 0:
            return False
        return True


class CompInstCPPTest(SimpleTest):
    def __init__(self, makefile):
        SimpleTest.__init__(self, "CompInst (C++)", makefile)

    def getActualTest(self):
        return "compcpp"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        if system("grep \"^TAU_COMPINST_OPTION\" " + self.fullmakefile) != 0:
            return False
        return True


class CompInstF90Test(SimpleTest):
    def __init__(self, makefile):
        SimpleTest.__init__(self, "CompInst (F90)", makefile)

    def getActualTest(self):
        return "compf"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(self.makefile, "upc"):
            return False
        if system("grep \"^TAU_F90 \" " + self.fullmakefile) != 0:
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        if system("grep \"^TAU_COMPINST_OPTION\" " + self.fullmakefile) != 0:
            return False
        return True


class FflinkTest(SimpleTest):
    def __init__(self, makefile):
        SimpleTest.__init__(self, "Fortran (flink)", makefile)

    def getActualTest(self):
        return "fflink"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(self.makefile, "upc"):
            return False
        if system("grep \"^TAU_F90 \" " + self.fullmakefile) != 0:
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        return True


class FcpplinkTest(SimpleTest):
    def __init__(self, makefile):
        SimpleTest.__init__(self, "Fortran (cpplink)", makefile)

    def getActualTest(self):
        return "fcpplink"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(self.makefile, "upc"):
            return False
        if system("grep \"^TAU_F90 \" " + self.fullmakefile) != 0:
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        return True


class FclinkTest(SimpleTest):
    def __init__(self, makefile):
        SimpleTest.__init__(self, "Fortran (clink)", makefile)

    def getActualTest(self):
        return "fclink"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(self.makefile, "upc"):
            return False
        if system("grep \"^TAU_F90 \" " + self.fullmakefile) != 0:
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        return True


class MpiCTest(SimpleTest):
    def __init__(self, makefile):
        SimpleTest.__init__(self, "MPI (C)", makefile)

    def getActualTest(self):
        return "mpic"

    def checkApplicable(self):
        if not usesOption(self.makefile, "mpi"):
            output("Skipping, not configured with MPI")
            return False
        return True

    def runTest(self, timeout):
        self.runMPITest(timeout)


class MpiFTest(SimpleTest):
    def __init__(self, makefile):
        SimpleTest.__init__(self, "MPI (Fortran)", makefile)

    def getActualTest(self):
        return "mpif"

    def checkApplicable(self):
        if usesOption(self.makefile, "upc"):
            return False
        if not usesOption(self.makefile, "mpi"):
            output("Skipping, not configured with MPI")
            return False
        if system("grep \"^TAU_F90 \" " + self.fullmakefile) != 0:
            return False
        return True

    def runTest(self, timeout):
        self.runMPITest(timeout)


class PdtTest(Test):
    def __init__(self, name, makefile):
        Test.__init__(self, name, makefile)

    def getActualTest(self):
        return "pdt"

    def buildTest(self):
        outputHeader(
            "Build: Test=" +
            self.getActualTest() +
            ", Makefile=" +
            self.fullmakefile +
            ")")

        if not usesOption(self.makefile, "pdt"):
            output("Skipping, not configured with PDT")
            return

        if not self.checkApplicable():
            return

        chdir(TEST_ROOT + "/" + self.getActualTest())
        system("rm -rf " + self.buildpath)

        system(
            "TAU_MAKEFILE=" +
            self.fullmakefile +
            " TAU_TEST_MAKEFILE=" +
            self.fullmakefile +
            " make clean")
        system("rm -f *.inst.*")
        retval = system(
            "TAU_MAKEFILE=" +
            self.fullmakefile +
            " TAU_TEST_MAKEFILE=" +
            self.fullmakefile +
            " make")
        if retval != 0:
            self.error("Error: failed to build")
            self.buildresult = "fail"
            return

        if len(glob.glob("*.inst.*")) < 1:
            self.error("Error: failed to instrument")
            self.buildresult = "fail"
            return

        self.buildresult = "pass"
        system("mkdir " + self.buildpath)
        system("cp simple " + self.buildpath)

    def runTest(self, timeout):
        outputHeader(
            "Run: Test=" +
            self.getActualTest() +
            ", Makefile=" +
            self.fullmakefile +
            ")")

        if not usesOption(self.makefile, "pdt"):
            output("Skipping, not configured with PDT")
            return

        if not self.checkApplicable():
            return

        chdir(TEST_ROOT + "/" + self.getActualTest())
        if not os.path.exists(self.buildpath):
            output("Nothing to run")
            return
        chdir(self.buildpath)
        os.environ['TAU_METRICS'] = 'GET_TIME_OF_DAY'
        retval = system(serial_runner, timeout)
        if retval > 0:
            self.error("Error: failed to run")
            self.runresult = "fail"
        elif retval < 0:
            self.error("Error: timeout in run")
            self.runresult = "timeout"
        else:
            self.checkResults()

    def runMPITest(self, timeout):
        outputHeader(
            "Run (MPI): Test=" +
            self.getActualTest() +
            ", Makefile=" +
            self.fullmakefile +
            ")")

        if parallel_runner == "":
            output("Skipping Test (TAU_VALIDATE_PARALLEL is not set, can't run MPI)")
            return

        if not usesOption(self.makefile, "pdt"):
            output("Skipping, not configured with PDT")
            return

        if not self.checkApplicable():
            return

        chdir(TEST_ROOT + "/" + self.getActualTest())
        if not os.path.exists(self.buildpath):
            output("Nothing to run")
            return
        chdir(self.buildpath)
        os.environ['TAU_METRICS'] = 'GET_TIME_OF_DAY'
        retval = system(parallel_runner, timeout)
        if retval > 0:
            self.error("Error: failed to run")
            self.runresult = "fail"
        elif retval < 0:
            self.error("Error: timeout in run")
            self.runresult = "timeout"
        else:
            self.checkResults()


class PdtTestC(PdtTest):
    def __init__(self, makefile):
        PdtTest.__init__(self, "PDT (C)", makefile)

    def getActualTest(self):
        return "pdtc"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        return True

    def runTest(self, timeout):
        if usesOption(self.makefile, "mpi"):
            self.runMPITest(timeout)
        else:
            PdtTest.runTest(self, timeout)


class PdtTestCPP(PdtTest):
    def __init__(self, makefile):
        PdtTest.__init__(self, "PDT (C++)", makefile)

    def getActualTest(self):
        return "pdtcpp"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        return True


class PdtTestF(PdtTest):
    def __init__(self, makefile):
        PdtTest.__init__(self, "PDT (Fortran)", makefile)

    def getActualTest(self):
        return "pdtf"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(self.makefile, "upc"):
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        if system("grep \"^TAU_F90 \" " + self.fullmakefile) != 0:
            return False
        return True


class PdtTestGF(PdtTest):
    def __init__(self, makefile):
        PdtTest.__init__(self, "PDT (GFortran)", makefile)

    def getActualTest(self):
        return "pdtgf"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "scorep"):
            return False
        if usesOption(self.makefile, "upc"):
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        if system("grep \"^TAU_F90 \" " + self.fullmakefile) != 0:
            return False
        return True


class PdtMPITestC(PdtTest):
    def __init__(self, makefile):
        PdtTest.__init__(self, "PDT-MPI (C)", makefile)

    def getActualTest(self):
        return "pdtmpic"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        if not usesOption(self.makefile, "mpi"):
            output("Skipping, not configured with MPI")
            return False
        return True

    def runTest(self, timeout):
        self.runMPITest(timeout)


class PdtMPITestCPP(PdtTest):
    def __init__(self, makefile):
        PdtTest.__init__(self, "PDT-MPI (C++)", makefile)

    def getActualTest(self):
        return "pdtmpicpp"

    def checkApplicable(self):
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        if not usesOption(self.makefile, "mpi"):
            output("Skipping, not configured with MPI")
            return False
        return True

    def runTest(self, timeout):
        self.runMPITest(timeout)


class PdtMPITestF(PdtTest):
    def __init__(self, makefile):
        PdtTest.__init__(self, "PDT-MPI (Fortran)", makefile)

    def getActualTest(self):
        return "pdtmpif"

    def checkApplicable(self):
        if usesOption(self.makefile, "upc"):
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        if not usesOption(self.makefile, "mpi"):
            output("Skipping, not configured with MPI")
            return False
        if system("grep \"^TAU_F90 \" " + self.fullmakefile) != 0:
            return False
        return True

    def runTest(self, timeout):
        self.runMPITest(timeout)


class PdtMPITestGF(PdtTest):
    def __init__(self, makefile):
        PdtTest.__init__(self, "PDT-MPI (GFortran)", makefile)

    def getActualTest(self):
        return "pdtmpigf"

    def checkApplicable(self):
        if usesOption(self.makefile, "upc"):
            return False
        if usesOption(
                self.makefile, "mpi") and usesOption(
                self.makefile, "epilog"):
            return False
        if not usesOption(self.makefile, "mpi"):
            output("Skipping, not configured with MPI")
            return False
        if system("grep \"^TAU_F90 \" " + self.fullmakefile) != 0:
            return False
        return True

    def runTest(self, timeout):
        self.runMPITest(timeout)


def usage():
    print("")
    print(
        "Usage: tau_validate [-v] [--html] [--tag <tag>] [--timeout <timeout>]")
    print("                    [--table <file>] [--build] [--run] <target>")
    print("")
    print("Options:")
    print("")
    print("-v                   Verbose output")
    print("--html               Output results in HTML")
    print("--tag <tag>          Validate only the subset of TAU stub makefiles matching <tag>")
    print("--timeout <timeout>  Give up if a test does not succeed after timeout ")
    print("                     seconds of runtime")
    print("--table <file>       Real-time creation of an addition <file> containing ")
    print("                     the summary table of the tests")
    print("--build              Only build")
    print("--run                Only run")
    print("<target>             Specify an arch directory (e.g. rs6000), or the lib")
    print("                     directory (rs6000/lib), or a specific makefile.")
    print("                     Relative or absolute paths are ok.")
    print("")
    print("Notes:")
    print("tau_validate will attempt to validate a TAU installation by performing")
    print("various tests on each TAU stub Makefile.  Some degree of logic exists")
    print("to determine if a given test applies to a given makefile, but it's not")
    print("perfect.")
    print("")
    print("Example:")
    print("")
    print("bash : ./tau_validate --html --table table.html --timeout 180 x86_64 &> results.html")
    print("tcsh : ./tau_validate --html --table table.html --timeout 180 x86_64 >& results.html")
    print("")
    print("Optional run scripts:")
    print("Using the environment variables TAU_VALIDATE_SERIAL and TAU_VALIDATE_PARALLEL")
    print("you can do custom execution of jobs.  A sample parallel runner (app will ")
    print("always be called 'simple'). The following is an example script parallel.sh")
    print("(make sure it has execute permissions before running tau_validate):")
    print("")
    print("#!/bin/bash")
    print("mpirun -np 2 ./simple")
    print("")
    print("With parallel runner:")
    print("")
    print("(using bash)")
    print("export TAU_VALIDATE_PARALLEL=`pwd`/parallel.sh; ./tau_validate -v --html x86_64 &> results.html")
    print("")

    sys.exit(-1)


def cleanup():
    chdir(TEST_ROOT)
    system("find . -name \"build-*\" | xargs rm -rf")
    system("find . -name \"profile.*\" | xargs rm -f")
    system("find . -name \"scorep*\" | xargs rm -rf")
    system("find . -name \"*.trc\" | xargs rm -f")
    system("find . -name \"*.edf\" | xargs rm -f")
    system("find . -name \"*.pdb\" | xargs rm -f")
    system("find . -name \"*.elg\" | xargs rm -f")
    system("find . -name \"core.*\" | xargs rm -f")
    system("find . -name \"*.o\" | xargs rm -f")
    system("find . -name \"*.inst.*\" | xargs rm -f")
    system("find . -name \"simple\" | xargs rm -f")


# execution starts here
errorsFound = 0

PWD = os.getcwd()
TEST_ROOT = PWD + "/examples/validate"


args = sys.argv[1:]
verbose = False
html = False
target = ""
optRun = False
optBuild = False
optClean = False
optTag = False
nextArgTag = False
nextArgTimeout = False
separateTable = True
separateTable = False
nextArgTable = False
table_filename = None
tag = ""
timeout_sec = None
for arg in args:
    if nextArgTag:
        tag = arg
        nextArgTag = False
    elif nextArgTimeout:
        try:
            timeout_sec = int(arg)
        except ValueError:
            print("timeout must be an integer number of seconds")
            print("you provided: ", arg)
            usage()
        nextArgTimeout = False
    elif nextArgTable:
        table_filename = arg
        nextArgTable = False
    elif arg == "-v":
        verbose = True
    elif arg == "--html":
        verbose = True
        html = True
    elif arg == "--build":
        optBuild = True
    elif arg == "--run":
        optRun = True
    elif arg == "--clean":
        optClean = True
    elif arg == "--tag":
        optTag = True
        nextArgTag = True
    elif arg == "--timeout":
        nextArgTimeout = True
    elif arg == "--table":
        separateTable = True
        nextArgTable = True
    else:
        if target != "":
            usage()
        target = arg

if optClean:
    print("Cleaning...")
    verbose = True
    cleanup()
    sys.exit(0)


# if neither is specified, do both
if not optRun and not optBuild:
    optBuild = True
    optRun = True

if target == "":
    usage()


target = os.path.realpath(target)


if 'TAU_VALIDATE_SERIAL' in os.environ:
    serial_runner = os.environ['TAU_VALIDATE_SERIAL']

if 'TAU_VALIDATE_PARALLEL' in os.environ:
    parallel_runner = os.environ['TAU_VALIDATE_PARALLEL']

begin()


def runTest(test, tests):
    if optBuild:
        test.buildTest()
    if optRun:
        test.runTest(timeout_sec)
    tests.append(test)
    print(
        "%4s/%-4s : %-20s : %s %s" %
        (test.buildresult,
         test.runresult,
         test.name,
         makefile,
         test.message))


if not os.path.exists(target):
    print("Couldn't find: " + target)
    usage()

directory = False
if os.path.isdir(target):
    directory = True

    if os.path.exists(target + "/lib"):
        target = target + "/lib"

    chdir(target)
    if optTag:
        makefiles = glob.glob("Makefile.tau*-%s-*" % (tag))
        makefiles = makefiles + glob.glob("Makefile.tau*-%s" % (tag))
    else:
        makefiles = glob.glob("Makefile.tau*")

else:
    makefiles = [target]

testgrid = {}
found = 0
first = 1

for makefile in makefiles:
    found = 1

    tests = []
    if directory:
        fullmakefile = target + "/" + makefile
    else:
        fullmakefile = os.path.realpath(target)

    runTest(CTest(fullmakefile), tests)

    runTest(FflinkTest(fullmakefile), tests)
    runTest(FcpplinkTest(fullmakefile), tests)
    runTest(FclinkTest(fullmakefile), tests)

    runTest(MpiCTest(fullmakefile), tests)
    runTest(MpiFTest(fullmakefile), tests)

    runTest(CompInstCTest(fullmakefile), tests)
    runTest(CompInstCPPTest(fullmakefile), tests)
    runTest(CompInstF90Test(fullmakefile), tests)

    runTest(PdtTestC(fullmakefile), tests)
    runTest(PdtTestCPP(fullmakefile), tests)
    runTest(PdtTestF(fullmakefile), tests)
    runTest(PdtTestGF(fullmakefile), tests)

    runTest(PdtMPITestC(fullmakefile), tests)
    runTest(PdtMPITestCPP(fullmakefile), tests)
    runTest(PdtMPITestF(fullmakefile), tests)
    runTest(PdtMPITestGF(fullmakefile), tests)

    testgrid[makefile] = tests

    if verbose:
        if html:
            print("<hr>")
        else:
            print("----------------")

    if separateTable:
        if first == 1:
            first = 0
            saveout = sys.stdout
            fsock = open(PWD + "/" + table_filename, 'w')
            sys.stdout = fsock
            print("<html><body>")
            openTable(tests)

        sys.stdout = fsock
        outputSingle(tests, makefile)
        fsock.flush()
        sys.stdout = saveout

if found == 0:
    print("Couldn't find any makefiles in " + target)

if separateTable:
    sys.stdout = fsock
    closeTable()
    print("</body><html>")
    sys.stdout = saveout
    fsock.close()

outputSummary(testgrid)

end()
