# -*- Python -*-

import json
import os
import platform
import re
import shutil
import site
import subprocess
import sys

import lit.formats
from lit.llvm import llvm_config
from lit.llvm.subst import FindTool
from lit.llvm.subst import ToolSubst

site.addsitedir(os.path.dirname(__file__))
from helper import toolchain

# name: The name of this test suite.
config.name = "lldb-shell"

# testFormat: The test format to use to interpret tests.
# We prefer the lit internal shell which provides a better user experience on
# failures and is faster unless the user explicitly disables it with
# LIT_USE_INTERNAL_SHELL=0 env var.
use_lit_shell = True
lit_shell_env = os.environ.get("LIT_USE_INTERNAL_SHELL")
if lit_shell_env:
    use_lit_shell = lit.util.pythonize_bool(lit_shell_env)

config.test_format = toolchain.ShTestLldb(not use_lit_shell)

# suffixes: A list of file extensions to treat as test files. This is overriden
# by individual lit.local.cfg files in the test subdirectories.
config.suffixes = [".test", ".cpp", ".s", ".m", ".ll", ".c"]

# excludes: A list of directories to exclude from the testsuite. The 'Inputs'
# subdirectories contain auxiliary inputs for various tests in their parent
# directories.
config.excludes = ["Inputs", "CMakeLists.txt", "README.txt", "LICENSE.txt"]

# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)

# test_exec_root: The root path where tests should be run.
config.test_exec_root = os.path.join(config.lldb_obj_root, "test", "Shell")

# Propagate environment vars.
llvm_config.with_system_environment(
    [
        "FREEBSD_LEGACY_PLUGIN",
        "HOME",
        "TEMP",
        "TMP",
        "XDG_CACHE_HOME",
    ]
)

# Enable sanitizer runtime flags.
if config.llvm_use_sanitizer:
    config.environment["ASAN_OPTIONS"] = "detect_stack_use_after_return=1"
    config.environment["TSAN_OPTIONS"] = "halt_on_error=1"
    config.environment["MallocNanoZone"] = "0"

if config.lldb_platform_url and config.cmake_sysroot and config.enable_remote:
    if re.match(r".*-linux.*", config.target_triple):
        config.available_features.add("remote-linux")
else:
    # After this, enable_remote == True iff remote testing is going to be used.
    config.enable_remote = False

llvm_config.use_default_substitutions()
toolchain.use_lldb_substitutions(config)
toolchain.use_support_substitutions(config)

if re.match(r"^arm(hf.*-linux)|(.*-linux-gnuabihf)", config.target_triple):
    config.available_features.add("armhf-linux")

if re.match(r".*-(windows|mingw32)", config.target_triple):
    config.available_features.add("target-windows")

if re.match(r".*-(windows-msvc)$", config.target_triple):
    config.available_features.add("windows-msvc")

if re.match(r".*-(windows-gnu|mingw32)$", config.target_triple):
    config.available_features.add("windows-gnu")


def calculate_arch_features(arch_string):
    # This will add a feature such as x86, arm, mips, etc for each built
    # target
    features = []
    for arch in arch_string.split():
        features.append(arch.lower())
    return features


# Run llvm-config and add automatically add features for whether we have
# assertions enabled, whether we are in debug mode, and what targets we
# are built for.
llvm_config.feature_config(
    [
        ("--assertion-mode", {"ON": "asserts"}),
        ("--build-mode", {"DEBUG": "debug"}),
        ("--targets-built", calculate_arch_features),
    ]
)

# Clean the module caches in the test build directory. This is necessary in an
# incremental build whenever clang changes underneath, so doing it once per
# lit.py invocation is close enough.
for cachedir in [config.clang_module_cache, config.lldb_module_cache]:
    if os.path.isdir(cachedir):
        lit_config.note("Deleting module cache at %s." % cachedir)
        shutil.rmtree(cachedir)

# Set a default per-test timeout of 10 minutes. Setting a timeout per test
# requires that killProcessAndChildren() is supported on the platform and
# lit complains if the value is set but it is not supported.
supported, errormsg = lit_config.maxIndividualTestTimeIsSupported
if supported:
    lit_config.maxIndividualTestTime = 600
else:
    lit_config.warning("Could not set a default per-test timeout. " + errormsg)


# If running tests natively, check for CPU features needed for some tests.

if "native" in config.available_features:
    cpuid_exe = lit.util.which("lit-cpuid", config.lldb_tools_dir)
    if cpuid_exe is None:
        lit_config.warning(
            "lit-cpuid not found, tests requiring CPU extensions will be skipped"
        )
    else:
        out, err, exitcode = lit.util.executeCommand([cpuid_exe])
        if exitcode == 0:
            for x in out.split():
                config.available_features.add("native-cpu-%s" % x)
        else:
            lit_config.warning("lit-cpuid failed: %s" % err)

if config.lldb_enable_python:
    config.available_features.add("python")

if getattr(config, "lldb_enable_mte", False):
    config.available_features.add("lldb-mte")

if config.lldb_enable_lua:
    config.available_features.add("lua")

if config.lldb_enable_lzma:
    config.available_features.add("lzma")

if shutil.which("xz") is not None:
    config.available_features.add("xz")

if config.lldb_system_debugserver:
    config.available_features.add("system-debugserver")

if config.have_lldb_server:
    config.available_features.add("lldb-server")

if config.objc_gnustep_dir:
    config.available_features.add("objc-gnustep")
    if platform.system() == "Windows":
        # objc.dll must be in PATH since Windows has no rpath
        config.environment["PATH"] = os.path.pathsep.join(
            (
                os.path.join(config.objc_gnustep_dir, "lib"),
                config.environment.get("PATH", ""),
            )
        )

if config.have_dia_sdk:
    config.available_features.add("diasdk")

# NetBSD permits setting dbregs either if one is root
# or if user_set_dbregs is enabled
can_set_dbregs = True
if platform.system() == "NetBSD" and os.geteuid() != 0:
    try:
        output = (
            subprocess.check_output(
                ["/sbin/sysctl", "-n", "security.models.extensions.user_set_dbregs"]
            )
            .decode()
            .strip()
        )
        if output != "1":
            can_set_dbregs = False
    except subprocess.CalledProcessError:
        can_set_dbregs = False
if can_set_dbregs:
    config.available_features.add("dbregs-set")

if "LD_PRELOAD" in os.environ:
    config.available_features.add("ld_preload-present")

# Determine if a specific version of Xcode's linker contains a bug. We want to
# skip affected tests if they contain this bug.
if platform.system() == "Darwin":
    try:
        raw_version_details = subprocess.check_output(
            ("xcrun", "ld", "-version_details")
        )
        version_details = json.loads(raw_version_details)
        version = version_details.get("version", "0")
        version_tuple = tuple(int(x) for x in version.split("."))
        if (1000,) <= version_tuple <= (1109,):
            config.available_features.add("ld_new-bug")
    except:
        pass

# Some shell tests dynamically link with python.dll and need to know the
# location of the Python libraries. This ensures that we use the same
# version of Python that was used to build lldb to run our tests.
config.environment["PYTHONHOME"] = config.python_root_dir
config.environment["PATH"] = os.path.pathsep.join(
    (config.python_root_dir, config.environment.get("PATH", ""))
)
