xref: /llvm-project/lldb/packages/Python/lldbsuite/test/builders/builder.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
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 (
51            lldb_test
52            and configuration.test_build_dir
53            and test_subdir
54            and test_name
55            and (not os.path.isabs(test_subdir))
56        ):
57            raise Exception("Could not derive test directories")
58        build_dir = os.path.join(configuration.test_build_dir, test_subdir, test_name)
59        src_dir = os.path.join(configuration.test_src_root, test_subdir)
60        # This is a bit of a hack to make inline testcases work.
61        makefile = os.path.join(src_dir, "Makefile")
62        if not os.path.isfile(makefile):
63            makefile = os.path.join(build_dir, "Makefile")
64        return [
65            make,
66            "VPATH=" + src_dir,
67            "-C",
68            build_dir,
69            "-I",
70            src_dir,
71            "-I",
72            os.path.join(lldb_test, "make"),
73            "-f",
74            makefile,
75        ]
76
77    def getCmdLine(self, d):
78        """
79        Helper function to return a command line argument string used for the
80        make system.
81        """
82
83        # If d is None or an empty mapping, just return an empty list.
84        if not d:
85            return []
86
87        def setOrAppendVariable(k, v):
88            append_vars = ["CFLAGS", "CFLAGS_EXTRAS", "LD_EXTRAS"]
89            if k in append_vars and k in os.environ:
90                v = os.environ[k] + " " + v
91            return "%s=%s" % (k, v)
92
93        cmdline = [setOrAppendVariable(k, v) for k, v in list(d.items())]
94
95        return cmdline
96
97    def getArchSpec(self, architecture):
98        """
99        Helper function to return the key-value string to specify the architecture
100        used for the make system.
101        """
102        return ["ARCH=" + architecture] if architecture else []
103
104    def getCCSpec(self, compiler):
105        """
106        Helper function to return the key-value string to specify the compiler
107        used for the make system.
108        """
109        cc = compiler if compiler else None
110        if not cc and configuration.compiler:
111            cc = configuration.compiler
112        if cc:
113            return ['CC="%s"' % cc]
114        return []
115
116    def getSDKRootSpec(self):
117        """
118        Helper function to return the key-value string to specify the SDK root
119        used for the make system.
120        """
121        if configuration.sdkroot:
122            return ["SDKROOT={}".format(configuration.sdkroot)]
123        return []
124
125    def getModuleCacheSpec(self):
126        """
127        Helper function to return the key-value string to specify the clang
128        module cache used for the make system.
129        """
130        if configuration.clang_module_cache_dir:
131            return [
132                "CLANG_MODULE_CACHE_DIR={}".format(configuration.clang_module_cache_dir)
133            ]
134        return []
135
136    def getLibCxxArgs(self):
137        if configuration.libcxx_include_dir and configuration.libcxx_library_dir:
138            libcpp_args = [
139                "LIBCPP_INCLUDE_DIR={}".format(configuration.libcxx_include_dir),
140                "LIBCPP_LIBRARY_DIR={}".format(configuration.libcxx_library_dir),
141            ]
142            if configuration.libcxx_include_target_dir:
143                libcpp_args.append(
144                    "LIBCPP_INCLUDE_TARGET_DIR={}".format(
145                        configuration.libcxx_include_target_dir
146                    )
147                )
148            return libcpp_args
149        return []
150
151    def _getDebugInfoArgs(self, debug_info):
152        if debug_info is None:
153            return []
154        if debug_info == "dwarf":
155            return ["MAKE_DSYM=NO"]
156        if debug_info == "dwo":
157            return ["MAKE_DSYM=NO", "MAKE_DWO=YES"]
158        if debug_info == "gmodules":
159            return ["MAKE_DSYM=NO", "MAKE_GMODULES=YES"]
160        return None
161
162    def getBuildCommand(
163        self,
164        debug_info,
165        architecture=None,
166        compiler=None,
167        dictionary=None,
168        testdir=None,
169        testname=None,
170        make_targets=None,
171    ):
172        debug_info_args = self._getDebugInfoArgs(debug_info)
173        if debug_info_args is None:
174            return None
175        if make_targets is None:
176            make_targets = ["all"]
177        command_parts = [
178            self.getMake(testdir, testname),
179            debug_info_args,
180            make_targets,
181            self.getArchCFlags(architecture),
182            self.getArchSpec(architecture),
183            self.getCCSpec(compiler),
184            self.getExtraMakeArgs(),
185            self.getSDKRootSpec(),
186            self.getModuleCacheSpec(),
187            self.getLibCxxArgs(),
188            self.getCmdLine(dictionary),
189        ]
190        command = list(itertools.chain(*command_parts))
191
192        return command
193
194    def cleanup(self, dictionary=None):
195        """Perform a platform-specific cleanup after the test."""
196        return True
197