Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Side by Side Diff: tools/auto_bisect/fetch_build.py

Issue 548233002: Add a module to fetch builds from different types of builders. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/auto_bisect/cloud_storage_wrapper.py ('k') | tools/auto_bisect/fetch_build_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 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 """This module contains functions for fetching and extracting archived builds.
6
7 The builds may be stored in different places by different types builders;
8 for example, builders on tryserver.chromium.perf stores builds in one place,
9 while builders on chromium.linux store builds in another.
10
11 This module can be either imported or run as a stand-alone script to download
12 and extract a build.
13
14 Usage: fetch_build.py <type> <revision> <output_dir> [options]
15 """
16
17 import argparse
18 import errno
19 import os
20 import sys
21 import zipfile
22
23 import bisect_utils
24 import cloud_storage_wrapper as cloud_storage
25
26 # Possible builder types.
27 PERF_BUILDER = 'perf'
28 FULL_BUILDER = 'full'
29
30
31 def FetchBuild(builder_type, revision, output_dir, target_arch='x64',
32 target_platform='chromium', deps_patch_sha=None):
33 """Downloads and extracts a build for a particular revision.
34
35 If the build is successfully downloaded and extracted to |output_dir|, the
36 downloaded archive file is also deleted.
37
38 Args:
39 revision: Revision string, e.g. a git commit hash.
prasadv 2014/09/11 17:04:10 Before Git migration builds were stored as SVN rev
qyearsley 2014/09/11 23:05:43 Right, comment updated.
40 builder_type: Type of build archive.
41 target_arch: Architecture, e.g. "ia32".
42 target_platform: Platform name, e.g. "chromium" or "android".
43 deps_patch_sha: SHA1 hash of a DEPS file, if we want to fetch a build for
44 a Chromium revision with custom dependencies.
45
46 Returns:
47 The directory path where the build has been extracted to.
prasadv 2014/09/11 17:04:10 What will this function return in case of any fail
qyearsley 2014/09/11 23:05:43 It could return None, but another approach would b
48 """
49 build_archive = BuildArchive.Create(
50 builder_type, target_arch=target_arch, target_platform=target_platform)
51 bucket = build_archive.BucketName()
52 remote_path = build_archive.FilePath(revision, deps_patch_sha=deps_patch_sha)
53 filename = FetchFromCloudStorage(bucket, remote_path, output_dir)
prasadv 2014/09/11 17:04:10 Shouldn't we validate return values from FetchFrom
qyearsley 2014/09/11 23:05:43 Good point. In the case of Unzip, it may raise var
54 Unzip(filename, output_dir)
prasadv 2014/09/11 17:04:10 This function should return directory path right?
qyearsley 2014/09/11 23:05:43 That's what I was thinking, but the directory path
55
56
57 class BuildArchive(object):
58 """Represents a place where builds of some type are stored.
59
60 Subclasses of this class contain specific logic about which directory names
61 and file names should be used when fetching builds.
62 """
63
64 @staticmethod
65 def Create(builder_type, target_arch='x64', target_platform='chromium'):
66 """Creates a BuildArchive instance for fetching builds of some type."""
67 if builder_type == PERF_BUILDER:
68 return PerfBuildArchive(target_arch, target_platform)
69 if builder_type == FULL_BUILDER:
70 return FullBuildArchive(target_arch, target_platform)
71 raise NotImplementedError('Builder type "%s" not supported.' % builder_type)
72
73 def __init__(self, target_arch='x86', target_platform='chromium'):
74 self._target_arch = target_arch
75 self._target_platform = target_platform
76
77 def BucketName(self):
78 raise NotImplementedError()
79
80 def FilePath(self, revision, deps_patch_sha=None):
81 raise NotImplementedError()
82
83 def _ZipFileName(self, revision, deps_patch_sha=None):
84 """Gets the file name of a zip archive for a particular revision.
85
86 This returns a file name of the form full-build-<platform>_<revision>.zip,
87 which is a format used by multiple types of builders that store archives.
88
89 Args:
90 revision: A git commit hash or other revision string.
91 deps_patch_sha: SHA1 hash of a DEPS file patch.
92
93 Returns:
94 The archive file name.
95 """
96 base_name = 'full-build-%s' % self._PlatformName()
97 if deps_patch_sha:
98 revision = '%s_%s' % (revision , deps_patch_sha)
99 return '%s_%s.zip' % (base_name, revision)
100
101 @staticmethod
102 def _PlatformName():
103 """Return a string to be used in paths for the platform."""
104 if bisect_utils.IsWindowsHost() or bisect_utils.Is64BitWindows():
105 # Build archive for x64 is still stored with "win32" in the name.
106 return 'win32'
107 if bisect_utils.IsLinuxHost():
108 # Android builds are also stored with "linux" in the name.
109 return 'linux'
110 if bisect_utils.IsMacHost():
111 return 'mac'
112 raise NotImplementedError('Unknown platform "%s".' % sys.platform)
113
114
115 class PerfBuildArchive(BuildArchive):
116 """Provides information about where perf builds are stored."""
117
118 def BucketName(self):
119 return 'chrome-perf'
120
121 def FilePath(self, revision, deps_patch_sha=None):
122 return '%s/%s' % (self._Directory(),
123 self._ZipFileName(revision, deps_patch_sha))
124
125 def _Directory(self):
126 """Returns the root folder name to download from for perf builds."""
127 if bisect_utils.Is64BitWindows() and self._target_arch == 'x64':
128 return 'Win x64 Builder'
129 if bisect_utils.IsWindowsHost():
130 return 'Win Builder'
131 if bisect_utils.IsLinuxHost() and self._target_platform == 'android':
132 return 'android_perf_rel'
133 if bisect_utils.IsLinuxHost():
134 return 'Linux Builder'
135 if bisect_utils.IsMacHost():
136 return 'Mac Builder'
137 raise NotImplementedError('Unsupported Platform: "%s".' % sys.platform)
138
139
140 class FullBuildArchive(BuildArchive):
141 """Provides information about where perf builds are stored."""
142
143 def BucketName(self):
144 if bisect_utils.IsWindowsHost() or bisect_utils.Is64BitWindows():
145 return 'chromium-win-archive'
146 if bisect_utils.IsLinuxHost() and self._target_platform == 'android':
147 return 'chromium-android'
148 if bisect_utils.IsLinuxHost():
149 return 'chromium-linux-archive'
150 if bisect_utils.IsMacHost():
151 return 'chromium-mac-archive'
152 raise NotImplementedError('Unknown platform "%s".' % sys.platform)
153
154 def FilePath(self, revision, deps_patch_sha=None):
155 return '%s/%s' % (self._Directory(),
156 self._ZipFileName(revision, deps_patch_sha))
157
158 def _Directory(self):
prasadv 2014/09/11 17:04:10 s/_Directory/_ArchiveDirectory?
qyearsley 2014/09/11 23:05:43 Done.
159 if bisect_utils.Is64BitWindows() and self._target_arch == 'x64':
160 return 'chromium.win/Win x64 Builder'
161 if bisect_utils.IsWindowsHost():
162 return 'chromium.win/Win Builder'
163 if bisect_utils.IsLinuxHost() and self._target_platform == 'android':
164 return 'android_main_rel'
165 if bisect_utils.IsLinuxHost():
166 return 'chromium.linux/Linux Builder'
167 if bisect_utils.Mac():
168 return 'chromium.mac/Mac Builder'
169 raise NotImplementedError('Unknown platform "%s".' % sys.platform)
170
171
172 def FetchFromCloudStorage(bucket_name, source_path, destination_dir):
173 """Fetches file(s) from the Google Cloud Storage.
174
175 As a side-effect, this prints messages to stdout about what's happening.
176
177 Args:
178 bucket_name: Google Storage bucket name.
179 source_path: Source file path.
180 destination_dir: Destination file path.
181
182 Returns:
183 Downloaded file path if exists, otherwise None.
184 """
185 target_file = os.path.join(destination_dir, os.path.basename(source_path))
186 gs_url = 'gs://%s/%s' % (bucket_name, source_path)
187 try:
188 if cloud_storage.Exists(bucket_name, source_path):
189 print 'Fetching file from %s...' % gs_url
190 cloud_storage.Get(bucket_name, source_path, target_file)
191 if os.path.exists(target_file):
192 return target_file
193 else:
194 print 'File %s not found in cloud storage.' % gs_url
195 except Exception as e:
196 print 'Exception while fetching from cloud storage: %s' % e
197 if os.path.exists(target_file):
198 os.remove(target_file)
199 return None
200
201
202 def Unzip(filename, output_dir, verbose=True):
203 """Extracts a zip archive's contents into the given output directory.
204
205 This was based on ExtractZip from build/scripts/common/chromium_utils.py.
206
207 Args:
208 filename: Name of the zip file to extract.
209 output_dir: Path to the destination directory.
210 verbose: Whether to print out what is being extracted.
211
212 Raises:
213 IOError: The unzip command had a non-zero exit code.
214 RuntimeError: Failed to create the output directory.
215 """
216 _MakeDirectory(output_dir)
217
218 # On Linux and Mac, we use the unzip command because it handles links and
219 # file permissions bits, so achieving this behavior is easier than with
220 # ZipInfo options.
221 #
222 # The Mac Version of unzip unfortunately does not support Zip64, whereas
223 # the python module does, so we have to fall back to the python zip module
224 # on Mac if the file size is greater than 4GB.
225 mac_zip_size_limit = 2 ** 32 # 4GB
226 if (bisect_utils.IsLinuxHost() or
227 (bisect_utils.IsMacHost()
228 and os.path.getsize(filename) < mac_zip_size_limit)):
229 unzip_command = ['unzip', '-o']
230 _UnzipUsingCommand(unzip_command, filename, output_dir)
231 return
232
233 # On Windows, try to use 7z if it is installed, otherwise fall back to the
234 # Python zipfile module. If 7z is not installed, then this may fail if the
235 # zip file is larger than 512MB.
236 sevenzip_path = r'C:\Program Files\7-Zip\7z.exe'
237 if (bisect_utils.IsWindowsHost() and os.path.exists(sevenzip_path)):
238 unzip_command = [sevenzip_path, 'x', '-y']
239 _UnzipUsingCommand(unzip_command, filename, output_dir)
240 return
241
242 _UnzipUsingZipFile(filename, output_dir, verbose)
243
244
245 def _MakeDirectory(path):
246 """Creates a directory if it doesn't already exist."""
247 try:
248 os.makedirs(path)
249 except OSError as e:
250 # Reference: https://docs.python.org/2/library/errno.html
251 if e.errno != errno.EEXIST:
252 raise RuntimeError('Failed to make directory: %s' % path)
253
254
255 def _UnzipUsingCommand(unzip_command, filename, output_dir):
256 """Extracts a zip file using an external command.
257
258 Args:
259 unzip_command: An unzipping command, as a string list, without the filename.
260 filename: Path to the zip file.
261 output_dir: The directory which the contents should be extracted to.
262
263 Raises:
264 IOError: The command had a non-zero exit code.
265 """
266 absolute_filepath = os.path.abspath(filename)
267 command = unzip_command + [absolute_filepath]
268 return_code = _RunCommandInDirectory(output_dir, command)
269 if return_code:
270 raise IOError('Unzip failed: %s => %s' % (str(command), return_code))
271
272
273 def _RunCommandInDirectory(directory, command):
274 """Changes to a directory, runs a command, then changes back."""
275 saved_dir = os.getcwd()
276 os.chdir(directory)
277 return_code = bisect_utils.RunProcess(command)
278 os.chdir(saved_dir)
279 return return_code
280
281
282 def _UnzipUsingZipFile(filename, output_dir, verbose=True):
283 """Extracts a zip file using the Python zipfile module."""
284 assert bisect_utils.IsWindowsHost() or bisect_utils.IsMacHost()
285 zf = zipfile.ZipFile(filename)
286 for name in zf.namelist():
287 if verbose:
288 print 'Extracting %s' % name
289 zf.extract(name, output_dir)
290 if bisect_utils.IsMacHost():
291 # Restore file permission bits.
292 mode = zf.getinfo(name).external_attr >> 16
293 os.chmod(os.path.join(output_dir, name), mode)
294
295
296 def Main(argv):
297 """Downloads and extracts a build based on the command line arguments."""
298 parser = argparse.ArgumentParser()
299 parser.add_argument('builder_type')
300 parser.add_argument('revision')
301 parser.add_argument('output_dir')
302 parser.add_argument('--target-arch', default='x64')
prasadv 2014/09/11 17:04:10 I think default should be ia32.
qyearsley 2014/09/11 23:05:43 Done.
303 parser.add_argument('--target-platform', default='chromium')
304 parser.add_argument('--deps-patch-sha')
305 args = parser.parse_args(argv[1:])
306
307 FetchBuild(
308 args.builder_type, args.revision, args.output_dir,
309 target_arch=args.target_arch, target_platform=args.target_platform,
310 deps_patch_sha=args.deps_patch_sha)
311
312 print 'Build has been downloaded to and extracted in %s.' % args.output_dir
313
314 return 0
315
316
317 if __name__ == '__main__':
318 sys.exit(Main(sys.argv))
319
OLDNEW
« no previous file with comments | « tools/auto_bisect/cloud_storage_wrapper.py ('k') | tools/auto_bisect/fetch_build_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698