1import os 2import pathlib 3import platform 4import subprocess 5import sys 6import itertools 7 8import lldbsuite.test.lldbtest as lldbtest 9import lldbsuite.test.lldbplatformutil as lldbplatformutil 10import lldbsuite.test.lldbutil as lldbutil 11from lldbsuite.test import configuration 12from lldbsuite.test_event import build_exception 13 14 15class Builder: 16 def getArchitecture(self): 17 """Returns the architecture in effect the test suite is running with.""" 18 return configuration.arch if configuration.arch else "" 19 20 def getCompiler(self): 21 """Returns the compiler in effect the test suite is running with.""" 22 compiler = configuration.compiler if configuration.compiler else "clang" 23 compiler = lldbutil.which(compiler) 24 return os.path.abspath(compiler) 25 26 def getTriple(self, arch): 27 """Returns the triple for the given architecture or None.""" 28 return None 29 30 def getExtraMakeArgs(self): 31 """ 32 Helper function to return extra argumentsfor the make system. This 33 method is meant to be overridden by platform specific builders. 34 """ 35 return [] 36 37 def getArchCFlags(self, architecture): 38 """Returns the ARCH_CFLAGS for the make system.""" 39 return [] 40 41 def getMake(self, test_subdir, test_name): 42 """Returns the invocation for GNU make. 43 The first argument is a tuple of the relative path to the testcase 44 and its filename stem.""" 45 # Construct the base make invocation. 46 lldb_test = os.environ["LLDB_TEST"] 47 if not ( 48 lldb_test 49 and configuration.test_build_dir 50 and test_subdir 51 and test_name 52 and (not os.path.isabs(test_subdir)) 53 ): 54 raise Exception("Could not derive test directories") 55 build_dir = os.path.join(configuration.test_build_dir, test_subdir, test_name) 56 src_dir = os.path.join(configuration.test_src_root, test_subdir) 57 # This is a bit of a hack to make inline testcases work. 58 makefile = os.path.join(src_dir, "Makefile") 59 if not os.path.isfile(makefile): 60 makefile = os.path.join(build_dir, "Makefile") 61 return [ 62 configuration.make_path, 63 "VPATH=" + src_dir, 64 "-C", 65 build_dir, 66 "-I", 67 src_dir, 68 "-I", 69 os.path.join(lldb_test, "make"), 70 "-f", 71 makefile, 72 ] 73 74 def getCmdLine(self, d): 75 """ 76 Helper function to return a command line argument string used for the 77 make system. 78 """ 79 80 # If d is None or an empty mapping, just return an empty list. 81 if not d: 82 return [] 83 84 def setOrAppendVariable(k, v): 85 append_vars = ["CFLAGS", "CFLAGS_EXTRAS", "LD_EXTRAS"] 86 if k in append_vars and k in os.environ: 87 v = os.environ[k] + " " + v 88 return "%s=%s" % (k, v) 89 90 cmdline = [setOrAppendVariable(k, v) for k, v in list(d.items())] 91 92 return cmdline 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 getToolchainSpec(self, compiler): 102 """ 103 Helper function to return the key-value strings to specify the toolchain 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 110 if not cc: 111 return [] 112 113 exe_ext = "" 114 if lldbplatformutil.getHostPlatform() == "windows": 115 exe_ext = ".exe" 116 117 cc = cc.strip() 118 cc_path = pathlib.Path(cc) 119 120 # We can get CC compiler string in the following formats: 121 # [<tool>] <compiler> - such as 'xrun clang', 'xrun /usr/bin/clang' & etc 122 # 123 # Where <compiler> could contain the following parts: 124 # <simple-name>[.<exe-ext>] - sucn as 'clang', 'clang.exe' ('clang-cl.exe'?) 125 # <target-triple>-<simple-name>[.<exe-ext>] - such as 'armv7-linux-gnueabi-gcc' 126 # <path>/<simple-name>[.<exe-ext>] - such as '/usr/bin/clang', 'c:\path\to\compiler\clang,exe' 127 # <path>/<target-triple>-<simple-name>[.<exe-ext>] - such as '/usr/bin/clang', 'c:\path\to\compiler\clang,exe' 128 129 cc_ext = cc_path.suffix 130 # Compiler name without extension 131 cc_name = cc_path.stem.split(" ")[-1] 132 133 # A kind of compiler (canonical name): clang, gcc, cc & etc. 134 cc_type = cc_name 135 # A triple prefix of compiler name: <armv7-none-linux-gnu->gcc 136 cc_prefix = "" 137 if not "clang-cl" in cc_name and not "llvm-gcc" in cc_name: 138 cc_name_parts = cc_name.split("-") 139 cc_type = cc_name_parts[-1] 140 if len(cc_name_parts) > 1: 141 cc_prefix = "-".join(cc_name_parts[:-1]) + "-" 142 143 # A kind of C++ compiler. 144 cxx_types = { 145 "icc": "icpc", 146 "llvm-gcc": "llvm-g++", 147 "gcc": "g++", 148 "cc": "c++", 149 "clang": "clang++", 150 } 151 cxx_type = cxx_types.get(cc_type, cc_type) 152 153 cc_dir = cc_path.parent 154 155 def getToolchainUtil(util_name): 156 return os.path.join(configuration.llvm_tools_dir, util_name + exe_ext) 157 158 cxx = cc_dir / (cc_prefix + cxx_type + cc_ext) 159 160 util_names = { 161 "OBJCOPY": "objcopy", 162 "STRIP": "strip", 163 "ARCHIVER": "ar", 164 "DWP": "dwp", 165 } 166 utils = [] 167 168 # Required by API TestBSDArchives.py tests. 169 if not os.getenv("LLVM_AR"): 170 utils.extend(["LLVM_AR=%s" % getToolchainUtil("llvm-ar")]) 171 172 if cc_type in ["clang", "cc", "gcc"]: 173 util_paths = {} 174 # Assembly a toolchain side tool cmd based on passed CC. 175 for var, name in util_names.items(): 176 # Do not override explicity specified tool from the cmd line. 177 if not os.getenv(var): 178 util_paths[var] = getToolchainUtil("llvm-" + name) 179 else: 180 util_paths[var] = os.getenv(var) 181 utils.extend(["AR=%s" % util_paths["ARCHIVER"]]) 182 183 # Look for llvm-dwp or gnu dwp 184 if not lldbutil.which(util_paths["DWP"]): 185 util_paths["DWP"] = getToolchainUtil("llvm-dwp") 186 if not lldbutil.which(util_paths["DWP"]): 187 util_paths["DWP"] = lldbutil.which("llvm-dwp") 188 if not util_paths["DWP"]: 189 util_paths["DWP"] = lldbutil.which("dwp") 190 if not util_paths["DWP"]: 191 del util_paths["DWP"] 192 193 for var, path in util_paths.items(): 194 utils.append("%s=%s" % (var, path)) 195 196 if lldbplatformutil.platformIsDarwin(): 197 utils.extend(["AR=%slibtool" % os.getenv("CROSS_COMPILE", "")]) 198 199 return [ 200 "CC=%s" % cc, 201 "CC_TYPE=%s" % cc_type, 202 "CXX=%s" % cxx, 203 ] + utils 204 205 def getSDKRootSpec(self): 206 """ 207 Helper function to return the key-value string to specify the SDK root 208 used for the make system. 209 """ 210 if configuration.sdkroot: 211 return ["SDKROOT={}".format(configuration.sdkroot)] 212 return [] 213 214 def getModuleCacheSpec(self): 215 """ 216 Helper function to return the key-value string to specify the clang 217 module cache used for the make system. 218 """ 219 if configuration.clang_module_cache_dir: 220 return [ 221 "CLANG_MODULE_CACHE_DIR={}".format(configuration.clang_module_cache_dir) 222 ] 223 return [] 224 225 def getLibCxxArgs(self): 226 if configuration.libcxx_include_dir and configuration.libcxx_library_dir: 227 libcpp_args = [ 228 "LIBCPP_INCLUDE_DIR={}".format(configuration.libcxx_include_dir), 229 "LIBCPP_LIBRARY_DIR={}".format(configuration.libcxx_library_dir), 230 ] 231 if configuration.libcxx_include_target_dir: 232 libcpp_args.append( 233 "LIBCPP_INCLUDE_TARGET_DIR={}".format( 234 configuration.libcxx_include_target_dir 235 ) 236 ) 237 return libcpp_args 238 return [] 239 240 def getLLDBObjRoot(self): 241 return ["LLDB_OBJ_ROOT={}".format(configuration.lldb_obj_root)] 242 243 def _getDebugInfoArgs(self, debug_info): 244 if debug_info is None: 245 return [] 246 if debug_info == "dwarf": 247 return ["MAKE_DSYM=NO"] 248 if debug_info == "dwo": 249 return ["MAKE_DSYM=NO", "MAKE_DWO=YES"] 250 if debug_info == "gmodules": 251 return ["MAKE_DSYM=NO", "MAKE_GMODULES=YES"] 252 return None 253 254 def getBuildCommand( 255 self, 256 debug_info, 257 architecture=None, 258 compiler=None, 259 dictionary=None, 260 testdir=None, 261 testname=None, 262 make_targets=None, 263 ): 264 debug_info_args = self._getDebugInfoArgs(debug_info) 265 if debug_info_args is None: 266 return None 267 if make_targets is None: 268 make_targets = ["all"] 269 command_parts = [ 270 self.getMake(testdir, testname), 271 debug_info_args, 272 make_targets, 273 self.getArchCFlags(architecture), 274 self.getArchSpec(architecture), 275 self.getToolchainSpec(compiler), 276 self.getExtraMakeArgs(), 277 self.getSDKRootSpec(), 278 self.getModuleCacheSpec(), 279 self.getLibCxxArgs(), 280 self.getLLDBObjRoot(), 281 self.getCmdLine(dictionary), 282 ] 283 command = list(itertools.chain(*command_parts)) 284 285 return command 286 287 def cleanup(self, dictionary=None): 288 """Perform a platform-specific cleanup after the test.""" 289 return True 290