[libcxx] Codesign test executables if necessary

If LLVM_CODESIGNING_IDENTITY is set, test executables need to be
codesigned.

Differential Revision: https://reviews.llvm.org/D66496

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@371126 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/test/libcxx/strings/basic.string/PR42676.sh.cpp b/test/libcxx/strings/basic.string/PR42676.sh.cpp
index 5aa2402..7037482 100644
--- a/test/libcxx/strings/basic.string/PR42676.sh.cpp
+++ b/test/libcxx/strings/basic.string/PR42676.sh.cpp
@@ -9,7 +9,7 @@
 // Regression test for PR42676.
 
 // RUN: %cxx %flags %s -o %t.exe %compile_flags %link_flags -D_LIBCPP_HIDE_FROM_ABI_PER_TU
-// RUN: %t.exe
+// RUN: %run
 
 #include <memory>
 #include <string>
diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in
index fafc8fe..a435452 100644
--- a/test/lit.site.cfg.in
+++ b/test/lit.site.cfg.in
@@ -37,6 +37,9 @@
 config.pstl_obj_root            = "@ParallelSTL_BINARY_DIR@" if @LIBCXX_ENABLE_PARALLEL_ALGORITHMS@ else None
 config.libcxx_gdb               = "@LIBCXX_GDB@"
 
+# Code signing
+config.llvm_codesign_identity   = "@LLVM_CODESIGNING_IDENTITY@"
+
 # Let the main config do the real work.
 config.loaded_site_config = True
 lit_config.load_config(config, "@LIBCXX_SOURCE_DIR@/test/lit.cfg")
diff --git a/utils/libcxx/compiler.py b/utils/libcxx/compiler.py
index a553dbf..dd334cd 100644
--- a/utils/libcxx/compiler.py
+++ b/utils/libcxx/compiler.py
@@ -17,12 +17,13 @@
     CM_Compile = 2
     CM_Link = 3
 
-    def __init__(self, path, flags=None, compile_flags=None, link_flags=None,
+    def __init__(self, config, path, flags=None, compile_flags=None, link_flags=None,
                  warning_flags=None, verify_supported=None,
                  verify_flags=None, use_verify=False,
                  modules_flags=None, use_modules=False,
                  use_ccache=False, use_warnings=False, compile_env=None,
                  cxx_type=None, cxx_version=None):
+        self.libcxx_config = config
         self.source_lang = 'c++'
         self.path = path
         self.flags = list(flags or [])
@@ -163,17 +164,34 @@
                                                   cwd=cwd)
         return cmd, out, err, rc
 
-    def link(self, source_files, out=None, flags=[], cwd=None):
-        cmd = self.linkCmd(source_files, out, flags)
+    def link(self, source_files, exec_path=None, flags=[], cwd=None):
+        cmd = self.linkCmd(source_files, exec_path, flags)
         out, err, rc = libcxx.util.executeCommand(cmd, env=self.compile_env,
                                                   cwd=cwd)
+        cs_cmd, cs_out, cs_err, cs_rc = self.codesign(exec_path, cwd)
+        if cs_rc != 0:
+            return cs_cmd, cs_out, cs_err, cs_rc
         return cmd, out, err, rc
 
-    def compileLink(self, source_files, out=None, flags=[],
+    def compileLink(self, source_files, exec_path=None, flags=[],
                     cwd=None):
-        cmd = self.compileLinkCmd(source_files, out, flags)
+        cmd = self.compileLinkCmd(source_files, exec_path, flags)
         out, err, rc = libcxx.util.executeCommand(cmd, env=self.compile_env,
                                                   cwd=cwd)
+        cs_cmd, cs_out, cs_err, cs_rc = self.codesign(exec_path, cwd)
+        if cs_rc != 0:
+            return cs_cmd, cs_out, cs_err, cs_rc
+        return cmd, out, err, rc
+
+    def codesign(self, exec_path, cwd=None):
+        null_op = [], '', '', 0
+        if not exec_path:
+            return null_op
+        codesign_ident = self.libcxx_config.get_lit_conf('llvm_codesign_identity', '')
+        if not codesign_ident:
+            return null_op
+        cmd = ['xcrun', 'codesign', '-s', codesign_ident, exec_path]
+        out, err, rc = libcxx.util.executeCommand(cmd, cwd=cwd)
         return cmd, out, err, rc
 
     def compileLinkTwoSteps(self, source_file, out=None, object_file=None,
@@ -193,7 +211,7 @@
                 return cc_cmd, cc_stdout, cc_stderr, rc
 
             link_cmd, link_stdout, link_stderr, rc = self.link(
-                object_file, out=out, flags=flags, cwd=cwd)
+                object_file, exec_path=out, flags=flags, cwd=cwd)
             return (cc_cmd + ['&&'] + link_cmd, cc_stdout + link_stdout,
                     cc_stderr + link_stderr, rc)
 
diff --git a/utils/libcxx/test/config.py b/utils/libcxx/test/config.py
index 3be1aa1..670063b 100644
--- a/utils/libcxx/test/config.py
+++ b/utils/libcxx/test/config.py
@@ -228,7 +228,7 @@
         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.cxx = CXXCompiler(self, 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:
@@ -260,7 +260,7 @@
         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,
+        return CXXCompiler(self, clang_path, flags=flags,
                            compile_flags=compile_flags,
                            link_flags=link_flags)
 
@@ -1035,8 +1035,14 @@
             self.cxx.useModules()
 
     def configure_substitutions(self):
+        tool_env = ''
+        if platform.system() == 'Darwin':
+            # Do not pass DYLD_LIBRARY_PATH to the compiler, linker, etc. as
+            # these tools are not meant to exercise the just-built libraries.
+            tool_env += 'DYLD_LIBRARY_PATH="" '
+
         sub = self.config.substitutions
-        cxx_path = pipes.quote(self.cxx.path)
+        cxx_path = tool_env + pipes.quote(self.cxx.path)
         # Configure compiler substitutions
         sub.append(('%cxx', cxx_path))
         sub.append(('%libcxx_src_root', self.libcxx_src_root))
@@ -1071,7 +1077,11 @@
         sub.append(('%build', build_str))
         # Configure exec prefix substitutions.
         # Configure run env substitution.
-        sub.append(('%run', '%t.exe'))
+        codesign_ident = self.get_lit_conf('llvm_codesign_identity', '')
+        run_py = os.path.join(self.libcxx_src_root, 'utils', 'run.py')
+        run_str = '%s %s "%s" %%t.exe' % (pipes.quote(sys.executable), \
+                                          pipes.quote(run_py), codesign_ident)
+        sub.append(('%run', run_str))
         # Configure not program substitutions
         not_py = os.path.join(self.libcxx_src_root, 'utils', 'not.py')
         not_str = '%s %s ' % (pipes.quote(sys.executable), pipes.quote(not_py))
diff --git a/utils/run.py b/utils/run.py
new file mode 100644
index 0000000..fcfee96
--- /dev/null
+++ b/utils/run.py
@@ -0,0 +1,38 @@
+#===----------------------------------------------------------------------===##
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+#===----------------------------------------------------------------------===##
+
+"""run.py is a utility for running a program.
+
+It can perform code signing, forward arguments to the program, and return the
+program's error code.
+"""
+
+import subprocess
+import sys
+
+
+def main():
+    codesign_ident = sys.argv[1]
+
+    # Ignore 'run.py' and the codesigning identity.
+    argv = sys.argv[2:]
+
+    exec_path = argv[0]
+
+    # Do any necessary codesigning.
+    if codesign_ident:
+        sign_cmd = ['xcrun', 'codesign', '-f', '-s', codesign_ident, exec_path]
+        cs_rc = subprocess.call(sign_cmd, env={})
+        if cs_rc != 0:
+            sys.stderr.write('Failed to codesign: ' + exec_path)
+            return cs_rc
+
+    return subprocess.call(argv)
+
+if __name__ == '__main__':
+    exit(main())