#!/usr/bin/env python3
# mypy: disallow-untyped-defs
# pylint: disable=C0103,C0114,C0115,C0116,C0209,R0801,R0915
######################################################################

import argparse
import multiprocessing
import os
import shutil
import subprocess
import sys

######################################################################


def test() -> None:
    if not os.path.exists("nodist/install_test"):
        sys.exit("%Error: Run from the top of the verilator kit")

    cleanenv()
    if os.path.exists("Makefile"):
        run("make distclean")

    # Try building from a scratch area
    srcdir = os.getcwd()
    blddir = srcdir + "/test_regress/obj_dir/install_test_bld"
    prefix = srcdir + "/test_regress/obj_dir/install_test_prefix"
    testdirp = srcdir + "/test_regress/obj_dir/install_test_testp"
    testdirn = srcdir + "/test_regress/obj_dir/install_test_testn"

    if Args.stage <= 0:
        print("== stage 0")
        run("/bin/rm -rf " + blddir)
        run("/bin/mkdir -p " + blddir)
        # Matches Ubuntu's e.g. /usr/share/pkgconfig/verilator.pc
        run("cd " + blddir + " && " + srcdir + "/configure --prefix " + prefix +
            " --exec-prefix " + prefix + " --datarootdir " + prefix + "/share" + " --includedir " +
            prefix + "/share/verilator/include")
        run("cd " + blddir + " && make -j " + str(calc_jobs()))

    # Install it under the prefix
    if Args.stage <= 1:
        print("== stage 1")
        run("/bin/rm -rf " + prefix)
        run("/bin/mkdir -p " + prefix)
        run("cd " + blddir + " && make install")
        run("test -e " + prefix + "/share/man/man1/verilator.1")
        run("test -e " + prefix + "/share/verilator/examples/make_tracing_c/Makefile")
        run("test -e " + prefix + "/share/verilator/include/verilated.h")
        run("test -e " + prefix + "/bin/verilator")
        run("test -e " + prefix + "/bin/verilator_bin")
        run("test -e " + prefix + "/bin/verilator_bin_dbg")
        run("test -e " + prefix + "/bin/verilator_gantt")
        run("test -e " + prefix + "/bin/verilator_profcfunc")

    # run a test using just the path
    if Args.stage <= 2:
        print("== stage 2")
        odir = testdirp
        run("/bin/rm -rf   " + odir)
        run("/bin/mkdir -p " + odir)
        path = prefix + "/bin" + ":" + prefix + "/share/bin"
        write_verilog(odir)
        run("cd " + odir + " && PATH=" + path + ":$PATH verilator --cc top.v --exe sim_main.cpp")
        run("cd " + odir + "/obj_dir && PATH=" + path + ":$PATH make -f Vtop.mk")
        run("cd " + odir + " && PATH=" + path + ":$PATH obj_dir/Vtop")

    # run a test using exact path to binary
    if Args.stage <= 3:
        print("== stage 3")
        odir = testdirn
        run("/bin/rm -rf   " + odir)
        run("/bin/mkdir -p " + odir)
        write_verilog(odir)
        bin1 = prefix + "/bin"
        run("cd " + odir + " && " + bin1 + "/verilator --cc top.v --exe sim_main.cpp")
        run("cd " + odir + "/obj_dir && make -f Vtop.mk")
        run("cd " + odir + "/obj_dir && ./Vtop")

    if Args.stage <= 9:
        print("*-* All Finished *-*")


def write_verilog(odir: str) -> None:
    shutil.copy2("examples/make_hello_c/top.v", odir + "/top.v")
    shutil.copy2("examples/make_hello_c/sim_main.cpp", odir + "/sim_main.cpp")


def cleanenv() -> None:
    for var in os.environ:
        if var in ('VERILATOR_ROOT', 'VERILATOR_INCLUDE', 'VERILATOR_NO_OPT_BUILD'):
            print("unset %s # Was '%s'" % (var, os.environ[var]))
            del os.environ[var]


def get_cpu_count() -> int:
    try:
        return len(os.sched_getaffinity(0))
    except AttributeError:
        return multiprocessing.cpu_count()


def calc_jobs() -> int:
    return get_cpu_count() + 1


def run(command: str) -> None:
    # run a system command, check errors
    print("\t%s" % command)
    os.system(command)
    status = subprocess.call(command, shell=True)
    if status < 0:
        raise RuntimeError("%Error: Command failed " + command + ", stopped")


#######################################################################
#######################################################################

parser = argparse.ArgumentParser(
    allow_abbrev=False,
    formatter_class=argparse.RawDescriptionHelpFormatter,
    description="""install_test performs several make-and-install iterations to verify the
Verilator kit.  It isn't part of the normal "make test" due to the number
of builds required.""",
    epilog="""This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 3
or the Perl Artistic License Version 2.0.

SPDX-FileCopyrightText: 2009-2026 Wilson Snyder
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
""")
parser.add_argument('--debug', action='store_const', const=9, help='enable debug')
parser.add_argument('--stage',
                    type=int,
                    default=0,
                    help='run a specific test stage (see the script)')

Args = parser.parse_args()
test()

######################################################################
# Local Variables:
# compile-command: "cd .. ; nodist/install_test"
# End:
