1*58b9f456SAndroid Build Coastguard Worker#===----------------------------------------------------------------------===## 2*58b9f456SAndroid Build Coastguard Worker# 3*58b9f456SAndroid Build Coastguard Worker# The LLVM Compiler Infrastructure 4*58b9f456SAndroid Build Coastguard Worker# 5*58b9f456SAndroid Build Coastguard Worker# This file is dual licensed under the MIT and the University of Illinois Open 6*58b9f456SAndroid Build Coastguard Worker# Source Licenses. See LICENSE.TXT for details. 7*58b9f456SAndroid Build Coastguard Worker# 8*58b9f456SAndroid Build Coastguard Worker#===----------------------------------------------------------------------===## 9*58b9f456SAndroid Build Coastguard Worker 10*58b9f456SAndroid Build Coastguard Workerimport platform 11*58b9f456SAndroid Build Coastguard Workerimport os 12*58b9f456SAndroid Build Coastguard Worker 13*58b9f456SAndroid Build Coastguard Workerfrom libcxx.test import tracing 14*58b9f456SAndroid Build Coastguard Workerfrom libcxx.util import executeCommand 15*58b9f456SAndroid Build Coastguard Worker 16*58b9f456SAndroid Build Coastguard Worker 17*58b9f456SAndroid Build Coastguard Workerclass Executor(object): 18*58b9f456SAndroid Build Coastguard Worker def run(self, exe_path, cmd, local_cwd, file_deps=None, env=None): 19*58b9f456SAndroid Build Coastguard Worker """Execute a command. 20*58b9f456SAndroid Build Coastguard Worker Be very careful not to change shared state in this function. 21*58b9f456SAndroid Build Coastguard Worker Executor objects are shared between python processes in `lit -jN`. 22*58b9f456SAndroid Build Coastguard Worker Args: 23*58b9f456SAndroid Build Coastguard Worker exe_path: str: Local path to the executable to be run 24*58b9f456SAndroid Build Coastguard Worker cmd: [str]: subprocess.call style command 25*58b9f456SAndroid Build Coastguard Worker local_cwd: str: Local path to the working directory 26*58b9f456SAndroid Build Coastguard Worker file_deps: [str]: Files required by the test 27*58b9f456SAndroid Build Coastguard Worker env: {str: str}: Environment variables to execute under 28*58b9f456SAndroid Build Coastguard Worker Returns: 29*58b9f456SAndroid Build Coastguard Worker cmd, out, err, exitCode 30*58b9f456SAndroid Build Coastguard Worker """ 31*58b9f456SAndroid Build Coastguard Worker raise NotImplementedError 32*58b9f456SAndroid Build Coastguard Worker 33*58b9f456SAndroid Build Coastguard Worker 34*58b9f456SAndroid Build Coastguard Workerclass LocalExecutor(Executor): 35*58b9f456SAndroid Build Coastguard Worker def __init__(self): 36*58b9f456SAndroid Build Coastguard Worker super(LocalExecutor, self).__init__() 37*58b9f456SAndroid Build Coastguard Worker self.is_windows = platform.system() == 'Windows' 38*58b9f456SAndroid Build Coastguard Worker 39*58b9f456SAndroid Build Coastguard Worker def run(self, exe_path, cmd=None, work_dir='.', file_deps=None, env=None): 40*58b9f456SAndroid Build Coastguard Worker cmd = cmd or [exe_path] 41*58b9f456SAndroid Build Coastguard Worker if work_dir == '.': 42*58b9f456SAndroid Build Coastguard Worker work_dir = os.getcwd() 43*58b9f456SAndroid Build Coastguard Worker out, err, rc = executeCommand(cmd, cwd=work_dir, env=env) 44*58b9f456SAndroid Build Coastguard Worker return (cmd, out, err, rc) 45*58b9f456SAndroid Build Coastguard Worker 46*58b9f456SAndroid Build Coastguard Worker 47*58b9f456SAndroid Build Coastguard Workerclass PrefixExecutor(Executor): 48*58b9f456SAndroid Build Coastguard Worker """Prefix an executor with some other command wrapper. 49*58b9f456SAndroid Build Coastguard Worker 50*58b9f456SAndroid Build Coastguard Worker Most useful for setting ulimits on commands, or running an emulator like 51*58b9f456SAndroid Build Coastguard Worker qemu and valgrind. 52*58b9f456SAndroid Build Coastguard Worker """ 53*58b9f456SAndroid Build Coastguard Worker def __init__(self, commandPrefix, chain): 54*58b9f456SAndroid Build Coastguard Worker super(PrefixExecutor, self).__init__() 55*58b9f456SAndroid Build Coastguard Worker 56*58b9f456SAndroid Build Coastguard Worker self.commandPrefix = commandPrefix 57*58b9f456SAndroid Build Coastguard Worker self.chain = chain 58*58b9f456SAndroid Build Coastguard Worker 59*58b9f456SAndroid Build Coastguard Worker def run(self, exe_path, cmd=None, work_dir='.', file_deps=None, env=None): 60*58b9f456SAndroid Build Coastguard Worker cmd = cmd or [exe_path] 61*58b9f456SAndroid Build Coastguard Worker return self.chain.run(exe_path, self.commandPrefix + cmd, work_dir, 62*58b9f456SAndroid Build Coastguard Worker file_deps, env=env) 63*58b9f456SAndroid Build Coastguard Worker 64*58b9f456SAndroid Build Coastguard Worker 65*58b9f456SAndroid Build Coastguard Workerclass PostfixExecutor(Executor): 66*58b9f456SAndroid Build Coastguard Worker """Postfix an executor with some args.""" 67*58b9f456SAndroid Build Coastguard Worker def __init__(self, commandPostfix, chain): 68*58b9f456SAndroid Build Coastguard Worker super(PostfixExecutor, self).__init__() 69*58b9f456SAndroid Build Coastguard Worker 70*58b9f456SAndroid Build Coastguard Worker self.commandPostfix = commandPostfix 71*58b9f456SAndroid Build Coastguard Worker self.chain = chain 72*58b9f456SAndroid Build Coastguard Worker 73*58b9f456SAndroid Build Coastguard Worker def run(self, exe_path, cmd=None, work_dir='.', file_deps=None, env=None): 74*58b9f456SAndroid Build Coastguard Worker cmd = cmd or [exe_path] 75*58b9f456SAndroid Build Coastguard Worker return self.chain.run(cmd + self.commandPostfix, work_dir, file_deps, 76*58b9f456SAndroid Build Coastguard Worker env=env) 77*58b9f456SAndroid Build Coastguard Worker 78*58b9f456SAndroid Build Coastguard Worker 79*58b9f456SAndroid Build Coastguard Worker 80*58b9f456SAndroid Build Coastguard Workerclass TimeoutExecutor(PrefixExecutor): 81*58b9f456SAndroid Build Coastguard Worker """Execute another action under a timeout. 82*58b9f456SAndroid Build Coastguard Worker 83*58b9f456SAndroid Build Coastguard Worker Deprecated. http://reviews.llvm.org/D6584 adds timeouts to LIT. 84*58b9f456SAndroid Build Coastguard Worker """ 85*58b9f456SAndroid Build Coastguard Worker def __init__(self, duration, chain): 86*58b9f456SAndroid Build Coastguard Worker super(TimeoutExecutor, self).__init__( 87*58b9f456SAndroid Build Coastguard Worker ['timeout', duration], chain) 88*58b9f456SAndroid Build Coastguard Worker 89*58b9f456SAndroid Build Coastguard Worker 90*58b9f456SAndroid Build Coastguard Workerclass RemoteExecutor(Executor): 91*58b9f456SAndroid Build Coastguard Worker def __init__(self): 92*58b9f456SAndroid Build Coastguard Worker self.local_run = executeCommand 93*58b9f456SAndroid Build Coastguard Worker 94*58b9f456SAndroid Build Coastguard Worker def remote_temp_dir(self): 95*58b9f456SAndroid Build Coastguard Worker return self._remote_temp(True) 96*58b9f456SAndroid Build Coastguard Worker 97*58b9f456SAndroid Build Coastguard Worker def remote_temp_file(self): 98*58b9f456SAndroid Build Coastguard Worker return self._remote_temp(False) 99*58b9f456SAndroid Build Coastguard Worker 100*58b9f456SAndroid Build Coastguard Worker def _remote_temp(self, is_dir): 101*58b9f456SAndroid Build Coastguard Worker raise NotImplementedError() 102*58b9f456SAndroid Build Coastguard Worker 103*58b9f456SAndroid Build Coastguard Worker def copy_in(self, local_srcs, remote_dsts): 104*58b9f456SAndroid Build Coastguard Worker # This could be wrapped up in a tar->scp->untar for performance 105*58b9f456SAndroid Build Coastguard Worker # if there are lots of files to be copied/moved 106*58b9f456SAndroid Build Coastguard Worker for src, dst in zip(local_srcs, remote_dsts): 107*58b9f456SAndroid Build Coastguard Worker self._copy_in_file(src, dst) 108*58b9f456SAndroid Build Coastguard Worker 109*58b9f456SAndroid Build Coastguard Worker def _copy_in_file(self, src, dst): 110*58b9f456SAndroid Build Coastguard Worker raise NotImplementedError() 111*58b9f456SAndroid Build Coastguard Worker 112*58b9f456SAndroid Build Coastguard Worker def delete_remote(self, remote): 113*58b9f456SAndroid Build Coastguard Worker try: 114*58b9f456SAndroid Build Coastguard Worker self._execute_command_remote(['rm', '-rf', remote]) 115*58b9f456SAndroid Build Coastguard Worker except OSError: 116*58b9f456SAndroid Build Coastguard Worker # TODO: Log failure to delete? 117*58b9f456SAndroid Build Coastguard Worker pass 118*58b9f456SAndroid Build Coastguard Worker 119*58b9f456SAndroid Build Coastguard Worker def run(self, exe_path, cmd=None, work_dir='.', file_deps=None, env=None): 120*58b9f456SAndroid Build Coastguard Worker target_exe_path = None 121*58b9f456SAndroid Build Coastguard Worker target_cwd = None 122*58b9f456SAndroid Build Coastguard Worker try: 123*58b9f456SAndroid Build Coastguard Worker target_cwd = self.remote_temp_dir() 124*58b9f456SAndroid Build Coastguard Worker target_exe_path = os.path.join(target_cwd, 'libcxx_test.exe') 125*58b9f456SAndroid Build Coastguard Worker if cmd: 126*58b9f456SAndroid Build Coastguard Worker # Replace exe_path with target_exe_path. 127*58b9f456SAndroid Build Coastguard Worker cmd = [c if c != exe_path else target_exe_path for c in cmd] 128*58b9f456SAndroid Build Coastguard Worker else: 129*58b9f456SAndroid Build Coastguard Worker cmd = [target_exe_path] 130*58b9f456SAndroid Build Coastguard Worker 131*58b9f456SAndroid Build Coastguard Worker srcs = [exe_path] 132*58b9f456SAndroid Build Coastguard Worker dsts = [target_exe_path] 133*58b9f456SAndroid Build Coastguard Worker if file_deps is not None: 134*58b9f456SAndroid Build Coastguard Worker dev_paths = [os.path.join(target_cwd, os.path.basename(f)) 135*58b9f456SAndroid Build Coastguard Worker for f in file_deps] 136*58b9f456SAndroid Build Coastguard Worker srcs.extend(file_deps) 137*58b9f456SAndroid Build Coastguard Worker dsts.extend(dev_paths) 138*58b9f456SAndroid Build Coastguard Worker self.copy_in(srcs, dsts) 139*58b9f456SAndroid Build Coastguard Worker # TODO(jroelofs): capture the copy_in and delete_remote commands, 140*58b9f456SAndroid Build Coastguard Worker # and conjugate them with '&&'s around the first tuple element 141*58b9f456SAndroid Build Coastguard Worker # returned here: 142*58b9f456SAndroid Build Coastguard Worker return self._execute_command_remote(cmd, target_cwd, env) 143*58b9f456SAndroid Build Coastguard Worker finally: 144*58b9f456SAndroid Build Coastguard Worker if target_cwd: 145*58b9f456SAndroid Build Coastguard Worker self.delete_remote(target_cwd) 146*58b9f456SAndroid Build Coastguard Worker 147*58b9f456SAndroid Build Coastguard Worker def _execute_command_remote(self, cmd, remote_work_dir='.', env=None): 148*58b9f456SAndroid Build Coastguard Worker raise NotImplementedError() 149*58b9f456SAndroid Build Coastguard Worker 150*58b9f456SAndroid Build Coastguard Worker 151*58b9f456SAndroid Build Coastguard Workerclass SSHExecutor(RemoteExecutor): 152*58b9f456SAndroid Build Coastguard Worker def __init__(self, host, username=None): 153*58b9f456SAndroid Build Coastguard Worker super(SSHExecutor, self).__init__() 154*58b9f456SAndroid Build Coastguard Worker 155*58b9f456SAndroid Build Coastguard Worker self.user_prefix = username + '@' if username else '' 156*58b9f456SAndroid Build Coastguard Worker self.host = host 157*58b9f456SAndroid Build Coastguard Worker self.scp_command = 'scp' 158*58b9f456SAndroid Build Coastguard Worker self.ssh_command = 'ssh' 159*58b9f456SAndroid Build Coastguard Worker 160*58b9f456SAndroid Build Coastguard Worker # TODO(jroelofs): switch this on some -super-verbose-debug config flag 161*58b9f456SAndroid Build Coastguard Worker if False: 162*58b9f456SAndroid Build Coastguard Worker self.local_run = tracing.trace_function( 163*58b9f456SAndroid Build Coastguard Worker self.local_run, log_calls=True, log_results=True, 164*58b9f456SAndroid Build Coastguard Worker label='ssh_local') 165*58b9f456SAndroid Build Coastguard Worker 166*58b9f456SAndroid Build Coastguard Worker def _remote_temp(self, is_dir): 167*58b9f456SAndroid Build Coastguard Worker # TODO: detect what the target system is, and use the correct 168*58b9f456SAndroid Build Coastguard Worker # mktemp command for it. (linux and darwin differ here, and I'm 169*58b9f456SAndroid Build Coastguard Worker # sure windows has another way to do it) 170*58b9f456SAndroid Build Coastguard Worker 171*58b9f456SAndroid Build Coastguard Worker # Not sure how to do suffix on osx yet 172*58b9f456SAndroid Build Coastguard Worker dir_arg = '-d' if is_dir else '' 173*58b9f456SAndroid Build Coastguard Worker cmd = 'mktemp -q {} /tmp/libcxx.XXXXXXXXXX'.format(dir_arg) 174*58b9f456SAndroid Build Coastguard Worker _, temp_path, err, exitCode = self._execute_command_remote([cmd]) 175*58b9f456SAndroid Build Coastguard Worker temp_path = temp_path.strip() 176*58b9f456SAndroid Build Coastguard Worker if exitCode != 0: 177*58b9f456SAndroid Build Coastguard Worker raise RuntimeError(err) 178*58b9f456SAndroid Build Coastguard Worker return temp_path 179*58b9f456SAndroid Build Coastguard Worker 180*58b9f456SAndroid Build Coastguard Worker def _copy_in_file(self, src, dst): 181*58b9f456SAndroid Build Coastguard Worker scp = self.scp_command 182*58b9f456SAndroid Build Coastguard Worker remote = self.host 183*58b9f456SAndroid Build Coastguard Worker remote = self.user_prefix + remote 184*58b9f456SAndroid Build Coastguard Worker cmd = [scp, '-p', src, remote + ':' + dst] 185*58b9f456SAndroid Build Coastguard Worker self.local_run(cmd) 186*58b9f456SAndroid Build Coastguard Worker 187*58b9f456SAndroid Build Coastguard Worker def _execute_command_remote(self, cmd, remote_work_dir='.', env=None): 188*58b9f456SAndroid Build Coastguard Worker remote = self.user_prefix + self.host 189*58b9f456SAndroid Build Coastguard Worker ssh_cmd = [self.ssh_command, '-oBatchMode=yes', remote] 190*58b9f456SAndroid Build Coastguard Worker if env: 191*58b9f456SAndroid Build Coastguard Worker env_cmd = ['env'] + ['%s=%s' % (k, v) for k, v in env.items()] 192*58b9f456SAndroid Build Coastguard Worker else: 193*58b9f456SAndroid Build Coastguard Worker env_cmd = [] 194*58b9f456SAndroid Build Coastguard Worker remote_cmd = ' '.join(env_cmd + cmd) 195*58b9f456SAndroid Build Coastguard Worker if remote_work_dir != '.': 196*58b9f456SAndroid Build Coastguard Worker remote_cmd = 'cd ' + remote_work_dir + ' && ' + remote_cmd 197*58b9f456SAndroid Build Coastguard Worker out, err, rc = self.local_run(ssh_cmd + [remote_cmd]) 198*58b9f456SAndroid Build Coastguard Worker return (remote_cmd, out, err, rc) 199