| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2013 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 """ Utilities for generic build steps. """ | |
| 6 | |
| 7 import os | |
| 8 import shutil | |
| 9 import sys | |
| 10 | |
| 11 from utils import file_utils | |
| 12 from py.utils import shell_utils | |
| 13 | |
| 14 | |
| 15 class DeviceDirs(object): | |
| 16 def __init__(self, perf_data_dir, gm_actual_dir, gm_expected_dir, dm_dir, | |
| 17 resource_dir, skimage_in_dir, skimage_expected_dir, | |
| 18 skimage_out_dir, skp_dir, skp_perf_dir, | |
| 19 playback_actual_images_dir, playback_actual_summaries_dir, | |
| 20 playback_expected_summaries_dir, tmp_dir): | |
| 21 self._perf_data_dir = perf_data_dir | |
| 22 self._gm_actual_dir = gm_actual_dir | |
| 23 self._gm_expected_dir = gm_expected_dir | |
| 24 self._dm_dir = dm_dir | |
| 25 self._resource_dir = resource_dir | |
| 26 self._skimage_in_dir = skimage_in_dir | |
| 27 self._skimage_expected_dir = skimage_expected_dir | |
| 28 self._skimage_out_dir = skimage_out_dir | |
| 29 self._skp_dir = skp_dir | |
| 30 self._skp_perf_dir = skp_perf_dir | |
| 31 self._playback_actual_images_dir = playback_actual_images_dir | |
| 32 self._playback_actual_summaries_dir = playback_actual_summaries_dir | |
| 33 self._playback_expected_summaries_dir = playback_expected_summaries_dir | |
| 34 self._tmp_dir = tmp_dir | |
| 35 | |
| 36 def GMActualDir(self): | |
| 37 """Holds images and JSON summary written out by the 'gm' tool.""" | |
| 38 return self._gm_actual_dir | |
| 39 | |
| 40 def GMExpectedDir(self): | |
| 41 """Holds expectations JSON summary read by the 'gm' tool.""" | |
| 42 return self._gm_expected_dir | |
| 43 | |
| 44 def DMDir(self): | |
| 45 """Where DM writes.""" | |
| 46 return self._dm_dir | |
| 47 | |
| 48 def PerfDir(self): | |
| 49 return self._perf_data_dir | |
| 50 | |
| 51 def PlaybackActualImagesDir(self): | |
| 52 """Holds image files written out by the 'render_pictures' tool.""" | |
| 53 return self._playback_actual_images_dir | |
| 54 | |
| 55 def PlaybackActualSummariesDir(self): | |
| 56 """Holds actual-result JSON summaries written by 'render_pictures' tool.""" | |
| 57 return self._playback_actual_summaries_dir | |
| 58 | |
| 59 def PlaybackExpectedSummariesDir(self): | |
| 60 """Holds expected-result JSON summaries read by 'render_pictures' tool.""" | |
| 61 return self._playback_expected_summaries_dir | |
| 62 | |
| 63 def ResourceDir(self): | |
| 64 return self._resource_dir | |
| 65 | |
| 66 def SKImageInDir(self): | |
| 67 return self._skimage_in_dir | |
| 68 | |
| 69 def SKImageExpectedDir(self): | |
| 70 return self._skimage_expected_dir | |
| 71 | |
| 72 def SKImageOutDir(self): | |
| 73 return self._skimage_out_dir | |
| 74 | |
| 75 def SKPDir(self): | |
| 76 """Holds SKP files that are consumed by RenderSKPs and BenchPictures.""" | |
| 77 return self._skp_dir | |
| 78 | |
| 79 def SKPPerfDir(self): | |
| 80 return self._skp_perf_dir | |
| 81 | |
| 82 def TmpDir(self): | |
| 83 return self._tmp_dir | |
| 84 | |
| 85 | |
| 86 class DefaultBuildStepUtils: | |
| 87 """ Utilities to be used by subclasses of BuildStep. | |
| 88 | |
| 89 The methods in this class define how certain high-level functions should work. | |
| 90 Each build step flavor should correspond to a subclass of BuildStepUtils which | |
| 91 may override any of these functions as appropriate for that flavor. | |
| 92 | |
| 93 For example, the AndroidBuildStepUtils will override the functions for copying | |
| 94 files between the host and Android device, as well as the RunFlavoredCmd | |
| 95 function, so that commands may be run through ADB. """ | |
| 96 | |
| 97 def __init__(self, build_step_instance): | |
| 98 self._step = build_step_instance | |
| 99 | |
| 100 def ListBuildStepExecutables(self): | |
| 101 """ Called by subclasses that may need to install the executables. """ | |
| 102 return ['dm', 'gm', 'render_pictures', 'render_pdfs', | |
| 103 'skimage', 'nanobench'] | |
| 104 | |
| 105 def _PathToBinary(self, binary): | |
| 106 """ Returns the path to the given built executable. """ | |
| 107 return os.path.join('out', self._step.configuration, binary) | |
| 108 | |
| 109 def RunFlavoredCmd(self, app, args): | |
| 110 """ Override this in new BuildStepUtils flavors. """ | |
| 111 if (sys.platform == 'linux2' and 'x86_64' in self._step.builder_name | |
| 112 and not 'TSAN' in self._step.builder_name): | |
| 113 cmd = ['catchsegv', self._PathToBinary(app)] | |
| 114 else: | |
| 115 cmd = [self._PathToBinary(app)] | |
| 116 shell_utils.run(cmd + args) | |
| 117 | |
| 118 def ReadFileOnDevice(self, filepath): | |
| 119 """ Read the contents of a file on the associated device. Subclasses should | |
| 120 override this method with one appropriate for reading the contents of a file | |
| 121 on the device side. """ | |
| 122 with open(filepath) as f: | |
| 123 return f.read() | |
| 124 | |
| 125 def CopyDirectoryContentsToDevice(self, host_dir, device_dir): | |
| 126 """ Copy the contents of a host-side directory to a clean directory on the | |
| 127 device side. Subclasses should override this method with one appropriate for | |
| 128 copying the contents of a host-side directory to a clean device-side | |
| 129 directory. | |
| 130 | |
| 131 TODO(epoger): Clarify the description a bit: this method does not expect | |
| 132 device_dir to be an empty directory before it is called. Implementations | |
| 133 of this method for other device types create an empty directory at | |
| 134 device_dir as the first step. | |
| 135 """ | |
| 136 # For "normal" builders who don't have an attached device, we expect | |
| 137 # host_dir and device_dir to be the same. | |
| 138 if host_dir != device_dir: | |
| 139 raise ValueError('For builders who do not have attached devices, copying ' | |
| 140 'from host to device is undefined and only allowed if ' | |
| 141 'host_dir and device_dir are the same.') | |
| 142 | |
| 143 def CopyDirectoryContentsToHost(self, device_dir, host_dir): | |
| 144 """ Copy the contents of a device-side directory to a clean directory on the | |
| 145 host side. Subclasses should override this method with one appropriate for | |
| 146 copying the contents of a device-side directory to a clean host-side | |
| 147 directory.""" | |
| 148 # For "normal" builders who don't have an attached device, we expect | |
| 149 # host_dir and device_dir to be the same. | |
| 150 if host_dir != device_dir: | |
| 151 raise ValueError('For builders who do not have attached devices, copying ' | |
| 152 'from host to device is undefined and only allowed if ' | |
| 153 'host_dir and device_dir are the same.') | |
| 154 | |
| 155 def PushFileToDevice(self, src, dst): | |
| 156 """ Copy the a single file from path "src" on the host to path "dst" on | |
| 157 the device. If the host IS the device we are testing, it's just a filecopy. | |
| 158 Subclasses should override this method with one appropriate for | |
| 159 pushing the file to the device. """ | |
| 160 shutil.copy(src, dst) | |
| 161 | |
| 162 def DeviceListDir(self, directory): | |
| 163 """ List the contents of a directory on the connected device. """ | |
| 164 return os.listdir(directory) | |
| 165 | |
| 166 def DevicePathExists(self, path): | |
| 167 """ Like os.path.exists(), but for a path on the connected device. """ | |
| 168 return os.path.exists(path) | |
| 169 | |
| 170 def DevicePathJoin(self, *args): | |
| 171 """ Like os.path.join(), but for paths that will target the connected | |
| 172 device. """ | |
| 173 return os.sep.join(args) | |
| 174 | |
| 175 def CreateCleanDeviceDirectory(self, directory): | |
| 176 """ Creates an empty directory on an attached device. Subclasses with | |
| 177 attached devices should override. """ | |
| 178 file_utils.create_clean_local_dir(directory) | |
| 179 | |
| 180 def CreateCleanHostDirectory(self, directory): | |
| 181 """ Creates an empty directory on the host. Can be overridden by subclasses, | |
| 182 but that should not be necessary. """ | |
| 183 file_utils.create_clean_local_dir(directory) | |
| 184 | |
| 185 def Install(self): | |
| 186 """ Install the Skia executables. """ | |
| 187 pass | |
| 188 | |
| 189 def RunGYP(self): | |
| 190 self.Compile('gyp') | |
| 191 | |
| 192 def Compile(self, target): | |
| 193 """ Compile the Skia executables. """ | |
| 194 # TODO(borenet): It would be nice to increase code sharing here. | |
| 195 if 'Win8' in self._step.builder_name: | |
| 196 os.environ['GYP_MSVS_VERSION'] = '2012' | |
| 197 print 'GYP_MSVS_VERSION="%s"' % os.environ['GYP_MSVS_VERSION'] | |
| 198 | |
| 199 os.environ['CHROME_PATH'] = os.path.join(os.path.expanduser('~'), 'src') | |
| 200 print 'CHROME_PATH="%s"' % os.environ['CHROME_PATH'] | |
| 201 | |
| 202 os.environ['GYP_DEFINES'] = self._step.args['gyp_defines'] | |
| 203 print 'GYP_DEFINES="%s"' % os.environ['GYP_DEFINES'] | |
| 204 make_cmd = 'make' | |
| 205 if os.name == 'nt': | |
| 206 make_cmd = 'make.bat' | |
| 207 cmd = [make_cmd, | |
| 208 target, | |
| 209 'BUILDTYPE=%s' % self._step.configuration, | |
| 210 ] | |
| 211 cmd.extend(self._step.default_make_flags) | |
| 212 cmd.extend(self._step.make_flags) | |
| 213 | |
| 214 # TODO(epoger): Maybe remove this once we fix the underlying problem in | |
| 215 # https://code.google.com/p/skia/issues/detail?id=2393 ('recurring RunGYP | |
| 216 # failures on multiple Test-Win7-ShuttleA-HD2000-* bots') | |
| 217 print 'about to run cmd %s' % cmd | |
| 218 cwd = os.getcwd() | |
| 219 print 'cwd is %s' % cwd | |
| 220 print 'contents of cwd are %s' % os.listdir(cwd) | |
| 221 | |
| 222 shell_utils.run(cmd) | |
| 223 | |
| 224 def MakeClean(self): | |
| 225 make_cmd = 'make' | |
| 226 if os.name == 'nt': | |
| 227 make_cmd = 'make.bat' | |
| 228 shell_utils.run([make_cmd, 'clean']) | |
| 229 | |
| 230 def PreRun(self): | |
| 231 """ Preprocessing step to run before the BuildStep itself. """ | |
| 232 pass | |
| 233 | |
| 234 def GetDeviceDirs(self): | |
| 235 """ Set the directories which will be used by the BuildStep. | |
| 236 | |
| 237 In the case of DefaultBuildStepUtils, host_dirs and device_dirs are the | |
| 238 same, which is why CopyDirectoryContentsToDevice() is a no-op. | |
| 239 """ | |
| 240 return DeviceDirs( | |
| 241 perf_data_dir=self._step.perf_data_dir, | |
| 242 # TODO(epoger): Instead, set gm_actual_dir to self._step._gm_actual_dir | |
| 243 # and remove os.path.join() with self._builder_name in postrender.py ? | |
| 244 # (CopyDirectoryContentsToHost fails if the paths are different when | |
| 245 # host==device, so why not just make them inherently equal?) | |
| 246 gm_actual_dir=os.path.join(os.pardir, os.pardir, 'gm', 'actual'), | |
| 247 gm_expected_dir=os.path.join(os.pardir, os.pardir, 'gm', 'expected'), | |
| 248 dm_dir=os.path.join(os.pardir, os.pardir, 'dm'), | |
| 249 resource_dir=self._step.resource_dir, | |
| 250 skimage_in_dir=self._step.skimage_in_dir, | |
| 251 skimage_expected_dir=os.path.join(os.pardir, os.pardir, 'skimage', | |
| 252 'expected'), | |
| 253 skimage_out_dir=self._step.skimage_out_dir, | |
| 254 skp_dir=self._step.skp_dir, | |
| 255 skp_perf_dir=self._step.perf_data_dir, | |
| 256 playback_actual_images_dir=self._step.playback_actual_images_dir, | |
| 257 playback_actual_summaries_dir=self._step.playback_actual_summaries_dir, | |
| 258 playback_expected_summaries_dir=( | |
| 259 self._step.playback_expected_summaries_dir), | |
| 260 tmp_dir=os.path.join(os.pardir, 'tmp')) | |
| OLD | NEW |