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 |