[libc++] SSH: Properly handle test-executables that are not the first argument
If a ShTest has for example another command in front of the test
executable it wants to execute, ssh.py needs to properly translate
the path of that test executable to the executable on the remote host.
For example, running '%{exec} ! %t.exe', we can't assume that the
test-executable is the first argument after '%{exec}'.
diff --git a/libcxx/utils/ssh.py b/libcxx/utils/ssh.py
index 2ae97a99..5ba267b 100644
--- a/libcxx/utils/ssh.py
+++ b/libcxx/utils/ssh.py
@@ -31,31 +31,34 @@
if len(remaining) < 2:
sys.stderr.write('Missing actual commands to run')
return 1
- remaining = remaining[1:] # Skip the '--'
- # HACK:
- # If the first argument is a file that ends in `.tmp.exe`, assume it is
- # the name of an executable generated by a test file. This allows us to
- # do custom processing like codesigning the executable and changing its
- # path when running on the remote host. It's possible for there to be no
- # such executable, for example in the case of a .sh.cpp test.
- exe = None
- if os.path.exists(remaining[0]) and remaining[0].endswith('.tmp.exe'):
- exe = remaining.pop(0)
-
- # If there's an executable, do any necessary codesigning.
- if exe and args.codesign_identity:
- rc = subprocess.call(['xcrun', 'codesign', '-f', '-s', args.codesign_identity, exe], env={})
- if rc != 0:
- sys.stderr.write('Failed to codesign: {}'.format(exe))
- return rc
+ commandLine = remaining[1:] # Skip the '--'
ssh = lambda command: ['ssh', '-oBatchMode=yes', args.host, command]
scp = lambda src, dst: ['scp', '-oBatchMode=yes', '-r', src, '{}:{}'.format(args.host, dst)]
# Create a temporary directory where the test will be run.
tmp = subprocess.check_output(ssh('mktemp -d /tmp/libcxx.XXXXXXXXXX'), universal_newlines=True).strip()
+
+ # HACK:
+ # If an argument is a file that ends in `.tmp.exe`, assume it is the name
+ # of an executable generated by a test file. We call these test-executables
+ # below. This allows us to do custom processing like codesigning test-executables
+ # and changing their path when running on the remote host. It's also possible
+ # for there to be no such executable, for example in the case of a .sh.cpp
+ # test.
+ isTestExe = lambda exe: exe.endswith('.tmp.exe') and os.path.exists(exe)
+ testExeOnRemote = lambda exe: posixpath.join(tmp, os.path.basename(exe))
+
try:
+ # Do any necessary codesigning of test-executables found in the command line.
+ if args.codesign_identity:
+ for exe in filter(isTestExe, commandLine):
+ rc = subprocess.call(['xcrun', 'codesign', '-f', '-s', args.codesign_identity, exe], env={})
+ if rc != 0:
+ sys.stderr.write('Failed to codesign: {}'.format(exe))
+ return rc
+
# Ensure the test dependencies exist and scp them to the temporary directory.
# Test dependencies can be either files or directories, so the `scp` command
# needs to use `-r`.
@@ -68,27 +71,24 @@
sys.stderr.write('Failed to copy dependency "{}" to remote host'.format(dep))
return rc
- # If there's an executable, change its path to be in the temporary directory.
- # We know it has been copied to the remote host when we handled the test
- # dependencies above.
- if exe:
- exe = posixpath.join(tmp, os.path.basename(exe))
-
- # If there's an executable, make sure it has 'execute' permissions on the
- # remote host. The host that compiled the executable might not have a notion
- # of 'executable' permissions.
- if exe:
+ # Make sure all test-executables in the remote command line have 'execute'
+ # permissions on the remote host. The host that compiled the test-executable
+ # might not have a notion of 'executable' permissions.
+ for exe in map(testExeOnRemote, filter(isTestExe, commandLine)):
rc = subprocess.call(ssh('chmod +x {}'.format(exe)))
if rc != 0:
sys.stderr.write('Failed to chmod +x test-executable "{}" on the remote host'.format(exe))
return rc
# Execute the command through SSH in the temporary directory, with the
- # correct environment.
+ # correct environment. We tweak the command line to run it on the remote
+ # host by transforming the path of test-executables to their path in the
+ # temporary directory, where we know they have been copied when we handled
+ # test dependencies above.
commands = [
'cd {}'.format(tmp),
'export {}'.format(' '.join(args.env)),
- ' '.join([exe] + remaining if exe else remaining)
+ ' '.join(testExeOnRemote(x) if isTestExe(x) else x for x in commandLine)
]
rc = subprocess.call(ssh(' && '.join(commands)))
return rc