xref: /llvm-project/lldb/packages/Python/lldbsuite/test/builders/builder.py (revision 0f12cf7ebaaf9f2a52be3baef7d25562a5ac15cb)
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 runBuildCommands(self, commands):
86        try:
87            lldbtest.system(commands)
88        except subprocess.CalledProcessError as called_process_error:
89            # Convert to a build-specific error.
90            # We don't do that in lldbtest.system() since that
91            # is more general purpose.
92            raise build_exception.BuildError(called_process_error)
93
94    def getArchSpec(self, architecture):
95        """
96        Helper function to return the key-value string to specify the architecture
97        used for the make system.
98        """
99        return ["ARCH=" + architecture] if architecture else []
100
101    def getCCSpec(self, compiler):
102        """
103        Helper function to return the key-value string to specify the compiler
104        used for the make system.
105        """
106        cc = compiler if compiler else None
107        if not cc and configuration.compiler:
108            cc = configuration.compiler
109        if cc:
110            return ["CC=\"%s\"" % cc]
111        return []
112
113    def getSDKRootSpec(self):
114        """
115        Helper function to return the key-value string to specify the SDK root
116        used for the make system.
117        """
118        if configuration.sdkroot:
119            return ["SDKROOT={}".format(configuration.sdkroot)]
120        return []
121
122    def getModuleCacheSpec(self):
123        """
124        Helper function to return the key-value string to specify the clang
125        module cache used for the make system.
126        """
127        if configuration.clang_module_cache_dir:
128            return ["CLANG_MODULE_CACHE_DIR={}".format(
129                configuration.clang_module_cache_dir)]
130        return []
131
132    def _getDebugInfoArgs(self, debug_info):
133        if debug_info is None:
134            return []
135        if debug_info == "dwarf":
136            return ["MAKE_DSYM=NO"]
137        if debug_info == "dwo":
138            return ["MAKE_DSYM=NO", "MAKE_DWO=YES"]
139        if debug_info == "gmodules":
140            return ["MAKE_DSYM=NO", "MAKE_GMODULES=YES"]
141        return None
142
143    def build(self, debug_info, architecture=None, compiler=None,
144            dictionary=None, testdir=None, testname=None):
145        debug_info_args = self._getDebugInfoArgs(debug_info)
146        if debug_info_args is None:
147            return False
148
149        command_parts = [
150            self.getMake(testdir, testname), debug_info_args, ["all"],
151            self.getArchCFlags(architecture), self.getArchSpec(architecture),
152            self.getCCSpec(compiler), self.getExtraMakeArgs(),
153            self.getSDKRootSpec(), self.getModuleCacheSpec(),
154            self.getCmdLine(dictionary)]
155        command = list(itertools.chain(*command_parts))
156
157        self.runBuildCommands([command])
158        return True
159
160    def cleanup(self, dictionary=None):
161        """Perform a platform-specific cleanup after the test."""
162        return True
163