1import itertools 2import os 3import platform 4import re 5import subprocess 6import sys 7import errno 8 9import lit.util 10from lit.llvm.subst import FindTool 11from lit.llvm.subst import ToolSubst 12 13lit_path_displayed = False 14 15class LLVMConfig(object): 16 17 def __init__(self, lit_config, config): 18 self.lit_config = lit_config 19 self.config = config 20 21 features = config.available_features 22 23 self.use_lit_shell = False 24 # Tweak PATH for Win32 to decide to use bash.exe or not. 25 if sys.platform == 'win32': 26 # Seek necessary tools in directories and set to $PATH. 27 path = None 28 lit_tools_dir = getattr(config, 'lit_tools_dir', None) 29 required_tools = [ 30 'cmp.exe', 'grep.exe', 'sed.exe', 'diff.exe', 'echo.exe'] 31 path = self.lit_config.getToolsPath(lit_tools_dir, 32 config.environment['PATH'], 33 required_tools) 34 if path is None: 35 path = self._find_git_windows_unix_tools(required_tools) 36 if path is not None: 37 self.with_environment('PATH', path, append_path=True) 38 # Many tools behave strangely if these environment variables aren't 39 # set. 40 self.with_system_environment( 41 ['SystemDrive', 'SystemRoot', 'TEMP', 'TMP']) 42 self.use_lit_shell = True 43 44 global lit_path_displayed 45 if not self.lit_config.quiet and lit_path_displayed is False: 46 self.lit_config.note("using lit tools: {}".format(path)) 47 lit_path_displayed = True 48 49 # Choose between lit's internal shell pipeline runner and a real shell. 50 # If LIT_USE_INTERNAL_SHELL is in the environment, we use that as an 51 # override. 52 lit_shell_env = os.environ.get('LIT_USE_INTERNAL_SHELL') 53 if lit_shell_env: 54 self.use_lit_shell = lit.util.pythonize_bool(lit_shell_env) 55 56 if not self.use_lit_shell: 57 features.add('shell') 58 59 # Running on Darwin OS 60 if platform.system() == 'Darwin': 61 # FIXME: lld uses the first, other projects use the second. 62 # We should standardize on the former. 63 features.add('system-linker-mach-o') 64 features.add('system-darwin') 65 elif platform.system() == 'Windows': 66 # For tests that require Windows to run. 67 features.add('system-windows') 68 elif platform.system() == 'Linux': 69 features.add('system-linux') 70 elif platform.system() in ['FreeBSD']: 71 features.add('system-freebsd') 72 elif platform.system() == 'NetBSD': 73 features.add('system-netbsd') 74 elif platform.system() == 'AIX': 75 features.add('system-aix') 76 elif platform.system() == 'SunOS': 77 features.add('system-solaris') 78 79 # Native compilation: host arch == default triple arch 80 # Both of these values should probably be in every site config (e.g. as 81 # part of the standard header. But currently they aren't) 82 host_triple = getattr(config, 'host_triple', None) 83 target_triple = getattr(config, 'target_triple', None) 84 if host_triple and host_triple == target_triple: 85 features.add('native') 86 87 # Sanitizers. 88 sanitizers = getattr(config, 'llvm_use_sanitizer', '') 89 sanitizers = frozenset(x.lower() for x in sanitizers.split(';')) 90 if 'address' in sanitizers: 91 features.add('asan') 92 if 'memory' in sanitizers or 'memorywithorigins' in sanitizers: 93 features.add('msan') 94 if 'undefined' in sanitizers: 95 features.add('ubsan') 96 97 have_zlib = getattr(config, 'have_zlib', None) 98 if have_zlib: 99 features.add('zlib') 100 101 # Check if we should run long running tests. 102 long_tests = lit_config.params.get('run_long_tests', None) 103 if lit.util.pythonize_bool(long_tests): 104 features.add('long_tests') 105 106 if target_triple: 107 if re.match(r'^x86_64.*-apple', target_triple): 108 features.add('x86_64-apple') 109 host_cxx = getattr(config, 'host_cxx', None) 110 if ('address' in sanitizers and 111 self.get_clang_has_lsan(host_cxx, target_triple)): 112 self.with_environment( 113 'ASAN_OPTIONS', 'detect_leaks=1', append_path=True) 114 if re.match(r'^x86_64.*-linux', target_triple): 115 features.add('x86_64-linux') 116 if re.match(r'^i.86.*', target_triple): 117 features.add('target-x86') 118 elif re.match(r'^x86_64.*', target_triple): 119 features.add('target-x86_64') 120 elif re.match(r'^aarch64.*', target_triple): 121 features.add('target-aarch64') 122 elif re.match(r'^arm.*', target_triple): 123 features.add('target-arm') 124 125 use_gmalloc = lit_config.params.get('use_gmalloc', None) 126 if lit.util.pythonize_bool(use_gmalloc): 127 # Allow use of an explicit path for gmalloc library. 128 # Will default to '/usr/lib/libgmalloc.dylib' if not set. 129 gmalloc_path_str = lit_config.params.get( 130 'gmalloc_path', '/usr/lib/libgmalloc.dylib') 131 if gmalloc_path_str is not None: 132 self.with_environment( 133 'DYLD_INSERT_LIBRARIES', gmalloc_path_str) 134 135 def _find_git_windows_unix_tools(self, tools_needed): 136 assert(sys.platform == 'win32') 137 if sys.version_info.major >= 3: 138 import winreg 139 else: 140 import _winreg as winreg 141 142 # Search both the 64 and 32-bit hives, as well as HKLM + HKCU 143 masks = [0, winreg.KEY_WOW64_64KEY] 144 hives = [winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER] 145 for mask, hive in itertools.product(masks, hives): 146 try: 147 with winreg.OpenKey(hive, r"SOFTWARE\GitForWindows", 0, 148 winreg.KEY_READ | mask) as key: 149 install_root, _ = winreg.QueryValueEx(key, 'InstallPath') 150 151 if not install_root: 152 continue 153 candidate_path = os.path.join(install_root, 'usr', 'bin') 154 if not lit.util.checkToolsPath( 155 candidate_path, tools_needed): 156 continue 157 158 # We found it, stop enumerating. 159 return lit.util.to_string(candidate_path) 160 except: 161 continue 162 163 return None 164 165 def with_environment(self, variable, value, append_path=False): 166 if append_path: 167 # For paths, we should be able to take a list of them and process 168 # all of them. 169 paths_to_add = value 170 if lit.util.is_string(paths_to_add): 171 paths_to_add = [paths_to_add] 172 173 def norm(x): 174 return os.path.normcase(os.path.normpath(x)) 175 176 current_paths = self.config.environment.get(variable, None) 177 if current_paths: 178 current_paths = current_paths.split(os.path.pathsep) 179 paths = [norm(p) for p in current_paths] 180 else: 181 paths = [] 182 183 # If we are passed a list [a b c], then iterating this list forwards 184 # and adding each to the beginning would result in c b a. So we 185 # need to iterate in reverse to end up with the original ordering. 186 for p in reversed(paths_to_add): 187 # Move it to the front if it already exists, otherwise insert 188 # it at the beginning. 189 p = norm(p) 190 try: 191 paths.remove(p) 192 except ValueError: 193 pass 194 paths = [p] + paths 195 value = os.pathsep.join(paths) 196 self.config.environment[variable] = value 197 198 def with_system_environment(self, variables, append_path=False): 199 if lit.util.is_string(variables): 200 variables = [variables] 201 for v in variables: 202 value = os.environ.get(v) 203 if value: 204 self.with_environment(v, value, append_path) 205 206 def clear_environment(self, variables): 207 for name in variables: 208 if name in self.config.environment: 209 del self.config.environment[name] 210 211 def get_process_output(self, command): 212 try: 213 cmd = subprocess.Popen( 214 command, stdout=subprocess.PIPE, 215 stderr=subprocess.PIPE, env=self.config.environment) 216 stdout, stderr = cmd.communicate() 217 stdout = lit.util.to_string(stdout) 218 stderr = lit.util.to_string(stderr) 219 return (stdout, stderr) 220 except OSError: 221 self.lit_config.fatal('Could not run process %s' % command) 222 223 def feature_config(self, features): 224 # Ask llvm-config about the specified feature. 225 arguments = [x for (x, _) in features] 226 config_path = os.path.join(self.config.llvm_tools_dir, 'llvm-config') 227 228 output, _ = self.get_process_output([config_path] + arguments) 229 lines = output.split('\n') 230 231 for (feature_line, (_, patterns)) in zip(lines, features): 232 # We should have either a callable or a dictionary. If it's a 233 # dictionary, grep each key against the output and use the value if 234 # it matches. If it's a callable, it does the entire translation. 235 if callable(patterns): 236 features_to_add = patterns(feature_line) 237 self.config.available_features.update(features_to_add) 238 else: 239 for (re_pattern, feature) in patterns.items(): 240 if re.search(re_pattern, feature_line): 241 self.config.available_features.add(feature) 242 243 # Note that when substituting %clang_cc1 also fill in the include directory 244 # of the builtin headers. Those are part of even a freestanding 245 # environment, but Clang relies on the driver to locate them. 246 def get_clang_builtin_include_dir(self, clang): 247 # FIXME: Rather than just getting the version, we should have clang 248 # print out its resource dir here in an easy to scrape form. 249 clang_dir, _ = self.get_process_output( 250 [clang, '-print-file-name=include']) 251 252 if not clang_dir: 253 print(clang) 254 self.lit_config.fatal( 255 "Couldn't find the include dir for Clang ('%s')" % clang) 256 257 clang_dir = clang_dir.strip() 258 if sys.platform in ['win32'] and not self.use_lit_shell: 259 # Don't pass dosish path separator to msys bash.exe. 260 clang_dir = clang_dir.replace('\\', '/') 261 # Ensure the result is an ascii string, across Python2.5+ - Python3. 262 return clang_dir 263 264 # On macOS, LSan is only supported on clang versions 5 and higher 265 def get_clang_has_lsan(self, clang, triple): 266 if not clang: 267 self.lit_config.warning( 268 'config.host_cxx is unset but test suite is configured ' 269 'to use sanitizers.') 270 return False 271 272 clang_binary = clang.split()[0] 273 version_string, _ = self.get_process_output( 274 [clang_binary, '--version']) 275 if not 'clang' in version_string: 276 self.lit_config.warning( 277 "compiler '%s' does not appear to be clang, " % clang_binary + 278 'but test suite is configured to use sanitizers.') 279 return False 280 281 if re.match(r'.*-linux', triple): 282 return True 283 284 if re.match(r'^x86_64.*-apple', triple): 285 version_regex = re.search(r'version ([0-9]+)\.([0-9]+).([0-9]+)', 286 version_string) 287 major_version_number = int(version_regex.group(1)) 288 minor_version_number = int(version_regex.group(2)) 289 patch_version_number = int(version_regex.group(3)) 290 if ('Apple LLVM' in version_string or 291 'Apple clang' in version_string): 292 # Apple clang doesn't yet support LSan 293 return False 294 return major_version_number >= 5 295 296 return False 297 298 def make_itanium_abi_triple(self, triple): 299 m = re.match(r'(\w+)-(\w+)-(\w+)', triple) 300 if not m: 301 self.lit_config.fatal( 302 "Could not turn '%s' into Itanium ABI triple" % triple) 303 if m.group(3).lower() != 'windows': 304 # All non-windows triples use the Itanium ABI. 305 return triple 306 return m.group(1) + '-' + m.group(2) + '-' + m.group(3) + '-gnu' 307 308 def make_msabi_triple(self, triple): 309 m = re.match(r'(\w+)-(\w+)-(\w+)', triple) 310 if not m: 311 self.lit_config.fatal( 312 "Could not turn '%s' into MS ABI triple" % triple) 313 isa = m.group(1).lower() 314 vendor = m.group(2).lower() 315 os = m.group(3).lower() 316 if os == 'windows' and re.match(r'.*-msvc$', triple): 317 # If the OS is windows and environment is msvc, we're done. 318 return triple 319 if isa.startswith('x86') or isa == 'amd64' or re.match(r'i\d86', isa): 320 # For x86 ISAs, adjust the OS. 321 return isa + '-' + vendor + '-windows-msvc' 322 # -msvc is not supported for non-x86 targets; use a default. 323 return 'i686-pc-windows-msvc' 324 325 def add_tool_substitutions(self, tools, search_dirs=None): 326 if not search_dirs: 327 search_dirs = [self.config.llvm_tools_dir] 328 329 if lit.util.is_string(search_dirs): 330 search_dirs = [search_dirs] 331 332 tools = [x if isinstance(x, ToolSubst) else ToolSubst(x) 333 for x in tools] 334 335 search_dirs = os.pathsep.join(search_dirs) 336 substitutions = [] 337 338 for tool in tools: 339 match = tool.resolve(self, search_dirs) 340 341 # Either no match occurred, or there was an unresolved match that 342 # is ignored. 343 if not match: 344 continue 345 346 subst_key, tool_pipe, command = match 347 348 # An unresolved match occurred that can't be ignored. Fail without 349 # adding any of the previously-discovered substitutions. 350 if not command: 351 return False 352 353 substitutions.append((subst_key, tool_pipe + command)) 354 355 self.config.substitutions.extend(substitutions) 356 return True 357 358 def add_err_msg_substitutions(self): 359 # Python's strerror may not supply the same message 360 # as C++ std::error_code. One example of such a platform is 361 # Visual Studio. errc_messages may be supplied which contains the error 362 # messages for ENOENT, EISDIR, EINVAL and EACCES as a semi colon 363 # separated string. LLVM testsuites can use get_errc_messages in cmake 364 # to automatically get the messages and pass them into lit. 365 errc_messages = getattr(self.config, 'errc_messages', '') 366 if len(errc_messages) != 0: 367 (errc_enoent, errc_eisdir, 368 errc_einval, errc_eacces) = errc_messages.split(';') 369 self.config.substitutions.append( 370 ('%errc_ENOENT', '\'' + errc_enoent + '\'')) 371 self.config.substitutions.append( 372 ('%errc_EISDIR', '\'' + errc_eisdir + '\'')) 373 self.config.substitutions.append( 374 ('%errc_EINVAL', '\'' + errc_einval + '\'')) 375 self.config.substitutions.append( 376 ('%errc_EACCES', '\'' + errc_eacces + '\'')) 377 else: 378 self.config.substitutions.append( 379 ('%errc_ENOENT', '\'' + os.strerror(errno.ENOENT) + '\'')) 380 self.config.substitutions.append( 381 ('%errc_EISDIR', '\'' + os.strerror(errno.EISDIR) + '\'')) 382 self.config.substitutions.append( 383 ('%errc_EINVAL', '\'' + os.strerror(errno.EINVAL) + '\'')) 384 self.config.substitutions.append( 385 ('%errc_EACCES', '\'' + os.strerror(errno.EACCES) + '\'')) 386 387 def use_default_substitutions(self): 388 tool_patterns = [ 389 ToolSubst('FileCheck', unresolved='fatal'), 390 # Handle these specially as they are strings searched for during 391 # testing. 392 ToolSubst(r'\| \bcount\b', command=FindTool('count'), 393 verbatim=True, unresolved='fatal'), 394 ToolSubst(r'\| \bnot\b', command=FindTool('not'), 395 verbatim=True, unresolved='fatal')] 396 397 self.config.substitutions.append(('%python', '"%s"' % (sys.executable))) 398 399 self.add_tool_substitutions( 400 tool_patterns, [self.config.llvm_tools_dir]) 401 402 self.add_err_msg_substitutions() 403 404 def use_llvm_tool(self, name, search_env=None, required=False, quiet=False, 405 search_paths=None, use_installed=False): 406 """Find the executable program 'name', optionally using the specified 407 environment variable as an override before searching the build directory 408 and then optionally the configuration's PATH.""" 409 # If the override is specified in the environment, use it without 410 # validation. 411 tool = None 412 if search_env: 413 tool = self.config.environment.get(search_env) 414 415 if not tool: 416 if search_paths is None: 417 search_paths = [self.config.llvm_tools_dir] 418 # Use the specified search paths. 419 path = os.pathsep.join(search_paths) 420 tool = lit.util.which(name, path) 421 422 if not tool and use_installed: 423 # Otherwise look in the path, if enabled. 424 tool = lit.util.which(name, self.config.environment['PATH']) 425 426 if required and not tool: 427 message = "couldn't find '{}' program".format(name) 428 if search_env: 429 message = message + \ 430 ', try setting {} in your environment'.format(search_env) 431 self.lit_config.fatal(message) 432 433 if tool: 434 tool = os.path.normpath(tool) 435 if not self.lit_config.quiet and not quiet: 436 self.lit_config.note('using {}: {}'.format(name, tool)) 437 return tool 438 439 def use_clang(self, additional_tool_dirs=[], additional_flags=[], 440 required=True, use_installed=False): 441 """Configure the test suite to be able to invoke clang. 442 443 Sets up some environment variables important to clang, locates a 444 just-built or optionally an installed clang, and add a set of standard 445 substitutions useful to any test suite that makes use of clang. 446 447 """ 448 # Clear some environment variables that might affect Clang. 449 # 450 # This first set of vars are read by Clang, but shouldn't affect tests 451 # that aren't specifically looking for these features, or are required 452 # simply to run the tests at all. 453 # 454 # FIXME: Should we have a tool that enforces this? 455 456 # safe_env_vars = ( 457 # 'TMPDIR', 'TEMP', 'TMP', 'USERPROFILE', 'PWD', 458 # 'MACOSX_DEPLOYMENT_TARGET', 'IPHONEOS_DEPLOYMENT_TARGET', 459 # 'VCINSTALLDIR', 'VC100COMNTOOLS', 'VC90COMNTOOLS', 460 # 'VC80COMNTOOLS') 461 possibly_dangerous_env_vars = [ 462 'COMPILER_PATH', 'RC_DEBUG_OPTIONS', 463 'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH', 464 'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH', 465 'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH', 466 'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING', 467 'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX', 468 'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS', 469 'LIBCLANG_RESOURCE_USAGE', 470 'LIBCLANG_CODE_COMPLETION_LOGGING', 471 ] 472 # Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it. 473 if platform.system() != 'Windows': 474 possibly_dangerous_env_vars.append('INCLUDE') 475 476 self.clear_environment(possibly_dangerous_env_vars) 477 478 # Tweak the PATH to include the tools dir and the scripts dir. 479 # Put Clang first to avoid LLVM from overriding out-of-tree clang 480 # builds. 481 exe_dir_props = [self.config.name.lower() + '_tools_dir', 482 'clang_tools_dir', 'llvm_tools_dir'] 483 paths = [getattr(self.config, pp) for pp in exe_dir_props 484 if getattr(self.config, pp, None)] 485 paths = additional_tool_dirs + paths 486 self.with_environment('PATH', paths, append_path=True) 487 488 lib_dir_props = [ 489 self.config.name.lower() + '_libs_dir', 490 'clang_libs_dir', 491 'llvm_shlib_dir', 492 'llvm_libs_dir', 493 ] 494 lib_paths = [getattr(self.config, pp) for pp in lib_dir_props 495 if getattr(self.config, pp, None)] 496 497 self.with_environment('LD_LIBRARY_PATH', lib_paths, append_path=True) 498 499 shl = getattr(self.config, 'llvm_shlib_dir', None) 500 pext = getattr(self.config, 'llvm_plugin_ext', None) 501 if shl: 502 self.config.substitutions.append(('%llvmshlibdir', shl)) 503 if pext: 504 self.config.substitutions.append(('%pluginext', pext)) 505 506 # Discover the 'clang' and 'clangcc' to use. 507 self.config.clang = self.use_llvm_tool( 508 'clang', search_env='CLANG', required=required, 509 search_paths=paths, use_installed=use_installed) 510 if self.config.clang: 511 self.config.available_features.add('clang') 512 builtin_include_dir = self.get_clang_builtin_include_dir( 513 self.config.clang) 514 tool_substitutions = [ 515 ToolSubst('%clang', command=self.config.clang, 516 extra_args=additional_flags), 517 ToolSubst('%clang_analyze_cc1', command='%clang_cc1', 518 extra_args=['-analyze', '%analyze', 519 '-setup-static-analyzer']+additional_flags), 520 ToolSubst('%clang_cc1', command=self.config.clang, 521 extra_args=['-cc1', '-internal-isystem', 522 builtin_include_dir, '-nostdsysteminc'] + 523 additional_flags), 524 ToolSubst('%clang_cpp', command=self.config.clang, 525 extra_args=['--driver-mode=cpp']+additional_flags), 526 ToolSubst('%clang_cl', command=self.config.clang, 527 extra_args=['--driver-mode=cl']+additional_flags), 528 ToolSubst('%clangxx', command=self.config.clang, 529 extra_args=['--driver-mode=g++']+additional_flags), 530 ] 531 self.add_tool_substitutions(tool_substitutions) 532 self.config.substitutions.append( 533 ('%resource_dir', builtin_include_dir)) 534 535 self.config.substitutions.append( 536 ('%itanium_abi_triple', 537 self.make_itanium_abi_triple(self.config.target_triple))) 538 self.config.substitutions.append( 539 ('%ms_abi_triple', 540 self.make_msabi_triple(self.config.target_triple))) 541 542 # The host triple might not be set, at least if we're compiling clang 543 # from an already installed llvm. 544 if (self.config.host_triple and 545 self.config.host_triple != '@LLVM_HOST_TRIPLE@'): 546 self.config.substitutions.append( 547 ('%target_itanium_abi_host_triple', 548 '--target=' + self.make_itanium_abi_triple( 549 self.config.host_triple))) 550 else: 551 self.config.substitutions.append( 552 ('%target_itanium_abi_host_triple', '')) 553 554 # FIXME: Find nicer way to prohibit this. 555 def prefer(this, to): 556 return '''\"*** Do not use '%s' in tests, use '%s'. ***\"''' % ( 557 to, this) 558 self.config.substitutions.append( 559 (' clang ', prefer('%clang', 'clang'))) 560 self.config.substitutions.append( 561 (r' clang\+\+ ', prefer('%clangxx', 'clang++'))) 562 self.config.substitutions.append( 563 (' clang-cc ', prefer('%clang_cc1', 'clang-cc'))) 564 self.config.substitutions.append( 565 (' clang-cl ', prefer('%clang_cl', 'clang-cl'))) 566 self.config.substitutions.append( 567 (' clang -cc1 -analyze ', 568 prefer('%clang_analyze_cc1', 'clang -cc1 -analyze'))) 569 self.config.substitutions.append( 570 (' clang -cc1 ', prefer('%clang_cc1', 'clang -cc1'))) 571 self.config.substitutions.append( 572 (' %clang-cc1 ', 573 '''\"*** invalid substitution, use '%clang_cc1'. ***\"''')) 574 self.config.substitutions.append( 575 (' %clang-cpp ', 576 '''\"*** invalid substitution, use '%clang_cpp'. ***\"''')) 577 self.config.substitutions.append( 578 (' %clang-cl ', 579 '''\"*** invalid substitution, use '%clang_cl'. ***\"''')) 580 581 def use_lld(self, additional_tool_dirs=[], required=True, 582 use_installed=False): 583 """Configure the test suite to be able to invoke lld. 584 585 Sets up some environment variables important to lld, locates a 586 just-built or optionally an installed lld, and add a set of standard 587 substitutions useful to any test suite that makes use of lld. 588 589 """ 590 591 # Tweak the PATH to include the tools dir and the scripts dir. 592 exe_dir_props = [self.config.name.lower() + '_tools_dir', 593 'lld_tools_dir', 'llvm_tools_dir'] 594 paths = [getattr(self.config, pp) for pp in exe_dir_props 595 if getattr(self.config, pp, None)] 596 paths = additional_tool_dirs + paths 597 self.with_environment('PATH', paths, append_path=True) 598 599 lib_dir_props = [self.config.name.lower() + '_libs_dir', 600 'lld_libs_dir', 'llvm_libs_dir'] 601 lib_paths = [getattr(self.config, pp) for pp in lib_dir_props 602 if getattr(self.config, pp, None)] 603 604 self.with_environment('LD_LIBRARY_PATH', lib_paths, append_path=True) 605 606 # Discover the LLD executables to use. 607 608 ld_lld = self.use_llvm_tool('ld.lld', required=required, 609 search_paths=paths, 610 use_installed=use_installed) 611 lld_link = self.use_llvm_tool('lld-link', required=required, 612 search_paths=paths, 613 use_installed=use_installed) 614 ld64_lld = self.use_llvm_tool('ld64.lld', required=required, 615 search_paths=paths, 616 use_installed=use_installed) 617 wasm_ld = self.use_llvm_tool('wasm-ld', required=required, 618 search_paths=paths, 619 use_installed=use_installed) 620 621 was_found = ld_lld and lld_link and ld64_lld and wasm_ld 622 tool_substitutions = [] 623 if ld_lld: 624 tool_substitutions.append(ToolSubst(r'ld\.lld', command=ld_lld)) 625 self.config.available_features.add('ld.lld') 626 if lld_link: 627 tool_substitutions.append(ToolSubst('lld-link', command=lld_link)) 628 self.config.available_features.add('lld-link') 629 if ld64_lld: 630 tool_substitutions.append(ToolSubst(r'ld64\.lld', command=ld64_lld)) 631 self.config.available_features.add('ld64.lld') 632 if wasm_ld: 633 tool_substitutions.append(ToolSubst('wasm-ld', command=wasm_ld)) 634 self.config.available_features.add('wasm-ld') 635 self.add_tool_substitutions(tool_substitutions) 636 637 return was_found 638