bcraig updated this revision to Diff 105268.
bcraig added a comment.

Rebased.
Separating out logging into it's own class.
Also tweaked the output slightly so that the language dialect under test shows 
up as the first line of output.


https://reviews.llvm.org/D34170

Files:
  test/lit.cfg
  utils/libcxx/compiler.py
  utils/libcxx/test/config.py
  utils/libcxx/test/format.py
  utils/libcxx/test/target_info.py
  utils/libcxx/util.py

Index: utils/libcxx/util.py
===================================================================
--- utils/libcxx/util.py
+++ utils/libcxx/util.py
@@ -16,7 +16,50 @@
 import sys
 import tempfile
 import threading
-
+import inspect
+
+class Logger:
+    def __init__(self):
+        self._notes = []
+        self._flushing = False
+
+    def _write_message(self, kind, message):
+        # Get the file/line where this message was generated.
+        f = inspect.currentframe()
+        # Step out of _write_message, and then out of wrapper.
+        f = f.f_back.f_back
+        file,line,_,_,_ = inspect.getframeinfo(f)
+        location = '%s:%d' % (file, line)
+
+        sys.stderr.write('%s: %s: %s\n' % (location, kind, message))
+
+    def note(self, message):
+        sys.stderr.write('%s\n' % (message))
+
+    def deferred_note(self, message):
+        if self._flushing:
+            self._write_message('note', message)
+        else:
+            self._notes.append(message)
+
+    def flush_notes(self):
+        for note in self._notes:
+            self._write_message('note', note)
+        _flushing = True
+
+    def warning(self, message):
+        self._write_message('warning', message)
+        self.numWarnings += 1
+
+    def error(self, message):
+        self._write_message('error', message)
+        self.numErrors += 1
+
+    def fatal(self, message):
+        self._write_message('fatal', message)
+        sys.exit(2)
+
+lit_logger = Logger()
 
 # FIXME: Most of these functions are cribbed from LIT
 def to_bytes(str):
Index: utils/libcxx/test/target_info.py
===================================================================
--- utils/libcxx/test/target_info.py
+++ utils/libcxx/test/target_info.py
@@ -14,6 +14,7 @@
 import re
 import subprocess
 import sys
+from libcxx.util import lit_logger
 
 class DefaultTargetInfo(object):
     def __init__(self, full_config):
@@ -23,7 +24,7 @@
         return sys.platform.lower().strip()
 
     def add_locale_features(self, features):
-        self.full_config.lit_config.warning(
+        lit_logger.warning(
             "No locales entry for target_system: %s" % self.platform())
 
     def add_cxx_compile_flags(self, flags): pass
@@ -46,7 +47,7 @@
         locale.setlocale(locale.LC_ALL, default_locale)
 
 
-def add_common_locales(features, lit_config, is_windows=False):
+def add_common_locales(features, is_windows=False):
     # A list of locales needed by the test-suite.
     # The list uses the canonical name for the locale used in the test-suite
     # TODO: On Linux ISO8859 *may* needs to hyphenated.
@@ -63,7 +64,7 @@
         if test_locale(loc_name):
             features.add('locale.{0}'.format(loc_id))
         else:
-            lit_config.warning('The locale {0} is not supported by '
+            lit_logger.warning('The locale {0} is not supported by '
                                'your platform. Some tests will be '
                                'unsupported.'.format(loc_name))
 
@@ -92,7 +93,7 @@
             pass
 
         if not out:
-            self.full_config.lit_config.fatal(
+            lit_logger.fatal(
                     "cannot infer sdk version with: %r" % cmd)
 
         return re.sub(r'.*/[^0-9]+([0-9.]+)\.sdk', r'\1', out)
@@ -119,7 +120,7 @@
         return (True, name, version)
 
     def add_locale_features(self, features):
-        add_common_locales(features, self.full_config.lit_config)
+        add_common_locales(features)
 
     def add_cxx_compile_flags(self, flags):
         if self.full_config.use_deployment:
@@ -134,7 +135,7 @@
             res = -1
         if res == 0 and out:
             sdk_path = out
-            self.full_config.lit_config.note('using SDKROOT: %r' % sdk_path)
+            lit_logger.deferred_note('using SDKROOT: %r' % sdk_path)
             flags += ["-isysroot", sdk_path]
 
     def add_cxx_link_flags(self, flags):
@@ -179,7 +180,7 @@
         super(FreeBSDLocalTI, self).__init__(full_config)
 
     def add_locale_features(self, features):
-        add_common_locales(features, self.full_config.lit_config)
+        add_common_locales(features)
 
     def add_cxx_link_flags(self, flags):
         flags += ['-lc', '-lm', '-lpthread', '-lgcc_s', '-lcxxrt']
@@ -203,7 +204,7 @@
         return ver # Permitted to be None.
 
     def add_locale_features(self, features):
-        add_common_locales(features, self.full_config.lit_config)
+        add_common_locales(features)
         # Some linux distributions have different locale data than others.
         # Insert the distributions name and name-version into the available
         # features to allow tests to XFAIL on them.
@@ -253,8 +254,7 @@
         super(WindowsLocalTI, self).__init__(full_config)
 
     def add_locale_features(self, features):
-        add_common_locales(features, self.full_config.lit_config,
-                           is_windows=True)
+        add_common_locales(features, is_windows=True)
 
     def use_lit_shell_default(self):
         # Default to the internal shell on Windows, as bash on Windows is
@@ -269,7 +269,7 @@
         mod_path, _, info = info_str.rpartition('.')
         mod = importlib.import_module(mod_path)
         target_info = getattr(mod, info)(full_config)
-        full_config.lit_config.note("inferred target_info as: %r" % info_str)
+        lit_logger.deferred_note("inferred target_info as: %r" % info_str)
         return target_info
     target_system = platform.system()
     if target_system == 'Darwin':  return DarwinLocalTI(full_config)
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,14 +13,11 @@
 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 *
+from libcxx.util import lit_logger
 import libcxx.util
 
 def loadSiteConfig(lit_config, config, param_name, env_name):
@@ -31,14 +28,14 @@
     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!'
+        lit_logger.warning('No site specific configuration file found!'
                            ' Running the tests in the default configuration.')
     elif not os.path.isfile(site_cfg):
-        lit_config.fatal(
+        lit_logger.fatal(
             "Specified site configuration file does not exist: '%s'" %
             site_cfg)
     else:
-        lit_config.note('using site specific configuration at %s' % site_cfg)
+        lit_logger.deferred_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
@@ -57,7 +54,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
@@ -94,25 +90,20 @@
                 return True
             if value.lower() in ('', '0', 'false'):
                 return False
-            self.lit_config.fatal(
+            lit_logger.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(
+                lit_logger.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 get_modules_enabled(self):
-        return self.get_lit_bool('enable_modules',
-                                default=False,
-                                env_var='LIBCXX_ENABLE_MODULES')
-
     def make_static_lib_name(self, name):
         """Return the full filename for the specified library name"""
         if self.is_windows:
@@ -132,45 +123,37 @@
         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.config.available_features)
         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()
         # Print as list to prevent "set([...])" from being printed.
-        self.lit_config.note('Using available_features: %s' %
+        lit_logger.deferred_note('Using available_features: %s' %
                              list(self.config.available_features))
         show_env_vars = {}
         for k,v in self.exec_env.items():
             if k not in os.environ or os.environ[k] != v:
                 show_env_vars[k] = v
-        self.lit_config.note('Adding environment variables: %r' % show_env_vars)
+        lit_logger.deferred_note('Adding environment variables: %r' % show_env_vars)
+        lit_logger.flush_notes()
         sys.stderr.flush()  # Force flushing to avoid broken output on Windows
 
     def get_test_format(self):
@@ -186,79 +169,27 @@
         exec_str = self.get_lit_conf('executor', "None")
         te = eval(exec_str)
         if te:
-            self.lit_config.note("Using executor: %r" % exec_str)
+            lit_logger.deferred_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 "
+                lit_logger.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):
+        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(
@@ -274,12 +205,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
@@ -293,21 +218,21 @@
             self.use_system_cxx_lib = False
         elif self.use_system_cxx_lib:
             assert os.path.isdir(self.use_system_cxx_lib)
-        self.lit_config.note(
+        lit_logger.deferred_note(
             "inferred use_system_cxx_lib as: %r" % self.use_system_cxx_lib)
 
     def configure_availability(self):
         # See http://llvm.org/docs/AvailabilityMarkup.html
         self.with_availability = self.get_lit_bool('with_availability', False)
-        self.lit_config.note(
+        lit_logger.deferred_note(
             "inferred with_availability as: %r" % self.with_availability)
 
     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(
+            lit_logger.fatal(
                 'unsupported value for "cxx_stdlib_under_test": %s'
                 % self.cxx_stdlib_under_test)
         self.config.available_features.add(self.cxx_stdlib_under_test)
@@ -331,19 +256,11 @@
             # 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()
-            self.lit_config.note(
+            lit_logger.deferred_note(
                 "inferred use_clang_verify as: %r" % self.use_clang_verify)
         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
@@ -359,13 +276,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))
@@ -422,37 +332,17 @@
         if self.long_tests is None:
             # Default to running long tests.
             self.long_tests = True
-            self.lit_config.note(
+            lit_logger.deferred_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')
+        self.cxx.add_features(self.config.available_features, self.target_info)
 
         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++':
@@ -463,587 +353,49 @@
                 # 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
-        # FIXME: This is a hack that should be fixed using module maps (or something)
-        # If modules are enabled then we have to lift all of the definitions
-        # in __config_site onto the command line.
-        modules_enabled = self.get_modules_enabled()
-        self.cxx.compile_flags += ['-Wno-macro-redefined']
-        # 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 modules_enabled:
-                define = '-D%s' % m
-                if feature_macros[m]:
-                    define += '=%s' % (feature_macros[m])
-                self.cxx.compile_flags += [define]
-            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(
+            lit_logger.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]
+        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".'
+            lit_logger.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_modules_enabled()
-        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'))
@@ -1067,7 +419,7 @@
         target_triple = self.get_lit_conf('target_triple')
         self.use_target = self.get_lit_bool('use_target', False)
         if self.use_target and target_triple:
-            self.lit_config.warning('use_target is true but no triple is specified')
+            lit_logger.warning('use_target is true but no triple is specified')
 
         # Use deployment if possible.
         self.use_deployment = not self.use_target and self.can_use_deployment()
@@ -1077,7 +429,7 @@
         # Save the triple (and warn on Apple platforms).
         self.config.target_triple = target_triple
         if self.use_target and 'apple' in target_triple:
-            self.lit_config.warning('consider using arch and platform instead'
+            lit_logger.warning('consider using arch and platform instead'
                                     ' of target_triple on Apple platforms')
 
         # If no target triple was given, try to infer it from the compiler
@@ -1098,18 +450,18 @@
                 target_triple.endswith('suse-linux')):
                 target_triple += '-gnu'
             self.config.target_triple = target_triple
-            self.lit_config.note(
+            lit_logger.deferred_note(
                 "inferred target_triple as: %r" % self.config.target_triple)
 
     def configure_deployment(self):
         assert not self.use_deployment is None
         assert not self.use_target is None
         if not self.use_deployment:
             # Warn about ignored parameters.
             if self.get_lit_conf('arch'):
-                self.lit_config.warning('ignoring arch, using target_triple')
+                lit_logger.warning('ignoring arch, using target_triple')
             if self.get_lit_conf('platform'):
-                self.lit_config.warning('ignoring platform, using target_triple')
+                lit_logger.warning('ignoring platform, using target_triple')
             return
 
         assert not self.use_target
@@ -1122,16 +474,16 @@
         arch = self.get_lit_conf('arch')
         if not arch:
             arch = self.cxx.getTriple().split('-', 1)[0]
-            self.lit_config.note("inferred arch as: %r" % arch)
+            lit_logger.deferred_note("inferred arch as: %r" % arch)
 
         inferred_platform, name, version = self.target_info.get_platform()
         if inferred_platform:
-            self.lit_config.note("inferred platform as: %r" % (name + version))
+            lit_logger.deferred_note("inferred platform as: %r" % (name + version))
         self.config.deployment = (arch, name, version)
 
         # Set the target triple for use by lit.
         self.config.target_triple = arch + '-apple-' + name + version
-        self.lit_config.note(
+        lit_logger.deferred_note(
             "computed target_triple as: %r" % self.config.target_triple)
 
     def configure_env(self):
Index: utils/libcxx/compiler.py
===================================================================
--- utils/libcxx/compiler.py
+++ utils/libcxx/compiler.py
@@ -9,10 +9,108 @@
 
 import platform
 import os
+import shlex
 import libcxx.util
+import shutil
+import pipes
+from libcxx.util import lit_logger
 
+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
+            lit_logger.deferred_note(
+                "inferred cxx_under_test as: %r" % cxx_path)
+        elif cxx_is_clang_cl:
+            lit_logger.fatal('Failed to find clang++ substitution for'
+                                  ' clang-cl')
+    if not cxx_path:
+        lit_logger.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): pass
+    def configure_use_thread_safety(self, features): pass
+    def configure_ccache(self, full_config): pass
+    def add_features(self, features, target_info): 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):
+        lit_logger.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 +146,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):
+        lit_logger.deferred_note('Using compiler: %s' % self.path)
+        lit_logger.deferred_note('Using flags: %s' % self.flags)
+        if self.use_modules:
+            lit_logger.deferred_note('Using modules flags: %s' %
+                                 self.modules_flags)
+        lit_logger.deferred_note('Using compile flags: %s'
+                             % self.compile_flags)
+        if len(self.warning_flags):
+            lit_logger.deferred_note('Using warnings: %s' % self.warning_flags)
+        lit_logger.deferred_note('Using link flags: %s' % self.link_flags)
+
+    def configure_use_thread_safety(self, features):
+        has_thread_safety = self.hasCompileFlag('-Werror=thread-safety')
+        if has_thread_safety:
+            self.compile_flags += ['-Werror=thread-safety']
+            features.add('thread-safety')
+            lit_logger.deferred_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
+            lit_logger.deferred_note('enabling ccache')
+
+    def add_features(self, features, target_info):
+        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 target_info.platform() == "darwin" and \
+           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 +265,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 +383,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 +399,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 +479,576 @@
                 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
+                    # We want the C++ version to be the first line of output
+                    lit_logger.note(
+                        'Dialect: %s' % std)
+                    lit_logger.deferred_note(
+                        'inferred language dialect as: %s' % std)
+                    break
+            if not std:
+                lit_logger.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]):
+                lit_logger.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++'):
+            lit_logger.deferred_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):
+            lit_logger.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)
+        lit_logger.deferred_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 get_modules_enabled(self, full_config):
+        return full_config.get_lit_bool('enable_modules',
+                                default=False,
+                                env_var='LIBCXX_ENABLE_MODULES')
+
+    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
+        # FIXME: This is a hack that should be fixed using module maps (or something)
+        # If modules are enabled then we have to lift all of the definitions
+        # in __config_site onto the command line.
+        modules_enabled = self.get_modules_enabled(full_config)
+        self.compile_flags += ['-Wno-macro-redefined']
+        # 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 modules_enabled:
+                define = '-D%s' % m
+                if feature_macros[m]:
+                    define += '=%s' % (feature_macros[m])
+                self.compile_flags += [define]
+            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:
+                lit_logger.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:
+            lit_logger.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 != '':
+            lit_logger.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):
+            lit_logger.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:
+                lit_logger.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:
+                lit_logger.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 = self.get_modules_enabled(full_config)
+        if enable_modules and not supports_modules:
+            lit_logger.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']
Index: test/lit.cfg
===================================================================
--- test/lit.cfg
+++ test/lit.cfg
@@ -23,6 +23,7 @@
 loaded_site_cfg = getattr(config, 'loaded_site_config', False)
 if not loaded_site_cfg:
     import libcxx.test.config
+    from libcxx.util import lit_logger
     libcxx.test.config.loadSiteConfig(lit_config, config, 'libcxx_site_config',
                                       'LIBCXX_SITE_CONFIG')
 
@@ -35,14 +36,14 @@
     if obj_root is None:
         import tempfile
         obj_root = tempfile.mkdtemp(prefix='libcxx-testsuite-')
-        lit_config.warning('Creating temporary directory for object root: %s' %
+        lit_logger.warning('Creating temporary directory for object root: %s' %
                            obj_root)
 
 config.test_exec_root = os.path.join(obj_root, 'test')
 
 cfg_variant = getattr(config, 'configuration_variant', 'libcxx')
 if cfg_variant:
-    lit_config.note('Using configuration variant: %s' % cfg_variant)
+    lit_logger.deferred_note('Using configuration variant: %s' % cfg_variant)
 
 # Load the Configuration class from the module name <cfg_variant>.test.config.
 config_module_name = '.'.join([cfg_variant, 'test', 'config'])
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to