1import os
2import platform
3import subprocess
4import sys
5import itertools
6
7import lldbsuite.test.lldbtest as lldbtest
8import lldbsuite.test.lldbutil as lldbutil
9from lldbsuite.test import configuration
10from lldbsuite.test_event import build_exception
11
12
13class Builder:
14    def getArchitecture(self):
15        """Returns the architecture in effect the test suite is running with."""
16        return configuration.arch if configuration.arch else ""
17
18    def getCompiler(self):
19        """Returns the compiler in effect the test suite is running with."""
20        compiler = configuration.compiler if configuration.compiler else "clang"
21        compiler = lldbutil.which(compiler)
22        return os.path.abspath(compiler)
23
24    def getTriple(self, arch):
25        """Returns the triple for the given architecture or None."""
26        return None
27
28    def getExtraMakeArgs(self):
29        """
30        Helper function to return extra argumentsfor the make system. This
31        method is meant to be overridden by platform specific builders.
32        """
33        return []
34
35    def getArchCFlags(self, architecture):
36        """Returns the ARCH_CFLAGS for the make system."""
37        return []
38
39    def getMake(self, test_subdir, test_name):
40        """Returns the invocation for GNU make.
41        The first argument is a tuple of the relative path to the testcase
42        and its filename stem."""
43        if platform.system() == "FreeBSD" or platform.system() == "NetBSD":
44            make = "gmake"
45        else:
46            make = "make"
47
48        # Construct the base make invocation.
49        lldb_test = os.environ["LLDB_TEST"]
50        if not (lldb_test and configuration.test_build_dir and test_subdir
51                and test_name and (not os.path.isabs(test_subdir))):
52            raise Exception("Could not derive test directories")
53        build_dir = os.path.join(configuration.test_build_dir, test_subdir,
54                                 test_name)
55        src_dir = os.path.join(configuration.test_src_root, test_subdir)
56        # This is a bit of a hack to make inline testcases work.
57        makefile = os.path.join(src_dir, "Makefile")
58        if not os.path.isfile(makefile):
59            makefile = os.path.join(build_dir, "Makefile")
60        return [
61            make, "VPATH=" + src_dir, "-C", build_dir, "-I", src_dir, "-I",
62            os.path.join(lldb_test, "make"), "-f", makefile
63        ]
64
65    def getCmdLine(self, d):
66        """
67        Helper function to return a command line argument string used for the
68        make system.
69        """
70
71        # If d is None or an empty mapping, just return an empty list.
72        if not d:
73            return []
74
75        def setOrAppendVariable(k, v):
76            append_vars = ["CFLAGS", "CFLAGS_EXTRAS", "LD_EXTRAS"]
77            if k in append_vars and k in os.environ:
78                v = os.environ[k] + " " + v
79            return '%s=%s' % (k, v)
80
81        cmdline = [setOrAppendVariable(k, v) for k, v in list(d.items())]
82
83        return cmdline
84
85    def getArchSpec(self, architecture):
86        """
87        Helper function to return the key-value string to specify the architecture
88        used for the make system.
89        """
90        return ["ARCH=" + architecture] if architecture else []
91
92    def getCCSpec(self, compiler):
93        """
94        Helper function to return the key-value string to specify the compiler
95        used for the make system.
96        """
97        cc = compiler if compiler else None
98        if not cc and configuration.compiler:
99            cc = configuration.compiler
100        if cc:
101            return ["CC=\"%s\"" % cc]
102        return []
103
104    def getSDKRootSpec(self):
105        """
106        Helper function to return the key-value string to specify the SDK root
107        used for the make system.
108        """
109        if configuration.sdkroot:
110            return ["SDKROOT={}".format(configuration.sdkroot)]
111        return []
112
113    def getModuleCacheSpec(self):
114        """
115        Helper function to return the key-value string to specify the clang
116        module cache used for the make system.
117        """
118        if configuration.clang_module_cache_dir:
119            return ["CLANG_MODULE_CACHE_DIR={}".format(
120                configuration.clang_module_cache_dir)]
121        return []
122
123    def getLibCxxArgs(self):
124        if configuration.libcxx_include_dir and configuration.libcxx_library_dir:
125            libcpp_args = ["LIBCPP_INCLUDE_DIR={}".format(configuration.libcxx_include_dir),
126                           "LIBCPP_LIBRARY_DIR={}".format(configuration.libcxx_library_dir)]
127            if configuration.libcxx_include_target_dir:
128                libcpp_args.append("LIBCPP_INCLUDE_TARGET_DIR={}".format(
129                    configuration.libcxx_include_target_dir))
130            return libcpp_args
131        return []
132
133    def _getDebugInfoArgs(self, debug_info):
134        if debug_info is None:
135            return []
136        if debug_info == "dwarf":
137            return ["MAKE_DSYM=NO"]
138        if debug_info == "dwo":
139            return ["MAKE_DSYM=NO", "MAKE_DWO=YES"]
140        if debug_info == "gmodules":
141            return ["MAKE_DSYM=NO", "MAKE_GMODULES=YES"]
142        return None
143
144    def getBuildCommand(self, debug_info, architecture=None, compiler=None,
145            dictionary=None, testdir=None, testname=None, make_targets=None):
146        debug_info_args = self._getDebugInfoArgs(debug_info)
147        if debug_info_args is None:
148            return None
149        if make_targets is None:
150            make_targets = ["all"]
151        command_parts = [
152            self.getMake(testdir, testname), debug_info_args, make_targets,
153            self.getArchCFlags(architecture), self.getArchSpec(architecture),
154            self.getCCSpec(compiler), self.getExtraMakeArgs(),
155            self.getSDKRootSpec(), self.getModuleCacheSpec(),
156            self.getLibCxxArgs(), self.getCmdLine(dictionary)]
157        command = list(itertools.chain(*command_parts))
158
159        return command
160
161    def cleanup(self, dictionary=None):
162        """Perform a platform-specific cleanup after the test."""
163        return True
164