310 lines · python
1import os2import pathlib3import platform4import subprocess5import sys6import itertools7 8import lldbsuite.test.lldbtest as lldbtest9import lldbsuite.test.lldbplatformutil as lldbplatformutil10import lldbsuite.test.lldbutil as lldbutil11from lldbsuite.test import configuration12from lldbsuite.test_event import build_exception13from lldbsuite.support import seven14 15 16class Builder:17 def getArchitecture(self):18 """Returns the architecture in effect the test suite is running with."""19 return configuration.arch if configuration.arch else ""20 21 def getCompiler(self):22 """Returns the compiler in effect the test suite is running with."""23 compiler = configuration.compiler if configuration.compiler else "clang"24 compiler = lldbutil.which(compiler)25 return os.path.abspath(compiler)26 27 def getTriple(self, arch):28 """Returns the triple for the given architecture or None."""29 return configuration.triple30 31 def getExtraMakeArgs(self):32 """33 Helper function to return extra argumentsfor the make system. This34 method is meant to be overridden by platform specific builders.35 """36 return []37 38 def getArchCFlags(self, architecture):39 """Returns the ARCH_CFLAGS for the make system."""40 triple = self.getTriple(architecture)41 if triple:42 return ["ARCH_CFLAGS=-target {}".format(triple)]43 return []44 45 def getMake(self, test_subdir, test_name):46 """Returns the invocation for GNU make.47 The first argument is a tuple of the relative path to the testcase48 and its filename stem."""49 # Construct the base make invocation.50 lldb_test = os.environ["LLDB_TEST"]51 if not (52 lldb_test53 and configuration.test_build_dir54 and test_subdir55 and test_name56 and (not os.path.isabs(test_subdir))57 ):58 raise Exception("Could not derive test directories")59 build_dir = os.path.join(configuration.test_build_dir, test_subdir, test_name)60 src_dir = os.path.join(configuration.test_src_root, test_subdir)61 # This is a bit of a hack to make inline testcases work.62 makefile = os.path.join(src_dir, "Makefile")63 if not os.path.isfile(makefile):64 makefile = os.path.join(build_dir, "Makefile")65 return [66 configuration.make_path,67 "VPATH=" + src_dir,68 "-C",69 build_dir,70 "-I",71 src_dir,72 "-I",73 os.path.join(lldb_test, "make"),74 "-f",75 makefile,76 ]77 78 def getCmdLine(self, d):79 """80 Helper function to return a command line argument string used for the81 make system.82 """83 84 # If d is None or an empty mapping, just return an empty list.85 if not d:86 return []87 88 def setOrAppendVariable(k, v):89 append_vars = ["CFLAGS", "CFLAGS_EXTRAS", "LD_EXTRAS"]90 if k in append_vars and k in os.environ:91 v = os.environ[k] + " " + v92 return "%s=%s" % (k, v)93 94 cmdline = [setOrAppendVariable(k, v) for k, v in list(d.items())]95 96 return cmdline97 98 def getArchSpec(self, architecture):99 """100 Helper function to return the key-value string to specify the architecture101 used for the make system.102 """103 return ["ARCH=" + architecture] if architecture else []104 105 def getToolchainSpec(self, compiler):106 """107 Helper function to return the key-value strings to specify the toolchain108 used for the make system.109 """110 cc = compiler if compiler else None111 if not cc and configuration.compiler:112 cc = configuration.compiler113 114 if not cc:115 return []116 117 exe_ext = ""118 if lldbplatformutil.getHostPlatform() == "windows":119 exe_ext = ".exe"120 121 cc = cc.strip()122 cc_path = pathlib.Path(cc)123 124 # We can get CC compiler string in the following formats:125 # [<tool>] <compiler> - such as 'xrun clang', 'xrun /usr/bin/clang' & etc126 #127 # Where <compiler> could contain the following parts:128 # <simple-name>[.<exe-ext>] - sucn as 'clang', 'clang.exe' ('clang-cl.exe'?)129 # <target-triple>-<simple-name>[.<exe-ext>] - such as 'armv7-linux-gnueabi-gcc'130 # <path>/<simple-name>[.<exe-ext>] - such as '/usr/bin/clang', 'c:\path\to\compiler\clang,exe'131 # <path>/<target-triple>-<simple-name>[.<exe-ext>] - such as '/usr/bin/clang', 'c:\path\to\compiler\clang,exe'132 133 cc_ext = cc_path.suffix134 # Compiler name without extension135 cc_name = cc_path.stem.split(" ")[-1]136 137 # A kind of compiler (canonical name): clang, gcc, cc & etc.138 cc_type = cc_name139 # A triple prefix of compiler name: <armv7-none-linux-gnu->gcc140 cc_prefix = ""141 if not "clang-cl" in cc_name and not "llvm-gcc" in cc_name:142 cc_name_parts = cc_name.split("-")143 cc_type = cc_name_parts[-1]144 if len(cc_name_parts) > 1:145 cc_prefix = "-".join(cc_name_parts[:-1]) + "-"146 147 # A kind of C++ compiler.148 cxx_types = {149 "icc": "icpc",150 "llvm-gcc": "llvm-g++",151 "gcc": "g++",152 "cc": "c++",153 "clang": "clang++",154 }155 cxx_type = cxx_types.get(cc_type, cc_type)156 157 cc_dir = cc_path.parent158 159 def getToolchainUtil(util_name):160 return os.path.join(configuration.llvm_tools_dir, util_name + exe_ext)161 162 cxx = cc_dir / (cc_prefix + cxx_type + cc_ext)163 164 util_names = {165 "OBJCOPY": "objcopy",166 "STRIP": "strip",167 "ARCHIVER": "ar",168 "DWP": "dwp",169 }170 utils = []171 172 # Required by API TestBSDArchives.py tests.173 if not os.getenv("LLVM_AR"):174 utils.extend(["LLVM_AR=%s" % getToolchainUtil("llvm-ar")])175 176 if cc_type in ["clang", "cc", "gcc"]:177 util_paths = {}178 # Assembly a toolchain side tool cmd based on passed CC.179 for var, name in util_names.items():180 # Do not override explicity specified tool from the cmd line.181 if not os.getenv(var):182 util_paths[var] = getToolchainUtil("llvm-" + name)183 else:184 util_paths[var] = os.getenv(var)185 utils.extend(["AR=%s" % util_paths["ARCHIVER"]])186 187 # Look for llvm-dwp or gnu dwp188 if not lldbutil.which(util_paths["DWP"]):189 util_paths["DWP"] = getToolchainUtil("llvm-dwp")190 if not lldbutil.which(util_paths["DWP"]):191 util_paths["DWP"] = lldbutil.which("llvm-dwp")192 if not util_paths["DWP"]:193 util_paths["DWP"] = lldbutil.which("dwp")194 if not util_paths["DWP"]:195 del util_paths["DWP"]196 197 if lldbplatformutil.platformIsDarwin():198 util_paths["STRIP"] = seven.get_command_output("xcrun -f strip")199 200 for var, path in util_paths.items():201 utils.append("%s=%s" % (var, path))202 203 if lldbplatformutil.platformIsDarwin():204 utils.extend(["AR=%slibtool" % os.getenv("CROSS_COMPILE", "")])205 206 return [207 "CC=%s" % cc,208 "CC_TYPE=%s" % cc_type,209 "CXX=%s" % cxx,210 ] + utils211 212 def getSDKRootSpec(self):213 """214 Helper function to return the key-value string to specify the SDK root215 used for the make system.216 """217 if configuration.sdkroot:218 return ["SDKROOT={}".format(configuration.sdkroot)]219 return []220 221 def getModuleCacheSpec(self):222 """223 Helper function to return the key-value string to specify the clang224 module cache used for the make system.225 """226 if configuration.clang_module_cache_dir:227 return [228 "CLANG_MODULE_CACHE_DIR={}".format(configuration.clang_module_cache_dir)229 ]230 return []231 232 def getLibCxxArgs(self):233 if configuration.libcxx_include_dir and configuration.libcxx_library_dir:234 libcpp_args = [235 "LIBCPP_INCLUDE_DIR={}".format(configuration.libcxx_include_dir),236 "LIBCPP_LIBRARY_DIR={}".format(configuration.libcxx_library_dir),237 ]238 if configuration.libcxx_include_target_dir:239 libcpp_args.append(240 "LIBCPP_INCLUDE_TARGET_DIR={}".format(241 configuration.libcxx_include_target_dir242 )243 )244 return libcpp_args245 return []246 247 def getLLDBObjRoot(self):248 return ["LLDB_OBJ_ROOT={}".format(configuration.lldb_obj_root)]249 250 def _getDebugInfoArgs(self, debug_info):251 if debug_info is None:252 return []253 254 debug_options = debug_info if isinstance(debug_info, list) else [debug_info]255 option_flags = {256 "dwarf": {"MAKE_DSYM": "NO"},257 "dwo": {"MAKE_DSYM": "NO", "MAKE_DWO": "YES"},258 "gmodules": {"MAKE_DSYM": "NO", "MAKE_GMODULES": "YES"},259 "debug_names": {"MAKE_DEBUG_NAMES": "YES"},260 "dwp": {"MAKE_DSYM": "NO", "MAKE_DWP": "YES"},261 "pdb": {"MAKE_PDB": "YES"},262 }263 264 # Collect all flags, with later options overriding earlier ones265 flags = {}266 267 for option in debug_options:268 if not option or option not in option_flags:269 return None # Invalid options270 flags.update(option_flags[option])271 272 return [f"{key}={value}" for key, value in flags.items()]273 274 def getBuildCommand(275 self,276 debug_info,277 architecture=None,278 compiler=None,279 dictionary=None,280 testdir=None,281 testname=None,282 make_targets=None,283 ):284 debug_info_args = self._getDebugInfoArgs(debug_info)285 if debug_info_args is None:286 return None287 if make_targets is None:288 make_targets = ["all"]289 command_parts = [290 self.getMake(testdir, testname),291 debug_info_args,292 make_targets,293 self.getArchCFlags(architecture),294 self.getArchSpec(architecture),295 self.getToolchainSpec(compiler),296 self.getExtraMakeArgs(),297 self.getSDKRootSpec(),298 self.getModuleCacheSpec(),299 self.getLibCxxArgs(),300 self.getLLDBObjRoot(),301 self.getCmdLine(dictionary),302 ]303 command = list(itertools.chain(*command_parts))304 305 return command306 307 def cleanup(self, dictionary=None):308 """Perform a platform-specific cleanup after the test."""309 return True310