OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # | |
3 # Copyright (c) 2014 The Chromium Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 """Gets a Chromium archived build, and unpacks it | |
8 into a target directory. | |
9 | |
10 Use -r option to specify the revison number | |
11 Use -t option to specify the directory to unzip the build into. | |
12 | |
13 Usage: | |
14 $ /path/to/get_chromium_build.py -r <revision> -t <target> | |
15 """ | |
16 | |
17 import logging | |
18 import optparse | |
19 import os | |
20 import platform | |
21 import shutil | |
22 import subprocess | |
23 import sys | |
24 import time | |
25 import urllib | |
26 import urllib2 | |
27 import zipfile | |
28 | |
29 # Example chromium build location: | |
30 # gs://chromium-browser-snapshots/Linux_x64/228977/chrome-linux.zip | |
31 CHROMIUM_URL_FMT = ('http://commondatastorage.googleapis.com/' | |
32 'chromium-browser-snapshots/%s/%s/%s') | |
33 | |
34 class BuildUpdater(object): | |
35 _PLATFORM_PATHS_MAP = { | |
36 'Linux': { 'zipfiles': ['chrome-linux.zip'], | |
37 'folder': 'chrome_linux', | |
38 'archive_path': 'Linux_x64'}, | |
39 'Darwin': {'zipfiles': ['chrome-mac.zip'], | |
40 'folder': 'chrome_mac', | |
41 'archive_path': 'Mac'}, | |
42 'Windows': {'zipfiles': ['chrome-win32.zip', | |
43 'chrome-win32-syms.zip'], | |
44 'folder': 'chrome_win', | |
45 'archive_path': 'Win'}} | |
46 | |
47 def __init__(self, options): | |
48 platform_data = BuildUpdater._PLATFORM_PATHS_MAP[platform.system()] | |
49 self._zipfiles = platform_data['zipfiles'] | |
50 self._folder = platform_data['folder'] | |
51 self._archive_path = platform_data['archive_path'] | |
52 self._revision = int(options.revision) | |
53 self._target_dir = options.target_dir | |
54 self._download_dir = os.path.join(self._target_dir, 'downloads') | |
55 | |
56 def _GetBuildUrl(self, revision, filename): | |
57 return CHROMIUM_URL_FMT % (self._archive_path, revision, filename) | |
58 | |
59 def _FindBuildRevision(self, revision, filename): | |
60 MAX_REVISIONS_PER_BUILD = 100 | |
61 for revision_guess in xrange(revision, revision + MAX_REVISIONS_PER_BUILD): | |
62 if self._DoesBuildExist(revision_guess, filename): | |
63 return revision_guess | |
64 else: | |
65 time.sleep(.1) | |
66 return None | |
67 | |
68 def _DoesBuildExist(self, revision_guess, filename): | |
69 url = self._GetBuildUrl(revision_guess, filename) | |
70 | |
71 r = urllib2.Request(url) | |
72 r.get_method = lambda: 'HEAD' | |
73 try: | |
74 urllib2.urlopen(r) | |
75 return True | |
76 except urllib2.HTTPError, err: | |
77 if err.code == 404: | |
78 return False | |
79 | |
80 def _DownloadBuild(self): | |
81 if not os.path.exists(self._download_dir): | |
82 os.makedirs(self._download_dir) | |
83 for zipfile in self._zipfiles: | |
84 build_revision = self._FindBuildRevision(self._revision, zipfile) | |
85 if not build_revision: | |
86 logging.critical('Failed to find %s build for r%s\n', | |
87 self._archive_path, | |
88 self._revision) | |
89 sys.exit(1) | |
90 url = self._GetBuildUrl(build_revision, zipfile) | |
91 logging.info('Downloading %s', url) | |
92 r = urllib2.urlopen(url) | |
93 with file(os.path.join(self._download_dir, zipfile), 'wb') as f: | |
94 f.write(r.read()) | |
95 | |
96 def _UnzipFile(self, dl_file, dest_dir): | |
97 if not zipfile.is_zipfile(dl_file): | |
98 return False | |
99 logging.info('Unzipping %s', dl_file) | |
100 with zipfile.ZipFile(dl_file, 'r') as z: | |
101 for content in z.namelist(): | |
102 dest = os.path.join(dest_dir, content[content.find('/')+1:]) | |
103 # Create dest parent dir if it does not exist. | |
104 if not os.path.isdir(os.path.dirname(dest)): | |
105 logging.info('Making %s', dest) | |
106 os.makedirs(os.path.dirname(dest)) | |
107 # If dest is just a dir listing, do nothing. | |
108 if not os.path.basename(dest): | |
109 continue | |
110 with z.open(content) as unzipped_content: | |
111 logging.info('Extracting %s to %s (%s)', content, dest, dl_file) | |
112 with file(dest, 'wb') as dest_file: | |
113 dest_file.write(unzipped_content.read()) | |
114 permissions = z.getinfo(content).external_attr >> 16 | |
115 if permissions: | |
116 os.chmod(dest, permissions) | |
117 return True | |
118 | |
119 def _ClearDir(self, dir): | |
120 """Clears all files in |dir| except for hidden files and folders.""" | |
121 for root, dirs, files in os.walk(dir): | |
122 # Skip hidden files and folders (like .svn and .git). | |
123 files = [f for f in files if f[0] != '.'] | |
124 dirs[:] = [d for d in dirs if d[0] != '.'] | |
125 | |
126 for f in files: | |
127 os.remove(os.path.join(root, f)) | |
128 | |
129 def _ExtractBuild(self): | |
130 dest_dir = os.path.join(self._target_dir, self._folder) | |
131 self._ClearDir(dest_dir) | |
132 for root, _, dl_files in os.walk(os.path.join(self._download_dir)): | |
133 for dl_file in dl_files: | |
134 dl_file = os.path.join(root, dl_file) | |
135 if not self._UnzipFile(dl_file, dest_dir): | |
136 logging.info('Copying %s to %s', dl_file, dest_dir) | |
137 shutil.copy(dl_file, dest_dir) | |
138 shutil.rmtree(self._download_dir) | |
139 | |
140 def DownloadAndUpdateBuild(self): | |
141 self._DownloadBuild() | |
142 self._ExtractBuild() | |
143 | |
144 | |
145 def ParseOptions(argv): | |
146 parser = optparse.OptionParser() | |
147 usage = 'usage: %prog <options>' | |
148 parser.set_usage(usage) | |
149 parser.add_option('-r', dest='revision', | |
150 help='Revision to download.') | |
151 parser.add_option('-t', dest='target_dir', | |
152 help='Target directory for unzipped Chromium.') | |
153 | |
154 (options, _) = parser.parse_args(argv) | |
155 if not options.revision: | |
156 logging.critical('Must specify -r.\n') | |
157 sys.exit(1) | |
158 if not options.target_dir: | |
159 logging.critical('Must specify -t.\n') | |
160 sys.exit(1) | |
161 return options | |
162 | |
163 def main(argv): | |
164 logging.getLogger().setLevel(logging.DEBUG) | |
165 options = ParseOptions(argv) | |
166 b = BuildUpdater(options) | |
167 b.DownloadAndUpdateBuild() | |
168 logging.info('Successfully got archived Chromium build.') | |
169 | |
170 if __name__ == '__main__': | |
171 sys.exit(main(sys.argv)) | |
OLD | NEW |