OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 """Updates the Chrome reference builds. | 7 """Updates the Chrome reference builds. |
8 | 8 |
9 To update Chromium (unofficial) reference build, use the -r option and give a | 9 To update a Chrome (official) build, use the -v option and give a Chrome |
10 Chromium SVN revision number, like 228977. To update a Chrome (official) build, | 10 version number like 30.0.1595.0. |
11 use the -v option and give a Chrome version number like 30.0.1595.0. | |
12 | |
13 If you're getting a Chrome build, you can give the flag --gs to fetch from | |
14 Google Storage. Otherwise, it will be fetched from go/chrome_official_builds. | |
15 | 11 |
16 Before running this script, you should first verify that you are authenticated | 12 Before running this script, you should first verify that you are authenticated |
17 for SVN. You can do this by running: | 13 for SVN. You can do this by running: |
qyearsley
2014/08/22 22:06:15
Will this work after flag day when Chromium SVN re
tonyg
2014/08/22 22:18:53
I'm not sure about that. I'll probably get rid of
| |
18 $ svn ls svn://svn.chromium.org/chrome/trunk/deps/reference_builds | 14 $ svn ls svn://svn.chromium.org/chrome/trunk/deps/reference_builds |
19 You may need to get your SVN password from https://chromium-access.appspot.com/. | 15 You may need to get your SVN password from https://chromium-access.appspot.com/. |
20 | 16 |
21 Usage: | 17 Usage: |
22 $ cd /tmp | 18 $ cd /tmp |
23 $ /path/to/update_reference_build.py --gs -v <version> | 19 $ /path/to/update_reference_build.py -v <version> |
24 $ cd reference_builds/reference_builds | 20 $ cd reference_builds/reference_builds |
25 $ gcl change | 21 $ gcl change |
26 $ gcl upload <change> | 22 $ gcl upload <change> |
27 $ gcl commit <change> | 23 $ gcl commit <change> |
28 """ | 24 """ |
29 | 25 |
30 import logging | 26 import logging |
31 import optparse | 27 import optparse |
32 import os | 28 import os |
33 import shutil | 29 import shutil |
34 import subprocess | 30 import subprocess |
35 import sys | 31 import sys |
36 import time | 32 import time |
37 import urllib | 33 import urllib |
38 import urllib2 | 34 import urllib2 |
39 import zipfile | 35 import zipfile |
40 | 36 |
41 # Example chromium build location: | |
42 # gs://chromium-browser-snapshots/Linux/228977/chrome-linux.zip | |
43 CHROMIUM_URL_FMT = ('http://commondatastorage.googleapis.com/' | |
44 'chromium-browser-snapshots/%s/%s/%s') | |
45 | |
46 # Chrome official build storage | |
47 # https://wiki.corp.google.com/twiki/bin/view/Main/ChromeOfficialBuilds | |
48 | |
49 # Internal Google archive of official Chrome builds, example: | |
50 # https://goto.google.com/chrome_official_builds/ | |
51 # 32.0.1677.0/precise32bit/chrome-precise32bit.zip | |
52 CHROME_INTERNAL_URL_FMT = ('http://master.chrome.corp.google.com/' | |
53 'official_builds/%s/%s/%s') | |
54 | 37 |
55 # Google storage location (no public web URL's), example: | 38 # Google storage location (no public web URL's), example: |
56 # gs://chrome-archive/30/30.0.1595.0/precise32bit/chrome-precise32bit.zip | 39 # gs://chrome-unsigned/desktop-*/30.0.1595.0/precise32/chrome-precise32.zip |
57 CHROME_GS_URL_FMT = ('gs://chrome-archive/%s/%s/%s/%s') | 40 CHROME_GS_URL_FMT = ('gs://chrome-unsigned/desktop-*/%s/%s/%s') |
58 | 41 |
59 | 42 |
60 class BuildUpdater(object): | 43 class BuildUpdater(object): |
61 _PLATFORM_FILES_MAP = { | 44 _CHROME_PLATFORM_FILES_MAP = { |
62 'Win': [ | 45 'Win': [ |
63 'chrome-win32.zip', | 46 'chrome-win.zip', |
64 'chrome-win32-syms.zip', | 47 'chrome-win32-syms.zip', |
65 ], | 48 ], |
66 'Mac': [ | 49 'Mac': [ |
67 'chrome-mac.zip', | 50 'chrome-mac.zip', |
68 ], | 51 ], |
69 'Linux': [ | 52 'Linux': [ |
70 'chrome-linux.zip', | 53 'chrome-precise32.zip', |
71 ], | 54 ], |
72 'Linux_x64': [ | 55 'Linux_x64': [ |
73 'chrome-linux.zip', | 56 'chrome-precise64.zip', |
74 ], | |
75 } | |
76 | |
77 _CHROME_PLATFORM_FILES_MAP = { | |
78 'Win': [ | |
79 'chrome-win32.zip', | |
80 'chrome-win32-syms.zip', | |
81 ], | |
82 'Mac': [ | |
83 'chrome-mac.zip', | |
84 ], | |
85 'Linux': [ | |
86 'chrome-precise32bit.zip', | |
87 ], | |
88 'Linux_x64': [ | |
89 'chrome-precise64bit.zip', | |
90 ], | 57 ], |
91 } | 58 } |
92 | 59 |
93 # Map of platform names to gs:// Chrome build names. | 60 # Map of platform names to gs:// Chrome build names. |
94 _BUILD_PLATFORM_MAP = { | 61 _BUILD_PLATFORM_MAP = { |
95 'Linux': 'precise32bit', | 62 'Linux': 'precise32', |
96 'Linux_x64': 'precise64bit', | 63 'Linux_x64': 'precise64', |
97 'Win': 'win', | 64 'Win': 'win', |
98 'Mac': 'mac', | 65 'Mac': 'mac', |
99 } | 66 } |
100 | 67 |
101 _PLATFORM_DEST_MAP = { | 68 _PLATFORM_DEST_MAP = { |
102 'Linux': 'chrome_linux', | 69 'Linux': 'chrome_linux', |
103 'Linux_x64': 'chrome_linux64', | 70 'Linux_x64': 'chrome_linux64', |
104 'Win': 'chrome_win', | 71 'Win': 'chrome_win', |
105 'Mac': 'chrome_mac', | 72 'Mac': 'chrome_mac', |
106 } | 73 } |
107 | 74 |
108 def __init__(self, options): | 75 def __init__(self, options): |
109 self._platforms = options.platforms.split(',') | 76 self._platforms = options.platforms.split(',') |
110 self._version_or_revision = options.version or int(options.revision) | 77 self._version = options.version |
111 self._use_official_version = bool(options.version) | |
112 self._use_gs = options.use_gs | |
113 | 78 |
114 @staticmethod | 79 @staticmethod |
115 def _GetCmdStatusAndOutput(args, cwd=None, shell=False): | 80 def _GetCmdStatusAndOutput(args, cwd=None, shell=False): |
116 """Executes a subprocess and returns its exit code and output. | 81 """Executes a subprocess and returns its exit code and output. |
117 | 82 |
118 Args: | 83 Args: |
119 args: A string or a sequence of program arguments. | 84 args: A string or a sequence of program arguments. |
120 cwd: If not None, the subprocess's current directory will be changed to | 85 cwd: If not None, the subprocess's current directory will be changed to |
121 |cwd| before it's executed. | 86 |cwd| before it's executed. |
122 shell: Whether to execute args as a shell command. | 87 shell: Whether to execute args as a shell command. |
123 | 88 |
124 Returns: | 89 Returns: |
125 The tuple (exit code, output). | 90 The tuple (exit code, output). |
126 """ | 91 """ |
127 logging.info(str(args) + ' ' + (cwd or '')) | 92 logging.info(str(args) + ' ' + (cwd or '')) |
128 p = subprocess.Popen(args=args, cwd=cwd, stdout=subprocess.PIPE, | 93 p = subprocess.Popen(args=args, cwd=cwd, stdout=subprocess.PIPE, |
129 stderr=subprocess.PIPE, shell=shell) | 94 stderr=subprocess.PIPE, shell=shell) |
130 stdout, stderr = p.communicate() | 95 stdout, stderr = p.communicate() |
131 exit_code = p.returncode | 96 exit_code = p.returncode |
132 if stderr: | 97 if stderr: |
133 logging.critical(stderr) | 98 logging.critical(stderr) |
134 logging.info(stdout) | 99 logging.info(stdout) |
135 return (exit_code, stdout) | 100 return (exit_code, stdout) |
136 | 101 |
137 def _GetBuildUrl(self, platform, version_or_revision, filename): | 102 def _GetBuildUrl(self, platform, version, filename): |
138 """Returns the URL for fetching one file. | 103 """Returns the URL for fetching one file. |
139 | 104 |
140 Args: | 105 Args: |
141 platform: Platform name, must be a key in |self._BUILD_PLATFORM_MAP|. | 106 platform: Platform name, must be a key in |self._BUILD_PLATFORM_MAP|. |
142 version_or_revision: Either an SVN revision, e.g. 234567, or a Chrome | 107 version: A Chrome version number, e.g. 30.0.1600.1. |
143 version number, e.g. 30.0.1600.1. | |
144 filename: Name of the file to fetch. | 108 filename: Name of the file to fetch. |
145 | 109 |
146 Returns: | 110 Returns: |
147 The URL for fetching a file. This may be a GS or HTTP URL. | 111 The URL for fetching a file. This may be a GS or HTTP URL. |
148 """ | 112 """ |
149 if self._use_official_version: | 113 return CHROME_GS_URL_FMT % ( |
150 # Chrome Google storage bucket. | 114 version, self._BUILD_PLATFORM_MAP[platform], filename) |
151 version = version_or_revision | |
152 if self._use_gs: | |
153 release = version[:version.find('.')] | |
154 return (CHROME_GS_URL_FMT % ( | |
155 release, | |
156 version, | |
157 self._BUILD_PLATFORM_MAP[platform], | |
158 filename)) | |
159 # Chrome internal archive. | |
160 return (CHROME_INTERNAL_URL_FMT % ( | |
161 version, | |
162 self._BUILD_PLATFORM_MAP[platform], | |
163 filename)) | |
164 # Chromium archive. | |
165 revision = version_or_revision | |
166 return CHROMIUM_URL_FMT % (urllib.quote_plus(platform), revision, filename) | |
167 | 115 |
168 def _FindBuildVersionOrRevision( | 116 def _FindBuildVersion(self, platform, version, filename): |
169 self, platform, version_or_revision, filename): | 117 """Searches for a version where a filename can be found. |
170 """Searches for a version or revision where a filename can be found. | |
171 | 118 |
172 Args: | 119 Args: |
173 platform: Platform name. | 120 platform: Platform name. |
174 version_or_revision: Either Chrome version or Chromium revision. | 121 version: A Chrome version number, e.g. 30.0.1600.1. |
175 filename: Filename to look for. | 122 filename: Filename to look for. |
176 | 123 |
177 Returns: | 124 Returns: |
178 A version or revision where the file could be found, or None. | 125 A version where the file could be found, or None. |
179 """ | 126 """ |
180 # TODO(shadi): Iterate over official versions to find a valid one. | 127 # TODO(shadi): Iterate over official versions to find a valid one. |
181 if self._use_official_version: | 128 return (version |
182 version = version_or_revision | 129 if self._DoesBuildExist(platform, version, filename) else None) |
183 return (version | |
184 if self._DoesBuildExist(platform, version, filename) else None) | |
185 | |
186 revision = version_or_revision | |
187 MAX_REVISIONS_PER_BUILD = 100 | |
188 for revision_guess in xrange(revision, revision + MAX_REVISIONS_PER_BUILD): | |
189 if self._DoesBuildExist(platform, revision_guess, filename): | |
190 return revision_guess | |
191 else: | |
192 time.sleep(.1) | |
193 return None | |
194 | 130 |
195 def _DoesBuildExist(self, platform, version, filename): | 131 def _DoesBuildExist(self, platform, version, filename): |
196 """Checks whether a file can be found for the given Chrome version. | 132 """Checks whether a file can be found for the given Chrome version. |
197 | 133 |
198 Args: | 134 Args: |
199 platform: Platform name. | 135 platform: Platform name. |
200 version: Chrome version number, e.g. 30.0.1600.1. | 136 version: Chrome version number, e.g. 30.0.1600.1. |
201 filename: Filename to look for. | 137 filename: Filename to look for. |
202 | 138 |
203 Returns: | 139 Returns: |
204 True if the file could be found, False otherwise. | 140 True if the file could be found, False otherwise. |
205 """ | 141 """ |
206 url = self._GetBuildUrl(platform, version, filename) | 142 url = self._GetBuildUrl(platform, version, filename) |
207 if self._use_gs: | 143 return self._DoesGSFileExist(url) |
208 return self._DoesGSFileExist(url) | |
209 | |
210 request = urllib2.Request(url) | |
211 request.get_method = lambda: 'HEAD' | |
212 try: | |
213 urllib2.urlopen(request) | |
214 return True | |
215 except urllib2.HTTPError, err: | |
216 if err.code == 404: | |
217 return False | |
218 | 144 |
219 def _DoesGSFileExist(self, gs_file_name): | 145 def _DoesGSFileExist(self, gs_file_name): |
220 """Returns True if the GS file can be found, False otherwise.""" | 146 """Returns True if the GS file can be found, False otherwise.""" |
221 exit_code = BuildUpdater._GetCmdStatusAndOutput( | 147 exit_code = BuildUpdater._GetCmdStatusAndOutput( |
222 ['gsutil', 'ls', gs_file_name])[0] | 148 ['gsutil', 'ls', gs_file_name])[0] |
223 return not exit_code | 149 return not exit_code |
224 | 150 |
225 def _GetPlatformFiles(self, platform): | 151 def _GetPlatformFiles(self, platform): |
226 """Returns a list of filenames to fetch for the given platform.""" | 152 """Returns a list of filenames to fetch for the given platform.""" |
227 if self._use_official_version: | 153 return BuildUpdater._CHROME_PLATFORM_FILES_MAP[platform] |
228 return BuildUpdater._CHROME_PLATFORM_FILES_MAP[platform] | |
229 return BuildUpdater._PLATFORM_FILES_MAP[platform] | |
230 | 154 |
231 def _DownloadBuilds(self): | 155 def _DownloadBuilds(self): |
232 for platform in self._platforms: | 156 for platform in self._platforms: |
233 for filename in self._GetPlatformFiles(platform): | 157 for filename in self._GetPlatformFiles(platform): |
234 output = os.path.join('dl', platform, | 158 output = os.path.join('dl', platform, |
235 '%s_%s_%s' % (platform, | 159 '%s_%s_%s' % (platform, |
236 self._version_or_revision, | 160 self._version, |
237 filename)) | 161 filename)) |
238 if os.path.exists(output): | 162 if os.path.exists(output): |
239 logging.info('%s alread exists, skipping download', output) | 163 logging.info('%s alread exists, skipping download', output) |
240 continue | 164 continue |
241 version_or_revision = self._FindBuildVersionOrRevision( | 165 version = self._FindBuildVersion(platform, self._version, filename) |
242 platform, self._version_or_revision, filename) | 166 if not version: |
243 if not version_or_revision: | |
244 logging.critical('Failed to find %s build for r%s\n', platform, | 167 logging.critical('Failed to find %s build for r%s\n', platform, |
245 self._version_or_revision) | 168 self._version) |
246 sys.exit(1) | 169 sys.exit(1) |
247 dirname = os.path.dirname(output) | 170 dirname = os.path.dirname(output) |
248 if dirname and not os.path.exists(dirname): | 171 if dirname and not os.path.exists(dirname): |
249 os.makedirs(dirname) | 172 os.makedirs(dirname) |
250 url = self._GetBuildUrl(platform, version_or_revision, filename) | 173 url = self._GetBuildUrl(platform, version, filename) |
251 self._DownloadFile(url, output) | 174 self._DownloadFile(url, output) |
252 | 175 |
253 def _DownloadFile(self, url, output): | 176 def _DownloadFile(self, url, output): |
254 logging.info('Downloading %s, saving to %s', url, output) | 177 logging.info('Downloading %s, saving to %s', url, output) |
255 if self._use_official_version and self._use_gs: | 178 BuildUpdater._GetCmdStatusAndOutput(['gsutil', 'cp', url, output]) |
256 BuildUpdater._GetCmdStatusAndOutput(['gsutil', 'cp', url, output]) | |
257 else: | |
258 response = urllib2.urlopen(url) | |
259 with file(output, 'wb') as f: | |
260 f.write(response.read()) | |
261 | 179 |
262 def _FetchSvnRepos(self): | 180 def _FetchSvnRepos(self): |
263 if not os.path.exists('reference_builds'): | 181 if not os.path.exists('reference_builds'): |
264 os.makedirs('reference_builds') | 182 os.makedirs('reference_builds') |
265 BuildUpdater._GetCmdStatusAndOutput( | 183 BuildUpdater._GetCmdStatusAndOutput( |
266 ['gclient', 'config', | 184 ['gclient', 'config', |
267 'svn://svn.chromium.org/chrome/trunk/deps/reference_builds'], | 185 'svn://svn.chromium.org/chrome/trunk/deps/reference_builds'], |
268 'reference_builds') | 186 'reference_builds') |
269 BuildUpdater._GetCmdStatusAndOutput( | 187 BuildUpdater._GetCmdStatusAndOutput( |
270 ['gclient', 'sync'], 'reference_builds') | 188 ['gclient', 'sync'], 'reference_builds') |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
347 | 265 |
348 def DownloadAndUpdateBuilds(self): | 266 def DownloadAndUpdateBuilds(self): |
349 self._DownloadBuilds() | 267 self._DownloadBuilds() |
350 self._FetchSvnRepos() | 268 self._FetchSvnRepos() |
351 self._ExtractBuilds() | 269 self._ExtractBuilds() |
352 self._SvnAddAndRemove() | 270 self._SvnAddAndRemove() |
353 | 271 |
354 | 272 |
355 def ParseOptions(argv): | 273 def ParseOptions(argv): |
356 parser = optparse.OptionParser() | 274 parser = optparse.OptionParser() |
357 usage = 'usage: %prog <options>' | 275 usage = 'Usage: %prog -v version [-p platforms]' |
358 parser.set_usage(usage) | 276 parser.set_usage(usage) |
359 parser.add_option('-v', dest='version', | 277 parser.add_option('-v', dest='version', |
360 help='Chrome official version to pick up ' | 278 help='Chrome official version to pick up ' |
361 '(e.g. 30.0.1600.1).') | 279 '(e.g. 30.0.1600.1).') |
362 parser.add_option('--gs', dest='use_gs', action='store_true', default=False, | |
363 help='Use Google storage for official builds. Used with -b ' | |
364 'option. Default is false (i.e. use internal storage.') | |
365 parser.add_option('-p', dest='platforms', | 280 parser.add_option('-p', dest='platforms', |
366 default='Win,Mac,Linux,Linux_x64', | 281 default='Win,Mac,Linux,Linux_x64', |
367 help='Comma separated list of platforms to download ' | 282 help='Comma separated list of platforms to download ' |
368 '(as defined by the chromium builders).') | 283 '(as defined by the chromium builders).') |
369 parser.add_option('-r', dest='revision', | |
370 help='Chromium revision to pick up (e.g. 234567).') | |
371 | 284 |
372 (options, _) = parser.parse_args(argv) | 285 (options, _) = parser.parse_args(argv) |
373 if not options.revision and not options.version: | 286 if not options.version: |
374 logging.critical('Must specify either -r or -v.\n') | 287 logging.critical('Must specify -v version.\n') |
qyearsley
2014/08/22 22:06:15
Note: Since version is not actually optional anymo
tonyg
2014/08/22 22:18:53
Done.
| |
375 sys.exit(1) | |
376 if options.revision and options.version: | |
377 logging.critical('Must specify either -r or -v but not both.\n') | |
378 sys.exit(1) | |
379 if options.use_gs and not options.version: | |
380 logging.critical('Can only use --gs with -v option.\n') | |
381 sys.exit(1) | 288 sys.exit(1) |
382 | 289 |
383 return options | 290 return options |
384 | 291 |
385 | 292 |
386 def main(argv): | 293 def main(argv): |
387 logging.getLogger().setLevel(logging.DEBUG) | 294 logging.getLogger().setLevel(logging.DEBUG) |
388 options = ParseOptions(argv) | 295 options = ParseOptions(argv) |
389 b = BuildUpdater(options) | 296 b = BuildUpdater(options) |
390 b.DownloadAndUpdateBuilds() | 297 b.DownloadAndUpdateBuilds() |
391 logging.info('Successfully updated reference builds. Move to ' | 298 logging.info('Successfully updated reference builds. Move to ' |
392 'reference_builds/reference_builds and make a change with gcl.') | 299 'reference_builds/reference_builds and make a change with gcl.') |
393 | 300 |
394 if __name__ == '__main__': | 301 if __name__ == '__main__': |
395 sys.exit(main(sys.argv)) | 302 sys.exit(main(sys.argv)) |
OLD | NEW |