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