Chromium Code Reviews| 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 |
| 11 you're trying to build an historical version of Chromium from before the | 11 you're trying to build an historical version of Chromium from before the |
| 12 toolchain upgrade, this will cause you to build with a newer toolchain than | 12 toolchain upgrade, this will cause you to build with a newer toolchain than |
| 13 was available when that code was committed. This is done for a two main | 13 was available when that code was committed. This is done for a two main |
| 14 reasons: 1) it would likely be annoying to have the up-to-date toolchain | 14 reasons: 1) it would likely be annoying to have the up-to-date toolchain |
| 15 removed and replaced by one without a service pack applied); 2) it would | 15 removed and replaced by one without a service pack applied); 2) it would |
| 16 require maintaining scripts that can build older not-up-to-date revisions of | 16 require maintaining scripts that can build older not-up-to-date revisions of |
| 17 the toolchain. This is likely to be a poorly tested code path that probably | 17 the toolchain. This is likely to be a poorly tested code path that probably |
| 18 won't be properly maintained. See http://crbug.com/323300. | 18 won't be properly maintained. See http://crbug.com/323300. |
| 19 | 19 |
| 20 This does not extend to major versions of the toolchain however, on the | 20 This does not extend to major versions of the toolchain however, on the |
| 21 assumption that there are more likely to be source incompatibilities between | 21 assumption that there are more likely to be source incompatibilities between |
| 22 major revisions. This script calls a subscript (currently, toolchain2013.py) | 22 major revisions. This script calls a subscript (currently, toolchain2013.py) |
| 23 to do the main work. It is expected that toolchain2013.py will always be able | 23 to do the main work. It is expected that toolchain2013.py will always be able |
| 24 to acquire/build the most current revision of a VS2013-based toolchain. In the | 24 to acquire/build the most current revision of a VS2013-based toolchain. In the |
| 25 future when a hypothetical VS2015 is released, the 2013 script will be | 25 future when a hypothetical VS2015 is released, the 2013 script will be |
| 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 _winreg | |
| 29 import hashlib | 30 import hashlib |
| 30 import json | 31 import json |
| 31 import optparse | 32 import optparse |
| 32 import os | 33 import os |
| 34 import platform | |
| 33 import shutil | 35 import shutil |
| 34 import subprocess | 36 import subprocess |
| 35 import sys | 37 import sys |
| 36 import tempfile | 38 import tempfile |
| 37 import time | 39 import time |
| 38 import zipfile | 40 import zipfile |
| 39 | 41 |
| 40 | 42 |
| 41 BASEDIR = os.path.dirname(os.path.abspath(__file__)) | 43 BASEDIR = os.path.dirname(os.path.abspath(__file__)) |
| 42 DEPOT_TOOLS_PATH = os.path.join(BASEDIR, '..') | 44 DEPOT_TOOLS_PATH = os.path.join(BASEDIR, '..') |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 210 else: | 212 else: |
| 211 temp_dir, local_zip = DownloadUsingGsutil(tree_sha1 + '.zip') | 213 temp_dir, local_zip = DownloadUsingGsutil(tree_sha1 + '.zip') |
| 212 sys.stdout.write('Extracting %s...\n' % local_zip) | 214 sys.stdout.write('Extracting %s...\n' % local_zip) |
| 213 sys.stdout.flush() | 215 sys.stdout.flush() |
| 214 with zipfile.ZipFile(local_zip, 'r', zipfile.ZIP_DEFLATED, True) as zf: | 216 with zipfile.ZipFile(local_zip, 'r', zipfile.ZIP_DEFLATED, True) as zf: |
| 215 zf.extractall(target_dir) | 217 zf.extractall(target_dir) |
| 216 if temp_dir: | 218 if temp_dir: |
| 217 RmDir(temp_dir) | 219 RmDir(temp_dir) |
| 218 | 220 |
| 219 | 221 |
| 222 def GetInstallerName(): | |
| 223 """Return the name of the Windows 10 Universal C Runtime installer for the | |
| 224 current platform, or None if installer is not needed or not applicable. | |
| 225 The registry has to be used instead of sys.getwindowsversion() because | |
| 226 Python 2.7 is only manifested as being compatible up to Windows 8, so the | |
| 227 version APIs helpfully return a maximum of 6.2 (Windows 8). | |
| 228 """ | |
| 229 key_name = r'Software\Microsoft\Windows NT\CurrentVersion' | |
| 230 key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key_name) | |
| 231 value, keytype = _winreg.QueryValueEx(key, "CurrentVersion") | |
| 232 key.Close() | |
| 233 if keytype != _winreg.REG_SZ: | |
| 234 raise Exception("Unexpected type in registry") | |
| 235 if value == '6.1': | |
| 236 # Windows 7 and Windows Server 2008 R2 | |
| 237 return 'Windows6.1-KB2999226-x64.msu' | |
| 238 elif value == '6.2': | |
| 239 # Windows 8 and Windows Server 2012 | |
| 240 return 'Windows8-RT-KB2999226-x64.msu' | |
| 241 elif value == '6.3': | |
| 242 # Windows 8.1, Windows Server 2012 R2, and Windows 10. | |
| 243 # The Windows 8.1 installer doesn't work on Windows 10, but it will never | |
| 244 # be used because the UCRT is always installed on Windows 10. | |
| 245 return 'Windows8.1-KB2999226-x64.msu' | |
| 246 else: | |
| 247 # Some future OS. | |
| 248 return None | |
| 249 | |
| 250 | |
| 251 def InstallUniversalCRTIfNeeded(abs_target_dir): | |
| 252 installer_name = GetInstallerName() | |
| 253 if not installer_name: | |
| 254 return | |
| 255 | |
| 256 bitness = platform.architecture()[0] | |
| 257 # When running 64-bit python the x64 DLLs will be in System32 | |
| 258 x64_path = 'System32' if bitness == '64bit' else 'Sysnative' | |
| 259 x64_path = os.path.join(r'C:\Windows', x64_path) | |
| 260 sample_crt_file = os.path.join(x64_path, 'ucrtbase.dll') | |
| 261 | |
| 262 if os.path.exists(sample_crt_file): | |
| 263 # Nothing to do. | |
| 264 return | |
| 265 | |
| 266 print ('%s does not exist - installing Windows 10 Universal C Runtime' % | |
| 267 sample_crt_file) | |
|
scottmg
2016/01/15 19:06:44
The indent should be after the (, aligned with '.
brucedawson
2016/01/15 19:16:46
Done.
| |
| 268 | |
| 269 installer = os.path.join(abs_target_dir, "installers", installer_name) | |
| 270 command = r'wusa.exe /quiet "%s"' % installer | |
| 271 print 'Running %s' % command | |
| 272 | |
| 273 try: | |
| 274 subprocess.check_call(command) | |
| 275 except WindowsError as e: | |
| 276 if e.winerror == 740: # The requested operation requires elevation | |
| 277 print | |
| 278 print '-'*80 | |
| 279 print | |
| 280 print 'Elevation required. You must manually install this update:' | |
| 281 print ' %s' % installer | |
| 282 print | |
| 283 print '-'*80 | |
| 284 print | |
| 285 raise Exception('Elevation required. You must manually install %s' % | |
| 286 installer) | |
| 287 raise e | |
| 288 | |
| 289 | |
| 220 def main(): | 290 def main(): |
| 221 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) | 291 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) |
| 222 parser.add_option('--output-json', metavar='FILE', | 292 parser.add_option('--output-json', metavar='FILE', |
| 223 help='write information about toolchain to FILE') | 293 help='write information about toolchain to FILE') |
| 224 parser.add_option('--force', action='store_true', | 294 parser.add_option('--force', action='store_true', |
| 225 help='force script to run on non-Windows hosts') | 295 help='force script to run on non-Windows hosts') |
| 226 options, args = parser.parse_args() | 296 options, args = parser.parse_args() |
| 227 | 297 |
| 228 if not (sys.platform.startswith(('cygwin', 'win32')) or options.force): | 298 if not (sys.platform.startswith(('cygwin', 'win32')) or options.force): |
| 229 return 0 | 299 return 0 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 274 print('\n\n\nPlease follow the instructions at ' | 344 print('\n\n\nPlease follow the instructions at ' |
| 275 'https://www.chromium.org/developers/how-tos/' | 345 'https://www.chromium.org/developers/how-tos/' |
| 276 'build-instructions-windows\n\n') | 346 'build-instructions-windows\n\n') |
| 277 return 1 | 347 return 1 |
| 278 print('Windows toolchain out of date or doesn\'t exist, updating (Pro)...') | 348 print('Windows toolchain out of date or doesn\'t exist, updating (Pro)...') |
| 279 print(' current_hash: %s' % current_hash) | 349 print(' current_hash: %s' % current_hash) |
| 280 print(' desired_hashes: %s' % ', '.join(desired_hashes)) | 350 print(' desired_hashes: %s' % ', '.join(desired_hashes)) |
| 281 sys.stdout.flush() | 351 sys.stdout.flush() |
| 282 DelayBeforeRemoving(target_dir) | 352 DelayBeforeRemoving(target_dir) |
| 283 if sys.platform == 'win32': | 353 if sys.platform == 'win32': |
| 284 # This stays resident and will make the rmdir below fail. | 354 # These stay resident and will make the rmdir below fail. |
| 285 with open(os.devnull, 'wb') as nul: | 355 kill_list = [ |
| 286 subprocess.call(['taskkill', '/f', '/im', 'mspdbsrv.exe'], | 356 'mspdbsrv.exe', |
| 287 stdin=nul, stdout=nul, stderr=nul) | 357 'vctip.exe', # Compiler and tools experience improvement data uploader. |
| 358 ] | |
| 359 for process_name in kill_list: | |
| 360 with open(os.devnull, 'wb') as nul: | |
| 361 subprocess.call(['taskkill', '/f', '/im', process_name], | |
| 362 stdin=nul, stdout=nul, stderr=nul) | |
| 288 if os.path.isdir(target_dir): | 363 if os.path.isdir(target_dir): |
| 289 RmDir(target_dir) | 364 RmDir(target_dir) |
| 290 | 365 |
| 291 DoTreeMirror(target_dir, desired_hashes[0]) | 366 DoTreeMirror(target_dir, desired_hashes[0]) |
| 292 | 367 |
| 293 got_new_toolchain = True | 368 got_new_toolchain = True |
| 294 | 369 |
| 295 win_sdk = os.path.join(abs_target_dir, 'win_sdk') | 370 win_sdk = os.path.join(abs_target_dir, 'win_sdk') |
| 296 try: | 371 try: |
| 297 with open(os.path.join(target_dir, 'VS_VERSION'), 'rb') as f: | 372 with open(os.path.join(target_dir, 'VS_VERSION'), 'rb') as f: |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 324 'Got wrong hash after pulling a new toolchain. ' | 399 'Got wrong hash after pulling a new toolchain. ' |
| 325 'Wanted one of \'%s\', got \'%s\'.' % ( | 400 'Wanted one of \'%s\', got \'%s\'.' % ( |
| 326 ', '.join(desired_hashes), current_hash)) | 401 ', '.join(desired_hashes), current_hash)) |
| 327 return 1 | 402 return 1 |
| 328 SaveTimestampsAndHash(target_dir, current_hash) | 403 SaveTimestampsAndHash(target_dir, current_hash) |
| 329 | 404 |
| 330 if options.output_json: | 405 if options.output_json: |
| 331 shutil.copyfile(os.path.join(target_dir, '..', 'data.json'), | 406 shutil.copyfile(os.path.join(target_dir, '..', 'data.json'), |
| 332 options.output_json) | 407 options.output_json) |
| 333 | 408 |
| 409 if os.environ.get('GYP_MSVS_VERSION') == '2015': | |
| 410 InstallUniversalCRTIfNeeded(abs_target_dir) | |
| 411 | |
| 334 return 0 | 412 return 0 |
| 335 | 413 |
| 336 | 414 |
| 337 if __name__ == '__main__': | 415 if __name__ == '__main__': |
| 338 sys.exit(main()) | 416 sys.exit(main()) |
| OLD | NEW |