xref: /llvm-project/lldb/packages/Python/lldbsuite/test/builders/builder.py (revision 5dc70332d53cc5744aedf72a12d0367988559776)
1import os
2import platform
3import subprocess
4import sys
5
6import lldbsuite.test.lldbtest as lldbtest
7import lldbsuite.test.lldbutil as lldbutil
8from lldbsuite.test import configuration
9from lldbsuite.test_event import build_exception
10
11
12class Builder:
13    def getArchitecture(self):
14        """Returns the architecture in effect the test suite is running with."""
15        return configuration.arch if configuration.arch else ""
16
17    def getCompiler(self):
18        """Returns the compiler in effect the test suite is running with."""
19        compiler = configuration.compiler if configuration.compiler else "clang"
20        compiler = lldbutil.which(compiler)
21        return os.path.abspath(compiler)
22
23    def getExtraMakeArgs(self):
24        """
25        Helper function to return extra argumentsfor the make system. This
26        method is meant to be overridden by platform specific builders.
27        """
28        return ""
29
30    def getArchCFlags(self, architecture):
31        """Returns the ARCH_CFLAGS for the make system."""
32        return ""
33
34    def getMake(self, test_subdir, test_name):
35        """Returns the invocation for GNU make.
36        The first argument is a tuple of the relative path to the testcase
37        and its filename stem."""
38        if platform.system() == "FreeBSD" or platform.system() == "NetBSD":
39            make = "gmake"
40        else:
41            make = "make"
42
43        # Construct the base make invocation.
44        lldb_test = os.environ["LLDB_TEST"]
45        if not (lldb_test and configuration.test_build_dir and test_subdir
46                and test_name and (not os.path.isabs(test_subdir))):
47            raise Exception("Could not derive test directories")
48        build_dir = os.path.join(configuration.test_build_dir, test_subdir,
49                                 test_name)
50        src_dir = os.path.join(configuration.test_src_root, test_subdir)
51        # This is a bit of a hack to make inline testcases work.
52        makefile = os.path.join(src_dir, "Makefile")
53        if not os.path.isfile(makefile):
54            makefile = os.path.join(build_dir, "Makefile")
55        return [
56            make, "VPATH=" + src_dir, "-C", build_dir, "-I", src_dir, "-I",
57            os.path.join(lldb_test, "make"), "-f", makefile
58        ]
59
60    def getCmdLine(self, d):
61        """
62        Helper function to return a properly formatted command line argument(s)
63        string used for the make system.
64        """
65
66        # If d is None or an empty mapping, just return an empty string.
67        if not d:
68            return ""
69        pattern = '%s="%s"' if "win32" in sys.platform else "%s='%s'"
70
71        def setOrAppendVariable(k, v):
72            append_vars = ["CFLAGS", "CFLAGS_EXTRAS", "LD_EXTRAS"]
73            if k in append_vars and k in os.environ:
74                v = os.environ[k] + " " + v
75            return pattern % (k, v)
76
77        cmdline = " ".join(
78            [setOrAppendVariable(k, v) for k, v in list(d.items())])
79
80        return cmdline
81
82    def runBuildCommands(self, commands, sender):
83        try:
84            lldbtest.system(commands, sender=sender)
85        except subprocess.CalledProcessError as called_process_error:
86            # Convert to a build-specific error.
87            # We don't do that in lldbtest.system() since that
88            # is more general purpose.
89            raise build_exception.BuildError(called_process_error)
90
91    def getArchSpec(self, architecture):
92        """
93        Helper function to return the key-value string to specify the architecture
94        used for the make system.
95        """
96        arch = architecture if architecture else None
97        if not arch and configuration.arch:
98            arch = configuration.arch
99
100        return ("ARCH=" + arch) if arch else ""
101
102    def getCCSpec(self, compiler):
103        """
104        Helper function to return the key-value string to specify the compiler
105        used for the make system.
106        """
107        cc = compiler if compiler else None
108        if not cc and configuration.compiler:
109            cc = configuration.compiler
110        if cc:
111            return "CC=\"%s\"" % cc
112        else:
113            return ""
114
115    def getSDKRootSpec(self):
116        """
117        Helper function to return the key-value string to specify the SDK root
118        used for the make system.
119        """
120        if configuration.sdkroot:
121            return "SDKROOT={}".format(configuration.sdkroot)
122        return ""
123
124    def getModuleCacheSpec(self):
125        """
126        Helper function to return the key-value string to specify the clang
127        module cache used for the make system.
128        """
129        if configuration.clang_module_cache_dir:
130            return "CLANG_MODULE_CACHE_DIR={}".format(
131                configuration.clang_module_cache_dir)
132        return ""
133
134    def buildDefault(self,
135                     sender=None,
136                     architecture=None,
137                     compiler=None,
138                     dictionary=None,
139                     testdir=None,
140                     testname=None):
141        """Build the binaries the default way."""
142        commands = []
143        commands.append(
144            self.getMake(testdir, testname) + [
145                "all",
146                self.getArchCFlags(architecture),
147                self.getArchSpec(architecture),
148                self.getCCSpec(compiler),
149                self.getExtraMakeArgs(),
150                self.getSDKRootSpec(),
151                self.getModuleCacheSpec(),
152                self.getCmdLine(dictionary)
153            ])
154
155        self.runBuildCommands(commands, sender=sender)
156
157        # True signifies that we can handle building default.
158        return True
159
160    def buildDwarf(self,
161                   sender=None,
162                   architecture=None,
163                   compiler=None,
164                   dictionary=None,
165                   testdir=None,
166                   testname=None):
167        """Build the binaries with dwarf debug info."""
168        commands = []
169        commands.append(
170            self.getMake(testdir, testname) + [
171                "MAKE_DSYM=NO",
172                self.getArchCFlags(architecture),
173                self.getArchSpec(architecture),
174                self.getCCSpec(compiler),
175                self.getExtraMakeArgs(),
176                self.getSDKRootSpec(),
177                self.getModuleCacheSpec(),
178                self.getCmdLine(dictionary)
179            ])
180
181        self.runBuildCommands(commands, sender=sender)
182        # True signifies that we can handle building dwarf.
183        return True
184
185    def buildDwo(self,
186                 sender=None,
187                 architecture=None,
188                 compiler=None,
189                 dictionary=None,
190                 testdir=None,
191                 testname=None):
192        """Build the binaries with dwarf debug info."""
193        commands = []
194        commands.append(
195            self.getMake(testdir, testname) + [
196                "MAKE_DSYM=NO", "MAKE_DWO=YES",
197                self.getArchCFlags(architecture),
198                self.getArchSpec(architecture),
199                self.getCCSpec(compiler),
200                self.getExtraMakeArgs(),
201                self.getSDKRootSpec(),
202                self.getModuleCacheSpec(),
203                self.getCmdLine(dictionary)
204            ])
205
206        self.runBuildCommands(commands, sender=sender)
207        # True signifies that we can handle building dwo.
208        return True
209
210    def buildGModules(self,
211                      sender=None,
212                      architecture=None,
213                      compiler=None,
214                      dictionary=None,
215                      testdir=None,
216                      testname=None):
217        """Build the binaries with dwarf debug info."""
218        commands = []
219        commands.append(
220            self.getMake(testdir, testname) + [
221                "MAKE_DSYM=NO", "MAKE_GMODULES=YES",
222                self.getArchCFlags(architecture),
223                self.getArchSpec(architecture),
224                self.getCCSpec(compiler),
225                self.getExtraMakeArgs(),
226                self.getSDKRootSpec(),
227                self.getModuleCacheSpec(),
228                self.getCmdLine(dictionary)
229            ])
230
231        self.runBuildCommands(commands, sender=sender)
232        # True signifies that we can handle building with gmodules.
233        return True
234
235    def buildDsym(self,
236                  sender=None,
237                  architecture=None,
238                  compiler=None,
239                  dictionary=None,
240                  testdir=None,
241                  testname=None):
242        # False signifies that we cannot handle building with dSYM.
243        return False
244
245    def cleanup(self, sender=None, dictionary=None):
246        """Perform a platform-specific cleanup after the test."""
247        return True
248