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