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