[libc++] Execute tests from the Lit execution root instead of the test tree

Instead of executing tests from within the libc++ test suite, we execute
them from the Lit execution directory. However, since some tests have
file dependencies, we must copy those dependencies to the execution
directory where they are executed.

This has the major benefit that if a test modifies a file (whether it
is wanted or not), other tests will not see those modifications. This
is good because current tests assume that input data is never modified,
however this could be an incorrect assumption if some test does not
behave properly.
diff --git a/libcxx/utils/libcxx/test/config.py b/libcxx/utils/libcxx/test/config.py
index 3e983e1..20d0a79 100644
--- a/libcxx/utils/libcxx/test/config.py
+++ b/libcxx/utils/libcxx/test/config.py
@@ -1050,7 +1050,6 @@
             exec_args.append('--host {}'.format(self.executor.user_prefix + self.executor.host))
             executor = os.path.join(self.libcxx_src_root, 'utils', 'ssh.py')
         else:
-            exec_args.append('--working_directory "%S"')
             executor = os.path.join(self.libcxx_src_root, 'utils', 'run.py')
         sub.append(('%{exec}', '{} {} {} -- '.format(pipes.quote(sys.executable),
                                                      pipes.quote(executor),
diff --git a/libcxx/utils/libcxx/test/executor.py b/libcxx/utils/libcxx/test/executor.py
index b555b1f..c34310c 100644
--- a/libcxx/utils/libcxx/test/executor.py
+++ b/libcxx/utils/libcxx/test/executor.py
@@ -10,6 +10,7 @@
 import os
 import posixpath
 import ntpath
+import shutil
 
 from libcxx.test import tracing
 from libcxx.util import executeCommand
@@ -61,6 +62,12 @@
         if env:
             env = self.merge_environments(os.environ, env)
 
+        for dep in file_deps:
+            if os.path.isdir(dep):
+                shutil.copytree(dep, os.path.join(work_dir, os.path.basename(dep)), symlinks=True)
+            else:
+                shutil.copy2(dep, work_dir)
+
         out, err, rc = executeCommand(cmd, cwd=work_dir, env=env)
         return (cmd, out, err, rc)
 
diff --git a/libcxx/utils/libcxx/test/format.py b/libcxx/utils/libcxx/test/format.py
index a026c4f..1bc85f2 100644
--- a/libcxx/utils/libcxx/test/format.py
+++ b/libcxx/utils/libcxx/test/format.py
@@ -9,6 +9,8 @@
 import copy
 import errno
 import os
+import shutil
+import tempfile
 import time
 import random
 
@@ -209,16 +211,21 @@
                 report += "Compilation failed unexpectedly!"
                 return lit.Test.Result(lit.Test.FAIL, report)
             # Run the test
-            local_cwd = os.path.dirname(source_path)
             env = None
             if self.exec_env:
                 env = self.exec_env
 
             max_retry = test.allowed_retries + 1
             for retry_count in range(max_retry):
-                cmd, out, err, rc = self.executor.run(exec_path, [exec_path],
-                                                      local_cwd, data_files,
-                                                      env)
+                # Create a temporary directory just for that test and run the
+                # test in that directory
+                try:
+                    execDirTmp = tempfile.mkdtemp(dir=execDir)
+                    cmd, out, err, rc = self.executor.run(exec_path, [exec_path],
+                                                          execDirTmp, data_files,
+                                                          env)
+                finally:
+                    shutil.rmtree(execDirTmp)
                 report = "Compiled With: '%s'\n" % ' '.join(compile_cmd)
                 report += libcxx.util.makeReport(cmd, out, err, rc)
                 if rc == 0:
diff --git a/libcxx/utils/run.py b/libcxx/utils/run.py
index 6a89a2b..7de82c7 100644
--- a/libcxx/utils/run.py
+++ b/libcxx/utils/run.py
@@ -14,14 +14,15 @@
 
 import argparse
 import os
+import shutil
 import subprocess
 import sys
+import tempfile
 
 
 def main():
     parser = argparse.ArgumentParser()
     parser.add_argument('--codesign_identity', type=str, required=False)
-    parser.add_argument('--working_directory', type=str, required=True)
     parser.add_argument('--dependencies', type=str, nargs='*', required=True)
     parser.add_argument('--env', type=str, nargs='*', required=True)
     (args, remaining) = parser.parse_known_args(sys.argv[1:])
@@ -42,14 +43,23 @@
     # Extract environment variables into a dictionary
     env = {k : v  for (k, v) in map(lambda s: s.split('=', 1), args.env)}
 
-    # Ensure the file dependencies exist
-    for file in args.dependencies:
-        if not os.path.exists(file):
-            sys.stderr.write('Missing file {} marked as a dependency of a test'.format(file))
-            exit(1)
+    try:
+        tmpDir = tempfile.mkdtemp()
 
-    # Run the executable with the given environment in the given working directory
-    return subprocess.call(' '.join(remaining), cwd=args.working_directory, env=env, shell=True)
+        # Ensure the file dependencies exist and copy them to a temporary directory.
+        for dep in args.dependencies:
+            if not os.path.exists(dep):
+                sys.stderr.write('Missing file or directory "{}" marked as a dependency of a test'.format(dep))
+                exit(1)
+            if os.path.isdir(dep):
+                shutil.copytree(dep, os.path.join(tmpDir, os.path.basename(dep)), symlinks=True)
+            else:
+                shutil.copy2(dep, tmpDir)
+
+        # Run the executable with the given environment in the temporary directory.
+        return subprocess.call(' '.join(remaining), cwd=tmpDir, env=env, shell=True)
+    finally:
+        shutil.rmtree(tmpDir)
 
 if __name__ == '__main__':
     exit(main())