| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2014 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """ BuildStepUtils for a remote-execution-via-SSH environment. """ | |
| 6 | |
| 7 | |
| 8 from default_build_step_utils import DefaultBuildStepUtils, DeviceDirs | |
| 9 from slave import slave_utils | |
| 10 from utils import old_gs_utils as gs_utils | |
| 11 from py.utils import shell_utils | |
| 12 from py.utils import ssh_utils | |
| 13 import os | |
| 14 import posixpath | |
| 15 import shutil | |
| 16 | |
| 17 | |
| 18 class SshBuildStepUtils(DefaultBuildStepUtils): | |
| 19 def __init__(self, build_step_instance): | |
| 20 DefaultBuildStepUtils.__init__(self, build_step_instance) | |
| 21 self._unique_prefix = 'skia_' | |
| 22 args = self._step.args | |
| 23 self._ssh = ssh_utils.SshDestination( | |
| 24 args.get('ssh_host', 'localhost'), | |
| 25 args.get('ssh_port', 22), | |
| 26 args.get('ssh_user', 'root')) | |
| 27 if self._ssh.host == 'localhost': | |
| 28 self._ssh.options = ['-o', 'NoHostAuthenticationForLocalhost=yes'] | |
| 29 | |
| 30 self._build_dir = args.get('skia_out', os.path.abspath('out')) | |
| 31 self._remote_dir = args.get('remote_dir', 'skia') | |
| 32 | |
| 33 # Subclass constructors can change these fields. | |
| 34 # self._unique_prefix - (string) prefix to add to target | |
| 35 # executables. | |
| 36 # self._ssh.host - (string) hostname to pass to ssh | |
| 37 # self._ssh.port - (string) ssh port on the remote host | |
| 38 # self._ssh.user - (string) username on the remote host | |
| 39 # self._remote_dir - (string) absolute or relative path of a | |
| 40 # directory on the remote system. | |
| 41 # self._build_dir - (string) This script assumes that the compiled | |
| 42 # executables will be located in os.path.join( | |
| 43 # self._build_dir, self._step.configuration) | |
| 44 # | |
| 45 # Subclasses should override Compile(). These values are | |
| 46 # available if needed: | |
| 47 # self._step.configuration | |
| 48 # self._step.gyp_defines | |
| 49 # self._step.deps_target_os | |
| 50 # self._step.make_flags | |
| 51 | |
| 52 def RunFlavoredCmd(self, app, args): | |
| 53 """ Override this in new BuildStep flavors. """ | |
| 54 assert app in self.ListBuildStepExecutables() | |
| 55 executable = self.DevicePathJoin( | |
| 56 self._remote_dir, self._unique_prefix + app) | |
| 57 self._ssh.Run([executable] + args) | |
| 58 | |
| 59 def ReadFileOnDevice(self, filepath): | |
| 60 """ Read the contents of a file on the device. """ | |
| 61 return self._ssh.Run(['cat', filepath], echo=False) | |
| 62 | |
| 63 def _RemoveDirectoryOnDevice(self, directory): | |
| 64 """ Delete a directory on the device. """ | |
| 65 try: | |
| 66 self._ssh.Run(['rm', '-rf', directory]) | |
| 67 except shell_utils.CommandFailedException: | |
| 68 # `rm -f` will return 0 for nonexistent operands. Other failurs | |
| 69 # will return 1, leading to a CommandFailedException. | |
| 70 raise Exception('Failed to remove %s' % directory) | |
| 71 | |
| 72 def _CreateDirectoryOnDevice(self, directory): | |
| 73 """ Create a directory on the device. """ | |
| 74 self._ssh.Run(['mkdir', '-p', directory]) | |
| 75 | |
| 76 def PushFileToDevice(self, src, dst): | |
| 77 """ Overrides build_step.PushFileToDevice() """ | |
| 78 self._ssh.Put(src, dst) | |
| 79 | |
| 80 def PushMultipleFilesToDevice(self, src_files, dst): | |
| 81 self._ssh.MultiPut(src_files, dst) | |
| 82 | |
| 83 def DeviceListDir(self, directory): | |
| 84 """ Overrides build_step.DeviceListDir() """ | |
| 85 ls = self._ssh.Run(['ls', directory], echo=False) | |
| 86 # ''.split('\n') evaluates to [''], when we want []. | |
| 87 return [path for path in ls.split('\n') if path != ''] | |
| 88 | |
| 89 def _RunAndReturnSuccess(self, command): | |
| 90 try: | |
| 91 self._ssh.Run(command) | |
| 92 except shell_utils.CommandFailedException: | |
| 93 return False | |
| 94 return True | |
| 95 | |
| 96 def DevicePathExists(self, path): | |
| 97 """ Overrides build_step.DevicePathExists() """ | |
| 98 return self._RunAndReturnSuccess(['test', '-e', path]) | |
| 99 | |
| 100 def DeviceDirectoryExists(self, path): | |
| 101 return self._RunAndReturnSuccess(['test', '-d', path]) | |
| 102 | |
| 103 def DevicePathJoin(self, *args): | |
| 104 """ Overrides build_step.DevicePathJoin() """ | |
| 105 return posixpath.sep.join(args) | |
| 106 | |
| 107 def CreateCleanDeviceDirectory(self, directory): | |
| 108 # Raises exeption if either the directory exists but is | |
| 109 # un-removeable or if mkdir fails. | |
| 110 self._ssh.RunCmd('rm -rf "%s" && mkdir -p "%s"' % (directory, directory)) | |
| 111 | |
| 112 def CopyDirectoryContentsToDevice(self, host_dir, device_dir): | |
| 113 """ Copy the contents of a host-side directory to a clean | |
| 114 directory on the device side. | |
| 115 """ | |
| 116 self.CreateCleanDeviceDirectory(device_dir) | |
| 117 upload_list = [] | |
| 118 for filename in os.listdir(host_dir): | |
| 119 if filename != gs_utils.TIMESTAMP_COMPLETED_FILENAME: | |
| 120 path = os.path.join(host_dir, filename) | |
| 121 if os.path.isfile(path): | |
| 122 upload_list.append(path) | |
| 123 if upload_list: | |
| 124 self.PushMultipleFilesToDevice(upload_list, device_dir) | |
| 125 ts_filepath = os.path.join(host_dir, gs_utils.TIMESTAMP_COMPLETED_FILENAME) | |
| 126 if os.path.isfile(ts_filepath): | |
| 127 self.PushFileToDevice(ts_filepath, device_dir) | |
| 128 | |
| 129 def CopyDirectoryContentsToHost(self, device_dir, host_dir): | |
| 130 """ Copy the contents of a device-side directory to a clean | |
| 131 directory on the host side. | |
| 132 """ | |
| 133 self.CreateCleanHostDirectory(host_dir) | |
| 134 if not self.DeviceDirectoryExists(device_dir): | |
| 135 print 'Device directory "%s" does not exist.' % device_dir | |
| 136 return | |
| 137 if not self.DeviceListDir(device_dir): | |
| 138 print 'Device directory "%s" is empty.' % device_dir | |
| 139 return | |
| 140 self._ssh.Get(host_dir, posixpath.join(device_dir, '*'), recurse=True) | |
| 141 | |
| 142 def Install(self): | |
| 143 """ Install the Skia executables. """ | |
| 144 for executable in self.ListBuildStepExecutables(): | |
| 145 # First, make sure that the program isn't running. | |
| 146 remote_executable = self._unique_prefix + executable | |
| 147 try: | |
| 148 self._ssh.Run(['killall', remote_executable]) | |
| 149 except Exception: | |
| 150 pass | |
| 151 remote_path = self.DevicePathJoin( | |
| 152 self._remote_dir, remote_executable) | |
| 153 local_path = os.path.join( | |
| 154 self._build_dir, self._step.configuration, executable) | |
| 155 assert os.path.isfile(local_path) | |
| 156 self._ssh.Put(local_path, remote_path) | |
| 157 | |
| 158 def AddGsutilToPath(self): | |
| 159 # Add gsutil to PATH | |
| 160 gsutil_dir = os.path.dirname(slave_utils.GSUtilSetup()) | |
| 161 if gsutil_dir not in os.environ['PATH'].split(os.pathsep): | |
| 162 os.environ['PATH'] += os.pathsep + gsutil_dir | |
| 163 | |
| 164 def GetDeviceDirs(self): | |
| 165 """ Set the directories which will be used by the BuildStep. """ | |
| 166 prefix = self.DevicePathJoin(self._remote_dir, self._unique_prefix) | |
| 167 return DeviceDirs( | |
| 168 perf_data_dir=prefix + 'perf', | |
| 169 gm_actual_dir=prefix + 'gm_actual', | |
| 170 gm_expected_dir=prefix + 'gm_expected', | |
| 171 dm_dir=prefix + 'dm_out', # Not dm, which will conflict with the binary. | |
| 172 resource_dir=prefix + 'resources', | |
| 173 skimage_in_dir=prefix + 'skimage_in', | |
| 174 skimage_expected_dir=prefix + 'skimage_expected', | |
| 175 skimage_out_dir=prefix + 'skimage_out', | |
| 176 skp_dir=prefix + 'skp', | |
| 177 skp_perf_dir=prefix + 'skp_perf', | |
| 178 playback_actual_images_dir=prefix + 'playback_actual_images', | |
| 179 playback_actual_summaries_dir=prefix + 'playback_actual_summaries', | |
| 180 playback_expected_summaries_dir=prefix + 'playback_expected_summaries', | |
| 181 tmp_dir=prefix + 'tmp_dir') | |
| 182 | |
| 183 def MakeClean(self): | |
| 184 """ Overridden from DefaultBuildStepUtils """ | |
| 185 if os.path.isdir(self._build_dir): | |
| 186 shutil.rmtree(os.path.realpath(self._build_dir)) | |
| OLD | NEW |