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