| import itertools |
| import os |
| import platform |
| import re |
| import subprocess |
| import sys |
| import errno |
| |
| import lit.util |
| from lit.llvm.subst import FindTool |
| from lit.llvm.subst import ToolSubst |
| |
| lit_path_displayed = False |
| |
| |
| class LLVMConfig(object): |
| def __init__(self, lit_config, config): |
| self.lit_config = lit_config |
| self.config = config |
| |
| features = config.available_features |
| |
| self.use_lit_shell = False |
| # Tweak PATH for Win32 to decide to use bash.exe or not. |
| if sys.platform == "win32": |
| # Seek necessary tools in directories and set to $PATH. |
| path = None |
| lit_tools_dir = getattr(config, "lit_tools_dir", None) |
| required_tools = ["cmp.exe", "grep.exe", "sed.exe", "diff.exe", "echo.exe"] |
| path = self.lit_config.getToolsPath( |
| lit_tools_dir, config.environment["PATH"], required_tools |
| ) |
| if path is None: |
| path = self._find_git_windows_unix_tools(required_tools) |
| if path is not None: |
| self.with_environment("PATH", path, append_path=True) |
| # Many tools behave strangely if these environment variables aren't |
| # set. |
| self.with_system_environment( |
| ["SystemDrive", "SystemRoot", "TEMP", "TMP", "PLATFORM"] |
| ) |
| self.use_lit_shell = True |
| |
| global lit_path_displayed |
| if not self.lit_config.quiet and lit_path_displayed is False: |
| self.lit_config.note("using lit tools: {}".format(path)) |
| lit_path_displayed = True |
| |
| # Choose between lit's internal shell pipeline runner and a real shell. |
| # If LIT_USE_INTERNAL_SHELL is in the environment, we use that as an |
| # override. |
| lit_shell_env = os.environ.get("LIT_USE_INTERNAL_SHELL") |
| if lit_shell_env: |
| self.use_lit_shell = lit.util.pythonize_bool(lit_shell_env) |
| |
| if not self.use_lit_shell: |
| features.add("shell") |
| |
| self.with_system_environment( |
| [ |
| "ASAN_SYMBOLIZER_PATH", |
| "HWASAN_SYMBOLIZER_PATH", |
| "MSAN_SYMBOLIZER_PATH", |
| "TSAN_SYMBOLIZER_PATH", |
| "UBSAN_SYMBOLIZER_PATH" "ASAN_OPTIONS", |
| "HWASAN_OPTIONS", |
| "MSAN_OPTIONS", |
| "TSAN_OPTIONS", |
| "UBSAN_OPTIONS", |
| ] |
| ) |
| |
| # Running on Darwin OS |
| if platform.system() == "Darwin": |
| # FIXME: lld uses the first, other projects use the second. |
| # We should standardize on the former. |
| features.add("system-linker-mach-o") |
| features.add("system-darwin") |
| elif platform.system() == "Windows": |
| # For tests that require Windows to run. |
| features.add("system-windows") |
| elif platform.system() == "Linux": |
| features.add("system-linux") |
| elif platform.system() in ["FreeBSD"]: |
| features.add("system-freebsd") |
| elif platform.system() == "NetBSD": |
| features.add("system-netbsd") |
| elif platform.system() == "AIX": |
| features.add("system-aix") |
| elif platform.system() == "SunOS": |
| features.add("system-solaris") |
| elif platform.system() == "OS/390": |
| features.add("system-zos") |
| |
| # Native compilation: host arch == default triple arch |
| # Both of these values should probably be in every site config (e.g. as |
| # part of the standard header. But currently they aren't) |
| host_triple = getattr(config, "host_triple", None) |
| target_triple = getattr(config, "target_triple", None) |
| features.add("target=%s" % target_triple) |
| if host_triple and host_triple == target_triple: |
| features.add("native") |
| |
| # Sanitizers. |
| sanitizers = getattr(config, "llvm_use_sanitizer", "") |
| sanitizers = frozenset(x.lower() for x in sanitizers.split(";")) |
| if "address" in sanitizers: |
| features.add("asan") |
| if "hwaddress" in sanitizers: |
| features.add("hwasan") |
| if "memory" in sanitizers or "memorywithorigins" in sanitizers: |
| features.add("msan") |
| if "undefined" in sanitizers: |
| features.add("ubsan") |
| |
| have_zlib = getattr(config, "have_zlib", None) |
| if have_zlib: |
| features.add("zlib") |
| have_zstd = getattr(config, "have_zstd", None) |
| if have_zstd: |
| features.add("zstd") |
| |
| if getattr(config, "reverse_iteration", None): |
| features.add("reverse_iteration") |
| |
| # Check if we should run long running tests. |
| long_tests = lit_config.params.get("run_long_tests", None) |
| if lit.util.pythonize_bool(long_tests): |
| features.add("long_tests") |
| |
| if target_triple: |
| if re.match(r"^x86_64.*-apple", target_triple): |
| features.add("x86_64-apple") |
| host_cxx = getattr(config, "host_cxx", None) |
| if "address" in sanitizers and self.get_clang_has_lsan( |
| host_cxx, target_triple |
| ): |
| self.with_environment( |
| "ASAN_OPTIONS", "detect_leaks=1", append_path=True |
| ) |
| if re.match(r"^x86_64.*-linux", target_triple): |
| features.add("x86_64-linux") |
| if re.match(r"^i.86.*", target_triple): |
| features.add("target-x86") |
| elif re.match(r"^x86_64.*", target_triple): |
| features.add("target-x86_64") |
| elif re.match(r"^aarch64.*", target_triple): |
| features.add("target-aarch64") |
| elif re.match(r"^arm64.*", target_triple): |
| features.add("target-aarch64") |
| elif re.match(r"^arm.*", target_triple): |
| features.add("target-arm") |
| if re.match(r'^ppc64le.*-linux', target_triple): |
| features.add('target=powerpc64le-linux') |
| |
| use_gmalloc = lit_config.params.get("use_gmalloc", None) |
| if lit.util.pythonize_bool(use_gmalloc): |
| # Allow use of an explicit path for gmalloc library. |
| # Will default to '/usr/lib/libgmalloc.dylib' if not set. |
| gmalloc_path_str = lit_config.params.get( |
| "gmalloc_path", "/usr/lib/libgmalloc.dylib" |
| ) |
| if gmalloc_path_str is not None: |
| self.with_environment("DYLD_INSERT_LIBRARIES", gmalloc_path_str) |
| |
| def _find_git_windows_unix_tools(self, tools_needed): |
| assert sys.platform == "win32" |
| if sys.version_info.major >= 3: |
| import winreg |
| else: |
| import _winreg as winreg |
| |
| # Search both the 64 and 32-bit hives, as well as HKLM + HKCU |
| masks = [0, winreg.KEY_WOW64_64KEY] |
| hives = [winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER] |
| for mask, hive in itertools.product(masks, hives): |
| try: |
| with winreg.OpenKey( |
| hive, r"SOFTWARE\GitForWindows", 0, winreg.KEY_READ | mask |
| ) as key: |
| install_root, _ = winreg.QueryValueEx(key, "InstallPath") |
| |
| if not install_root: |
| continue |
| candidate_path = os.path.join(install_root, "usr", "bin") |
| if not lit.util.checkToolsPath(candidate_path, tools_needed): |
| continue |
| |
| # We found it, stop enumerating. |
| return lit.util.to_string(candidate_path) |
| except: |
| continue |
| |
| return None |
| |
| def with_environment(self, variable, value, append_path=False): |
| if append_path: |
| # For paths, we should be able to take a list of them and process |
| # all of them. |
| paths_to_add = value |
| if lit.util.is_string(paths_to_add): |
| paths_to_add = [paths_to_add] |
| |
| def norm(x): |
| return os.path.normcase(os.path.normpath(x)) |
| |
| current_paths = self.config.environment.get(variable, None) |
| if current_paths: |
| current_paths = current_paths.split(os.path.pathsep) |
| paths = [norm(p) for p in current_paths] |
| else: |
| paths = [] |
| |
| # If we are passed a list [a b c], then iterating this list forwards |
| # and adding each to the beginning would result in c b a. So we |
| # need to iterate in reverse to end up with the original ordering. |
| for p in reversed(paths_to_add): |
| # Move it to the front if it already exists, otherwise insert |
| # it at the beginning. |
| p = norm(p) |
| try: |
| paths.remove(p) |
| except ValueError: |
| pass |
| paths = [p] + paths |
| value = os.pathsep.join(paths) |
| self.config.environment[variable] = value |
| |
| def with_system_environment(self, variables, append_path=False): |
| if lit.util.is_string(variables): |
| variables = [variables] |
| for v in variables: |
| value = os.environ.get(v) |
| if value: |
| self.with_environment(v, value, append_path) |
| |
| def clear_environment(self, variables): |
| for name in variables: |
| if name in self.config.environment: |
| del self.config.environment[name] |
| |
| def get_process_output(self, command): |
| try: |
| cmd = subprocess.Popen( |
| command, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE, |
| env=self.config.environment, |
| ) |
| stdout, stderr = cmd.communicate() |
| stdout = lit.util.to_string(stdout) |
| stderr = lit.util.to_string(stderr) |
| return (stdout, stderr) |
| except OSError: |
| self.lit_config.fatal("Could not run process %s" % command) |
| |
| def feature_config(self, features): |
| # Ask llvm-config about the specified feature. |
| arguments = [x for (x, _) in features] |
| config_path = os.path.join(self.config.llvm_tools_dir, "llvm-config") |
| |
| output, _ = self.get_process_output([config_path] + arguments) |
| lines = output.split("\n") |
| |
| for (feature_line, (_, patterns)) in zip(lines, features): |
| # We should have either a callable or a dictionary. If it's a |
| # dictionary, grep each key against the output and use the value if |
| # it matches. If it's a callable, it does the entire translation. |
| if callable(patterns): |
| features_to_add = patterns(feature_line) |
| self.config.available_features.update(features_to_add) |
| else: |
| for (re_pattern, feature) in patterns.items(): |
| if re.search(re_pattern, feature_line): |
| self.config.available_features.add(feature) |
| |
| # Note that when substituting %clang_cc1 also fill in the include directory |
| # of the builtin headers. Those are part of even a freestanding |
| # environment, but Clang relies on the driver to locate them. |
| def get_clang_builtin_include_dir(self, clang): |
| # FIXME: Rather than just getting the version, we should have clang |
| # print out its resource dir here in an easy to scrape form. |
| clang_dir, _ = self.get_process_output([clang, "-print-file-name=include"]) |
| |
| if not clang_dir: |
| print(clang) |
| self.lit_config.fatal( |
| "Couldn't find the include dir for Clang ('%s')" % clang |
| ) |
| |
| clang_dir = clang_dir.strip() |
| if sys.platform in ["win32"] and not self.use_lit_shell: |
| # Don't pass dosish path separator to msys bash.exe. |
| clang_dir = clang_dir.replace("\\", "/") |
| # Ensure the result is an ascii string, across Python2.5+ - Python3. |
| return clang_dir |
| |
| # On macOS, LSan is only supported on clang versions 5 and higher |
| def get_clang_has_lsan(self, clang, triple): |
| if not clang: |
| self.lit_config.warning( |
| "config.host_cxx is unset but test suite is configured " |
| "to use sanitizers." |
| ) |
| return False |
| |
| clang_binary = clang.split()[0] |
| version_string, _ = self.get_process_output([clang_binary, "--version"]) |
| if not "clang" in version_string: |
| self.lit_config.warning( |
| "compiler '%s' does not appear to be clang, " % clang_binary |
| + "but test suite is configured to use sanitizers." |
| ) |
| return False |
| |
| if re.match(r".*-linux", triple): |
| return True |
| |
| if re.match(r"^x86_64.*-apple", triple): |
| version_regex = re.search( |
| r"version ([0-9]+)\.([0-9]+).([0-9]+)", version_string |
| ) |
| major_version_number = int(version_regex.group(1)) |
| minor_version_number = int(version_regex.group(2)) |
| patch_version_number = int(version_regex.group(3)) |
| if "Apple LLVM" in version_string or "Apple clang" in version_string: |
| # Apple clang doesn't yet support LSan |
| return False |
| return major_version_number >= 5 |
| |
| return False |
| |
| def make_itanium_abi_triple(self, triple): |
| m = re.match(r"(\w+)-(\w+)-(\w+)", triple) |
| if not m: |
| self.lit_config.fatal( |
| "Could not turn '%s' into Itanium ABI triple" % triple |
| ) |
| if m.group(3).lower() != "windows": |
| # All non-windows triples use the Itanium ABI. |
| return triple |
| return m.group(1) + "-" + m.group(2) + "-" + m.group(3) + "-gnu" |
| |
| def make_msabi_triple(self, triple): |
| m = re.match(r"(\w+)-(\w+)-(\w+)", triple) |
| if not m: |
| self.lit_config.fatal("Could not turn '%s' into MS ABI triple" % triple) |
| isa = m.group(1).lower() |
| vendor = m.group(2).lower() |
| os = m.group(3).lower() |
| if os == "windows" and re.match(r".*-msvc$", triple): |
| # If the OS is windows and environment is msvc, we're done. |
| return triple |
| if isa.startswith("x86") or isa == "amd64" or re.match(r"i\d86", isa): |
| # For x86 ISAs, adjust the OS. |
| return isa + "-" + vendor + "-windows-msvc" |
| # -msvc is not supported for non-x86 targets; use a default. |
| return "i686-pc-windows-msvc" |
| |
| def add_tool_substitutions(self, tools, search_dirs=None): |
| if not search_dirs: |
| search_dirs = [self.config.llvm_tools_dir] |
| |
| if lit.util.is_string(search_dirs): |
| search_dirs = [search_dirs] |
| |
| tools = [x if isinstance(x, ToolSubst) else ToolSubst(x) for x in tools] |
| |
| search_dirs = os.pathsep.join(search_dirs) |
| substitutions = [] |
| |
| for tool in tools: |
| match = tool.resolve(self, search_dirs) |
| |
| # Either no match occurred, or there was an unresolved match that |
| # is ignored. |
| if not match: |
| continue |
| |
| subst_key, tool_pipe, command = match |
| |
| # An unresolved match occurred that can't be ignored. Fail without |
| # adding any of the previously-discovered substitutions. |
| if not command: |
| return False |
| |
| substitutions.append((subst_key, tool_pipe + command)) |
| |
| self.config.substitutions.extend(substitutions) |
| return True |
| |
| def add_err_msg_substitutions(self): |
| # Python's strerror may not supply the same message |
| # as C++ std::error_code. One example of such a platform is |
| # Visual Studio. errc_messages may be supplied which contains the error |
| # messages for ENOENT, EISDIR, EINVAL and EACCES as a semi colon |
| # separated string. LLVM testsuites can use get_errc_messages in cmake |
| # to automatically get the messages and pass them into lit. |
| errc_messages = getattr(self.config, "errc_messages", "") |
| if len(errc_messages) != 0: |
| (errc_enoent, errc_eisdir, errc_einval, errc_eacces) = errc_messages.split( |
| ";" |
| ) |
| self.config.substitutions.append(("%errc_ENOENT", "'" + errc_enoent + "'")) |
| self.config.substitutions.append(("%errc_EISDIR", "'" + errc_eisdir + "'")) |
| self.config.substitutions.append(("%errc_EINVAL", "'" + errc_einval + "'")) |
| self.config.substitutions.append(("%errc_EACCES", "'" + errc_eacces + "'")) |
| else: |
| self.config.substitutions.append( |
| ("%errc_ENOENT", "'" + os.strerror(errno.ENOENT) + "'") |
| ) |
| self.config.substitutions.append( |
| ("%errc_EISDIR", "'" + os.strerror(errno.EISDIR) + "'") |
| ) |
| self.config.substitutions.append( |
| ("%errc_EINVAL", "'" + os.strerror(errno.EINVAL) + "'") |
| ) |
| self.config.substitutions.append( |
| ("%errc_EACCES", "'" + os.strerror(errno.EACCES) + "'") |
| ) |
| |
| def use_default_substitutions(self): |
| tool_patterns = [ |
| ToolSubst("FileCheck", unresolved="fatal"), |
| # Handle these specially as they are strings searched for during |
| # testing. |
| ToolSubst( |
| r"\| \bcount\b", |
| command=FindTool("count"), |
| verbatim=True, |
| unresolved="fatal", |
| ), |
| ToolSubst( |
| r"\| \bnot\b", |
| command=FindTool("not"), |
| verbatim=True, |
| unresolved="fatal", |
| ), |
| ] |
| |
| self.config.substitutions.append(("%python", '"%s"' % (sys.executable))) |
| |
| self.add_tool_substitutions(tool_patterns, [self.config.llvm_tools_dir]) |
| |
| self.add_err_msg_substitutions() |
| |
| def use_llvm_tool( |
| self, |
| name, |
| search_env=None, |
| required=False, |
| quiet=False, |
| search_paths=None, |
| use_installed=False, |
| ): |
| """Find the executable program 'name', optionally using the specified |
| environment variable as an override before searching the build directory |
| and then optionally the configuration's PATH.""" |
| # If the override is specified in the environment, use it without |
| # validation. |
| tool = None |
| if search_env: |
| tool = self.config.environment.get(search_env) |
| |
| if not tool: |
| if search_paths is None: |
| search_paths = [self.config.llvm_tools_dir] |
| # Use the specified search paths. |
| path = os.pathsep.join(search_paths) |
| tool = lit.util.which(name, path) |
| |
| if not tool and use_installed: |
| # Otherwise look in the path, if enabled. |
| tool = lit.util.which(name, self.config.environment["PATH"]) |
| |
| if required and not tool: |
| message = "couldn't find '{}' program".format(name) |
| if search_env: |
| message = message + ", try setting {} in your environment".format( |
| search_env |
| ) |
| self.lit_config.fatal(message) |
| |
| if tool: |
| tool = os.path.normpath(tool) |
| if not self.lit_config.quiet and not quiet: |
| self.lit_config.note("using {}: {}".format(name, tool)) |
| return tool |
| |
| def use_clang( |
| self, |
| additional_tool_dirs=[], |
| additional_flags=[], |
| required=True, |
| use_installed=False, |
| ): |
| """Configure the test suite to be able to invoke clang. |
| |
| Sets up some environment variables important to clang, locates a |
| just-built or optionally an installed clang, and add a set of standard |
| substitutions useful to any test suite that makes use of clang. |
| |
| """ |
| # Clear some environment variables that might affect Clang. |
| # |
| # This first set of vars are read by Clang, but shouldn't affect tests |
| # that aren't specifically looking for these features, or are required |
| # simply to run the tests at all. |
| # |
| # FIXME: Should we have a tool that enforces this? |
| |
| # safe_env_vars = ( |
| # 'TMPDIR', 'TEMP', 'TMP', 'USERPROFILE', 'PWD', |
| # 'MACOSX_DEPLOYMENT_TARGET', 'IPHONEOS_DEPLOYMENT_TARGET', |
| # 'VCINSTALLDIR', 'VC100COMNTOOLS', 'VC90COMNTOOLS', |
| # 'VC80COMNTOOLS') |
| possibly_dangerous_env_vars = [ |
| "COMPILER_PATH", |
| "RC_DEBUG_OPTIONS", |
| "CINDEXTEST_PREAMBLE_FILE", |
| "LIBRARY_PATH", |
| "CPATH", |
| "C_INCLUDE_PATH", |
| "CPLUS_INCLUDE_PATH", |
| "OBJC_INCLUDE_PATH", |
| "OBJCPLUS_INCLUDE_PATH", |
| "LIBCLANG_TIMING", |
| "LIBCLANG_OBJTRACKING", |
| "LIBCLANG_LOGGING", |
| "LIBCLANG_BGPRIO_INDEX", |
| "LIBCLANG_BGPRIO_EDIT", |
| "LIBCLANG_NOTHREADS", |
| "LIBCLANG_RESOURCE_USAGE", |
| "LIBCLANG_CODE_COMPLETION_LOGGING", |
| ] |
| # Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it. |
| if platform.system() != "Windows": |
| possibly_dangerous_env_vars.append("INCLUDE") |
| |
| self.clear_environment(possibly_dangerous_env_vars) |
| |
| # Tweak the PATH to include the tools dir and the scripts dir. |
| # Put Clang first to avoid LLVM from overriding out-of-tree clang |
| # builds. |
| exe_dir_props = [ |
| self.config.name.lower() + "_tools_dir", |
| "clang_tools_dir", |
| "llvm_tools_dir", |
| ] |
| paths = [ |
| getattr(self.config, pp) |
| for pp in exe_dir_props |
| if getattr(self.config, pp, None) |
| ] |
| paths = additional_tool_dirs + paths |
| self.with_environment("PATH", paths, append_path=True) |
| |
| lib_dir_props = [ |
| self.config.name.lower() + "_libs_dir", |
| "llvm_shlib_dir", |
| "llvm_libs_dir", |
| ] |
| lib_paths = [ |
| getattr(self.config, pp) |
| for pp in lib_dir_props |
| if getattr(self.config, pp, None) |
| ] |
| |
| self.with_environment("LD_LIBRARY_PATH", lib_paths, append_path=True) |
| |
| shl = getattr(self.config, "llvm_shlib_dir", None) |
| pext = getattr(self.config, "llvm_plugin_ext", None) |
| if shl: |
| self.config.substitutions.append(("%llvmshlibdir", shl)) |
| if pext: |
| self.config.substitutions.append(("%pluginext", pext)) |
| |
| # Discover the 'clang' and 'clangcc' to use. |
| self.config.clang = self.use_llvm_tool( |
| "clang", |
| search_env="CLANG", |
| required=required, |
| search_paths=paths, |
| use_installed=use_installed, |
| ) |
| if self.config.clang: |
| self.config.available_features.add("clang") |
| builtin_include_dir = self.get_clang_builtin_include_dir(self.config.clang) |
| tool_substitutions = [ |
| ToolSubst( |
| "%clang", command=self.config.clang, extra_args=additional_flags |
| ), |
| ToolSubst( |
| "%clang_analyze_cc1", |
| command="%clang_cc1", |
| extra_args=["-analyze", "%analyze", "-setup-static-analyzer"] |
| + additional_flags, |
| ), |
| ToolSubst( |
| "%clang_cc1", |
| command=self.config.clang, |
| extra_args=[ |
| "-cc1", |
| "-internal-isystem", |
| builtin_include_dir, |
| "-nostdsysteminc", |
| ] |
| + additional_flags, |
| ), |
| ToolSubst( |
| "%clang_cpp", |
| command=self.config.clang, |
| extra_args=["--driver-mode=cpp"] + additional_flags, |
| ), |
| ToolSubst( |
| "%clang_cl", |
| command=self.config.clang, |
| extra_args=["--driver-mode=cl"] + additional_flags, |
| ), |
| ToolSubst( |
| "%clang_dxc", |
| command=self.config.clang, |
| extra_args=["--driver-mode=dxc"] + additional_flags, |
| ), |
| ToolSubst( |
| "%clangxx", |
| command=self.config.clang, |
| extra_args=["--driver-mode=g++"] + additional_flags, |
| ), |
| ] |
| self.add_tool_substitutions(tool_substitutions) |
| self.config.substitutions.append(("%resource_dir", builtin_include_dir)) |
| |
| self.config.substitutions.append( |
| ( |
| "%itanium_abi_triple", |
| self.make_itanium_abi_triple(self.config.target_triple), |
| ) |
| ) |
| self.config.substitutions.append( |
| ("%ms_abi_triple", self.make_msabi_triple(self.config.target_triple)) |
| ) |
| |
| # The host triple might not be set, at least if we're compiling clang |
| # from an already installed llvm. |
| if self.config.host_triple and self.config.host_triple != "@LLVM_HOST_TRIPLE@": |
| self.config.substitutions.append( |
| ( |
| "%target_itanium_abi_host_triple", |
| "--target=" + self.make_itanium_abi_triple(self.config.host_triple), |
| ) |
| ) |
| else: |
| self.config.substitutions.append(("%target_itanium_abi_host_triple", "")) |
| |
| # TODO: Many tests work across many language standards. Before |
| # https://discourse.llvm.org/t/lit-run-a-run-line-multiple-times-with-different-replacements/64932 |
| # has a solution, provide substitutions to conveniently try every standard with LIT_CLANG_STD_GROUP. |
| clang_std_group = int(os.environ.get("LIT_CLANG_STD_GROUP", "0")) |
| clang_std_values = ("98", "11", "14", "17", "20", "2b") |
| |
| def add_std_cxx(s): |
| t = s[8:] |
| if t.endswith("-"): |
| t += clang_std_values[-1] |
| l = clang_std_values.index(t[0:2] if t[0:2] != "23" else "2b") |
| h = clang_std_values.index(t[3:5]) |
| # Let LIT_CLANG_STD_GROUP=0 pick the highest value (likely the most relevant |
| # standard). |
| l = h - clang_std_group % (h - l + 1) |
| self.config.substitutions.append((s, "-std=c++" + clang_std_values[l])) |
| |
| add_std_cxx("%std_cxx98-14") |
| add_std_cxx("%std_cxx98-") |
| add_std_cxx("%std_cxx11-14") |
| add_std_cxx("%std_cxx11-") |
| add_std_cxx("%std_cxx14-") |
| add_std_cxx("%std_cxx17-20") |
| add_std_cxx("%std_cxx17-") |
| add_std_cxx("%std_cxx20-") |
| add_std_cxx("%std_cxx23-") |
| |
| # FIXME: Find nicer way to prohibit this. |
| def prefer(this, to): |
| return '''\"*** Do not use '%s' in tests, use '%s'. ***\"''' % (to, this) |
| |
| self.config.substitutions.append((" clang ", prefer("%clang", "clang"))) |
| self.config.substitutions.append( |
| (r" clang\+\+ ", prefer("%clangxx", "clang++")) |
| ) |
| self.config.substitutions.append( |
| (" clang-cc ", prefer("%clang_cc1", "clang-cc")) |
| ) |
| self.config.substitutions.append( |
| (" clang-cl ", prefer("%clang_cl", "clang-cl")) |
| ) |
| self.config.substitutions.append( |
| ( |
| " clang -cc1 -analyze ", |
| prefer("%clang_analyze_cc1", "clang -cc1 -analyze"), |
| ) |
| ) |
| self.config.substitutions.append( |
| (" clang -cc1 ", prefer("%clang_cc1", "clang -cc1")) |
| ) |
| self.config.substitutions.append( |
| (" %clang-cc1 ", '''\"*** invalid substitution, use '%clang_cc1'. ***\"''') |
| ) |
| self.config.substitutions.append( |
| (" %clang-cpp ", '''\"*** invalid substitution, use '%clang_cpp'. ***\"''') |
| ) |
| self.config.substitutions.append( |
| (" %clang-cl ", '''\"*** invalid substitution, use '%clang_cl'. ***\"''') |
| ) |
| |
| def use_lld(self, additional_tool_dirs=[], required=True, use_installed=False): |
| """Configure the test suite to be able to invoke lld. |
| |
| Sets up some environment variables important to lld, locates a |
| just-built or optionally an installed lld, and add a set of standard |
| substitutions useful to any test suite that makes use of lld. |
| |
| """ |
| |
| # Tweak the PATH to include the tools dir and the scripts dir. |
| exe_dir_props = [ |
| self.config.name.lower() + "_tools_dir", |
| "lld_tools_dir", |
| "llvm_tools_dir", |
| ] |
| paths = [ |
| getattr(self.config, pp) |
| for pp in exe_dir_props |
| if getattr(self.config, pp, None) |
| ] |
| paths = additional_tool_dirs + paths |
| self.with_environment("PATH", paths, append_path=True) |
| |
| lib_dir_props = [ |
| self.config.name.lower() + "_libs_dir", |
| "lld_libs_dir", |
| "llvm_shlib_dir", |
| "llvm_libs_dir", |
| ] |
| lib_paths = [ |
| getattr(self.config, pp) |
| for pp in lib_dir_props |
| if getattr(self.config, pp, None) |
| ] |
| |
| self.with_environment("LD_LIBRARY_PATH", lib_paths, append_path=True) |
| |
| # Discover the LLD executables to use. |
| |
| ld_lld = self.use_llvm_tool( |
| "ld.lld", required=required, search_paths=paths, use_installed=use_installed |
| ) |
| lld_link = self.use_llvm_tool( |
| "lld-link", |
| required=required, |
| search_paths=paths, |
| use_installed=use_installed, |
| ) |
| ld64_lld = self.use_llvm_tool( |
| "ld64.lld", |
| required=required, |
| search_paths=paths, |
| use_installed=use_installed, |
| ) |
| wasm_ld = self.use_llvm_tool( |
| "wasm-ld", |
| required=required, |
| search_paths=paths, |
| use_installed=use_installed, |
| ) |
| |
| was_found = ld_lld and lld_link and ld64_lld and wasm_ld |
| tool_substitutions = [] |
| if ld_lld: |
| tool_substitutions.append(ToolSubst(r"ld\.lld", command=ld_lld)) |
| self.config.available_features.add("ld.lld") |
| if lld_link: |
| tool_substitutions.append(ToolSubst("lld-link", command=lld_link)) |
| self.config.available_features.add("lld-link") |
| if ld64_lld: |
| tool_substitutions.append(ToolSubst(r"ld64\.lld", command=ld64_lld)) |
| self.config.available_features.add("ld64.lld") |
| if wasm_ld: |
| tool_substitutions.append(ToolSubst("wasm-ld", command=wasm_ld)) |
| self.config.available_features.add("wasm-ld") |
| self.add_tool_substitutions(tool_substitutions) |
| |
| return was_found |