Address @EricWF's driveby comments.

http://reviews.llvm.org/D7380

Files:
  test/CMakeLists.txt
  test/libcxx/test/config.py
  test/libcxx/test/format.py
  test/libcxx/test/remote.py
  test/lit.site.cfg.in

EMAIL PREFERENCES
  http://reviews.llvm.org/settings/panel/emailpreferences/
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -38,6 +38,8 @@
   pythonize_bool(LIBCXX_BUILD_32_BITS)
   pythonize_bool(LIBCXX_ENABLE_THREADS)
   pythonize_bool(LIBCXX_ENABLE_MONOTONIC_CLOCK)
+  set(LIBCXX_TARGET_EXECUTOR "None" CACHE STRING
+      "Executor to use when running tests.")
 
   set(AUTO_GEN_COMMENT "## Autogenerated by libcxx configuration.\n# Do not edit!")
 
Index: test/libcxx/test/config.py
===================================================================
--- test/libcxx/test/config.py
+++ test/libcxx/test/config.py
@@ -1,3 +1,4 @@
+import importlib
 import locale
 import os
 import platform
@@ -11,7 +12,7 @@
 
 from libcxx.test.format import LibcxxTestFormat
 from libcxx.compiler import CXXCompiler
-
+from libcxx.test.remote import *
 
 def loadSiteConfig(lit_config, config, param_name, env_name):
     # We haven't loaded the site specific configuration (the user is
@@ -57,9 +58,6 @@
         self.long_tests = None
         self.execute_external = False
 
-        if platform.system() not in ('Darwin', 'FreeBSD', 'Linux'):
-            self.lit_config.fatal("unrecognized system")
-
     def get_lit_conf(self, name, default=None):
         val = self.lit_config.params.get(name, None)
         if val is None:
@@ -80,6 +78,8 @@
             "parameter '{}' should be true or false".format(name))
 
     def configure(self):
+        self.configure_target_executor()
+        self.configure_target_info()
         self.configure_cxx()
         self.configure_triple()
         self.configure_src_root()
@@ -113,8 +113,35 @@
             self.cxx,
             self.use_clang_verify,
             self.execute_external,
+            self.target_executor,
             exec_env=self.env)
 
+    def configure_target_executor(self):
+        exec_str = self.get_lit_conf('target_executor', "None")
+        te = eval(exec_str)
+        if te:
+            self.lit_config.note("inferred target_executor as: %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 "
+                                      " target_executor.")
+        else:
+            te = LocalExecutor()
+            if self.lit_config.useValgrind:
+                te = ValgrindExecutor(self.lit_config.valgrindArgs, te)
+        self.target_executor = te
+
+    def configure_target_info(self):
+        info_str = self.get_lit_conf('target_info', None)
+        if info_str:
+            self.target_info = eval(info_str)
+            self.lit_config.note("inferred target_info as: %r" % info_str)
+        else:
+            self.target_info = LocalTI()
+
     def configure_cxx(self):
         # Gather various compiler parameters.
         cxx = self.get_lit_conf('cxx_under_test')
@@ -203,31 +230,31 @@
 
         # Figure out which of the required locales we support
         locales = {
-            'Darwin': {
+            'darwin': {
                 'en_US.UTF-8': 'en_US.UTF-8',
                 'cs_CZ.ISO8859-2': 'cs_CZ.ISO8859-2',
                 'fr_FR.UTF-8': 'fr_FR.UTF-8',
                 'fr_CA.ISO8859-1': 'cs_CZ.ISO8859-1',
                 'ru_RU.UTF-8': 'ru_RU.UTF-8',
                 'zh_CN.UTF-8': 'zh_CN.UTF-8',
             },
-            'FreeBSD': {
+            'freebsd': {
                 'en_US.UTF-8': 'en_US.UTF-8',
                 'cs_CZ.ISO8859-2': 'cs_CZ.ISO8859-2',
                 'fr_FR.UTF-8': 'fr_FR.UTF-8',
                 'fr_CA.ISO8859-1': 'fr_CA.ISO8859-1',
                 'ru_RU.UTF-8': 'ru_RU.UTF-8',
                 'zh_CN.UTF-8': 'zh_CN.UTF-8',
             },
-            'Linux': {
+            'linux': {
                 'en_US.UTF-8': 'en_US.UTF-8',
                 'cs_CZ.ISO8859-2': 'cs_CZ.ISO-8859-2',
                 'fr_FR.UTF-8': 'fr_FR.UTF-8',
                 'fr_CA.ISO8859-1': 'fr_CA.ISO-8859-1',
                 'ru_RU.UTF-8': 'ru_RU.UTF-8',
                 'zh_CN.UTF-8': 'zh_CN.UTF-8',
             },
-            'Windows': {
+            'windows': {
                 'en_US.UTF-8': 'English_United States.1252',
                 'cs_CZ.ISO8859-2': 'Czech_Czech Republic.1250',
                 'fr_FR.UTF-8': 'French_France.1252',
@@ -237,16 +264,18 @@
             },
         }
 
+        target_platform = self.target_info.platform()
+
         default_locale = locale.setlocale(locale.LC_ALL)
-        for feature, loc in locales[platform.system()].items():
-            try:
-                locale.setlocale(locale.LC_ALL, loc)
-                self.config.available_features.add(
-                    'locale.{0}'.format(feature))
-            except locale.Error:
-                self.lit_config.warning('The locale {0} is not supported by '
-                                        'your platform. Some tests will be '
-                                        'unsupported.'.format(loc))
+        if target_platform in locales:
+            for feature, loc in locales[target_platform].items():
+                if self.target_info.supports_locale(loc):
+                    self.config.available_features.add(
+                        'locale.{0}'.format(feature))
+                else:
+                    self.lit_config.warning('The locale {0} is not supported by'
+                                            ' your platform. Some tests will be'
+                                            ' unsupported.'.format(loc))
         locale.setlocale(locale.LC_ALL, default_locale)
 
         # Write an "available feature" that combines the triple when
@@ -257,13 +286,15 @@
             self.config.available_features.add(
                 'with_system_cxx_lib=%s' % self.config.target_triple)
 
+        # Insert the platform name into the available features as a lower case.
+        self.config.available_features.add(target_platform)
+
         # 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.
-        if sys.platform.startswith('linux'):
-            name, ver, _ = platform.linux_distribution()
-            name = name.lower().strip()
-            ver = ver.lower().strip()
+        if target_platform == 'linux':
+            name = self.target_info.platform_name()
+            ver = self.target_info.platform_ver()
             if name:
                 self.config.available_features.add(name)
             if name and ver:
@@ -299,7 +330,7 @@
         # Configure include paths
         self.cxx.compile_flags += ['-nostdinc++']
         self.configure_compile_flags_header_includes()
-        if sys.platform.startswith('linux'):
+        if self.target_info.platform() == 'linux':
             self.cxx.compile_flags += ['-D__STDC_FORMAT_MACROS',
                                        '-D__STDC_LIMIT_MACROS',
                                        '-D__STDC_CONSTANT_MACROS']
@@ -430,9 +461,10 @@
     def configure_extra_library_flags(self):
         enable_threads = self.get_lit_bool('enable_threads', True)
         llvm_unwinder = self.get_lit_bool('llvm_unwinder', False)
-        if sys.platform == 'darwin':
+        target_platform = self.target_info.platform()
+        if target_platform == 'darwin':
             self.cxx.link_flags += ['-lSystem']
-        elif sys.platform.startswith('linux'):
+        elif target_platform == 'linux':
             if not llvm_unwinder:
                 self.cxx.link_flags += ['-lgcc_eh']
             self.cxx.link_flags += ['-lc', '-lm']
@@ -443,10 +475,11 @@
                 self.cxx.link_flags += ['-lunwind', '-ldl']
             else:
                 self.cxx.link_flags += ['-lgcc_s']
-        elif sys.platform.startswith('freebsd'):
+        elif target_platform.startswith('freebsd'):
             self.cxx.link_flags += ['-lc', '-lm', '-lpthread', '-lgcc_s']
         else:
-            self.lit_config.fatal("unrecognized system: %r" % sys.platform)
+            self.lit_config.fatal("unrecognized system: %r" %
+                                  target_platform)
 
     def configure_sanitizer(self):
         san = self.get_lit_conf('use_sanitizer', '').strip()
@@ -463,7 +496,7 @@
                                              symbolizer_search_paths)
             # Setup the sanitizer compile flags
             self.cxx.flags += ['-g', '-fno-omit-frame-pointer']
-            if sys.platform.startswith('linux'):
+            if self.target_info.platform() == 'linux':
                 self.cxx.link_flags += ['-ldl']
             if san == 'Address':
                 self.cxx.flags += ['-fsanitize=address']
@@ -556,7 +589,8 @@
                 "inferred target_triple as: %r" % self.config.target_triple)
 
     def configure_env(self):
-        if sys.platform == 'darwin' and not self.use_system_cxx_lib:
+        if self.target_info.platform() == 'darwin' and not \
+           self.use_system_cxx_lib:
             libcxx_library = self.get_lit_conf('libcxx_library')
             if libcxx_library:
                 cxx_library_root = os.path.dirname(libcxx_library)
Index: test/libcxx/test/format.py
===================================================================
--- test/libcxx/test/format.py
+++ test/libcxx/test/format.py
@@ -20,10 +20,12 @@
       FOO.sh.cpp   - A test that uses LIT's ShTest format.
     """
 
-    def __init__(self, cxx, use_verify_for_fail, execute_external, exec_env):
+    def __init__(self, cxx, use_verify_for_fail, execute_external,
+                 target_executor, exec_env):
         self.cxx = cxx
         self.use_verify_for_fail = use_verify_for_fail
         self.execute_external = execute_external
+        self.target_executor = target_executor
         self.exec_env = dict(exec_env)
 
     # TODO: Move this into lit's FileBasedTest
@@ -73,21 +75,27 @@
 
         # Dispatch the test based on its suffix.
         if is_sh_test:
+            if self.target_executor:
+                # We can't run ShTest tests with a target_executor yet.
+                # For now, bail on trying to run them
+                return lit.Test.UNSUPPORTED, 'ShTest format not yet supported'
             return lit.TestRunner._runShTest(test, lit_config,
                                              self.execute_external, script,
                                              tmpBase, execDir)
         elif is_fail_test:
             return self._evaluate_fail_test(test)
         elif is_pass_test:
-            return self._evaluate_pass_test(test, tmpBase, execDir, lit_config)
+            return self._evaluate_pass_test(test, tmpBase, execDir, lit_config,
+                                            self.target_executor)
         else:
             # No other test type is supported
             assert False
 
     def _clean(self, exec_path):  # pylint: disable=no-self-use
         libcxx.util.cleanFile(exec_path)
 
-    def _evaluate_pass_test(self, test, tmpBase, execDir, lit_config):
+    def _evaluate_pass_test(self, test, tmpBase, execDir, lit_config,
+                            target_executor):
         source_path = test.getSourcePath()
         exec_path = tmpBase + '.exe'
         object_path = tmpBase + '.o'
@@ -104,15 +112,11 @@
                 report += "Compilation failed unexpectedly!"
                 return lit.Test.FAIL, report
             # Run the test
-            cmd = []
+            local_cwd = os.path.dirname(source_path)
+            env = None
             if self.exec_env:
-                cmd += ['env']
-                cmd += ['%s=%s' % (k, v) for k, v in self.exec_env.items()]
-            if lit_config.useValgrind:
-                cmd = lit_config.valgrindArgs + cmd
-            cmd += [exec_path]
-            out, err, rc = lit.util.executeCommand(
-                cmd, cwd=os.path.dirname(source_path))
+                env = self.exec_env
+            out, err, rc = target_executor.copy_run(exec_path, [], local_cwd, env)
             if rc != 0:
                 report = libcxx.util.makeReport(cmd, out, err, rc)
                 report = "Compiled With: %s\n%s" % (compile_cmd, report)
Index: test/libcxx/test/remote.py
===================================================================
--- test/libcxx/test/remote.py
+++ test/libcxx/test/remote.py
@@ -0,0 +1,428 @@
+import inspect
+import locale
+import os
+import platform
+import shutil
+import types
+
+from lit.util import executeCommand
+
+class TargetInfo(object):
+    def platform(self):
+        raise NotImplementedError
+
+    def platform_ver(self):
+        raise NotImplementedError
+
+    def platform_name(self):
+        raise NotImplementedError
+
+    def supports_locale(self, loc):
+        raise NotImplementedError
+
+
+class LocalTI(TargetInfo):
+    def platform(self):
+        platform_name = platform.system().lower().strip()
+        # Strip the '2' from linux2.
+        if platform_name.startswith('linux'):
+            platform_name = 'linux'
+        return platform_name
+
+    def platform_name(self):
+        if platform() == 'linux':
+            name, _, _ = platform.linux_distribution()
+            name = name.lower().strip()
+            if name:
+                return name
+        return None
+
+    def platform_ver(self):
+        if platform() == 'linux':
+            _, ver, _ = platform.linux_distribution()
+            ver = ver.lower().strip()
+            if ver:
+                return ver
+        return None
+
+    def supports_locale(self, loc):
+        try:
+            locale.setlocale(locale.LC_ALL, loc)
+            return True
+        except locale.Error:
+            return False
+
+
+class Executor(object):
+    def remote_temp_dir(self):
+        """Get a temporary directory on the remote host"""
+        raise NotImplementedError
+
+    def remote_temp_file(self, suffix):
+        """Get a temporary filename on the remote host"""
+        raise NotImplementedError
+
+    def remote_from_local_dir(self, local_dir):
+        """Translate a local dirname into a remote dirname"""
+        raise NotImplementedError
+
+    def remote_from_local_file(self, local_file):
+        """Translate a local filename into a remote filename"""
+        raise NotImplementedError
+
+    def copy_in(self, local_srcs, remote_dsts, delete=False):
+        """Copy test inputs & executables in to the test runner
+        Args:
+          local_srcs  (list): List of Host file paths to copy in to the runner
+          remote_dsts (list): Corresponding list of Target file locations
+          delete      (bool): Whether or not to delete the src after the copy
+        Returns:
+          void
+        """
+        raise NotImplementedError
+
+    def copy_out(self, remote_srcs, local_dsts, delete=False):
+        """Copy test outputs back from the test runner
+        Args:
+          remote_srcs (list): List of Target file paths to copy back from the runner
+          local_dsts  (list): Corresponding list of Host file locations
+          delete      (bool): Whether or not to delete the src after the copy
+        Returns:
+          void
+        """
+        raise NotImplementedError
+
+    def delete_remote(self, remote, ignore_error=True):
+        """Delete remote files
+        Args:
+          remote       (path): Remote file to delete
+          ignore_error (bool): Whether or not to ignore errors on delete
+        Returns:
+          void
+        """
+        raise NotImplementedError
+
+    def run(self, command, work_dir='.', env=None):
+        """Execute a particular command
+        Args:
+          command  (string): The command to execute
+          work_dir (string): The working directory to execute the test from
+        Returns:
+          out, err, exitCode
+        """
+        raise NotImplementedError
+
+    def copy_run(self, exe_path, args, local_cwd, env=None):
+        """Execute a command that might need to be copied to the target
+        Args:
+          exe_path     (string): Local path to the executable to be run
+          args (list of string): Arguments to run the exe with
+          local_cwd    (string): Local path to the working directory
+          env            (dict): Environment variables to execute under
+        Returns:
+          out, err, exitCode
+        """
+        cmd = args
+        target_exe_path = self.remote_from_local_file(exe_path)
+        target_cwd = self.remote_from_local_dir(local_cwd)
+        cmd += [target_exe_path]
+        try:
+            self.copy_in([exe_path], [target_exe_path])
+            return self.run(cmd, work_dir=target_cwd, env=env)
+        except:
+            raise
+        finally:
+            self.delete_remote(target_exe_path)
+            pass
+
+    def clean_temps(self):
+        """Delete all temporary files created by this executor"""
+        raise NotImplementedError
+
+class LocalExecutor(Executor):
+    def __init__(self):
+        Executor.__init__(self)
+
+    def remote_temp_dir(self):
+        return os.getcwd()
+
+    def remote_temp_file(self, suffix):
+        tmp_file = tempfile.NamedTemporaryFile(suffix=suffix, delete=False)
+        tmp_path = tmp_file.name
+        tmp_file.close()
+        return tmp_path
+
+    def delete_remote(self, remote, ignore_error=True):
+        try:
+            os.remove(remote)
+        except:
+            if ignore_error:
+                pass
+            else:
+                raise
+
+    def remote_from_local_dir(self, local_dir):
+        # Identity function, because LocalExecutor executes locally
+        return local_dir
+
+    def remote_from_local_file(self, local_file):
+        # Identity function, because LocalExecutor executes locally
+        return local_file
+
+    def copy_in(self, local_srcs, remote_dsts, delete=False):
+        self.__copyAll(local_srcs, remote_dsts, delete)
+
+    def copy_out(self, remote_srcs, local_dsts, delete=False):
+        self.__copyAll(remote_srcs, local_dsts, delete)
+
+    def run(self, command, work_dir='.', env=None):
+        env_cmd = []
+        if env:
+            env_cmd += ['env']
+            env_cmd += ['%s=%s' % (k, v) for k, v in env.items()]
+        if work_dir == '.':
+            work_dir = os.getcwd()
+        return executeCommand(env_cmd + command, cwd=work_dir)
+
+    def copy_run(self, exe_path, args, local_cwd, env=None):
+        return executeCommand([exe_path] + args, cwd=local_cwd, env=env)
+
+    def __copyAll(self, srcs, dsts, delete=False):
+        for src, dst in zip(srcs, dsts):
+            if src != dst:
+                if delete:
+                    move(src, dst)
+                else:
+                    shutil.copy(src, dst)
+            elif delete:
+                assert false, "Can't delete '%s' because src==dst" % src
+
+class PrefixExecutor(Executor):
+    """Prefix an executor with some other command wrapper
+    Most useful for setting ulimits on commands, or running an emulator like
+    qemu and valgrind.
+    """
+    def __init__(self, commandPrefix, chain):
+        Executor.__init__(self)
+
+        self.commandPrefix = commandPrefix
+        self.chain = chain
+
+    def remote_from_local_dir(self, local_dir):
+        return local_dir
+
+    def remote_from_local_file(self, local_file):
+        return local_file
+
+    def copy_in(self, local_srcs, remote_dsts, delete=False):
+        self.chain.copy_in(local_srcs, remote_dsts, delete)
+
+    def copy_out(self, remote_srcs, local_dsts, delete=False):
+        self.chain.copy_out(remote_srcs, local_dsts, delete)
+
+    def delete_remote(self, remote, ignore_error=True):
+        self.chain.delete_remote(remote, ignore_error)
+
+    def run(self, command, work_dir='.', env=None):
+        return self.chain.run(self.commandPrefix + command, work_dir, env=env)
+
+class PostfixExecutor(PrefixExecutor):
+    """Postfix an executor with some args
+    """
+    def __init__(self, commandPostfix, chain):
+        Executor.__init__(self)
+
+        self.commandPostfix = commandPostfix
+        self.chain = chain
+
+    def run(self, command, work_dir='.', env=None):
+        return self.chain.run(command + self.commandPostfix, work_dir, env=env)
+
+class ValgrindExecutor(PrefixExecutor):
+    pass
+
+class QEMUExecutor(PrefixExecutor):
+    """Execute another action under qemu"""
+    def __init__(self, qemuCommand, qemuArgs, chain):
+        PrefixExecutor.__init__(self, prefix, chain)
+        self.prefix = qemuCommand + ' ' + qemuArgs
+
+# Deprecated. lit timeouts subsume this
+class TimeoutExecutor(PrefixExecutor):
+    """Execute another action under a timeout"""
+    def __init__(self, duration, chain):
+        PrefixExecutor.__init__(self, 'timeout ' + duration, chain)
+
+class SSHExecutor(Executor):
+    def __init__(self, username, host):
+        Executor.__init__(self)
+
+        self.username = username
+        self.host = host
+        self.scp_command = 'scp'
+        self.ssh_command = 'ssh'
+        self.local_executor = LocalExecutor()
+
+        # Maps from local->remote to make sure we consistently return
+        # the same name for a given local version of the same file/dir
+        self.filemap = {}
+        self.dirmap = {}
+
+        # For temp cleanup
+        self.remote_temp_files = []
+        self.remote_temp_dirs = []
+
+        # TODO(jroelofs): switch this on some -super-verbose-debug config flag
+        if False:
+            self.local_executor = DebugExecutor("ssh_local", LocalExecutor(),
+                                                before=True, after=True)
+
+    def remote_temp_dir(self):
+        # TODO
+        return os.getcwd()
+
+    def remote_temp_file(self, suffix):
+        # TODO: detect what the target system is, and use the correct
+        # mktemp command for it. (linux and darwin differ here, and I'm
+        # sure windows has another way to do it)
+
+        # Not sure how to do suffix on osx yet
+        cmd = "mktemp -q /tmp/libcxx.XXXXXXXXXX"
+        res = self.__execute_command_remote([cmd])
+        temp_path, err, exitCode = res
+        temp_path = temp_path.rstrip()
+        if exitCode != 0:
+            raise ValueError(err)
+        self.remote_temp_files.append(temp_path)
+        return temp_path
+
+    def remote_from_local_dir(self, local_dir):
+        if not self.dirmap.has_key(local_dir):
+            remote_dir = self.remote_temp_dir()
+            self.dirmap[local_dir] = remote_dir
+            return remote_dir
+        else:
+            return self.dirmap[local_dir]
+
+    def remote_from_local_file(self, local_file):
+        if not local_file in self.filemap:
+            remote_file = self.remote_temp_file(".some_file")
+            self.filemap[local_file] = remote_file
+            return remote_file
+        else:
+            return self.filemap[local_file]
+
+    def copy_in(self, local_srcs, remote_dsts, delete=False):
+        scp = self.scp_command
+        remote = self.username + '@' + self.host
+
+        # This could be wrapped up in a tar->scp->untar for performance
+        # if there are lots of files to be copied/moved
+        for src, dst in zip(local_srcs, remote_dsts):
+            cmd = [scp, '-p', src, remote + ':' + dst]
+            self.local_executor.run(cmd)
+        if delete:
+            for src in local_srcs:
+                os.remove(src)
+
+    def copy_out(self, remote_srcs, local_dsts, delete=False):
+        scp = self.scp_command
+        remote = self.username + '@' + self.host
+
+        # This could be wrapped up in a tar->scp->untar for performance
+        # if there are lots of files to be copied/moved
+        for src, dst in zip(srcs, dsts):
+            cmd = [scp, '-p', remote + ':' + src, dst]
+            self.local_executor.run(cmd)
+        if delete:
+            for src in remote_srcs:
+                self.delete_remote(src)
+
+    def delete_remote(self, remote, ignore_error=True):
+        try:
+            self.__execute_command_remote(['rm', '-rf', remote])
+        except:
+            if ignore_error:
+                pass
+            else:
+                raise
+
+    def run(self, command, work_dir='.', env=None):
+        return self.__execute_command_remote(command, work_dir, env)
+
+    def __execute_command_remote(self, command, remote_work_dir='', env=None):
+        remote = self.username + '@' + self.host
+        cmd = [self.ssh_command]
+        cmd += ['-oBatchMode=yes']
+        cmd += [remote]
+        env_cmd = []
+        if env:
+            env_cmd += ['env']
+            env_cmd += ['%s=%s' % (k, v) for k, v in env.items()]
+        remote_cmd = ' '.join(env_cmd + command)
+        if remote_work_dir != '.' and remote_work_dir != '':
+          remote_cmd = 'cd ' + remote_work_dir + ' && ' + remote_cmd
+        cmd += [remote_cmd]
+        return self.local_executor.run(cmd)
+
+    def clean_temps(self):
+        for remote_file in self.remote_temp_files:
+            self.delete_remote(remote_file)
+
+        for remote_dir in self.remote_temp_dirs:
+            self.delete_remote(remote_dir)
+
+
+class DebugExecutor(Executor):
+    def __init__(self, label, chain, before=True, after=True):
+        Executor.__init__(self)
+        self.label = label
+        self.chain = chain
+        self.before = before
+        self.after = after
+
+        for n, f in inspect.getmembers(self):
+            if isinstance(f, types.UnboundMethodType):
+                # Skip meta-functions, decorate everything else
+                if not f.func_name.startswith("__"):
+                    setattr(self, n, self.__decorate(f))
+
+    def __decorate(self, func):
+        def wrapper(*func_args, **func_kwargs):
+            # Get the string representation of the function call
+            arg_names = func.func_code.co_varnames[1:func.func_code.co_argcount]
+            args = func_args[:len(arg_names)]
+            defaults = func.func_defaults or ()
+            args = args + defaults[len(defaults) - (func.func_code.co_argcount - len(args)):]
+            params = zip(arg_names, args)
+            args = func_args[len(arg_names):]
+            if args: params.append(('args', args))
+            if func_kwargs: params.append(('kwargs', func_kwargs))
+            arg_str = '(' + ', '.join('%s = %r' % p for p in params) + ' )'
+            call_str = func.func_name + arg_str
+
+            # Perform the call itself, logging before, after, and anything thrown
+            try:
+                self.__log_before(call_str)
+
+                # Forward the actual call to the chain, rather than calling
+                # func itself, which is a base-class method pointer
+                chain_func = getattr(self.chain, func.func_name)
+                res = chain_func(*func_args, **func_kwargs)
+
+                self.__log_after("%s = %r" % (call_str, res))
+                return res
+            except:
+                self.__log_after("%s raised" % (call_str))
+                raise
+
+        return wrapper
+
+    def __log_before(self, message):
+        if self.before:
+            print "BEFORE: %s.%s" % (self.label, message)
+
+    def __log_after(self, message):
+        if self.after:
+            print "AFTER:  %s.%s" % (self.label, message)
+
Index: test/lit.site.cfg.in
===================================================================
--- test/lit.site.cfg.in
+++ test/lit.site.cfg.in
@@ -17,6 +17,7 @@
 config.target_triple            = "@LIBCXX_TARGET_TRIPLE@"
 config.sysroot                  = "@LIBCXX_SYSROOT@"
 config.gcc_toolchain            = "@LIBCXX_GCC_TOOLCHAIN@"
+config.target_executor          = "@LIBCXX_TARGET_EXECUTOR@"
 
 # Let the main config do the real work.
 lit_config.load_config(config, "@LIBCXX_SOURCE_DIR@/test/lit.cfg")
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to