bcraig created this revision. format.py and config.py were routinely reaching into the innards of compiler.py, then setting variables in a very gcc / clang-centric way. Now, all the compiler specific code has been moved to compiler.py. These makes it possible to (in theory) build with MSVC instead, by switching to a different CXXCompilerInterface implementation.
This refactor did not attempt to make large, simplifying changes. It is mainly moving code to the right place so that later work can consolidate and clean as needed. As a proof of concept, I was able to switch out CXXCompilerInterface implementations and run the libc++ tests with MSVC 2017 and the MSVC 2017 STL, with 3486/5472 passing (I didn't dive into the failure cases). The MSVC test infrastructure port is not included in this review. https://reviews.llvm.org/D34170 Files: utils/libcxx/compiler.py utils/libcxx/test/config.py utils/libcxx/test/format.py
Index: utils/libcxx/test/format.py =================================================================== --- utils/libcxx/test/format.py +++ utils/libcxx/test/format.py @@ -129,25 +129,11 @@ 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) + test_cxx.add_extra_module_defines(extra_modules_defines, + test.getSourcePath()) if is_objcxx_test: - test_cxx.source_lang = 'objective-c++' - if is_objcxx_arc_test: - test_cxx.compile_flags += ['-fobjc-arc'] - else: - test_cxx.compile_flags += ['-fno-objc-arc'] - test_cxx.link_flags += ['-framework', 'Foundation'] + test_cxx.use_objcxx(is_objcxx_arc_test=is_objcxx_arc_test) # Dispatch the test based on its suffix. if is_sh_test: @@ -231,17 +217,7 @@ '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'] + test_cxx.configure_for_fail_test(use_verify=use_verify) cmd, out, err, rc = test_cxx.compile(source_path, out=os.devnull) expected_rc = 0 if use_verify else 1 Index: utils/libcxx/test/config.py =================================================================== --- utils/libcxx/test/config.py +++ utils/libcxx/test/config.py @@ -13,12 +13,8 @@ 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 @@ -57,7 +53,6 @@ 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 @@ -127,37 +122,28 @@ self.configure_src_root() self.configure_obj_root() self.configure_cxx_stdlib_under_test() - self.configure_cxx_library_root() + self.cxx.configure_cxx_library_root(self) self.configure_use_clang_verify() - self.configure_use_thread_safety() + self.cxx.configure_use_thread_safety(self) self.configure_execute_external() - self.configure_ccache() - self.configure_compile_flags() + self.cxx.configure_ccache(self) + self.cxx.configure_compile_flags(self) self.configure_filesystem_compile_flags() - self.configure_link_flags() + self.cxx.configure_link_flags(self) self.configure_env() - self.configure_color_diagnostics() + self.cxx.configure_color_diagnostics(self) self.configure_debug_mode() - self.configure_warnings() - self.configure_sanitizer() - self.configure_coverage() - self.configure_modules() - self.configure_coroutines() + self.cxx.configure_warnings(self) + self.cxx.configure_sanitizer(self) + self.cxx.configure_coverage(self) + self.cxx.configure_modules(self) + self.cxx.configure_coroutines(self) 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) + self.cxx.print_config_info(self) # Print as list to prevent "set([...])" from being printed. self.lit_config.note('Using available_features: %s' % list(self.config.available_features)) @@ -196,64 +182,12 @@ self.executor = te def configure_target_info(self): + from libcxx.test.target_info import make_target_info 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 _split_env_var('LIB'): - self.add_path(self.exec_env, path) - return CXXCompiler(clang_path, flags=flags, - compile_flags=compile_flags, - link_flags=link_flags) - + from libcxx.compiler import make_compiler + self.cxx = make_compiler(self) def configure_src_root(self): self.libcxx_src_root = self.get_lit_conf( @@ -269,12 +203,6 @@ 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 @@ -331,14 +259,6 @@ if self.use_clang_verify: self.config.available_features.add('verify-support') - 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 @@ -354,13 +274,6 @@ 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 add_deployment_feature(self, feature): (arch, name, version) = self.config.deployment self.config.available_features.add('%s=%s-%s' % (feature, arch, name)) @@ -423,31 +336,11 @@ 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') + self.cxx.add_features(self.config.available_features, self) 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') if self.cxx_stdlib_under_test == 'libc++': @@ -458,218 +351,6 @@ # using this feature. (Also see llvm.org/PR32730) self.config.available_features.add('LIBCXX-WINDOWS-FIXME') - # 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)) - - # Support Objective-C++ only on MacOS and if the compiler supports it. - if self.target_info.platform() == "darwin" and \ - self.target_info.is_host_macosx() and \ - self.cxx.hasCompileFlag(["-x", "objective-c++", "-fobjc-arc"]): - self.config.available_features.add("objective-c++") - - 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) - if self.is_windows: - # FIXME: Can we remove this? - self.cxx.compile_flags += ['-D_CRT_SECURE_NO_WARNINGS'] - # Required so that tests using min/max don't fail on Windows, - # and so that those tests don't have to be changed to tolerate - # this insanity. - self.cxx.compile_flags += ['-DNOMINMAX'] - - 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] - - # Disable availability unless explicitely requested - if not self.with_availability: - self.cxx.flags += ['-D_LIBCPP_DISABLE_AVAILABILITY'] - - def configure_compile_flags_header_includes(self): - support_path = os.path.join(self.libcxx_src_root, 'test', 'support') - self.configure_config_site_header() - 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') - ] - 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: @@ -683,354 +364,36 @@ '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] + self.cxx.add_pp_string_flag('LIBCXX_FILESYSTEM_STATIC_TEST_ROOT', 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.cxx.add_pp_string_flag('LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT', 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] + self.cxx.add_pp_string_flag('LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER', '%s %s' + % (sys.executable, dynamic_helper)) 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') - # FIXME: Remove this warning once the min/max handling patch lands - # See https://reviews.llvm.org/D33080 - self.cxx.addWarningFlagIfSupported('-Wno-#warnings') - 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_coroutines(self): - if self.cxx.hasCompileFlag('-fcoroutines-ts'): - macros = self.cxx.dumpMacros(flags=['-fcoroutines-ts']) - if '__cpp_coroutines' not in macros: - self.lit_config.warning('-fcoroutines-ts is supported but ' - '__cpp_coroutines is not defined') - # Consider coroutines supported only when the feature test macro - # reflects a recent value. - val = macros['__cpp_coroutines'].replace('L', '') - if int(val) >= 201703: - self.config.available_features.add('fcoroutines-ts') - - 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() + self.cxx.add_pp_int_flag('_LIBCPP_DEBUG', debug_level) 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)) + self.cxx.configure_substitutions(sub) + # Configure exec prefix substitutions. # Configure run env substitution. sub.append(('%run', '%t.exe')) Index: utils/libcxx/compiler.py =================================================================== --- utils/libcxx/compiler.py +++ utils/libcxx/compiler.py @@ -9,10 +9,107 @@ import platform import os +import shlex import libcxx.util +import shutil +import pipes +def make_compiler(full_config): + # Gather various compiler parameters. + cxx_path = full_config.get_lit_conf('cxx_under_test') -class CXXCompiler(object): + cxx_is_clang_cl = cxx_path is not None and \ + os.path.basename(cxx_path) == 'clang-cl.exe' + # If no specific cxx_under_test was given, attempt to infer it as + # clang++. + if cxx_path is None or cxx_is_clang_cl: + search_paths = full_config.config.environment['PATH'] + if cxx_path is not None and os.path.isabs(cxx_path): + search_paths = os.path.dirname(cxx_path) + clangxx = libcxx.util.which('clang++', search_paths) + if clangxx: + cxx_path = clangxx + full_config.lit_config.note( + "inferred cxx_under_test as: %r" % cxx_path) + elif cxx_is_clang_cl: + full_config.lit_config.fatal('Failed to find clang++ substitution for' + ' clang-cl') + if not cxx_path: + full_config.lit_config.fatal('must specify user parameter cxx_under_test ' + '(e.g., --param=cxx_under_test=clang++)') + + if cxx_is_clang_cl: + cxx = make_clang_cl(cxx_path, full_config) + else: + cxx = CXXCompiler(cxx_path) + cxx.compile_env = dict(os.environ) + + return cxx + +def make_clang_cl(cxx_conf, full_config): + 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))) + + flags = [] + compile_flags = _prefixed_env_list('INCLUDE', '-isystem') + link_flags = _prefixed_env_list('LIB', '-L') + for path in _split_env_var('LIB'): + full_config.add_path(full_config.exec_env, path) + return CXXCompiler(cxx_conf, flags=flags, + compile_flags=compile_flags, + link_flags=link_flags) + +class CXXCompilerInterface(object): + def __init__(self): + self.type = None + def add_pp_string_flag(self, name, value=None): pass + def add_pp_int_flag(self, name, value=None): pass + def print_config_info(self, full_config): pass + def configure_use_thread_safety(self, full_config): pass + def configure_ccache(self, full_config): pass + def add_features(self, features, full_config): pass + def configure_compile_flags(self, full_config): pass + def configure_link_flags(self, full_config): pass + def isVerifySupported(self): return False + def hasCompileFlag(self, flag): return False + def tryDumpMacros(self, source_files=None, flags=[], cwd=None): return {} + def addFlagIfSupported(self, flag): return False + def useCCache(self, value=True): pass + def useWarnings(self, value=True): pass + def hasWarningFlag(self, flag): return False + def addWarningFlagIfSupported(self, flag): return False + def useModules(self, value=True): pass + def getTriple(self): return None + def configure_warnings(self, full_config): pass + def configure_sanitizer(self, full_config): pass + def configure_coverage(self, full_config): pass + def configure_coroutines(self, full_config): pass + def configure_modules(self, full_config): pass + def configure_cxx_library_root(self, full_config): pass + def configure_substitutions(self, sub): pass + def add_extra_module_defines(self, extra_modules_defines, sourcePath): pass + def use_objcxx(self, is_objcxx_arc_test): pass + def configure_for_fail_test(self, use_verify): pass + + def compileLinkTwoSteps(self, source_file, out=None, object_file=None, + flags=[], cwd=None): + cmd, out, err, rc = ("", "", "", -1) + return cmd, out, err, rc + + def compile(self, source_files, out=None, flags=[], cwd=None): + cmd, out, err, rc = ("", "", "", -1) + return cmd, out, err, rc + + def configure_color_diagnostics(self, full_config): + full_config.lit_config.warning( + 'color diagnostics have been requested but are not supported ' + 'by the compiler') + +class CXXCompiler(CXXCompilerInterface): CM_Default = 0 CM_PreProcess = 1 CM_Compile = 2 @@ -48,6 +145,95 @@ self.version = cxx_version if self.type is None or self.version is None: self._initTypeAndVersion() + self.cxx_library_root = None + self.cxx_runtime_root = None + + def add_pp_string_flag(self, name, value=None): + if value is None: + self.compile_flags += ['-D%s' % name] + else: + self.compile_flags += ['-D%s="%s"' % (name, value)] + + def add_pp_int_flag(self, name, value=None): + if value is None: + self.compile_flags += ['-D%s' % name] + else: + self.compile_flags += ['-D%s=%s' % (name, value)] + + def print_config_info(self, full_config): + full_config.lit_config.note('Using compiler: %s' % self.path) + full_config.lit_config.note('Using flags: %s' % self.flags) + if self.use_modules: + full_config.lit_config.note('Using modules flags: %s' % + self.modules_flags) + full_config.lit_config.note('Using compile flags: %s' + % self.compile_flags) + if len(self.warning_flags): + full_config.lit_config.note('Using warnings: %s' % self.warning_flags) + full_config.lit_config.note('Using link flags: %s' % self.link_flags) + + def configure_use_thread_safety(self, full_config): + has_thread_safety = self.hasCompileFlag('-Werror=thread-safety') + if has_thread_safety: + self.compile_flags += ['-Werror=thread-safety'] + full_config.config.available_features.add('thread-safety') + full_config.lit_config.note("enabling thread-safety annotations") + + def configure_ccache(self, full_config): + use_ccache_default = os.environ.get('LIBCXX_USE_CCACHE') is not None + use_ccache = full_config.get_lit_bool('use_ccache', use_ccache_default) + if use_ccache: + # 'CCACHE_CPP2' prevents ccache from stripping comments while + # preprocessing. This is required to prevent stripping of '-verify' + # comments. + self.compile_env['CCACHE_CPP2'] = '1' + + self.use_ccache = True + full_config.lit_config.note('enabling ccache') + + def add_features(self, features, full_config): + if self.type is not None: + assert self.version is not None + maj_v, min_v, _ = self.version + features.add(self.type) + features.add('%s-%s' % (self.type, maj_v)) + features.add('%s-%s.%s' % (self.type, maj_v, min_v)) + # Run a compile test for the -fsized-deallocation flag. This is needed + # in test/std/language.support/support.dynamic/new.delete + if self.hasCompileFlag('-fsized-deallocation'): + features.add('fsized-deallocation') + + if self.hasCompileFlag('-faligned-allocation'): + features.add('-faligned-allocation') + else: + # FIXME remove this once more than just clang-4.0 support + # C++17 aligned allocation. + features.add('no-aligned-allocation') + + macros = self.dumpMacros() + if '__cpp_if_constexpr' not in macros: + features.add('libcpp-no-if-constexpr') + + if '__cpp_structured_bindings' not in macros: + features.add('libcpp-no-structured-bindings') + + if '__cpp_deduction_guides' not in macros: + features.add('libcpp-no-deduction-guides') + + # Attempt to detect the glibc version by querying for __GLIBC__ + # in 'features.h'. + macros = self.tryDumpMacros(flags=['-include', 'features.h']) + if macros is not None and '__GLIBC__' in macros: + maj_v, min_v = (macros['__GLIBC__'], macros['__GLIBC_MINOR__']) + features.add('glibc') + features.add('glibc-%s' % maj_v) + features.add('glibc-%s.%s' % (maj_v, min_v)) + + # Support Objective-C++ only on MacOS and if the compiler supports it. + if full_config.target_info.platform() == "darwin" and \ + full_config.target_info.is_host_macosx() and \ + self.hasCompileFlag(["-x", "objective-c++", "-fobjc-arc"]): + features.add("objective-c++") def isVerifySupported(self): if self.verify_supported is None: @@ -78,8 +264,6 @@ 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(): @@ -198,7 +382,7 @@ return (cc_cmd + ['&&'] + link_cmd, cc_stdout + link_stdout, cc_stderr + link_stderr, rc) - def dumpMacros(self, source_files=None, flags=[], cwd=None): + def tryDumpMacros(self, source_files=None, flags=[], cwd=None): if source_files is None: source_files = os.devnull flags = ['-dM'] + flags @@ -214,6 +398,11 @@ parsed_macros[macro] = value return parsed_macros + def dumpMacros(self, source_files=None, flags=[], cwd=None): + retval = self.tryDumpMacros(source_files, flags, cwd) + if retval is None: + raise RuntimeError('dumpMacros failed to run.\nSource: %s\nFlags %s' % (source_files, flags)) + return retval def getTriple(self): cmd = [self.path] + self.flags + ['-dumpmachine'] return libcxx.util.capture(cmd).strip() @@ -289,3 +478,560 @@ self.warning_flags += [flag] return True return False + + def configure_compile_flags(self, full_config): + no_default_flags = full_config.get_lit_bool('no_default_flags', False) + if not no_default_flags: + self.configure_default_compile_flags(full_config) + # This include is always needed so add so add it regardless of + # 'no_default_flags'. + support_path = os.path.join(full_config.libcxx_src_root, 'test/support') + self.compile_flags += ['-I' + support_path] + # Configure extra flags + compile_flags_str = full_config.get_lit_conf('compile_flags', '') + self.compile_flags += shlex.split(compile_flags_str) + if full_config.is_windows: + # FIXME: Can we remove this? + self.compile_flags += ['-D_CRT_SECURE_NO_WARNINGS'] + # Required so that tests using min/max don't fail on Windows, + # and so that those tests don't have to be changed to tolerate + # this insanity. + self.compile_flags += ['-DNOMINMAX'] + + def configure_default_compile_flags(self, full_config): + # 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 = full_config.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.type == 'gcc': + maj_v, _, _ = self.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.hasCompileFlag('-std=%s' % s): + std = s + full_config.lit_config.note( + 'inferred language dialect as: %s' % std) + break + if not std: + full_config.lit_config.fatal( + 'Failed to infer a supported language dialect from one of %r' + % possible_stds) + self.compile_flags += ['-std={0}'.format(std)] + full_config.config.available_features.add(std.replace('gnu++', 'c++')) + # Configure include paths + self.configure_compile_flags_header_includes(full_config) + full_config.target_info.add_cxx_compile_flags(self.compile_flags) + # Configure feature flags. + self.configure_compile_flags_exceptions(full_config) + self.configure_compile_flags_rtti(full_config) + self.configure_compile_flags_abi_version(full_config) + enable_32bit = full_config.get_lit_bool('enable_32bit', False) + if enable_32bit: + self.flags += ['-m32'] + # Use verbose output for better errors + self.flags += ['-v'] + sysroot = full_config.get_lit_conf('sysroot') + if sysroot: + self.flags += ['--sysroot', sysroot] + gcc_toolchain = full_config.get_lit_conf('gcc_toolchain') + if gcc_toolchain: + self.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 full_config.is_windows and full_config.debug_build: + self.compile_flags += ['-D_DEBUG'] + if full_config.use_target: + if not self.addFlagIfSupported( + ['-target', full_config.config.target_triple]): + full_config.lit_config.warning('use_target is true but -target is '\ + 'not supported by the compiler') + if full_config.use_deployment: + arch, name, version = full_config.config.deployment + self.flags += ['-arch', arch] + self.flags += ['-m' + name + '-version-min=' + version] + + # Disable availability unless explicitely requested + if not full_config.with_availability: + self.flags += ['-D_LIBCPP_DISABLE_AVAILABILITY'] + + def configure_compile_flags_header_includes(self, full_config): + support_path = os.path.join(full_config.libcxx_src_root, 'test', 'support') + self.configure_config_site_header(full_config) + if full_config.cxx_stdlib_under_test != 'libstdc++' and \ + not full_config.is_windows: + self.compile_flags += [ + '-include', os.path.join(support_path, 'nasty_macros.hpp')] + if full_config.cxx_stdlib_under_test == 'msvc': + self.compile_flags += [ + '-include', os.path.join(support_path, + 'msvc_stdlib_force_include.hpp')] + pass + if full_config.is_windows and full_config.debug_build and \ + full_config.cxx_stdlib_under_test != 'msvc': + self.compile_flags += [ + '-include', os.path.join(support_path, + 'set_windows_crt_report_mode.h') + ] + cxx_headers = full_config.get_lit_conf('cxx_headers') + if cxx_headers == '' or (cxx_headers is None + and full_config.cxx_stdlib_under_test != 'libc++'): + full_config.lit_config.note('using the system cxx headers') + return + self.compile_flags += ['-nostdinc++'] + if cxx_headers is None: + cxx_headers = os.path.join(full_config.libcxx_src_root, 'include') + if not os.path.isdir(cxx_headers): + full_config.lit_config.fatal("cxx_headers='%s' is not a directory." + % cxx_headers) + self.compile_flags += ['-I' + cxx_headers] + if full_config.libcxx_obj_root is not None: + cxxabi_headers = os.path.join(full_config.libcxx_obj_root, 'include', + 'c++build') + if os.path.isdir(cxxabi_headers): + self.compile_flags += ['-I' + cxxabi_headers] + + def configure_config_site_header(self, full_config): + # Check for a possible __config_site in the build directory. We + # use this if it exists. + if full_config.libcxx_obj_root is None: + return + config_site_header = os.path.join(full_config.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, + full_config) + full_config.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.compile_flags += ['-include', config_site_header] + + def parse_config_site_and_add_features(self, header, full_config): + """ 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.dumpMacros() + macros = self.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': + full_config.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('_', '-') + full_config.config.available_features.add(m) + return feature_macros + + def configure_compile_flags_exceptions(self, full_config): + enable_exceptions = full_config.get_lit_bool('enable_exceptions', True) + if not enable_exceptions: + full_config.config.available_features.add('libcpp-no-exceptions') + self.compile_flags += ['-fno-exceptions'] + + def configure_compile_flags_rtti(self, full_config): + enable_rtti = full_config.get_lit_bool('enable_rtti', True) + if not enable_rtti: + full_config.config.available_features.add('libcpp-no-rtti') + self.compile_flags += ['-fno-rtti', '-D_LIBCPP_NO_RTTI'] + + def configure_compile_flags_abi_version(self, full_config): + abi_version = full_config.get_lit_conf('abi_version', '').strip() + abi_unstable = full_config.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.compile_flags += ['-D_LIBCPP_ABI_VERSION=' + abi_version] + if abi_unstable: + full_config.config.available_features.add('libcpp-abi-unstable') + self.compile_flags += ['-D_LIBCPP_ABI_UNSTABLE'] + + def configure_link_flags(self, full_config): + no_default_flags = full_config.get_lit_bool('no_default_flags', False) + if not no_default_flags: + # Configure library path + self.configure_link_flags_cxx_library_path(full_config) + self.configure_link_flags_abi_library_path(full_config) + + # Configure libraries + if full_config.cxx_stdlib_under_test == 'libc++': + self.link_flags += ['-nodefaultlibs'] + # FIXME: Handle MSVCRT as part of the ABI library handling. + if full_config.is_windows: + self.link_flags += ['-nostdlib'] + self.configure_link_flags_cxx_library(full_config) + self.configure_link_flags_abi_library(full_config) + self.configure_extra_library_flags(full_config) + elif full_config.cxx_stdlib_under_test == 'libstdc++': + enable_fs = full_config.get_lit_bool('enable_filesystem', + default=False) + if enable_fs: + full_config.config.available_features.add('c++experimental') + self.link_flags += ['-lstdc++fs'] + self.link_flags += ['-lm', '-pthread'] + elif full_config.cxx_stdlib_under_test == 'msvc': + # FIXME: Correctly setup debug/release flags here. + pass + elif full_config.cxx_stdlib_under_test == 'cxx_default': + self.link_flags += ['-pthread'] + else: + full_config.lit_config.fatal( + 'unsupported value for "use_stdlib_type": %s' + % use_stdlib_type) + + link_flags_str = full_config.get_lit_conf('link_flags', '') + self.link_flags += shlex.split(link_flags_str) + + def configure_link_flags_cxx_library_path(self, full_config): + if not full_config.use_system_cxx_lib: + if self.cxx_library_root: + self.link_flags += ['-L' + self.cxx_library_root] + if full_config.is_windows and full_config.link_shared: + full_config.add_path(self.compile_env, self.cxx_library_root) + if self.cxx_runtime_root: + if not full_config.is_windows: + self.link_flags += ['-Wl,-rpath,' + + self.cxx_runtime_root] + elif full_config.is_windows and full_config.link_shared: + full_config.add_path(full_config.exec_env, self.cxx_runtime_root) + elif os.path.isdir(str(full_config.use_system_cxx_lib)): + self.link_flags += ['-L' + full_config.use_system_cxx_lib] + if not full_config.is_windows: + self.link_flags += ['-Wl,-rpath,' + + full_config.use_system_cxx_lib] + if full_config.is_windows and full_config.link_shared: + full_config.add_path(self.compile_env, full_config.use_system_cxx_lib) + + def configure_link_flags_abi_library_path(self, full_config): + # Configure ABI library paths. + full_config.abi_library_root = full_config.get_lit_conf('abi_library_path') + if full_config.abi_library_root: + self.link_flags += ['-L' + full_config.abi_library_root] + if not full_config.is_windows: + self.link_flags += ['-Wl,-rpath,' + full_config.abi_library_root] + else: + full_config.add_path(full_config.exec_env, full_config.abi_library_root) + + def configure_link_flags_cxx_library(self, full_config): + libcxx_experimental = full_config.get_lit_bool('enable_experimental', default=False) + if libcxx_experimental: + full_config.config.available_features.add('c++experimental') + self.link_flags += ['-lc++experimental'] + if full_config.link_shared: + self.link_flags += ['-lc++'] + else: + cxx_library_root = full_config.get_lit_conf('cxx_library_root') + if cxx_library_root: + libname = full_config.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.link_flags += [abs_path] + else: + self.link_flags += ['-lc++'] + + def configure_link_flags_abi_library(self, full_config): + cxx_abi = full_config.get_lit_conf('cxx_abi', 'libcxxabi') + if cxx_abi == 'libstdc++': + self.link_flags += ['-lstdc++'] + elif cxx_abi == 'libsupc++': + self.link_flags += ['-lsupc++'] + elif cxx_abi == 'libcxxabi': + if full_config.target_info.allow_cxxabi_link(): + libcxxabi_shared = full_config.get_lit_bool('libcxxabi_shared', default=True) + if libcxxabi_shared: + self.link_flags += ['-lc++abi'] + else: + cxxabi_library_root = full_config.get_lit_conf('abi_library_path') + if cxxabi_library_root: + libname = full_config.make_static_lib_name('c++abi') + abs_path = os.path.join(cxxabi_library_root, libname) + self.link_flags += [abs_path] + else: + self.link_flags += ['-lc++abi'] + elif cxx_abi == 'libcxxrt': + self.link_flags += ['-lcxxrt'] + elif cxx_abi == 'vcruntime': + debug_suffix = 'd' if full_config.debug_build else '' + self.link_flags += ['-l%s%s' % (lib, debug_suffix) for lib in + ['vcruntime', 'ucrt', 'msvcrt']] + elif cxx_abi == 'none' or cxx_abi == 'default': + if full_config.is_windows: + debug_suffix = 'd' if full_config.debug_build else '' + self.link_flags += ['-lmsvcrt%s' % debug_suffix] + else: + full_config.lit_config.fatal( + 'C++ ABI setting %s unsupported for tests' % cxx_abi) + + def configure_extra_library_flags(self, full_config): + if full_config.get_lit_bool('cxx_ext_threads', default=False): + self.link_flags += ['-lc++external_threads'] + full_config.target_info.add_cxx_link_flags(self.link_flags) + + def configure_color_diagnostics(self, full_config): + use_color = full_config.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 != '': + full_config.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.hasCompileFlag(color_flag): + full_config.lit_config.warning( + 'color diagnostics have been requested but are not supported ' + 'by the compiler') + else: + self.flags += [color_flag] + + def configure_warnings(self, full_config): + # Turn on warnings by default for Clang based compilers when C++ >= 11 + default_enable_warnings = self.type in ['clang', 'apple-clang'] \ + and len(full_config.config.available_features.intersection( + ['c++11', 'c++14', 'c++1z'])) != 0 + enable_warnings = full_config.get_lit_bool('enable_warnings', + default_enable_warnings) + self.useWarnings(enable_warnings) + self.warning_flags += [ + '-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER', + '-Wall', '-Wextra', '-Werror' + ] + if self.hasWarningFlag('-Wuser-defined-warnings'): + self.warning_flags += ['-Wuser-defined-warnings'] + full_config.config.available_features.add('diagnose-if-support') + self.addWarningFlagIfSupported('-Wshadow') + self.addWarningFlagIfSupported('-Wno-unused-command-line-argument') + self.addWarningFlagIfSupported('-Wno-attributes') + self.addWarningFlagIfSupported('-Wno-pessimizing-move') + self.addWarningFlagIfSupported('-Wno-c++11-extensions') + self.addWarningFlagIfSupported('-Wno-user-defined-literals') + self.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.addWarningFlagIfSupported('-Wsign-compare') + self.addWarningFlagIfSupported('-Wunused-variable') + self.addWarningFlagIfSupported('-Wunused-parameter') + self.addWarningFlagIfSupported('-Wunreachable-code') + # FIXME: Enable the two warnings below. + self.addWarningFlagIfSupported('-Wno-conversion') + self.addWarningFlagIfSupported('-Wno-unused-local-typedef') + # FIXME: Remove this warning once the min/max handling patch lands + # See https://reviews.llvm.org/D33080 + self.addWarningFlagIfSupported('-Wno-#warnings') + std = full_config.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.addWarningFlagIfSupported('-Wno-unused-local-typedef') + + def configure_sanitizer(self, full_config): + san = full_config.get_lit_conf('use_sanitizer', '').strip() + if san: + full_config.target_info.add_sanitizer_features(san, full_config.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.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.flags += ['-fsanitize=undefined', + '-fno-sanitize=vptr,function,float-divide-by-zero', + '-fno-sanitize-recover=all'] + full_config.exec_env['UBSAN_OPTIONS'] = 'print_stacktrace=1' + full_config.config.available_features.add('ubsan') + + # Setup the sanitizer compile flags + self.flags += ['-g', '-fno-omit-frame-pointer'] + if san == 'Address' or san == 'Address;Undefined' or san == 'Undefined;Address': + self.flags += ['-fsanitize=address'] + if llvm_symbolizer is not None: + full_config.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 + full_config.exec_env['ASAN_OPTIONS'] = 'detect_odr_violation=0' + full_config.config.available_features.add('asan') + full_config.config.available_features.add('sanitizer-new-delete') + self.compile_flags += ['-O1'] + if san == 'Address;Undefined' or san == 'Undefined;Address': + add_ubsan() + elif san == 'Memory' or san == 'MemoryWithOrigins': + self.flags += ['-fsanitize=memory'] + if san == 'MemoryWithOrigins': + self.compile_flags += [ + '-fsanitize-memory-track-origins'] + if llvm_symbolizer is not None: + full_config.exec_env['MSAN_SYMBOLIZER_PATH'] = llvm_symbolizer + full_config.config.available_features.add('msan') + full_config.config.available_features.add('sanitizer-new-delete') + self.compile_flags += ['-O1'] + elif san == 'Undefined': + add_ubsan() + self.compile_flags += ['-O2'] + elif san == 'Thread': + self.flags += ['-fsanitize=thread'] + full_config.config.available_features.add('tsan') + full_config.config.available_features.add('sanitizer-new-delete') + else: + full_config.lit_config.fatal('unsupported value for ' + 'use_sanitizer: {0}'.format(san)) + san_lib = full_config.get_lit_conf('sanitizer_library') + if san_lib: + self.link_flags += [ + san_lib, '-Wl,-rpath,%s' % os.path.dirname(san_lib)] + + def configure_coverage(self, full_config): + generate_coverage = full_config.get_lit_bool('generate_coverage', False) + if generate_coverage: + self.flags += ['-g', '--coverage'] + self.compile_flags += ['-O0'] + + def configure_coroutines(self, full_config): + if self.hasCompileFlag('-fcoroutines-ts'): + macros = self.dumpMacros(flags=['-fcoroutines-ts']) + if '__cpp_coroutines' not in macros: + full_config.lit_config.warning('-fcoroutines-ts is supported but ' + '__cpp_coroutines is not defined') + # Consider coroutines supported only when the feature test macro + # reflects a recent value. + val = macros['__cpp_coroutines'].replace('L', '') + if int(val) >= 201703: + full_config.config.available_features.add('fcoroutines-ts') + + def configure_modules(self, full_config): + modules_flags = ['-fmodules'] + if platform.system() != 'Darwin': + modules_flags += ['-Xclang', '-fmodules-local-submodule-visibility'] + supports_modules = self.hasCompileFlag(modules_flags) + enable_modules = full_config.get_lit_bool('enable_modules', + default=False, + env_var='LIBCXX_ENABLE_MODULES') + if enable_modules and not supports_modules: + full_config.lit_config.fatal( + '-fmodules is enabled but not supported by the compiler') + if not supports_modules: + return + full_config.config.available_features.add('modules-support') + module_cache = os.path.join(full_config.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.modules_flags = modules_flags + \ + ['-fmodules-cache-path=' + module_cache] + if enable_modules: + full_config.config.available_features.add('-fmodules') + self.useModules() + + def configure_cxx_library_root(self, full_config): + self.cxx_library_root = full_config.get_lit_conf('cxx_library_root', + full_config.libcxx_obj_root) + self.cxx_runtime_root = full_config.get_lit_conf('cxx_runtime_root', + self.cxx_library_root) + + def configure_substitutions(self, sub): + cxx_path = pipes.quote(self.path) + # Configure compiler substitutions + sub.append(('%cxx', cxx_path)) + # Configure flags substitutions + flags_str = ' '.join([pipes.quote(f) for f in self.flags]) + compile_flags_str = ' '.join([pipes.quote(f) for f in self.compile_flags]) + link_flags_str = ' '.join([pipes.quote(f) for f in self.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.isVerifySupported(): + verify_str = ' ' + ' '.join(self.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.use_modules: + sub.append(('%compile_module', compile_str)) + sub.append(('%build_module', build_str)) + elif self.modules_flags is not None: + modules_str = ' '.join(self.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)) + + def add_extra_module_defines(self, extra_modules_defines, sourcePath): + self.compile_flags += [('-D%s' % mdef.strip()) for + mdef in extra_modules_defines] + self.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(sourcePath, 'r') as f: + contents = f.read() + if '#define _LIBCPP_ASSERT' in contents: + self.useModules(False) + + def use_objcxx(self, is_objcxx_arc_test): + self.source_lang = 'objective-c++' + if is_objcxx_arc_test: + self.compile_flags += ['-fobjc-arc'] + else: + self.compile_flags += ['-fno-objc-arc'] + self.link_flags += ['-framework', 'Foundation'] + + def configure_for_fail_test(self, use_verify): + # 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 self.type != 'gcc': + self.flags += ['-fsyntax-only'] + if use_verify: + self.useVerify() + self.useWarnings() + if '-Wuser-defined-warnings' in self.warning_flags: + self.warning_flags += ['-Wno-error=user-defined-warnings']
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits