diff options
Diffstat (limited to 'utils')
-rwxr-xr-x | utils/gen_link_script.py (renamed from utils/gen_link_script/gen_link_script.py) | 0 | ||||
-rw-r--r-- | utils/libcxx/__init__.py | 17 | ||||
-rw-r--r-- | utils/libcxx/compiler.py | 290 | ||||
-rw-r--r-- | utils/libcxx/sym_check/__init__.py (renamed from utils/sym_check/sym_check/__init__.py) | 0 | ||||
-rw-r--r-- | utils/libcxx/sym_check/diff.py (renamed from utils/sym_check/sym_check/diff.py) | 8 | ||||
-rw-r--r-- | utils/libcxx/sym_check/extract.py (renamed from utils/sym_check/sym_check/extract.py) | 7 | ||||
-rw-r--r-- | utils/libcxx/sym_check/match.py (renamed from utils/sym_check/sym_check/match.py) | 0 | ||||
-rw-r--r-- | utils/libcxx/sym_check/util.py (renamed from utils/sym_check/sym_check/util.py) | 50 | ||||
-rw-r--r-- | utils/libcxx/test/__init__.py | 0 | ||||
-rw-r--r-- | utils/libcxx/test/config.py | 1069 | ||||
-rw-r--r-- | utils/libcxx/test/executor.py | 223 | ||||
-rw-r--r-- | utils/libcxx/test/format.py | 238 | ||||
-rw-r--r-- | utils/libcxx/test/target_info.py | 276 | ||||
-rw-r--r-- | utils/libcxx/test/tracing.py | 43 | ||||
-rw-r--r-- | utils/libcxx/util.py | 286 | ||||
-rw-r--r-- | utils/not.py (renamed from utils/not/not.py) | 0 | ||||
-rwxr-xr-x | utils/sym_diff.py (renamed from utils/sym_check/sym_diff.py) | 16 | ||||
-rwxr-xr-x | utils/sym_extract.py (renamed from utils/sym_check/sym_extract.py) | 2 | ||||
-rwxr-xr-x | utils/sym_match.py (renamed from utils/sym_check/sym_match.py) | 2 | ||||
-rw-r--r-- | utils/symcheck-blacklists/linux_blacklist.txt (renamed from utils/sym_check/linux_blacklist.txt) | 0 | ||||
-rw-r--r-- | utils/symcheck-blacklists/osx_blacklist.txt (renamed from utils/sym_check/osx_blacklist.txt) | 0 |
21 files changed, 2471 insertions, 56 deletions
diff --git a/utils/gen_link_script/gen_link_script.py b/utils/gen_link_script.py index 24fe5ce9904ef..24fe5ce9904ef 100755 --- a/utils/gen_link_script/gen_link_script.py +++ b/utils/gen_link_script.py diff --git a/utils/libcxx/__init__.py b/utils/libcxx/__init__.py new file mode 100644 index 0000000000000..7523a3d8bb9b2 --- /dev/null +++ b/utils/libcxx/__init__.py @@ -0,0 +1,17 @@ +#===----------------------------------------------------------------------===## +# +# The LLVM Compiler Infrastructure +# +# This file is dual licensed under the MIT and the University of Illinois Open +# Source Licenses. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===## + +"""libcxx python utilities""" + +__author__ = 'Eric Fiselier' +__email__ = 'eric@efcs.ca' +__versioninfo__ = (0, 1, 0) +__version__ = ' '.join(str(v) for v in __versioninfo__) + 'dev' + +__all__ = [] diff --git a/utils/libcxx/compiler.py b/utils/libcxx/compiler.py new file mode 100644 index 0000000000000..983bc26c80bd2 --- /dev/null +++ b/utils/libcxx/compiler.py @@ -0,0 +1,290 @@ +#===----------------------------------------------------------------------===## +# +# The LLVM Compiler Infrastructure +# +# This file is dual licensed under the MIT and the University of Illinois Open +# Source Licenses. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===## + +import platform +import os +import libcxx.util + + +class CXXCompiler(object): + CM_Default = 0 + CM_PreProcess = 1 + CM_Compile = 2 + CM_Link = 3 + + def __init__(self, path, flags=None, compile_flags=None, link_flags=None, + warning_flags=None, verify_supported=None, + verify_flags=None, use_verify=False, + modules_flags=None, use_modules=False, + use_ccache=False, use_warnings=False, compile_env=None, + cxx_type=None, cxx_version=None): + self.path = path + self.flags = list(flags or []) + self.compile_flags = list(compile_flags or []) + self.link_flags = list(link_flags or []) + self.warning_flags = list(warning_flags or []) + self.verify_supported = verify_supported + self.use_verify = use_verify + self.verify_flags = list(verify_flags or []) + assert not use_verify or verify_supported + assert not use_verify or verify_flags is not None + self.modules_flags = list(modules_flags or []) + self.use_modules = use_modules + assert not use_modules or modules_flags is not None + self.use_ccache = use_ccache + self.use_warnings = use_warnings + if compile_env is not None: + self.compile_env = dict(compile_env) + else: + self.compile_env = None + self.type = cxx_type + self.version = cxx_version + if self.type is None or self.version is None: + self._initTypeAndVersion() + + def isVerifySupported(self): + if self.verify_supported is None: + self.verify_supported = self.hasCompileFlag(['-Xclang', + '-verify-ignore-unexpected']) + if self.verify_supported: + self.verify_flags = [ + '-Xclang', '-verify', + '-Xclang', '-verify-ignore-unexpected=note', + '-ferror-limit=1024' + ] + return self.verify_supported + + def useVerify(self, value=True): + self.use_verify = value + assert not self.use_verify or self.verify_flags is not None + + def useModules(self, value=True): + self.use_modules = value + assert not self.use_modules or self.modules_flags is not None + + def useCCache(self, value=True): + self.use_ccache = value + + def useWarnings(self, value=True): + self.use_warnings = value + + def _initTypeAndVersion(self): + # Get compiler type and version + macros = self.dumpMacros() + if macros is None: + return + compiler_type = None + major_ver = minor_ver = patchlevel = None + if '__clang__' in macros.keys(): + compiler_type = 'clang' + # Treat apple's llvm fork differently. + if '__apple_build_version__' in macros.keys(): + compiler_type = 'apple-clang' + major_ver = macros['__clang_major__'] + minor_ver = macros['__clang_minor__'] + patchlevel = macros['__clang_patchlevel__'] + elif '__GNUC__' in macros.keys(): + compiler_type = 'gcc' + major_ver = macros['__GNUC__'] + minor_ver = macros['__GNUC_MINOR__'] + patchlevel = macros['__GNUC_PATCHLEVEL__'] + self.type = compiler_type + self.version = (major_ver, minor_ver, patchlevel) + + def _basicCmd(self, source_files, out, mode=CM_Default, flags=[], + input_is_cxx=False): + cmd = [] + if self.use_ccache \ + and not mode == self.CM_Link \ + and not mode == self.CM_PreProcess: + cmd += ['ccache'] + cmd += [self.path] + if out is not None: + cmd += ['-o', out] + if input_is_cxx: + cmd += ['-x', 'c++'] + if isinstance(source_files, list): + cmd += source_files + elif isinstance(source_files, str): + cmd += [source_files] + else: + raise TypeError('source_files must be a string or list') + if mode == self.CM_PreProcess: + cmd += ['-E'] + elif mode == self.CM_Compile: + cmd += ['-c'] + cmd += self.flags + if self.use_verify: + cmd += self.verify_flags + assert mode in [self.CM_Default, self.CM_Compile] + if self.use_modules: + cmd += self.modules_flags + if mode != self.CM_Link: + cmd += self.compile_flags + if self.use_warnings: + cmd += self.warning_flags + if mode != self.CM_PreProcess and mode != self.CM_Compile: + cmd += self.link_flags + cmd += flags + return cmd + + def preprocessCmd(self, source_files, out=None, flags=[]): + return self._basicCmd(source_files, out, flags=flags, + mode=self.CM_PreProcess, + input_is_cxx=True) + + def compileCmd(self, source_files, out=None, flags=[]): + return self._basicCmd(source_files, out, flags=flags, + mode=self.CM_Compile, + input_is_cxx=True) + ['-c'] + + def linkCmd(self, source_files, out=None, flags=[]): + return self._basicCmd(source_files, out, flags=flags, + mode=self.CM_Link) + + def compileLinkCmd(self, source_files, out=None, flags=[]): + return self._basicCmd(source_files, out, flags=flags) + + def preprocess(self, source_files, out=None, flags=[], cwd=None): + cmd = self.preprocessCmd(source_files, out, flags) + out, err, rc = libcxx.util.executeCommand(cmd, env=self.compile_env, + cwd=cwd) + return cmd, out, err, rc + + def compile(self, source_files, out=None, flags=[], cwd=None): + cmd = self.compileCmd(source_files, out, flags) + out, err, rc = libcxx.util.executeCommand(cmd, env=self.compile_env, + cwd=cwd) + return cmd, out, err, rc + + def link(self, source_files, out=None, flags=[], cwd=None): + cmd = self.linkCmd(source_files, out, flags) + out, err, rc = libcxx.util.executeCommand(cmd, env=self.compile_env, + cwd=cwd) + return cmd, out, err, rc + + def compileLink(self, source_files, out=None, flags=[], + cwd=None): + cmd = self.compileLinkCmd(source_files, out, flags) + out, err, rc = libcxx.util.executeCommand(cmd, env=self.compile_env, + cwd=cwd) + return cmd, out, err, rc + + def compileLinkTwoSteps(self, source_file, out=None, object_file=None, + flags=[], cwd=None): + if not isinstance(source_file, str): + raise TypeError('This function only accepts a single input file') + if object_file is None: + # Create, use and delete a temporary object file if none is given. + with_fn = lambda: libcxx.util.guardedTempFilename(suffix='.o') + else: + # Otherwise wrap the filename in a context manager function. + with_fn = lambda: libcxx.util.nullContext(object_file) + with with_fn() as object_file: + cc_cmd, cc_stdout, cc_stderr, rc = self.compile( + source_file, object_file, flags=flags, cwd=cwd) + if rc != 0: + return cc_cmd, cc_stdout, cc_stderr, rc + + link_cmd, link_stdout, link_stderr, rc = self.link( + object_file, out=out, flags=flags, cwd=cwd) + return (cc_cmd + ['&&'] + link_cmd, cc_stdout + link_stdout, + cc_stderr + link_stderr, rc) + + def dumpMacros(self, source_files=None, flags=[], cwd=None): + if source_files is None: + source_files = os.devnull + flags = ['-dM'] + flags + cmd, out, err, rc = self.preprocess(source_files, flags=flags, cwd=cwd) + if rc != 0: + return None + parsed_macros = {} + lines = [l.strip() for l in out.split('\n') if l.strip()] + for l in lines: + assert l.startswith('#define ') + l = l[len('#define '):] + macro, _, value = l.partition(' ') + parsed_macros[macro] = value + return parsed_macros + + def getTriple(self): + cmd = [self.path] + self.flags + ['-dumpmachine'] + return libcxx.util.capture(cmd).strip() + + def hasCompileFlag(self, flag): + if isinstance(flag, list): + flags = list(flag) + else: + flags = [flag] + # Add -Werror to ensure that an unrecognized flag causes a non-zero + # exit code. -Werror is supported on all known compiler types. + if self.type is not None: + flags += ['-Werror', '-fsyntax-only'] + cmd, out, err, rc = self.compile(os.devnull, out=os.devnull, + flags=flags) + return rc == 0 + + def addFlagIfSupported(self, flag): + if isinstance(flag, list): + flags = list(flag) + else: + flags = [flag] + if self.hasCompileFlag(flags): + self.flags += flags + return True + else: + return False + + def addCompileFlagIfSupported(self, flag): + if isinstance(flag, list): + flags = list(flag) + else: + flags = [flag] + if self.hasCompileFlag(flags): + self.compile_flags += flags + return True + else: + return False + + def hasWarningFlag(self, flag): + """ + hasWarningFlag - Test if the compiler supports a given warning flag. + Unlike addCompileFlagIfSupported, this function detects when + "-Wno-<warning>" flags are unsupported. If flag is a + "-Wno-<warning>" GCC will not emit an unknown option diagnostic unless + another error is triggered during compilation. + """ + assert isinstance(flag, str) + assert flag.startswith('-W') + if not flag.startswith('-Wno-'): + return self.hasCompileFlag(flag) + flags = ['-Werror', flag] + old_use_warnings = self.use_warnings + self.useWarnings(False) + cmd = self.compileCmd('-', os.devnull, flags) + self.useWarnings(old_use_warnings) + # Remove '-v' because it will cause the command line invocation + # to be printed as part of the error output. + # TODO(EricWF): Are there other flags we need to worry about? + if '-v' in cmd: + cmd.remove('-v') + out, err, rc = libcxx.util.executeCommand( + cmd, input=libcxx.util.to_bytes('#error\n')) + + assert rc != 0 + if flag in err: + return False + return True + + def addWarningFlagIfSupported(self, flag): + if self.hasWarningFlag(flag): + if flag not in self.warning_flags: + self.warning_flags += [flag] + return True + return False diff --git a/utils/sym_check/sym_check/__init__.py b/utils/libcxx/sym_check/__init__.py index 1aa2b450c12fb..1aa2b450c12fb 100644 --- a/utils/sym_check/sym_check/__init__.py +++ b/utils/libcxx/sym_check/__init__.py diff --git a/utils/sym_check/sym_check/diff.py b/utils/libcxx/sym_check/diff.py index 6502a4819a470..0821ef6f780a4 100644 --- a/utils/sym_check/sym_check/diff.py +++ b/utils/libcxx/sym_check/diff.py @@ -11,7 +11,7 @@ diff - A set of functions for diff-ing two symbol lists. """ -from sym_check import util +from libcxx.sym_check import util def _symbol_difference(lhs, rhs): @@ -43,7 +43,7 @@ def changed_symbols(old, new): continue new_sym = _find_by_key(new, old_sym['name']) if (new_sym is not None and not new_sym in old - and cmp(old_sym, new_sym) != 0): + and old_sym != new_sym): changed += [(old_sym, new_sym)] return changed @@ -98,4 +98,6 @@ def report_diff(added_syms, removed_syms, changed_syms, names_only=False, report += 'ABI BREAKAGE: SYMBOLS ADDED OR REMOVED!' else: report += 'Symbols match.' - return report, int(abi_break) + is_different = abi_break or bool(len(added_syms)) \ + or bool(len(changed_syms)) + return report, abi_break, is_different diff --git a/utils/sym_check/sym_check/extract.py b/utils/libcxx/sym_check/extract.py index d132e22822bb8..152ff97db1785 100644 --- a/utils/sym_check/sym_check/extract.py +++ b/utils/libcxx/sym_check/extract.py @@ -14,7 +14,8 @@ import distutils.spawn import sys import re -from sym_check import util +import libcxx.util +from libcxx.sym_check import util extract_ignore_names = ['_init', '_fini'] @@ -48,7 +49,7 @@ class NMExtractor(object): parsed symbols. """ cmd = [self.nm_exe] + self.flags + [lib] - out, _, exit_code = util.execute_command_verbose(cmd) + out, _, exit_code = libcxx.util.executeCommandVerbose(cmd) if exit_code != 0: raise RuntimeError('Failed to run %s on %s' % (self.nm_exe, lib)) fmt_syms = (self._extract_sym(l) @@ -134,7 +135,7 @@ class ReadElfExtractor(object): parsed symbols. """ cmd = [self.tool] + self.flags + [lib] - out, _, exit_code = util.execute_command_verbose(cmd) + out, _, exit_code = libcxx.util.executeCommandVerbose(cmd) if exit_code != 0: raise RuntimeError('Failed to run %s on %s' % (self.nm_exe, lib)) dyn_syms = self.get_dynsym_table(out) diff --git a/utils/sym_check/sym_check/match.py b/utils/libcxx/sym_check/match.py index fae400e4e77e8..fae400e4e77e8 100644 --- a/utils/sym_check/sym_check/match.py +++ b/utils/libcxx/sym_check/match.py diff --git a/utils/sym_check/sym_check/util.py b/utils/libcxx/sym_check/util.py index 9543984673cc7..8a4c4ab496ff5 100644 --- a/utils/sym_check/sym_check/util.py +++ b/utils/libcxx/sym_check/util.py @@ -9,43 +9,9 @@ import ast import distutils.spawn -import signal -import subprocess import sys import re - -def execute_command(cmd, input_str=None): - """ - Execute a command, capture and return its output. - """ - kwargs = { - 'stdin': subprocess.PIPE, - 'stdout': subprocess.PIPE, - 'stderr': subprocess.PIPE, - } - p = subprocess.Popen(cmd, **kwargs) - out, err = p.communicate(input=input_str) - exitCode = p.wait() - if exitCode == -signal.SIGINT: - raise KeyboardInterrupt - return out, err, exitCode - - -def execute_command_verbose(cmd, input_str=None): - """ - Execute a command and print its output on failure. - """ - out, err, exitCode = execute_command(cmd, input_str=input_str) - if exitCode != 0: - report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd]) - report += "Exit Code: %d\n" % exitCode - if out: - report += "Standard Output:\n--\n%s--" % out - if err: - report += "Standard Error:\n--\n%s--" % err - report += "\n\nFailed!" - sys.stderr.write('%s\n' % report) - return out, err, exitCode +import libcxx.util def read_syms_from_list(slist): @@ -97,21 +63,21 @@ _cppfilt_exe = distutils.spawn.find_executable('c++filt') def demangle_symbol(symbol): if _cppfilt_exe is None: return symbol - out, _, exit_code = execute_command_verbose( - [_cppfilt_exe], input_str=symbol) + out, _, exit_code = libcxx.util.executeCommandVerbose( + [_cppfilt_exe], input=symbol) if exit_code != 0: return symbol return out def is_elf(filename): - with open(filename, 'r') as f: + with open(filename, 'rb') as f: magic_bytes = f.read(4) - return magic_bytes == '\x7fELF' + return magic_bytes == b'\x7fELF' def is_mach_o(filename): - with open(filename, 'r') as f: + with open(filename, 'rb') as f: magic_bytes = f.read(4) return magic_bytes in [ '\xfe\xed\xfa\xce', # MH_MAGIC @@ -131,9 +97,9 @@ def is_library_file(filename): def extract_or_load(filename): - import sym_check.extract + import libcxx.sym_check.extract if is_library_file(filename): - return sym_check.extract.extract_symbols(filename) + return libcxx.sym_check.extract.extract_symbols(filename) return read_syms_from_file(filename) def adjust_mangled_name(name): diff --git a/utils/libcxx/test/__init__.py b/utils/libcxx/test/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d --- /dev/null +++ b/utils/libcxx/test/__init__.py diff --git a/utils/libcxx/test/config.py b/utils/libcxx/test/config.py new file mode 100644 index 0000000000000..c941f65b2c572 --- /dev/null +++ b/utils/libcxx/test/config.py @@ -0,0 +1,1069 @@ +#===----------------------------------------------------------------------===## +# +# The LLVM Compiler Infrastructure +# +# This file is dual licensed under the MIT and the University of Illinois Open +# Source Licenses. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===## + +import locale +import os +import platform +import pkgutil +import pipes +import re +import shlex +import shutil +import sys + +from libcxx.compiler import CXXCompiler +from libcxx.test.target_info import make_target_info +from libcxx.test.executor import * +from libcxx.test.tracing import * +import libcxx.util + +def loadSiteConfig(lit_config, config, param_name, env_name): + # We haven't loaded the site specific configuration (the user is + # probably trying to run on a test file directly, and either the site + # configuration hasn't been created by the build system, or we are in an + # out-of-tree build situation). + site_cfg = lit_config.params.get(param_name, + os.environ.get(env_name)) + if not site_cfg: + lit_config.warning('No site specific configuration file found!' + ' Running the tests in the default configuration.') + elif not os.path.isfile(site_cfg): + lit_config.fatal( + "Specified site configuration file does not exist: '%s'" % + site_cfg) + else: + lit_config.note('using site specific configuration at %s' % site_cfg) + ld_fn = lit_config.load_config + + # Null out the load_config function so that lit.site.cfg doesn't + # recursively load a config even if it tries. + # TODO: This is one hell of a hack. Fix it. + def prevent_reload_fn(*args, **kwargs): + pass + lit_config.load_config = prevent_reload_fn + ld_fn(config, site_cfg) + lit_config.load_config = ld_fn + +class Configuration(object): + # pylint: disable=redefined-outer-name + def __init__(self, lit_config, config): + self.lit_config = lit_config + self.config = config + self.is_windows = platform.system() == 'Windows' + self.cxx = None + self.cxx_is_clang_cl = None + self.cxx_stdlib_under_test = None + self.project_obj_root = None + self.libcxx_src_root = None + self.libcxx_obj_root = None + self.cxx_library_root = None + self.cxx_runtime_root = None + self.abi_library_root = None + self.link_shared = self.get_lit_bool('enable_shared', default=True) + self.debug_build = self.get_lit_bool('debug_build', default=False) + self.exec_env = {} + self.use_target = False + self.use_system_cxx_lib = False + self.use_clang_verify = False + self.long_tests = None + self.execute_external = False + + def get_lit_conf(self, name, default=None): + val = self.lit_config.params.get(name, None) + if val is None: + val = getattr(self.config, name, None) + if val is None: + val = default + return val + + def get_lit_bool(self, name, default=None, env_var=None): + def check_value(value, var_name): + if value is None: + return default + if isinstance(value, bool): + return value + if not isinstance(value, str): + raise TypeError('expected bool or string') + if value.lower() in ('1', 'true'): + return True + if value.lower() in ('', '0', 'false'): + return False + self.lit_config.fatal( + "parameter '{}' should be true or false".format(var_name)) + + conf_val = self.get_lit_conf(name) + if env_var is not None and env_var in os.environ and \ + os.environ[env_var] is not None: + val = os.environ[env_var] + if conf_val is not None: + self.lit_config.warning( + 'Environment variable %s=%s is overriding explicit ' + '--param=%s=%s' % (env_var, val, name, conf_val)) + return check_value(val, env_var) + return check_value(conf_val, name) + + def make_static_lib_name(self, name): + """Return the full filename for the specified library name""" + if self.is_windows: + return name + '.lib' + else: + return 'lib' + name + '.a' + + def configure(self): + self.configure_executor() + self.configure_use_system_cxx_lib() + self.configure_target_info() + self.configure_cxx() + self.configure_triple() + self.configure_deployment() + self.configure_src_root() + self.configure_obj_root() + self.configure_cxx_stdlib_under_test() + self.configure_cxx_library_root() + self.configure_use_clang_verify() + self.configure_use_thread_safety() + self.configure_execute_external() + self.configure_ccache() + self.configure_compile_flags() + self.configure_filesystem_compile_flags() + self.configure_link_flags() + self.configure_env() + self.configure_color_diagnostics() + self.configure_debug_mode() + self.configure_warnings() + self.configure_sanitizer() + self.configure_coverage() + self.configure_modules() + self.configure_substitutions() + self.configure_features() + + def print_config_info(self): + # Print the final compile and link flags. + self.lit_config.note('Using compiler: %s' % self.cxx.path) + self.lit_config.note('Using flags: %s' % self.cxx.flags) + if self.cxx.use_modules: + self.lit_config.note('Using modules flags: %s' % + self.cxx.modules_flags) + self.lit_config.note('Using compile flags: %s' + % self.cxx.compile_flags) + if len(self.cxx.warning_flags): + self.lit_config.note('Using warnings: %s' % self.cxx.warning_flags) + self.lit_config.note('Using link flags: %s' % self.cxx.link_flags) + # Print as list to prevent "set([...])" from being printed. + self.lit_config.note('Using available_features: %s' % + list(self.config.available_features)) + self.lit_config.note('Using environment: %r' % self.exec_env) + sys.stderr.flush() # Force flushing to avoid broken output on Windows + + def get_test_format(self): + from libcxx.test.format import LibcxxTestFormat + return LibcxxTestFormat( + self.cxx, + self.use_clang_verify, + self.execute_external, + self.executor, + exec_env=self.exec_env) + + def configure_executor(self): + exec_str = self.get_lit_conf('executor', "None") + te = eval(exec_str) + if te: + self.lit_config.note("Using executor: %r" % exec_str) + if self.lit_config.useValgrind: + # We have no way of knowing where in the chain the + # ValgrindExecutor is supposed to go. It is likely + # that the user wants it at the end, but we have no + # way of getting at that easily. + selt.lit_config.fatal("Cannot infer how to create a Valgrind " + " executor.") + else: + te = LocalExecutor() + if self.lit_config.useValgrind: + te = ValgrindExecutor(self.lit_config.valgrindArgs, te) + self.executor = te + + def configure_target_info(self): + self.target_info = make_target_info(self) + + def configure_cxx(self): + # Gather various compiler parameters. + cxx = self.get_lit_conf('cxx_under_test') + self.cxx_is_clang_cl = cxx is not None and \ + os.path.basename(cxx) == 'clang-cl.exe' + # If no specific cxx_under_test was given, attempt to infer it as + # clang++. + if cxx is None or self.cxx_is_clang_cl: + search_paths = self.config.environment['PATH'] + if cxx is not None and os.path.isabs(cxx): + search_paths = os.path.dirname(cxx) + clangxx = libcxx.util.which('clang++', search_paths) + if clangxx: + cxx = clangxx + self.lit_config.note( + "inferred cxx_under_test as: %r" % cxx) + elif self.cxx_is_clang_cl: + self.lit_config.fatal('Failed to find clang++ substitution for' + ' clang-cl') + if not cxx: + self.lit_config.fatal('must specify user parameter cxx_under_test ' + '(e.g., --param=cxx_under_test=clang++)') + self.cxx = CXXCompiler(cxx) if not self.cxx_is_clang_cl else \ + self._configure_clang_cl(cxx) + cxx_type = self.cxx.type + if cxx_type is not None: + assert self.cxx.version is not None + maj_v, min_v, _ = self.cxx.version + self.config.available_features.add(cxx_type) + self.config.available_features.add('%s-%s' % (cxx_type, maj_v)) + self.config.available_features.add('%s-%s.%s' % ( + cxx_type, maj_v, min_v)) + self.cxx.compile_env = dict(os.environ) + # 'CCACHE_CPP2' prevents ccache from stripping comments while + # preprocessing. This is required to prevent stripping of '-verify' + # comments. + self.cxx.compile_env['CCACHE_CPP2'] = '1' + + def _configure_clang_cl(self, clang_path): + def _split_env_var(var): + return [p.strip() for p in os.environ.get(var, '').split(';') if p.strip()] + + def _prefixed_env_list(var, prefix): + from itertools import chain + return list(chain.from_iterable((prefix, path) for path in _split_env_var(var))) + + assert self.cxx_is_clang_cl + flags = [] + compile_flags = _prefixed_env_list('INCLUDE', '-isystem') + link_flags = _prefixed_env_list('LIB', '-L') + for path in _list_env_var('LIB'): + self.add_path(self.exec_env, path) + return CXXCompiler(clang_path, flags=flags, + compile_flags=compile_flags, + link_flags=link_flags) + + + def configure_src_root(self): + self.libcxx_src_root = self.get_lit_conf( + 'libcxx_src_root', os.path.dirname(self.config.test_source_root)) + + def configure_obj_root(self): + self.project_obj_root = self.get_lit_conf('project_obj_root') + self.libcxx_obj_root = self.get_lit_conf('libcxx_obj_root') + if not self.libcxx_obj_root and self.project_obj_root is not None: + possible_root = os.path.join(self.project_obj_root, 'projects', 'libcxx') + if os.path.isdir(possible_root): + self.libcxx_obj_root = possible_root + else: + self.libcxx_obj_root = self.project_obj_root + + def configure_cxx_library_root(self): + self.cxx_library_root = self.get_lit_conf('cxx_library_root', + self.libcxx_obj_root) + self.cxx_runtime_root = self.get_lit_conf('cxx_runtime_root', + self.cxx_library_root) + + def configure_use_system_cxx_lib(self): + # This test suite supports testing against either the system library or + # the locally built one; the former mode is useful for testing ABI + # compatibility between the current headers and a shipping dynamic + # library. + # Default to testing against the locally built libc++ library. + self.use_system_cxx_lib = self.get_lit_conf('use_system_cxx_lib') + if self.use_system_cxx_lib == 'true': + self.use_system_cxx_lib = True + elif self.use_system_cxx_lib == 'false': + self.use_system_cxx_lib = False + elif self.use_system_cxx_lib: + assert os.path.isdir(self.use_system_cxx_lib) + self.lit_config.note( + "inferred use_system_cxx_lib as: %r" % self.use_system_cxx_lib) + + def configure_cxx_stdlib_under_test(self): + self.cxx_stdlib_under_test = self.get_lit_conf( + 'cxx_stdlib_under_test', 'libc++') + if self.cxx_stdlib_under_test not in \ + ['libc++', 'libstdc++', 'msvc', 'cxx_default']: + self.lit_config.fatal( + 'unsupported value for "cxx_stdlib_under_test": %s' + % self.cxx_stdlib_under_test) + self.config.available_features.add(self.cxx_stdlib_under_test) + if self.cxx_stdlib_under_test == 'libstdc++': + self.config.available_features.add('libstdc++') + # Manually enable the experimental and filesystem tests for libstdc++ + # if the options aren't present. + # FIXME this is a hack. + if self.get_lit_conf('enable_experimental') is None: + self.config.enable_experimental = 'true' + if self.get_lit_conf('enable_filesystem') is None: + self.config.enable_filesystem = 'true' + + def configure_use_clang_verify(self): + '''If set, run clang with -verify on failing tests.''' + self.use_clang_verify = self.get_lit_bool('use_clang_verify') + if self.use_clang_verify is None: + # NOTE: We do not test for the -verify flag directly because + # -verify will always exit with non-zero on an empty file. + self.use_clang_verify = self.cxx.isVerifySupported() + if self.use_clang_verify: + self.config.available_features.add('verify-support') + self.lit_config.note( + "inferred use_clang_verify as: %r" % self.use_clang_verify) + + def configure_use_thread_safety(self): + '''If set, run clang with -verify on failing tests.''' + has_thread_safety = self.cxx.hasCompileFlag('-Werror=thread-safety') + if has_thread_safety: + self.cxx.compile_flags += ['-Werror=thread-safety'] + self.config.available_features.add('thread-safety') + self.lit_config.note("enabling thread-safety annotations") + + def configure_execute_external(self): + # 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 the + # default value. Otherwise we ask the target_info. + use_lit_shell_default = os.environ.get('LIT_USE_INTERNAL_SHELL') + if use_lit_shell_default is not None: + use_lit_shell_default = use_lit_shell_default != '0' + else: + use_lit_shell_default = self.target_info.use_lit_shell_default() + # Check for the command line parameter using the default value if it is + # not present. + use_lit_shell = self.get_lit_bool('use_lit_shell', + use_lit_shell_default) + self.execute_external = not use_lit_shell + + def configure_ccache(self): + use_ccache_default = os.environ.get('LIBCXX_USE_CCACHE') is not None + use_ccache = self.get_lit_bool('use_ccache', use_ccache_default) + if use_ccache: + self.cxx.use_ccache = True + self.lit_config.note('enabling ccache') + + def configure_features(self): + additional_features = self.get_lit_conf('additional_features') + if additional_features: + for f in additional_features.split(','): + self.config.available_features.add(f.strip()) + self.target_info.add_locale_features(self.config.available_features) + + target_platform = self.target_info.platform() + + # Write an "available feature" that combines the triple when + # use_system_cxx_lib is enabled. This is so that we can easily write + # XFAIL markers for tests that are known to fail with versions of + # libc++ as were shipped with a particular triple. + if self.use_system_cxx_lib: + self.config.available_features.add('with_system_cxx_lib') + self.config.available_features.add( + 'with_system_cxx_lib=%s' % self.config.target_triple) + + # Insert the platform name into the available features as a lower case. + self.config.available_features.add(target_platform) + + # If we're using deployment, add sub-components of the triple using + # "darwin" instead of the platform name. + if self.use_deployment: + arch, _, _ = self.config.deployment + self.config.available_features.add('apple-darwin') + self.config.available_features.add(arch + '-apple-darwin') + + # Simulator testing can take a really long time for some of these tests + # so add a feature check so we can REQUIRES: long_tests in them + self.long_tests = self.get_lit_bool('long_tests') + if self.long_tests is None: + # Default to running long tests. + self.long_tests = True + self.lit_config.note( + "inferred long_tests as: %r" % self.long_tests) + + if self.long_tests: + self.config.available_features.add('long_tests') + + # Run a compile test for the -fsized-deallocation flag. This is needed + # in test/std/language.support/support.dynamic/new.delete + if self.cxx.hasCompileFlag('-fsized-deallocation'): + self.config.available_features.add('fsized-deallocation') + + if self.cxx.hasCompileFlag('-faligned-allocation'): + self.config.available_features.add('-faligned-allocation') + else: + # FIXME remove this once more than just clang-4.0 support + # C++17 aligned allocation. + self.config.available_features.add('no-aligned-allocation') + + if self.get_lit_bool('has_libatomic', False): + self.config.available_features.add('libatomic') + + macros = self.cxx.dumpMacros() + if '__cpp_if_constexpr' not in macros: + self.config.available_features.add('libcpp-no-if-constexpr') + + if '__cpp_structured_bindings' not in macros: + self.config.available_features.add('libcpp-no-structured-bindings') + + if '__cpp_deduction_guides' not in macros: + self.config.available_features.add('libcpp-no-deduction-guides') + + if self.is_windows: + self.config.available_features.add('windows') + + # Attempt to detect the glibc version by querying for __GLIBC__ + # in 'features.h'. + macros = self.cxx.dumpMacros(flags=['-include', 'features.h']) + if macros is not None and '__GLIBC__' in macros: + maj_v, min_v = (macros['__GLIBC__'], macros['__GLIBC_MINOR__']) + self.config.available_features.add('glibc') + self.config.available_features.add('glibc-%s' % maj_v) + self.config.available_features.add('glibc-%s.%s' % (maj_v, min_v)) + + def configure_compile_flags(self): + no_default_flags = self.get_lit_bool('no_default_flags', False) + if not no_default_flags: + self.configure_default_compile_flags() + # This include is always needed so add so add it regardless of + # 'no_default_flags'. + support_path = os.path.join(self.libcxx_src_root, 'test/support') + self.cxx.compile_flags += ['-I' + support_path] + # Configure extra flags + compile_flags_str = self.get_lit_conf('compile_flags', '') + self.cxx.compile_flags += shlex.split(compile_flags_str) + # FIXME: Can we remove this? + if self.is_windows: + self.cxx.compile_flags += ['-D_CRT_SECURE_NO_WARNINGS'] + + def configure_default_compile_flags(self): + # Try and get the std version from the command line. Fall back to + # default given in lit.site.cfg is not present. If default is not + # present then force c++11. + std = self.get_lit_conf('std') + if not std: + # Choose the newest possible language dialect if none is given. + possible_stds = ['c++1z', 'c++14', 'c++11', 'c++03'] + if self.cxx.type == 'gcc': + maj_v, _, _ = self.cxx.version + maj_v = int(maj_v) + if maj_v < 7: + possible_stds.remove('c++1z') + # FIXME: How many C++14 tests actually fail under GCC 5 and 6? + # Should we XFAIL them individually instead? + if maj_v <= 6: + possible_stds.remove('c++14') + for s in possible_stds: + if self.cxx.hasCompileFlag('-std=%s' % s): + std = s + self.lit_config.note( + 'inferred language dialect as: %s' % std) + break + if not std: + self.lit_config.fatal( + 'Failed to infer a supported language dialect from one of %r' + % possible_stds) + self.cxx.compile_flags += ['-std={0}'.format(std)] + self.config.available_features.add(std.replace('gnu++', 'c++')) + # Configure include paths + self.configure_compile_flags_header_includes() + self.target_info.add_cxx_compile_flags(self.cxx.compile_flags) + # Configure feature flags. + self.configure_compile_flags_exceptions() + self.configure_compile_flags_rtti() + self.configure_compile_flags_abi_version() + enable_32bit = self.get_lit_bool('enable_32bit', False) + if enable_32bit: + self.cxx.flags += ['-m32'] + # Use verbose output for better errors + self.cxx.flags += ['-v'] + sysroot = self.get_lit_conf('sysroot') + if sysroot: + self.cxx.flags += ['--sysroot', sysroot] + gcc_toolchain = self.get_lit_conf('gcc_toolchain') + if gcc_toolchain: + self.cxx.flags += ['-gcc-toolchain', gcc_toolchain] + # NOTE: the _DEBUG definition must preceed the triple check because for + # the Windows build of libc++, the forced inclusion of a header requires + # that _DEBUG is defined. Incorrect ordering will result in -target + # being elided. + if self.is_windows and self.debug_build: + self.cxx.compile_flags += ['-D_DEBUG'] + if self.use_target: + if not self.cxx.addFlagIfSupported( + ['-target', self.config.target_triple]): + self.lit_config.warning('use_target is true but -target is '\ + 'not supported by the compiler') + if self.use_deployment: + arch, name, version = self.config.deployment + self.cxx.flags += ['-arch', arch] + self.cxx.flags += ['-m' + name + '-version-min=' + version] + + def configure_compile_flags_header_includes(self): + support_path = os.path.join(self.libcxx_src_root, 'test', 'support') + if self.cxx_stdlib_under_test != 'libstdc++' and \ + not self.is_windows: + self.cxx.compile_flags += [ + '-include', os.path.join(support_path, 'nasty_macros.hpp')] + if self.cxx_stdlib_under_test == 'msvc': + self.cxx.compile_flags += [ + '-include', os.path.join(support_path, + 'msvc_stdlib_force_include.hpp')] + pass + if self.is_windows and self.debug_build and \ + self.cxx_stdlib_under_test != 'msvc': + self.cxx.compile_flags += [ + '-include', os.path.join(support_path, + 'set_windows_crt_report_mode.h') + ] + self.configure_config_site_header() + cxx_headers = self.get_lit_conf('cxx_headers') + if cxx_headers == '' or (cxx_headers is None + and self.cxx_stdlib_under_test != 'libc++'): + self.lit_config.note('using the system cxx headers') + return + self.cxx.compile_flags += ['-nostdinc++'] + if cxx_headers is None: + cxx_headers = os.path.join(self.libcxx_src_root, 'include') + if not os.path.isdir(cxx_headers): + self.lit_config.fatal("cxx_headers='%s' is not a directory." + % cxx_headers) + self.cxx.compile_flags += ['-I' + cxx_headers] + if self.libcxx_obj_root is not None: + cxxabi_headers = os.path.join(self.libcxx_obj_root, 'include', + 'c++build') + if os.path.isdir(cxxabi_headers): + self.cxx.compile_flags += ['-I' + cxxabi_headers] + + def configure_config_site_header(self): + # Check for a possible __config_site in the build directory. We + # use this if it exists. + if self.libcxx_obj_root is None: + return + config_site_header = os.path.join(self.libcxx_obj_root, '__config_site') + if not os.path.isfile(config_site_header): + return + contained_macros = self.parse_config_site_and_add_features( + config_site_header) + self.lit_config.note('Using __config_site header %s with macros: %r' + % (config_site_header, contained_macros)) + # FIXME: This must come after the call to + # 'parse_config_site_and_add_features(...)' in order for it to work. + self.cxx.compile_flags += ['-include', config_site_header] + + def parse_config_site_and_add_features(self, header): + """ parse_config_site_and_add_features - Deduce and add the test + features that that are implied by the #define's in the __config_site + header. Return a dictionary containing the macros found in the + '__config_site' header. + """ + # Parse the macro contents of __config_site by dumping the macros + # using 'c++ -dM -E' and filtering the predefines. + predefines = self.cxx.dumpMacros() + macros = self.cxx.dumpMacros(header) + feature_macros_keys = set(macros.keys()) - set(predefines.keys()) + feature_macros = {} + for k in feature_macros_keys: + feature_macros[k] = macros[k] + # We expect the header guard to be one of the definitions + assert '_LIBCPP_CONFIG_SITE' in feature_macros + del feature_macros['_LIBCPP_CONFIG_SITE'] + # The __config_site header should be non-empty. Otherwise it should + # have never been emitted by CMake. + assert len(feature_macros) > 0 + # Transform each macro name into the feature name used in the tests. + # Ex. _LIBCPP_HAS_NO_THREADS -> libcpp-has-no-threads + for m in feature_macros: + if m == '_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS': + continue + if m == '_LIBCPP_ABI_VERSION': + self.config.available_features.add('libcpp-abi-version-v%s' + % feature_macros[m]) + continue + assert m.startswith('_LIBCPP_HAS_') or m == '_LIBCPP_ABI_UNSTABLE' + m = m.lower()[1:].replace('_', '-') + self.config.available_features.add(m) + return feature_macros + + + + def configure_compile_flags_exceptions(self): + enable_exceptions = self.get_lit_bool('enable_exceptions', True) + if not enable_exceptions: + self.config.available_features.add('libcpp-no-exceptions') + self.cxx.compile_flags += ['-fno-exceptions'] + + def configure_compile_flags_rtti(self): + enable_rtti = self.get_lit_bool('enable_rtti', True) + if not enable_rtti: + self.config.available_features.add('libcpp-no-rtti') + self.cxx.compile_flags += ['-fno-rtti', '-D_LIBCPP_NO_RTTI'] + + def configure_compile_flags_abi_version(self): + abi_version = self.get_lit_conf('abi_version', '').strip() + abi_unstable = self.get_lit_bool('abi_unstable') + # Only add the ABI version when it is non-default. + # FIXME(EricWF): Get the ABI version from the "__config_site". + if abi_version and abi_version != '1': + self.cxx.compile_flags += ['-D_LIBCPP_ABI_VERSION=' + abi_version] + if abi_unstable: + self.config.available_features.add('libcpp-abi-unstable') + self.cxx.compile_flags += ['-D_LIBCPP_ABI_UNSTABLE'] + + def configure_filesystem_compile_flags(self): + enable_fs = self.get_lit_bool('enable_filesystem', default=False) + if not enable_fs: + return + enable_experimental = self.get_lit_bool('enable_experimental', default=False) + if not enable_experimental: + self.lit_config.fatal( + 'filesystem is enabled but libc++experimental.a is not.') + self.config.available_features.add('c++filesystem') + static_env = os.path.join(self.libcxx_src_root, 'test', 'std', + 'experimental', 'filesystem', 'Inputs', 'static_test_env') + static_env = os.path.realpath(static_env) + assert os.path.isdir(static_env) + self.cxx.compile_flags += ['-DLIBCXX_FILESYSTEM_STATIC_TEST_ROOT="%s"' % static_env] + + dynamic_env = os.path.join(self.config.test_exec_root, + 'filesystem', 'Output', 'dynamic_env') + dynamic_env = os.path.realpath(dynamic_env) + if not os.path.isdir(dynamic_env): + os.makedirs(dynamic_env) + self.cxx.compile_flags += ['-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT="%s"' % dynamic_env] + self.exec_env['LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT'] = ("%s" % dynamic_env) + + dynamic_helper = os.path.join(self.libcxx_src_root, 'test', 'support', + 'filesystem_dynamic_test_helper.py') + assert os.path.isfile(dynamic_helper) + + self.cxx.compile_flags += ['-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER="%s %s"' + % (sys.executable, dynamic_helper)] + + + def configure_link_flags(self): + no_default_flags = self.get_lit_bool('no_default_flags', False) + if not no_default_flags: + # Configure library path + self.configure_link_flags_cxx_library_path() + self.configure_link_flags_abi_library_path() + + # Configure libraries + if self.cxx_stdlib_under_test == 'libc++': + self.cxx.link_flags += ['-nodefaultlibs'] + # FIXME: Handle MSVCRT as part of the ABI library handling. + if self.is_windows: + self.cxx.link_flags += ['-nostdlib'] + self.configure_link_flags_cxx_library() + self.configure_link_flags_abi_library() + self.configure_extra_library_flags() + elif self.cxx_stdlib_under_test == 'libstdc++': + enable_fs = self.get_lit_bool('enable_filesystem', + default=False) + if enable_fs: + self.config.available_features.add('c++experimental') + self.cxx.link_flags += ['-lstdc++fs'] + self.cxx.link_flags += ['-lm', '-pthread'] + elif self.cxx_stdlib_under_test == 'msvc': + # FIXME: Correctly setup debug/release flags here. + pass + elif self.cxx_stdlib_under_test == 'cxx_default': + self.cxx.link_flags += ['-pthread'] + else: + self.lit_config.fatal( + 'unsupported value for "use_stdlib_type": %s' + % use_stdlib_type) + + link_flags_str = self.get_lit_conf('link_flags', '') + self.cxx.link_flags += shlex.split(link_flags_str) + + def configure_link_flags_cxx_library_path(self): + if not self.use_system_cxx_lib: + if self.cxx_library_root: + self.cxx.link_flags += ['-L' + self.cxx_library_root] + if self.is_windows and self.link_shared: + self.add_path(self.cxx.compile_env, self.cxx_library_root) + if self.cxx_runtime_root: + if not self.is_windows: + self.cxx.link_flags += ['-Wl,-rpath,' + + self.cxx_runtime_root] + elif self.is_windows and self.link_shared: + self.add_path(self.exec_env, self.cxx_runtime_root) + elif os.path.isdir(str(self.use_system_cxx_lib)): + self.cxx.link_flags += ['-L' + self.use_system_cxx_lib] + if not self.is_windows: + self.cxx.link_flags += ['-Wl,-rpath,' + + self.use_system_cxx_lib] + if self.is_windows and self.link_shared: + self.add_path(self.cxx.compile_env, self.use_system_cxx_lib) + + def configure_link_flags_abi_library_path(self): + # Configure ABI library paths. + self.abi_library_root = self.get_lit_conf('abi_library_path') + if self.abi_library_root: + self.cxx.link_flags += ['-L' + self.abi_library_root] + if not self.is_windows: + self.cxx.link_flags += ['-Wl,-rpath,' + self.abi_library_root] + else: + self.add_path(self.exec_env, self.abi_library_root) + + def configure_link_flags_cxx_library(self): + libcxx_experimental = self.get_lit_bool('enable_experimental', default=False) + if libcxx_experimental: + self.config.available_features.add('c++experimental') + self.cxx.link_flags += ['-lc++experimental'] + if self.link_shared: + self.cxx.link_flags += ['-lc++'] + else: + cxx_library_root = self.get_lit_conf('cxx_library_root') + if cxx_library_root: + libname = self.make_static_lib_name('c++') + abs_path = os.path.join(cxx_library_root, libname) + assert os.path.exists(abs_path) and \ + "static libc++ library does not exist" + self.cxx.link_flags += [abs_path] + else: + self.cxx.link_flags += ['-lc++'] + + def configure_link_flags_abi_library(self): + cxx_abi = self.get_lit_conf('cxx_abi', 'libcxxabi') + if cxx_abi == 'libstdc++': + self.cxx.link_flags += ['-lstdc++'] + elif cxx_abi == 'libsupc++': + self.cxx.link_flags += ['-lsupc++'] + elif cxx_abi == 'libcxxabi': + if self.target_info.allow_cxxabi_link(): + libcxxabi_shared = self.get_lit_bool('libcxxabi_shared', default=True) + if libcxxabi_shared: + self.cxx.link_flags += ['-lc++abi'] + else: + cxxabi_library_root = self.get_lit_conf('abi_library_path') + if cxxabi_library_root: + libname = self.make_static_lib_name('c++abi') + abs_path = os.path.join(cxxabi_library_root, libname) + self.cxx.link_flags += [abs_path] + else: + self.cxx.link_flags += ['-lc++abi'] + elif cxx_abi == 'libcxxrt': + self.cxx.link_flags += ['-lcxxrt'] + elif cxx_abi == 'vcruntime': + debug_suffix = 'd' if self.debug_build else '' + self.cxx.link_flags += ['-l%s%s' % (lib, debug_suffix) for lib in + ['vcruntime', 'ucrt', 'msvcrt']] + elif cxx_abi == 'none' or cxx_abi == 'default': + if self.is_windows: + debug_suffix = 'd' if self.debug_build else '' + self.cxx.link_flags += ['-lmsvcrt%s' % debug_suffix] + else: + self.lit_config.fatal( + 'C++ ABI setting %s unsupported for tests' % cxx_abi) + + def configure_extra_library_flags(self): + if self.get_lit_bool('cxx_ext_threads', default=False): + self.cxx.link_flags += ['-lc++external_threads'] + self.target_info.add_cxx_link_flags(self.cxx.link_flags) + + def configure_color_diagnostics(self): + use_color = self.get_lit_conf('color_diagnostics') + if use_color is None: + use_color = os.environ.get('LIBCXX_COLOR_DIAGNOSTICS') + if use_color is None: + return + if use_color != '': + self.lit_config.fatal('Invalid value for color_diagnostics "%s".' + % use_color) + color_flag = '-fdiagnostics-color=always' + # Check if the compiler supports the color diagnostics flag. Issue a + # warning if it does not since color diagnostics have been requested. + if not self.cxx.hasCompileFlag(color_flag): + self.lit_config.warning( + 'color diagnostics have been requested but are not supported ' + 'by the compiler') + else: + self.cxx.flags += [color_flag] + + def configure_debug_mode(self): + debug_level = self.get_lit_conf('debug_level', None) + if not debug_level: + return + if debug_level not in ['0', '1']: + self.lit_config.fatal('Invalid value for debug_level "%s".' + % debug_level) + self.cxx.compile_flags += ['-D_LIBCPP_DEBUG=%s' % debug_level] + + def configure_warnings(self): + # Turn on warnings by default for Clang based compilers when C++ >= 11 + default_enable_warnings = self.cxx.type in ['clang', 'apple-clang'] \ + and len(self.config.available_features.intersection( + ['c++11', 'c++14', 'c++1z'])) != 0 + enable_warnings = self.get_lit_bool('enable_warnings', + default_enable_warnings) + self.cxx.useWarnings(enable_warnings) + self.cxx.warning_flags += [ + '-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER', + '-Wall', '-Wextra', '-Werror' + ] + if self.cxx.hasWarningFlag('-Wuser-defined-warnings'): + self.cxx.warning_flags += ['-Wuser-defined-warnings'] + self.config.available_features.add('diagnose-if-support') + self.cxx.addWarningFlagIfSupported('-Wshadow') + self.cxx.addWarningFlagIfSupported('-Wno-unused-command-line-argument') + self.cxx.addWarningFlagIfSupported('-Wno-attributes') + self.cxx.addWarningFlagIfSupported('-Wno-pessimizing-move') + self.cxx.addWarningFlagIfSupported('-Wno-c++11-extensions') + self.cxx.addWarningFlagIfSupported('-Wno-user-defined-literals') + self.cxx.addWarningFlagIfSupported('-Wno-noexcept-type') + # These warnings should be enabled in order to support the MSVC + # team using the test suite; They enable the warnings below and + # expect the test suite to be clean. + self.cxx.addWarningFlagIfSupported('-Wsign-compare') + self.cxx.addWarningFlagIfSupported('-Wunused-variable') + self.cxx.addWarningFlagIfSupported('-Wunused-parameter') + self.cxx.addWarningFlagIfSupported('-Wunreachable-code') + # FIXME: Enable the two warnings below. + self.cxx.addWarningFlagIfSupported('-Wno-conversion') + self.cxx.addWarningFlagIfSupported('-Wno-unused-local-typedef') + std = self.get_lit_conf('std', None) + if std in ['c++98', 'c++03']: + # The '#define static_assert' provided by libc++ in C++03 mode + # causes an unused local typedef whenever it is used. + self.cxx.addWarningFlagIfSupported('-Wno-unused-local-typedef') + + def configure_sanitizer(self): + san = self.get_lit_conf('use_sanitizer', '').strip() + if san: + self.target_info.add_sanitizer_features(san, self.config.available_features) + # Search for llvm-symbolizer along the compiler path first + # and then along the PATH env variable. + symbolizer_search_paths = os.environ.get('PATH', '') + cxx_path = libcxx.util.which(self.cxx.path) + if cxx_path is not None: + symbolizer_search_paths = ( + os.path.dirname(cxx_path) + + os.pathsep + symbolizer_search_paths) + llvm_symbolizer = libcxx.util.which('llvm-symbolizer', + symbolizer_search_paths) + + def add_ubsan(): + self.cxx.flags += ['-fsanitize=undefined', + '-fno-sanitize=vptr,function,float-divide-by-zero', + '-fno-sanitize-recover=all'] + self.exec_env['UBSAN_OPTIONS'] = 'print_stacktrace=1' + self.config.available_features.add('ubsan') + + # Setup the sanitizer compile flags + self.cxx.flags += ['-g', '-fno-omit-frame-pointer'] + if san == 'Address' or san == 'Address;Undefined' or san == 'Undefined;Address': + self.cxx.flags += ['-fsanitize=address'] + if llvm_symbolizer is not None: + self.exec_env['ASAN_SYMBOLIZER_PATH'] = llvm_symbolizer + # FIXME: Turn ODR violation back on after PR28391 is resolved + # https://bugs.llvm.org/show_bug.cgi?id=28391 + self.exec_env['ASAN_OPTIONS'] = 'detect_odr_violation=0' + self.config.available_features.add('asan') + self.config.available_features.add('sanitizer-new-delete') + self.cxx.compile_flags += ['-O1'] + if san == 'Address;Undefined' or san == 'Undefined;Address': + add_ubsan() + elif san == 'Memory' or san == 'MemoryWithOrigins': + self.cxx.flags += ['-fsanitize=memory'] + if san == 'MemoryWithOrigins': + self.cxx.compile_flags += [ + '-fsanitize-memory-track-origins'] + if llvm_symbolizer is not None: + self.exec_env['MSAN_SYMBOLIZER_PATH'] = llvm_symbolizer + self.config.available_features.add('msan') + self.config.available_features.add('sanitizer-new-delete') + self.cxx.compile_flags += ['-O1'] + elif san == 'Undefined': + add_ubsan() + self.cxx.compile_flags += ['-O2'] + elif san == 'Thread': + self.cxx.flags += ['-fsanitize=thread'] + self.config.available_features.add('tsan') + self.config.available_features.add('sanitizer-new-delete') + else: + self.lit_config.fatal('unsupported value for ' + 'use_sanitizer: {0}'.format(san)) + san_lib = self.get_lit_conf('sanitizer_library') + if san_lib: + self.cxx.link_flags += [ + san_lib, '-Wl,-rpath,%s' % os.path.dirname(san_lib)] + + def configure_coverage(self): + self.generate_coverage = self.get_lit_bool('generate_coverage', False) + if self.generate_coverage: + self.cxx.flags += ['-g', '--coverage'] + self.cxx.compile_flags += ['-O0'] + + def configure_modules(self): + modules_flags = ['-fmodules'] + if platform.system() != 'Darwin': + modules_flags += ['-Xclang', '-fmodules-local-submodule-visibility'] + supports_modules = self.cxx.hasCompileFlag(modules_flags) + enable_modules = self.get_lit_bool('enable_modules', + default=False, + env_var='LIBCXX_ENABLE_MODULES') + if enable_modules and not supports_modules: + self.lit_config.fatal( + '-fmodules is enabled but not supported by the compiler') + if not supports_modules: + return + self.config.available_features.add('modules-support') + module_cache = os.path.join(self.config.test_exec_root, + 'modules.cache') + module_cache = os.path.realpath(module_cache) + if os.path.isdir(module_cache): + shutil.rmtree(module_cache) + os.makedirs(module_cache) + self.cxx.modules_flags = modules_flags + \ + ['-fmodules-cache-path=' + module_cache] + if enable_modules: + self.config.available_features.add('-fmodules') + self.cxx.useModules() + + def configure_substitutions(self): + sub = self.config.substitutions + cxx_path = pipes.quote(self.cxx.path) + # Configure compiler substitutions + sub.append(('%cxx', cxx_path)) + # Configure flags substitutions + flags_str = ' '.join([pipes.quote(f) for f in self.cxx.flags]) + compile_flags_str = ' '.join([pipes.quote(f) for f in self.cxx.compile_flags]) + link_flags_str = ' '.join([pipes.quote(f) for f in self.cxx.link_flags]) + all_flags = '%s %s %s' % (flags_str, compile_flags_str, link_flags_str) + sub.append(('%flags', flags_str)) + sub.append(('%compile_flags', compile_flags_str)) + sub.append(('%link_flags', link_flags_str)) + sub.append(('%all_flags', all_flags)) + if self.cxx.isVerifySupported(): + verify_str = ' ' + ' '.join(self.cxx.verify_flags) + ' ' + sub.append(('%verify', verify_str)) + # Add compile and link shortcuts + compile_str = (cxx_path + ' -o %t.o %s -c ' + flags_str + + ' ' + compile_flags_str) + link_str = (cxx_path + ' -o %t.exe %t.o ' + flags_str + ' ' + + link_flags_str) + assert type(link_str) is str + build_str = cxx_path + ' -o %t.exe %s ' + all_flags + if self.cxx.use_modules: + sub.append(('%compile_module', compile_str)) + sub.append(('%build_module', build_str)) + elif self.cxx.modules_flags is not None: + modules_str = ' '.join(self.cxx.modules_flags) + ' ' + sub.append(('%compile_module', compile_str + ' ' + modules_str)) + sub.append(('%build_module', build_str + ' ' + modules_str)) + sub.append(('%compile', compile_str)) + sub.append(('%link', link_str)) + sub.append(('%build', build_str)) + # Configure exec prefix substitutions. + exec_env_str = '' + if not self.is_windows and len(self.exec_env) != 0: + exec_env_str = 'env ' + for k, v in self.exec_env.items(): + exec_env_str += ' %s=%s' % (k, v) + # Configure run env substitution. + exec_str = exec_env_str + if self.lit_config.useValgrind: + exec_str = ' '.join(self.lit_config.valgrindArgs) + exec_env_str + sub.append(('%exec', exec_str)) + # Configure run shortcut + sub.append(('%run', exec_str + ' %t.exe')) + # Configure not program substitutions + not_py = os.path.join(self.libcxx_src_root, 'utils', 'not.py') + not_str = '%s %s ' % (pipes.quote(sys.executable), pipes.quote(not_py)) + sub.append(('not ', not_str)) + + def can_use_deployment(self): + # Check if the host is on an Apple platform using clang. + if not self.target_info.platform() == "darwin": + return False + if not self.target_info.is_host_macosx(): + return False + if not self.cxx.type.endswith('clang'): + return False + return True + + def configure_triple(self): + # Get or infer the target triple. + target_triple = self.get_lit_conf('target_triple') + self.use_target = self.get_lit_bool('use_target', False) + if self.use_target and target_triple: + self.lit_config.warning('use_target is true but no triple is specified') + + # Use deployment if possible. + self.use_deployment = not self.use_target and self.can_use_deployment() + if self.use_deployment: + return + + # Save the triple (and warn on Apple platforms). + self.config.target_triple = target_triple + if self.use_target and 'apple' in target_triple: + self.lit_config.warning('consider using arch and platform instead' + ' of target_triple on Apple platforms') + + # If no target triple was given, try to infer it from the compiler + # under test. + if not self.config.target_triple: + target_triple = self.cxx.getTriple() + # Drop sub-major version components from the triple, because the + # current XFAIL handling expects exact matches for feature checks. + # Example: x86_64-apple-darwin14.0.0 -> x86_64-apple-darwin14 + # The 5th group handles triples greater than 3 parts + # (ex x86_64-pc-linux-gnu). + target_triple = re.sub(r'([^-]+)-([^-]+)-([^.]+)([^-]*)(.*)', + r'\1-\2-\3\5', target_triple) + # linux-gnu is needed in the triple to properly identify linuxes + # that use GLIBC. Handle redhat and opensuse triples as special + # cases and append the missing `-gnu` portion. + if (target_triple.endswith('redhat-linux') or + target_triple.endswith('suse-linux')): + target_triple += '-gnu' + self.config.target_triple = target_triple + self.lit_config.note( + "inferred target_triple as: %r" % self.config.target_triple) + + def configure_deployment(self): + assert not self.use_deployment is None + assert not self.use_target is None + if not self.use_deployment: + # Warn about ignored parameters. + if self.get_lit_conf('arch'): + self.lit_config.warning('ignoring arch, using target_triple') + if self.get_lit_conf('platform'): + self.lit_config.warning('ignoring platform, using target_triple') + return + + assert not self.use_target + assert self.target_info.is_host_macosx() + + # Always specify deployment explicitly on Apple platforms, since + # otherwise a platform is picked up from the SDK. If the SDK version + # doesn't match the system version, tests that use the system library + # may fail spuriously. + arch = self.get_lit_conf('arch') + if not arch: + arch = self.cxx.getTriple().split('-', 1)[0] + self.lit_config.note("inferred arch as: %r" % arch) + + inferred_platform, name, version = self.target_info.get_platform() + if inferred_platform: + self.lit_config.note("inferred platform as: %r" % (name + version)) + self.config.deployment = (arch, name, version) + + # Set the target triple for use by lit. + self.config.target_triple = arch + '-apple-' + name + version + self.lit_config.note( + "computed target_triple as: %r" % self.config.target_triple) + + def configure_env(self): + self.target_info.configure_env(self.exec_env) + + def add_path(self, dest_env, new_path): + if 'PATH' not in dest_env: + dest_env['PATH'] = new_path + else: + split_char = ';' if self.is_windows else ':' + dest_env['PATH'] = '%s%s%s' % (new_path, split_char, + dest_env['PATH']) diff --git a/utils/libcxx/test/executor.py b/utils/libcxx/test/executor.py new file mode 100644 index 0000000000000..4a189174d915c --- /dev/null +++ b/utils/libcxx/test/executor.py @@ -0,0 +1,223 @@ +#===----------------------------------------------------------------------===## +# +# The LLVM Compiler Infrastructure +# +# This file is dual licensed under the MIT and the University of Illinois Open +# Source Licenses. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===## + +import platform +import os + +from libcxx.test import tracing +from libcxx.util import executeCommand + + +class Executor(object): + def run(self, exe_path, cmd, local_cwd, file_deps=None, env=None): + """Execute a command. + Be very careful not to change shared state in this function. + Executor objects are shared between python processes in `lit -jN`. + Args: + exe_path: str: Local path to the executable to be run + cmd: [str]: subprocess.call style command + local_cwd: str: Local path to the working directory + file_deps: [str]: Files required by the test + env: {str: str}: Environment variables to execute under + Returns: + cmd, out, err, exitCode + """ + raise NotImplementedError + + +class LocalExecutor(Executor): + def __init__(self): + super(LocalExecutor, self).__init__() + self.is_windows = platform.system() == 'Windows' + + def run(self, exe_path, cmd=None, work_dir='.', file_deps=None, env=None): + cmd = cmd or [exe_path] + env_cmd = [] + if env: + env_cmd += ['env'] + env_cmd += ['%s=%s' % (k, v) for k, v in env.items()] + if work_dir == '.': + work_dir = os.getcwd() + if not self.is_windows: + out, err, rc = executeCommand(env_cmd + cmd, cwd=work_dir) + else: + out, err, rc = executeCommand(cmd, cwd=work_dir, + env=self._build_windows_env(env)) + return (env_cmd + cmd, out, err, rc) + + def _build_windows_env(self, exec_env): + # FIXME: Finding Windows DLL's at runtime requires modifying the + # PATH environment variables. However we don't want to print out + # the entire PATH as part of the diagnostic for every failing test. + # Therefore this hack builds a new executable environment that + # merges the current environment and the supplied environment while + # still only printing the supplied environment in diagnostics. + if not self.is_windows or exec_env is None: + return None + new_env = dict(os.environ) + for key, value in exec_env.items(): + if key == 'PATH': + assert value.strip() != '' and "expected non-empty path" + new_env['PATH'] = "%s;%s" % (value, os.environ['PATH']) + else: + new_env[key] = value + return new_env + +class PrefixExecutor(Executor): + """Prefix an executor with some other command wrapper. + + Most useful for setting ulimits on commands, or running an emulator like + qemu and valgrind. + """ + def __init__(self, commandPrefix, chain): + super(PrefixExecutor, self).__init__() + + self.commandPrefix = commandPrefix + self.chain = chain + + def run(self, exe_path, cmd=None, work_dir='.', file_deps=None, env=None): + cmd = cmd or [exe_path] + return self.chain.run(exe_path, self.commandPrefix + cmd, work_dir, + file_deps, env=env) + + +class PostfixExecutor(Executor): + """Postfix an executor with some args.""" + def __init__(self, commandPostfix, chain): + super(PostfixExecutor, self).__init__() + + self.commandPostfix = commandPostfix + self.chain = chain + + def run(self, exe_path, cmd=None, work_dir='.', file_deps=None, env=None): + cmd = cmd or [exe_path] + return self.chain.run(cmd + self.commandPostfix, work_dir, file_deps, + env=env) + + + +class TimeoutExecutor(PrefixExecutor): + """Execute another action under a timeout. + + Deprecated. http://reviews.llvm.org/D6584 adds timeouts to LIT. + """ + def __init__(self, duration, chain): + super(TimeoutExecutor, self).__init__( + ['timeout', duration], chain) + + +class RemoteExecutor(Executor): + def __init__(self): + self.local_run = executeCommand + + def remote_temp_dir(self): + return self._remote_temp(True) + + def remote_temp_file(self): + return self._remote_temp(False) + + def _remote_temp(self, is_dir): + raise NotImplementedError() + + def copy_in(self, local_srcs, remote_dsts): + # This could be wrapped up in a tar->scp->untar for performance + # if there are lots of files to be copied/moved + for src, dst in zip(local_srcs, remote_dsts): + self._copy_in_file(src, dst) + + def _copy_in_file(self, src, dst): + raise NotImplementedError() + + def delete_remote(self, remote): + try: + self._execute_command_remote(['rm', '-rf', remote]) + except OSError: + # TODO: Log failure to delete? + pass + + def run(self, exe_path, cmd=None, work_dir='.', file_deps=None, env=None): + target_exe_path = None + target_cwd = None + try: + target_cwd = self.remote_temp_dir() + target_exe_path = os.path.join(target_cwd, 'libcxx_test.exe') + if cmd: + # Replace exe_path with target_exe_path. + cmd = [c if c != exe_path else target_exe_path for c in cmd] + else: + cmd = [target_exe_path] + + srcs = [exe_path] + dsts = [target_exe_path] + if file_deps is not None: + dev_paths = [os.path.join(target_cwd, os.path.basename(f)) + for f in file_deps] + srcs.extend(file_deps) + dsts.extend(dev_paths) + self.copy_in(srcs, dsts) + # TODO(jroelofs): capture the copy_in and delete_remote commands, + # and conjugate them with '&&'s around the first tuple element + # returned here: + return self._execute_command_remote(cmd, target_cwd, env) + finally: + if target_cwd: + self.delete_remote(target_cwd) + + def _execute_command_remote(self, cmd, remote_work_dir='.', env=None): + raise NotImplementedError() + + +class SSHExecutor(RemoteExecutor): + def __init__(self, host, username=None): + super(SSHExecutor, self).__init__() + + self.user_prefix = username + '@' if username else '' + self.host = host + self.scp_command = 'scp' + self.ssh_command = 'ssh' + + # TODO(jroelofs): switch this on some -super-verbose-debug config flag + if False: + self.local_run = tracing.trace_function( + self.local_run, log_calls=True, log_results=True, + label='ssh_local') + + def _remote_temp(self, is_dir): + # TODO: detect what the target system is, and use the correct + # mktemp command for it. (linux and darwin differ here, and I'm + # sure windows has another way to do it) + + # Not sure how to do suffix on osx yet + dir_arg = '-d' if is_dir else '' + cmd = 'mktemp -q {} /tmp/libcxx.XXXXXXXXXX'.format(dir_arg) + _, temp_path, err, exitCode = self._execute_command_remote([cmd]) + temp_path = temp_path.strip() + if exitCode != 0: + raise RuntimeError(err) + return temp_path + + def _copy_in_file(self, src, dst): + scp = self.scp_command + remote = self.host + remote = self.user_prefix + remote + cmd = [scp, '-p', src, remote + ':' + dst] + self.local_run(cmd) + + def _execute_command_remote(self, cmd, remote_work_dir='.', env=None): + remote = self.user_prefix + self.host + ssh_cmd = [self.ssh_command, '-oBatchMode=yes', remote] + if env: + env_cmd = ['env'] + ['%s=%s' % (k, v) for k, v in env.items()] + else: + env_cmd = [] + remote_cmd = ' '.join(env_cmd + cmd) + if remote_work_dir != '.': + remote_cmd = 'cd ' + remote_work_dir + ' && ' + remote_cmd + out, err, rc = self.local_run(ssh_cmd + [remote_cmd]) + return (remote_cmd, out, err, rc) diff --git a/utils/libcxx/test/format.py b/utils/libcxx/test/format.py new file mode 100644 index 0000000000000..c3bc97187ad45 --- /dev/null +++ b/utils/libcxx/test/format.py @@ -0,0 +1,238 @@ +#===----------------------------------------------------------------------===## +# +# The LLVM Compiler Infrastructure +# +# This file is dual licensed under the MIT and the University of Illinois Open +# Source Licenses. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===## + +import copy +import errno +import os +import time +import random + +import lit.Test # pylint: disable=import-error +import lit.TestRunner # pylint: disable=import-error +from lit.TestRunner import ParserKind, IntegratedTestKeywordParser \ + # pylint: disable=import-error + +from libcxx.test.executor import LocalExecutor as LocalExecutor +import libcxx.util + + +class LibcxxTestFormat(object): + """ + Custom test format handler for use with the test format use by libc++. + + Tests fall into two categories: + FOO.pass.cpp - Executable test which should compile, run, and exit with + code 0. + FOO.fail.cpp - Negative test case which is expected to fail compilation. + FOO.sh.cpp - A test that uses LIT's ShTest format. + """ + + def __init__(self, cxx, use_verify_for_fail, execute_external, + executor, exec_env): + self.cxx = copy.deepcopy(cxx) + self.use_verify_for_fail = use_verify_for_fail + self.execute_external = execute_external + self.executor = executor + self.exec_env = dict(exec_env) + + @staticmethod + def _make_custom_parsers(): + return [ + IntegratedTestKeywordParser('FLAKY_TEST.', ParserKind.TAG, + initial_value=False), + IntegratedTestKeywordParser('MODULES_DEFINES:', ParserKind.LIST, + initial_value=[]) + ] + + @staticmethod + def _get_parser(key, parsers): + for p in parsers: + if p.keyword == key: + return p + assert False and "parser not found" + + # TODO: Move this into lit's FileBasedTest + def getTestsInDirectory(self, testSuite, path_in_suite, + litConfig, localConfig): + source_path = testSuite.getSourcePath(path_in_suite) + for filename in os.listdir(source_path): + # Ignore dot files and excluded tests. + if filename.startswith('.') or filename in localConfig.excludes: + continue + + filepath = os.path.join(source_path, filename) + if not os.path.isdir(filepath): + if any([filename.endswith(ext) + for ext in localConfig.suffixes]): + yield lit.Test.Test(testSuite, path_in_suite + (filename,), + localConfig) + + def execute(self, test, lit_config): + while True: + try: + return self._execute(test, lit_config) + except OSError as oe: + if oe.errno != errno.ETXTBSY: + raise + time.sleep(0.1) + + def _execute(self, test, lit_config): + name = test.path_in_suite[-1] + name_root, name_ext = os.path.splitext(name) + is_libcxx_test = test.path_in_suite[0] == 'libcxx' + is_sh_test = name_root.endswith('.sh') + is_pass_test = name.endswith('.pass.cpp') + is_fail_test = name.endswith('.fail.cpp') + assert is_sh_test or name_ext == '.cpp', 'non-cpp file must be sh test' + + if test.config.unsupported: + return (lit.Test.UNSUPPORTED, + "A lit.local.cfg marked this unsupported") + + parsers = self._make_custom_parsers() + script = lit.TestRunner.parseIntegratedTestScript( + test, additional_parsers=parsers, require_script=is_sh_test) + # Check if a result for the test was returned. If so return that + # result. + if isinstance(script, lit.Test.Result): + return script + if lit_config.noExecute: + return lit.Test.Result(lit.Test.PASS) + + # Check that we don't have run lines on tests that don't support them. + if not is_sh_test and len(script) != 0: + lit_config.fatal('Unsupported RUN line found in test %s' % name) + + tmpDir, tmpBase = lit.TestRunner.getTempPaths(test) + substitutions = lit.TestRunner.getDefaultSubstitutions(test, tmpDir, + tmpBase) + script = lit.TestRunner.applySubstitutions(script, substitutions) + + test_cxx = copy.deepcopy(self.cxx) + if is_fail_test: + test_cxx.useCCache(False) + test_cxx.useWarnings(False) + extra_modules_defines = self._get_parser('MODULES_DEFINES:', + parsers).getValue() + if '-fmodules' in test.config.available_features: + test_cxx.compile_flags += [('-D%s' % mdef.strip()) for + mdef in extra_modules_defines] + test_cxx.addWarningFlagIfSupported('-Wno-macro-redefined') + # FIXME: libc++ debug tests #define _LIBCPP_ASSERT to override it + # If we see this we need to build the test against uniquely built + # modules. + if is_libcxx_test: + with open(test.getSourcePath(), 'r') as f: + contents = f.read() + if '#define _LIBCPP_ASSERT' in contents: + test_cxx.useModules(False) + + # Dispatch the test based on its suffix. + if is_sh_test: + if not isinstance(self.executor, LocalExecutor): + # We can't run ShTest tests with a executor yet. + # For now, bail on trying to run them + return lit.Test.UNSUPPORTED, 'ShTest format not yet supported' + test.config.enviroment = dict(self.exec_env) + return lit.TestRunner._runShTest(test, lit_config, + self.execute_external, script, + tmpBase) + elif is_fail_test: + return self._evaluate_fail_test(test, test_cxx, parsers) + elif is_pass_test: + return self._evaluate_pass_test(test, tmpBase, lit_config, + test_cxx, parsers) + else: + # No other test type is supported + assert False + + def _clean(self, exec_path): # pylint: disable=no-self-use + libcxx.util.cleanFile(exec_path) + + def _evaluate_pass_test(self, test, tmpBase, lit_config, + test_cxx, parsers): + execDir = os.path.dirname(test.getExecPath()) + source_path = test.getSourcePath() + exec_path = tmpBase + '.exe' + object_path = tmpBase + '.o' + # Create the output directory if it does not already exist. + libcxx.util.mkdir_p(os.path.dirname(tmpBase)) + try: + # Compile the test + cmd, out, err, rc = test_cxx.compileLinkTwoSteps( + source_path, out=exec_path, object_file=object_path, + cwd=execDir) + compile_cmd = cmd + if rc != 0: + report = libcxx.util.makeReport(cmd, out, err, rc) + report += "Compilation failed unexpectedly!" + return lit.Test.FAIL, report + # Run the test + local_cwd = os.path.dirname(source_path) + env = None + if self.exec_env: + env = self.exec_env + # TODO: Only list actually needed files in file_deps. + # Right now we just mark all of the .dat files in the same + # directory as dependencies, but it's likely less than that. We + # should add a `// FILE-DEP: foo.dat` to each test to track this. + data_files = [os.path.join(local_cwd, f) + for f in os.listdir(local_cwd) if f.endswith('.dat')] + is_flaky = self._get_parser('FLAKY_TEST.', parsers).getValue() + max_retry = 3 if is_flaky else 1 + for retry_count in range(max_retry): + cmd, out, err, rc = self.executor.run(exec_path, [exec_path], + local_cwd, data_files, + env) + if rc == 0: + res = lit.Test.PASS if retry_count == 0 else lit.Test.FLAKYPASS + return res, '' + elif rc != 0 and retry_count + 1 == max_retry: + report = libcxx.util.makeReport(cmd, out, err, rc) + report = "Compiled With: %s\n%s" % (compile_cmd, report) + report += "Compiled test failed unexpectedly!" + return lit.Test.FAIL, report + + assert False # Unreachable + finally: + # Note that cleanup of exec_file happens in `_clean()`. If you + # override this, cleanup is your reponsibility. + libcxx.util.cleanFile(object_path) + self._clean(exec_path) + + def _evaluate_fail_test(self, test, test_cxx, parsers): + source_path = test.getSourcePath() + # FIXME: lift this detection into LLVM/LIT. + with open(source_path, 'r') as f: + contents = f.read() + verify_tags = ['expected-note', 'expected-remark', 'expected-warning', + 'expected-error', 'expected-no-diagnostics'] + use_verify = self.use_verify_for_fail and \ + any([tag in contents for tag in verify_tags]) + # FIXME(EricWF): GCC 5 does not evaluate static assertions that + # are dependant on a template parameter when '-fsyntax-only' is passed. + # This is fixed in GCC 6. However for now we only pass "-fsyntax-only" + # when using Clang. + if test_cxx.type != 'gcc': + test_cxx.flags += ['-fsyntax-only'] + if use_verify: + test_cxx.useVerify() + test_cxx.useWarnings() + if '-Wuser-defined-warnings' in test_cxx.warning_flags: + test_cxx.warning_flags += ['-Wno-error=user-defined-warnings'] + + cmd, out, err, rc = test_cxx.compile(source_path, out=os.devnull) + expected_rc = 0 if use_verify else 1 + if rc == expected_rc: + return lit.Test.PASS, '' + else: + report = libcxx.util.makeReport(cmd, out, err, rc) + report_msg = ('Expected compilation to fail!' if not use_verify else + 'Expected compilation using verify to pass!') + return lit.Test.FAIL, report + report_msg + '\n' diff --git a/utils/libcxx/test/target_info.py b/utils/libcxx/test/target_info.py new file mode 100644 index 0000000000000..9a7ae05a0285c --- /dev/null +++ b/utils/libcxx/test/target_info.py @@ -0,0 +1,276 @@ +#===----------------------------------------------------------------------===// +# +# The LLVM Compiler Infrastructure +# +# This file is dual licensed under the MIT and the University of Illinois Open +# Source Licenses. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===// + +import importlib +import lit.util # pylint: disable=import-error,no-name-in-module +import locale +import os +import platform +import re +import sys + +class DefaultTargetInfo(object): + def __init__(self, full_config): + self.full_config = full_config + + def platform(self): + return sys.platform.lower().strip() + + def add_locale_features(self, features): + self.full_config.lit_config.warning( + "No locales entry for target_system: %s" % self.platform()) + + def add_cxx_compile_flags(self, flags): pass + def add_cxx_link_flags(self, flags): pass + def configure_env(self, env): pass + def allow_cxxabi_link(self): return True + def add_sanitizer_features(self, sanitizer_type, features): pass + def use_lit_shell_default(self): return False + + +def test_locale(loc): + assert loc is not None + default_locale = locale.setlocale(locale.LC_ALL) + try: + locale.setlocale(locale.LC_ALL, loc) + return True + except locale.Error: + return False + finally: + locale.setlocale(locale.LC_ALL, default_locale) + + +def add_common_locales(features, lit_config): + # A list of locales needed by the test-suite. + # The list uses the canonical name for the locale used in the test-suite + # TODO: On Linux ISO8859 *may* needs to hyphenated. + locales = [ + 'en_US.UTF-8', + 'fr_FR.UTF-8', + 'ru_RU.UTF-8', + 'zh_CN.UTF-8', + 'fr_CA.ISO8859-1', + 'cs_CZ.ISO8859-2' + ] + for loc in locales: + if test_locale(loc): + features.add('locale.{0}'.format(loc)) + else: + lit_config.warning('The locale {0} is not supported by ' + 'your platform. Some tests will be ' + 'unsupported.'.format(loc)) + + +class DarwinLocalTI(DefaultTargetInfo): + def __init__(self, full_config): + super(DarwinLocalTI, self).__init__(full_config) + + def is_host_macosx(self): + name = lit.util.capture(['sw_vers', '-productName']).strip() + return name == "Mac OS X" + + def get_macosx_version(self): + assert self.is_host_macosx() + version = lit.util.capture(['sw_vers', '-productVersion']).strip() + version = re.sub(r'([0-9]+\.[0-9]+)(\..*)?', r'\1', version) + return version + + def get_sdk_version(self, name): + assert self.is_host_macosx() + cmd = ['xcrun', '--sdk', name, '--show-sdk-path'] + try: + out = lit.util.capture(cmd).strip() + except OSError: + pass + + if not out: + self.full_config.lit_config.fatal( + "cannot infer sdk version with: %r" % cmd) + + return re.sub(r'.*/[^0-9]+([0-9.]+)\.sdk', r'\1', out) + + def get_platform(self): + platform = self.full_config.get_lit_conf('platform') + if platform: + platform = re.sub(r'([^0-9]+)([0-9\.]*)', r'\1-\2', platform) + name, version = tuple(platform.split('-', 1)) + else: + name = 'macosx' + version = None + + if version: + return (False, name, version) + + # Infer the version, either from the SDK or the system itself. For + # macosx, ignore the SDK version; what matters is what's at + # /usr/lib/libc++.dylib. + if name == 'macosx': + version = self.get_macosx_version() + else: + version = self.get_sdk_version(name) + return (True, name, version) + + def add_locale_features(self, features): + add_common_locales(features, self.full_config.lit_config) + + def add_cxx_compile_flags(self, flags): + if self.full_config.use_deployment: + _, name, _ = self.full_config.config.deployment + cmd = ['xcrun', '--sdk', name, '--show-sdk-path'] + else: + cmd = ['xcrun', '--show-sdk-path'] + try: + out = lit.util.capture(cmd).strip() + res = 0 + except OSError: + res = -1 + if res == 0 and out: + sdk_path = out + self.full_config.lit_config.note('using SDKROOT: %r' % sdk_path) + flags += ["-isysroot", sdk_path] + + def add_cxx_link_flags(self, flags): + flags += ['-lSystem'] + + def configure_env(self, env): + library_paths = [] + # Configure the library path for libc++ + if self.full_config.use_system_cxx_lib: + if (os.path.isdir(str(self.full_config.use_system_cxx_lib))): + library_paths += [self.full_config.use_system_cxx_lib] + pass + elif self.full_config.cxx_runtime_root: + library_paths += [self.full_config.cxx_runtime_root] + # Configure the abi library path + if self.full_config.abi_library_root: + library_paths += [self.full_config.abi_library_root] + if library_paths: + env['DYLD_LIBRARY_PATH'] = ':'.join(library_paths) + + def allow_cxxabi_link(self): + # FIXME: PR27405 + # libc++ *should* export all of the symbols found in libc++abi on OS X. + # For this reason LibcxxConfiguration will not link libc++abi in OS X. + # However __cxa_throw_bad_new_array_length doesn't get exported into + # libc++ yet so we still need to explicitly link libc++abi when testing + # libc++abi + # See PR22654. + if(self.full_config.get_lit_conf('name', '') == 'libc++abi'): + return True + # Don't link libc++abi explicitly on OS X because the symbols + # should be available in libc++ directly. + return False + + def add_sanitizer_features(self, sanitizer_type, features): + if sanitizer_type == 'Undefined': + features.add('sanitizer-new-delete') + + +class FreeBSDLocalTI(DefaultTargetInfo): + def __init__(self, full_config): + super(FreeBSDLocalTI, self).__init__(full_config) + + def add_locale_features(self, features): + add_common_locales(features, self.full_config.lit_config) + + def add_cxx_link_flags(self, flags): + flags += ['-lc', '-lm', '-lpthread', '-lgcc_s', '-lcxxrt'] + + +class LinuxLocalTI(DefaultTargetInfo): + def __init__(self, full_config): + super(LinuxLocalTI, self).__init__(full_config) + + def platform(self): + return 'linux' + + def platform_name(self): + name, _, _ = platform.linux_distribution() + name = name.lower().strip() + return name # Permitted to be None + + def platform_ver(self): + _, ver, _ = platform.linux_distribution() + ver = ver.lower().strip() + return ver # Permitted to be None. + + def add_locale_features(self, features): + add_common_locales(features, self.full_config.lit_config) + # Some linux distributions have different locale data than others. + # Insert the distributions name and name-version into the available + # features to allow tests to XFAIL on them. + name = self.platform_name() + ver = self.platform_ver() + if name: + features.add(name) + if name and ver: + features.add('%s-%s' % (name, ver)) + + def add_cxx_compile_flags(self, flags): + flags += ['-D__STDC_FORMAT_MACROS', + '-D__STDC_LIMIT_MACROS', + '-D__STDC_CONSTANT_MACROS'] + + def add_cxx_link_flags(self, flags): + enable_threads = ('libcpp-has-no-threads' not in + self.full_config.config.available_features) + llvm_unwinder = self.full_config.get_lit_bool('llvm_unwinder', False) + shared_libcxx = self.full_config.get_lit_bool('enable_shared', True) + flags += ['-lm'] + if not llvm_unwinder: + flags += ['-lgcc_s', '-lgcc'] + if enable_threads: + flags += ['-lpthread'] + if not shared_libcxx: + flags += ['-lrt'] + flags += ['-lc'] + if llvm_unwinder: + flags += ['-lunwind', '-ldl'] + else: + flags += ['-lgcc_s'] + flags += ['-lgcc'] + use_libatomic = self.full_config.get_lit_bool('use_libatomic', False) + if use_libatomic: + flags += ['-latomic'] + san = self.full_config.get_lit_conf('use_sanitizer', '').strip() + if san: + # The libraries and their order are taken from the + # linkSanitizerRuntimeDeps function in + # clang/lib/Driver/Tools.cpp + flags += ['-lpthread', '-lrt', '-lm', '-ldl'] + + +class WindowsLocalTI(DefaultTargetInfo): + def __init__(self, full_config): + super(WindowsLocalTI, self).__init__(full_config) + + def add_locale_features(self, features): + add_common_locales(features, self.full_config.lit_config) + + def use_lit_shell_default(self): + # Default to the internal shell on Windows, as bash on Windows is + # usually very slow. + return True + + +def make_target_info(full_config): + default = "libcxx.test.target_info.LocalTI" + info_str = full_config.get_lit_conf('target_info', default) + if info_str != default: + mod_path, _, info = info_str.rpartition('.') + mod = importlib.import_module(mod_path) + target_info = getattr(mod, info)(full_config) + full_config.lit_config.note("inferred target_info as: %r" % info_str) + return target_info + target_system = platform.system() + if target_system == 'Darwin': return DarwinLocalTI(full_config) + if target_system == 'FreeBSD': return FreeBSDLocalTI(full_config) + if target_system == 'Linux': return LinuxLocalTI(full_config) + if target_system == 'Windows': return WindowsLocalTI(full_config) + return DefaultTargetInfo(full_config) diff --git a/utils/libcxx/test/tracing.py b/utils/libcxx/test/tracing.py new file mode 100644 index 0000000000000..c590ba3efd014 --- /dev/null +++ b/utils/libcxx/test/tracing.py @@ -0,0 +1,43 @@ +#===----------------------------------------------------------------------===## +# +# The LLVM Compiler Infrastructure +# +# This file is dual licensed under the MIT and the University of Illinois Open +# Source Licenses. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===## + +import os +import inspect + + +def trace_function(function, log_calls, log_results, label=''): + def wrapper(*args, **kwargs): + kwarg_strs = ['{}={}'.format(k, v) for (k, v) in kwargs] + arg_str = ', '.join([str(a) for a in args] + kwarg_strs) + call_str = '{}({})'.format(function.func_name, arg_str) + + # Perform the call itself, logging before, after, and anything thrown. + try: + if log_calls: + print('{}: Calling {}'.format(label, call_str)) + res = function(*args, **kwargs) + if log_results: + print('{}: {} -> {}'.format(label, call_str, res)) + return res + except Exception as ex: + if log_results: + print('{}: {} raised {}'.format(label, call_str, type(ex))) + raise ex + + return wrapper + + +def trace_object(obj, log_calls, log_results, label=''): + for name, member in inspect.getmembers(obj): + if inspect.ismethod(member): + # Skip meta-functions, decorate everything else + if not member.func_name.startswith('__'): + setattr(obj, name, trace_function(member, log_calls, + log_results, label)) + return obj diff --git a/utils/libcxx/util.py b/utils/libcxx/util.py new file mode 100644 index 0000000000000..ecfb9afb73b0d --- /dev/null +++ b/utils/libcxx/util.py @@ -0,0 +1,286 @@ +#===----------------------------------------------------------------------===## +# +# The LLVM Compiler Infrastructure +# +# This file is dual licensed under the MIT and the University of Illinois Open +# Source Licenses. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===## + +from contextlib import contextmanager +import errno +import os +import platform +import signal +import subprocess +import sys +import tempfile +import threading + + +# FIXME: Most of these functions are cribbed from LIT +def to_bytes(str): + # Encode to UTF-8 to get binary data. + if isinstance(str, bytes): + return str + return str.encode('utf-8') + +def to_string(bytes): + if isinstance(bytes, str): + return bytes + return to_bytes(bytes) + +def convert_string(bytes): + try: + return to_string(bytes.decode('utf-8')) + except AttributeError: # 'str' object has no attribute 'decode'. + return str(bytes) + except UnicodeError: + return str(bytes) + + +def cleanFile(filename): + try: + os.remove(filename) + except OSError: + pass + + +@contextmanager +def guardedTempFilename(suffix='', prefix='', dir=None): + # Creates and yeilds a temporary filename within a with statement. The file + # is removed upon scope exit. + handle, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir) + os.close(handle) + yield name + cleanFile(name) + + +@contextmanager +def guardedFilename(name): + # yeilds a filename within a with statement. The file is removed upon scope + # exit. + yield name + cleanFile(name) + + +@contextmanager +def nullContext(value): + # yeilds a variable within a with statement. No action is taken upon scope + # exit. + yield value + + +def makeReport(cmd, out, err, rc): + report = "Command: %s\n" % cmd + report += "Exit Code: %d\n" % rc + if out: + report += "Standard Output:\n--\n%s--\n" % out + if err: + report += "Standard Error:\n--\n%s--\n" % err + report += '\n' + return report + + +def capture(args, env=None): + """capture(command) - Run the given command (or argv list) in a shell and + return the standard output. Raises a CalledProcessError if the command + exits with a non-zero status.""" + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + env=env) + out, err = p.communicate() + out = convert_string(out) + err = convert_string(err) + if p.returncode != 0: + raise subprocess.CalledProcessError(cmd=args, + returncode=p.returncode, + output="{}\n{}".format(out, err)) + return out + + +def which(command, paths = None): + """which(command, [paths]) - Look up the given command in the paths string + (or the PATH environment variable, if unspecified).""" + + if paths is None: + paths = os.environ.get('PATH','') + + # Check for absolute match first. + if os.path.isfile(command): + return command + + # Would be nice if Python had a lib function for this. + if not paths: + paths = os.defpath + + # Get suffixes to search. + # On Cygwin, 'PATHEXT' may exist but it should not be used. + if os.pathsep == ';': + pathext = os.environ.get('PATHEXT', '').split(';') + else: + pathext = [''] + + # Search the paths... + for path in paths.split(os.pathsep): + for ext in pathext: + p = os.path.join(path, command + ext) + if os.path.exists(p) and not os.path.isdir(p): + return p + + return None + + +def checkToolsPath(dir, tools): + for tool in tools: + if not os.path.exists(os.path.join(dir, tool)): + return False + return True + + +def whichTools(tools, paths): + for path in paths.split(os.pathsep): + if checkToolsPath(path, tools): + return path + return None + +def mkdir_p(path): + """mkdir_p(path) - Make the "path" directory, if it does not exist; this + will also make directories for any missing parent directories.""" + if not path or os.path.exists(path): + return + + parent = os.path.dirname(path) + if parent != path: + mkdir_p(parent) + + try: + os.mkdir(path) + except OSError: + e = sys.exc_info()[1] + # Ignore EEXIST, which may occur during a race condition. + if e.errno != errno.EEXIST: + raise + + +class ExecuteCommandTimeoutException(Exception): + def __init__(self, msg, out, err, exitCode): + assert isinstance(msg, str) + assert isinstance(out, str) + assert isinstance(err, str) + assert isinstance(exitCode, int) + self.msg = msg + self.out = out + self.err = err + self.exitCode = exitCode + +# Close extra file handles on UNIX (on Windows this cannot be done while +# also redirecting input). +kUseCloseFDs = not (platform.system() == 'Windows') +def executeCommand(command, cwd=None, env=None, input=None, timeout=0): + """ + Execute command ``command`` (list of arguments or string) + with + * working directory ``cwd`` (str), use None to use the current + working directory + * environment ``env`` (dict), use None for none + * Input to the command ``input`` (str), use string to pass + no input. + * Max execution time ``timeout`` (int) seconds. Use 0 for no timeout. + + Returns a tuple (out, err, exitCode) where + * ``out`` (str) is the standard output of running the command + * ``err`` (str) is the standard error of running the command + * ``exitCode`` (int) is the exitCode of running the command + + If the timeout is hit an ``ExecuteCommandTimeoutException`` + is raised. + """ + if input is not None: + input = to_bytes(input) + p = subprocess.Popen(command, cwd=cwd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, close_fds=kUseCloseFDs) + timerObject = None + # FIXME: Because of the way nested function scopes work in Python 2.x we + # need to use a reference to a mutable object rather than a plain + # bool. In Python 3 we could use the "nonlocal" keyword but we need + # to support Python 2 as well. + hitTimeOut = [False] + try: + if timeout > 0: + def killProcess(): + # We may be invoking a shell so we need to kill the + # process and all its children. + hitTimeOut[0] = True + killProcessAndChildren(p.pid) + + timerObject = threading.Timer(timeout, killProcess) + timerObject.start() + + out,err = p.communicate(input=input) + exitCode = p.wait() + finally: + if timerObject != None: + timerObject.cancel() + + # Ensure the resulting output is always of string type. + out = convert_string(out) + err = convert_string(err) + + if hitTimeOut[0]: + raise ExecuteCommandTimeoutException( + msg='Reached timeout of {} seconds'.format(timeout), + out=out, + err=err, + exitCode=exitCode + ) + + # Detect Ctrl-C in subprocess. + if exitCode == -signal.SIGINT: + raise KeyboardInterrupt + + return out, err, exitCode + + +def killProcessAndChildren(pid): + """ + This function kills a process with ``pid`` and all its + running children (recursively). It is currently implemented + using the psutil module which provides a simple platform + neutral implementation. + + TODO: Reimplement this without using psutil so we can + remove our dependency on it. + """ + import psutil + try: + psutilProc = psutil.Process(pid) + # Handle the different psutil API versions + try: + # psutil >= 2.x + children_iterator = psutilProc.children(recursive=True) + except AttributeError: + # psutil 1.x + children_iterator = psutilProc.get_children(recursive=True) + for child in children_iterator: + try: + child.kill() + except psutil.NoSuchProcess: + pass + psutilProc.kill() + except psutil.NoSuchProcess: + pass + + +def executeCommandVerbose(cmd, *args, **kwargs): + """ + Execute a command and print its output on failure. + """ + out, err, exitCode = executeCommand(cmd, *args, **kwargs) + if exitCode != 0: + report = makeReport(cmd, out, err, exitCode) + report += "\n\nFailed!" + sys.stderr.write('%s\n' % report) + return out, err, exitCode diff --git a/utils/not/not.py b/utils/not.py index d9ceb8515d036..d9ceb8515d036 100644 --- a/utils/not/not.py +++ b/utils/not.py diff --git a/utils/sym_check/sym_diff.py b/utils/sym_diff.py index 842e908dd42ea..c01f71c928d9f 100755 --- a/utils/sym_check/sym_diff.py +++ b/utils/sym_diff.py @@ -13,7 +13,7 @@ sym_diff - Compare two symbol lists and output the differences. from argparse import ArgumentParser import sys -from sym_check import diff, util +from libcxx.sym_check import diff, util def main(): @@ -30,6 +30,10 @@ def main(): parser.add_argument('--only-stdlib-symbols', dest='only_stdlib', help="Filter all symbols not related to the stdlib", action='store_true', default=False) + parser.add_argument('--strict', dest='strict', + help="Exit with a non-zero status if any symbols " + "differ", + action='store_true', default=False) parser.add_argument( '-o', '--output', dest='output', help='The output file. stdout is used if not given', @@ -54,16 +58,16 @@ def main(): added, removed, changed = diff.diff(old_syms_list, new_syms_list) if args.removed_only: added = {} - report, is_break = diff.report_diff(added, removed, changed, - names_only=args.names_only, - demangle=args.demangle) + report, is_break, is_different = diff.report_diff( + added, removed, changed, names_only=args.names_only, + demangle=args.demangle) if args.output is None: print(report) else: with open(args.output, 'w') as f: f.write(report + '\n') - sys.exit(is_break) - + exit_code = 1 if is_break or (args.strict and is_different) else 0 + sys.exit(exit_code) if __name__ == '__main__': main() diff --git a/utils/sym_check/sym_extract.py b/utils/sym_extract.py index 27765679ed809..0d9d2eeb8e5ae 100755 --- a/utils/sym_check/sym_extract.py +++ b/utils/sym_extract.py @@ -11,7 +11,7 @@ sym_extract - Extract and output a list of symbols from a shared library. """ from argparse import ArgumentParser -from sym_check import extract, util +from libcxx.sym_check import extract, util def main(): diff --git a/utils/sym_check/sym_match.py b/utils/sym_match.py index 231bdc8b367a9..48582ce666729 100755 --- a/utils/sym_check/sym_match.py +++ b/utils/sym_match.py @@ -13,7 +13,7 @@ sym_match - Match all symbols in a list against a list of regexes. """ from argparse import ArgumentParser import sys -from sym_check import util, match, extract +from libcxx.sym_check import util, match, extract def main(): diff --git a/utils/sym_check/linux_blacklist.txt b/utils/symcheck-blacklists/linux_blacklist.txt index 4d9d1d4b9e30d..4d9d1d4b9e30d 100644 --- a/utils/sym_check/linux_blacklist.txt +++ b/utils/symcheck-blacklists/linux_blacklist.txt diff --git a/utils/sym_check/osx_blacklist.txt b/utils/symcheck-blacklists/osx_blacklist.txt index cfa911ed74e5b..cfa911ed74e5b 100644 --- a/utils/sym_check/osx_blacklist.txt +++ b/utils/symcheck-blacklists/osx_blacklist.txt |