The path would previously get an extra leading space as the arguments would be parsed when generating the final command to run. Pretokenise the arguments to permit proper quoting of the paths. This avoids a number of ignoring non-existent path warnings from clang. git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@295511 91177308-0d34-0410-b5e6-96231b3b80d8
982 lines
46 KiB
Python
982 lines
46 KiB
Python
#===----------------------------------------------------------------------===##
|
|
#
|
|
# 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_target_info()
|
|
self.configure_cxx()
|
|
self.configure_triple()
|
|
self.configure_src_root()
|
|
self.configure_obj_root()
|
|
self.configure_cxx_stdlib_under_test()
|
|
self.configure_cxx_library_root()
|
|
self.configure_use_system_cxx_lib()
|
|
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.
|
|
self.use_system_cxx_lib = self.get_lit_bool('use_system_cxx_lib')
|
|
if self.use_system_cxx_lib is None:
|
|
# Default to testing against the locally built libc++ library.
|
|
self.use_system_cxx_lib = False
|
|
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)
|
|
|
|
# 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')
|
|
|
|
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')
|
|
|
|
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)
|
|
|
|
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')
|
|
# 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 configure_triple(self):
|
|
# Get or infer the target triple.
|
|
self.config.target_triple = self.get_lit_conf('target_triple')
|
|
self.use_target = self.get_lit_bool('use_target', False)
|
|
if self.use_target and self.config.target_triple:
|
|
self.lit_config.warning('use_target is true but no triple is specified')
|
|
# 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_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'])
|