OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2013 The Chromium Authors. All rights reserved. | 2 # Copyright 2013 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Downloads and unpacks a toolchain for building on Windows. The contents are | 6 """Downloads and unpacks a toolchain for building on Windows. The contents are |
7 matched by sha1 which will be updated when the toolchain is updated. | 7 matched by sha1 which will be updated when the toolchain is updated. |
8 | 8 |
9 Having a toolchain script in depot_tools means that it's not versioned | 9 Having a toolchain script in depot_tools means that it's not versioned |
10 directly with the source code. That is, if the toolchain is upgraded, but | 10 directly with the source code. That is, if the toolchain is upgraded, but |
(...skipping 15 matching lines...) Expand all Loading... |
26 maintained, and a new 2015 script would be added. | 26 maintained, and a new 2015 script would be added. |
27 """ | 27 """ |
28 | 28 |
29 import hashlib | 29 import hashlib |
30 import json | 30 import json |
31 import optparse | 31 import optparse |
32 import os | 32 import os |
33 import shutil | 33 import shutil |
34 import subprocess | 34 import subprocess |
35 import sys | 35 import sys |
| 36 import tempfile |
36 import time | 37 import time |
| 38 import zipfile |
37 | 39 |
38 | 40 |
39 BASEDIR = os.path.dirname(os.path.abspath(__file__)) | 41 BASEDIR = os.path.dirname(os.path.abspath(__file__)) |
40 DEPOT_TOOLS_PATH = os.path.join(BASEDIR, '..') | 42 DEPOT_TOOLS_PATH = os.path.join(BASEDIR, '..') |
41 sys.path.append(DEPOT_TOOLS_PATH) | 43 sys.path.append(DEPOT_TOOLS_PATH) |
42 import download_from_google_storage | 44 try: |
| 45 import download_from_google_storage |
| 46 except ImportError: |
| 47 # Allow use of utility functions in this script from package_from_installed |
| 48 # on bare VM that doesn't have a full depot_tools. |
| 49 pass |
43 | 50 |
44 if sys.platform != 'cygwin': | 51 if sys.platform != 'cygwin': |
45 import ctypes.wintypes | 52 import ctypes.wintypes |
46 GetFileAttributes = ctypes.windll.kernel32.GetFileAttributesW | 53 GetFileAttributes = ctypes.windll.kernel32.GetFileAttributesW |
47 GetFileAttributes.argtypes = (ctypes.wintypes.LPWSTR,) | 54 GetFileAttributes.argtypes = (ctypes.wintypes.LPWSTR,) |
48 GetFileAttributes.restype = ctypes.wintypes.DWORD | 55 GetFileAttributes.restype = ctypes.wintypes.DWORD |
49 FILE_ATTRIBUTE_HIDDEN = 0x2 | 56 FILE_ATTRIBUTE_HIDDEN = 0x2 |
50 FILE_ATTRIBUTE_SYSTEM = 0x4 | 57 FILE_ATTRIBUTE_SYSTEM = 0x4 |
51 | 58 |
52 | 59 |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 if (os.path.isdir(target_dir) and | 186 if (os.path.isdir(target_dir) and |
180 not bool(int(os.environ.get('CHROME_HEADLESS', '0')))): | 187 not bool(int(os.environ.get('CHROME_HEADLESS', '0')))): |
181 for i in range(9, 0, -1): | 188 for i in range(9, 0, -1): |
182 sys.stdout.write( | 189 sys.stdout.write( |
183 '\rRemoving old toolchain in %ds... (Ctrl-C to cancel)' % i) | 190 '\rRemoving old toolchain in %ds... (Ctrl-C to cancel)' % i) |
184 sys.stdout.flush() | 191 sys.stdout.flush() |
185 time.sleep(1) | 192 time.sleep(1) |
186 print | 193 print |
187 | 194 |
188 | 195 |
| 196 def DownloadUsingGsutil(filename): |
| 197 """Downloads the given file from Google Storage chrome-wintoolchain bucket.""" |
| 198 temp_dir = tempfile.mkdtemp() |
| 199 assert os.path.basename(filename) == filename |
| 200 target_path = os.path.join(temp_dir, filename) |
| 201 gsutil = download_from_google_storage.Gsutil( |
| 202 download_from_google_storage.GSUTIL_DEFAULT_PATH, boto_path=None) |
| 203 code = gsutil.call('cp', 'gs://chrome-wintoolchain/' + filename, target_path) |
| 204 if code != 0: |
| 205 sys.exit('gsutil failed') |
| 206 return temp_dir, target_path |
| 207 |
| 208 |
| 209 def DoTreeMirror(target_dir, tree_sha1): |
| 210 """In order to save temporary space on bots that do not have enough space to |
| 211 download ISOs, unpack them, and copy to the target location, the whole tree |
| 212 is uploaded as a zip to internal storage, and then mirrored here.""" |
| 213 temp_dir, local_zip = DownloadUsingGsutil(tree_sha1 + '.zip') |
| 214 sys.stdout.write('Extracting %s...\n' % local_zip) |
| 215 sys.stdout.flush() |
| 216 with zipfile.ZipFile(local_zip, 'r', zipfile.ZIP_DEFLATED, True) as zf: |
| 217 zf.extractall(target_dir) |
| 218 if temp_dir: |
| 219 subprocess.check_call('rmdir /s/q "%s"' % temp_dir, shell=True) |
| 220 |
| 221 |
189 def main(): | 222 def main(): |
190 if not sys.platform.startswith(('cygwin', 'win32')): | 223 if not sys.platform.startswith(('cygwin', 'win32')): |
191 return 0 | 224 return 0 |
192 | 225 |
193 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) | 226 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) |
194 parser.add_option('--output-json', metavar='FILE', | 227 parser.add_option('--output-json', metavar='FILE', |
195 help='write information about toolchain to FILE') | 228 help='write information about toolchain to FILE') |
196 options, args = parser.parse_args() | 229 options, args = parser.parse_args() |
197 | 230 |
198 if sys.platform == 'cygwin': | 231 if sys.platform == 'cygwin': |
199 # This script requires Windows Python, so invoke with depot_tools' Python. | 232 # This script requires Windows Python, so invoke with depot_tools' Python. |
200 def winpath(path): | 233 def winpath(path): |
201 return subprocess.check_output(['cygpath', '-w', path]).strip() | 234 return subprocess.check_output(['cygpath', '-w', path]).strip() |
202 python = os.path.join(DEPOT_TOOLS_PATH, 'python.bat') | 235 python = os.path.join(DEPOT_TOOLS_PATH, 'python.bat') |
203 cmd = [python, winpath(__file__)] | 236 cmd = [python, winpath(__file__)] |
204 if options.output_json: | 237 if options.output_json: |
205 cmd.extend(['--output-json', winpath(options.output_json)]) | 238 cmd.extend(['--output-json', winpath(options.output_json)]) |
206 cmd.extend(args) | 239 cmd.extend(args) |
207 sys.exit(subprocess.call(cmd)) | 240 sys.exit(subprocess.call(cmd)) |
208 | 241 |
209 # We assume that the Pro hash is the first one. | 242 # We assume that the Pro hash is the first one. |
210 desired_hashes = args | 243 desired_hashes = args |
211 if len(desired_hashes) == 0: | 244 if len(desired_hashes) == 0: |
212 sys.exit('Desired hashes are required.') | 245 sys.exit('Desired hashes are required.') |
213 | 246 |
214 # Move to depot_tools\win_toolchain where we'll store our files, and where | 247 # Move to depot_tools\win_toolchain where we'll store our files, and where |
215 # the downloader script is. | 248 # the downloader script is. |
216 os.chdir(os.path.normpath(os.path.join(BASEDIR))) | 249 os.chdir(os.path.normpath(os.path.join(BASEDIR))) |
217 toolchain_dir = '.' | 250 toolchain_dir = '.' |
218 target_dir = os.path.normpath(os.path.join(toolchain_dir, 'vs2013_files')) | 251 if os.environ.get('GYP_MSVS_VERSION') == '2015': |
| 252 target_dir = os.path.normpath(os.path.join(toolchain_dir, 'vs_files')) |
| 253 else: |
| 254 target_dir = os.path.normpath(os.path.join(toolchain_dir, 'vs2013_files')) |
| 255 abs_target_dir = os.path.abspath(target_dir) |
| 256 |
| 257 got_new_toolchain = False |
219 | 258 |
220 # If the current hash doesn't match what we want in the file, nuke and pave. | 259 # If the current hash doesn't match what we want in the file, nuke and pave. |
221 # Typically this script is only run when the .sha1 one file is updated, but | 260 # Typically this script is only run when the .sha1 one file is updated, but |
222 # directly calling "gclient runhooks" will also run it, so we cache | 261 # directly calling "gclient runhooks" will also run it, so we cache |
223 # based on timestamps to make that case fast. | 262 # based on timestamps to make that case fast. |
224 current_hash = CalculateHash(target_dir) | 263 current_hash = CalculateHash(target_dir) |
225 if current_hash not in desired_hashes: | 264 if current_hash not in desired_hashes: |
226 should_use_gs = False | 265 should_use_gs = False |
227 if (HaveSrcInternalAccess() or | 266 if (HaveSrcInternalAccess() or |
228 LooksLikeGoogler() or | 267 LooksLikeGoogler() or |
229 CanAccessToolchainBucket()): | 268 CanAccessToolchainBucket()): |
230 should_use_gs = True | 269 should_use_gs = True |
231 if not CanAccessToolchainBucket(): | 270 if not CanAccessToolchainBucket(): |
232 RequestGsAuthentication() | 271 RequestGsAuthentication() |
233 if not should_use_gs: | 272 if not should_use_gs: |
234 print('Please follow the instructions at ' | 273 print('Please follow the instructions at ' |
235 'http://www.chromium.org/developers/how-tos/' | 274 'http://www.chromium.org/developers/how-tos/' |
236 'build-instructions-windows') | 275 'build-instructions-windows') |
237 return 1 | 276 return 1 |
238 print('Windows toolchain out of date or doesn\'t exist, updating (Pro)...') | 277 print('Windows toolchain out of date or doesn\'t exist, updating (Pro)...') |
239 print(' current_hash: %s' % current_hash) | 278 print(' current_hash: %s' % current_hash) |
240 print(' desired_hashes: %s' % ', '.join(desired_hashes)) | 279 print(' desired_hashes: %s' % ', '.join(desired_hashes)) |
241 sys.stdout.flush() | 280 sys.stdout.flush() |
242 DelayBeforeRemoving(target_dir) | 281 DelayBeforeRemoving(target_dir) |
243 # This stays resident and will make the rmdir below fail. | 282 # This stays resident and will make the rmdir below fail. |
244 with open(os.devnull, 'wb') as nul: | 283 with open(os.devnull, 'wb') as nul: |
245 subprocess.call(['taskkill', '/f', '/im', 'mspdbsrv.exe'], | 284 subprocess.call(['taskkill', '/f', '/im', 'mspdbsrv.exe'], |
246 stdin=nul, stdout=nul, stderr=nul) | 285 stdin=nul, stdout=nul, stderr=nul) |
247 if os.path.isdir(target_dir): | 286 if os.path.isdir(target_dir): |
248 subprocess.check_call('rmdir /s/q "%s"' % target_dir, shell=True) | 287 subprocess.check_call('rmdir /s/q "%s"' % target_dir, shell=True) |
249 args = [sys.executable, | 288 |
250 'toolchain2013.py', | 289 DoTreeMirror(target_dir, desired_hashes[0]) |
251 '--targetdir', target_dir, | 290 |
252 '--sha1', desired_hashes[0], | 291 got_new_toolchain = True |
253 '--use-gs'] | 292 |
254 subprocess.check_call(args) | 293 win_sdk = os.path.join(abs_target_dir, 'win_sdk') |
| 294 try: |
| 295 with open(os.path.join(target_dir, 'VS_VERSION'), 'rb') as f: |
| 296 vs_version = f.read().strip() |
| 297 except IOError: |
| 298 # Older toolchains didn't have the VS_VERSION file, and used 'win8sdk' |
| 299 # instead of just 'win_sdk'. |
| 300 vs_version = '2013' |
| 301 win_sdk = os.path.join(abs_target_dir, 'win8sdk') |
| 302 |
| 303 data = { |
| 304 'path': abs_target_dir, |
| 305 'version': vs_version, |
| 306 'win_sdk': win_sdk, |
| 307 'wdk': os.path.join(abs_target_dir, 'wdk'), |
| 308 'runtime_dirs': [ |
| 309 os.path.join(abs_target_dir, 'sys64'), |
| 310 os.path.join(abs_target_dir, 'sys32'), |
| 311 ], |
| 312 } |
| 313 with open(os.path.join(target_dir, '..', 'data.json'), 'w') as f: |
| 314 json.dump(data, f) |
| 315 |
| 316 if got_new_toolchain: |
255 current_hash = CalculateHash(target_dir) | 317 current_hash = CalculateHash(target_dir) |
256 if current_hash not in desired_hashes: | 318 if current_hash not in desired_hashes: |
257 print >> sys.stderr, ( | 319 print >> sys.stderr, ( |
258 'Got wrong hash after pulling a new toolchain. ' | 320 'Got wrong hash after pulling a new toolchain. ' |
259 'Wanted one of \'%s\', got \'%s\'.' % ( | 321 'Wanted one of \'%s\', got \'%s\'.' % ( |
260 ', '.join(desired_hashes), current_hash)) | 322 ', '.join(desired_hashes), current_hash)) |
261 return 1 | 323 return 1 |
262 SaveTimestampsAndHash(target_dir, current_hash) | 324 SaveTimestampsAndHash(target_dir, current_hash) |
263 | 325 |
264 if options.output_json: | 326 if options.output_json: |
265 shutil.copyfile(os.path.join(target_dir, '..', 'data.json'), | 327 shutil.copyfile(os.path.join(target_dir, '..', 'data.json'), |
266 options.output_json) | 328 options.output_json) |
267 | 329 |
268 return 0 | 330 return 0 |
269 | 331 |
270 | 332 |
271 if __name__ == '__main__': | 333 if __name__ == '__main__': |
272 sys.exit(main()) | 334 sys.exit(main()) |
OLD | NEW |