Chromium Code Reviews

Side by Side Diff: win_toolchain/get_toolchain_if_necessary.py

Issue 1974453003: Improve the toolchains-hash calculation. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 54 matching lines...)
65 # Allow use of utility functions in this script from package_from_installed 65 # Allow use of utility functions in this script from package_from_installed
66 # on bare VM that doesn't have a full depot_tools. 66 # on bare VM that doesn't have a full depot_tools.
67 pass 67 pass
68 68
69 69
70 def GetFileList(root): 70 def GetFileList(root):
71 """Gets a normalized list of files under |root|.""" 71 """Gets a normalized list of files under |root|."""
72 assert not os.path.isabs(root) 72 assert not os.path.isabs(root)
73 assert os.path.normpath(root) == root 73 assert os.path.normpath(root) == root
74 file_list = [] 74 file_list = []
75 # Ignore WER ReportQueue entries that vctip/cl leave in the bin dir if/when
76 # they crash. Also ignores the content of the win_sdk/debuggers/x(86|64)/sym/
77 # directories as this is just the temporarily location that Windbg might use
78 # to store the symbol files.
79 ignored_directories = ['wer\\reportqueue',
scottmg 2016/05/11 19:21:36 Does this intentionally not have a trailing \?
Sébastien Marchand 2016/05/11 19:59:50 I don't know if reportqueue is the name of the dir
80 'win_sdk\\debuggers\\x86\\sym\\',
81 'win_sdk\\debuggers\\x64\\sym\\']
75 for base, _, files in os.walk(root): 82 for base, _, files in os.walk(root):
76 paths = [os.path.join(base, f) for f in files] 83 paths = [os.path.join(base, f).lower() for f in files]
77 # Ignore WER ReportQueue entries that vctip/cl leave in the bin dir if/when 84 for f in paths:
scottmg 2016/05/11 19:21:36 maybe p instead of f since f is used above
Sébastien Marchand 2016/05/11 19:59:50 Done.
78 # they crash. 85 if any(ignored_dir in f for ignored_dir in ignored_directories):
79 file_list.extend(x.lower() for x in paths if 'WER\\ReportQueue' not in x) 86 continue
87 file_list.append(f)
80 return sorted(file_list, key=lambda s: s.replace('/', '\\')) 88 return sorted(file_list, key=lambda s: s.replace('/', '\\'))
81 89
82 90
83 def MakeTimestampsFileName(root, sha1): 91 def MakeTimestampsFileName(root, sha1):
84 return os.path.join(root, os.pardir, '%s.timestamps' % sha1) 92 return os.path.join(root, os.pardir, '%s.timestamps' % sha1)
85 93
86 94
87 def CalculateHash(root, expected_hash): 95 def CalculateHash(root, expected_hash):
88 """Calculates the sha1 of the paths to all files in the given |root| and the 96 """Calculates the sha1 of the paths to all files in the given |root| and the
89 contents of those files, and returns as a hex string. 97 contents of those files, and returns as a hex string.
(...skipping 22 matching lines...)
112 matches = len(file_list) == len(timestamps_data['files']) 120 matches = len(file_list) == len(timestamps_data['files'])
113 # Don't check the timestamp of the version file as we touch this file to 121 # Don't check the timestamp of the version file as we touch this file to
114 # indicates which versions of the toolchain are still being used. 122 # indicates which versions of the toolchain are still being used.
115 vc_dir = os.path.join(full_root_path, 'VC').lower() 123 vc_dir = os.path.join(full_root_path, 'VC').lower()
116 if matches: 124 if matches:
117 for disk, cached in zip(file_list, timestamps_data['files']): 125 for disk, cached in zip(file_list, timestamps_data['files']):
118 if disk != cached[0] or ( 126 if disk != cached[0] or (
119 disk != vc_dir and os.path.getmtime(disk) != cached[1]): 127 disk != vc_dir and os.path.getmtime(disk) != cached[1]):
120 matches = False 128 matches = False
121 break 129 break
130 elif os.path.exists(timestamps_file):
131 # Print some information about the extra/missing files. Don't do this if we
132 # don't have a timestamp file, as all the files will be considered as
133 # missing.
134 timestamps_data_files = []
135 for f in timestamps_data['files']:
136 timestamps_data_files.append(f[0])
137 missing_files = [f for f in timestamps_data_files if f not in file_list]
138 if len(missing_files):
139 print ('Some files are missing from the %s version of the toolchain:' %
140 expected_hash)
141 for f in missing_files:
142 print '\t%s' % f
143 extra_files = [f for f in file_list if f not in timestamps_data_files]
144 if len(extra_files):
145 print ('There\'s some extra files in the %s version of the toolchain:' %
146 expected_hash)
147 for f in extra_files:
148 print '\t%s' % f
122 if matches: 149 if matches:
123 return timestamps_data['sha1'] 150 return timestamps_data['sha1']
124 151
125 # Make long hangs when updating the toolchain less mysterious. 152 # Make long hangs when updating the toolchain less mysterious.
126 print 'Calculating hash of toolchain in %s. Please wait...' % full_root_path 153 print 'Calculating hash of toolchain in %s. Please wait...' % full_root_path
127 sys.stdout.flush() 154 sys.stdout.flush()
128 digest = hashlib.sha1() 155 digest = hashlib.sha1()
129 for path in file_list: 156 for path in file_list:
130 path_without_hash = str(path).replace('/', '\\') 157 path_without_hash = str(path).replace('/', '\\')
131 if expected_hash: 158 if expected_hash:
132 path_without_hash = path_without_hash.replace( 159 path_without_hash = path_without_hash.replace(
133 os.path.join(root, expected_hash).replace('/', '\\'), root) 160 os.path.join(root, expected_hash).replace('/', '\\'), root)
134 digest.update(path_without_hash) 161 digest.update(path_without_hash)
135 with open(path, 'rb') as f: 162 with open(path, 'rb') as f:
136 digest.update(f.read()) 163 digest.update(f.read())
137 return digest.hexdigest() 164 return digest.hexdigest()
138 165
139 166
140 def CalculateToolchainHashes(root): 167 def CalculateToolchainHashes(root, remove_corrupt_toolchains):
scottmg 2016/05/11 19:21:36 This new argument doesn't seem to be used.
Sébastien Marchand 2016/05/11 19:59:50 Oops. Fixed.
141 """Calculate the hash of the different toolchains installed in the |root| 168 """Calculate the hash of the different toolchains installed in the |root|
142 directory.""" 169 directory."""
143 hashes = [] 170 hashes = []
144 dir_list = [ 171 dir_list = [
145 d for d in os.listdir(root) if os.path.isdir(os.path.join(root, d))] 172 d for d in os.listdir(root) if os.path.isdir(os.path.join(root, d))]
146 for d in dir_list: 173 for d in dir_list:
147 hashes.append(CalculateHash(root, d)) 174 toolchain_hash = CalculateHash(root, d)
175 if toolchain_hash != d:
176 print ('The hash of a version of the toolchain has an unexpected value ('
177 '%s instead of %s), removing it.' % (toolchain_hash, d))
178 RemoveToolchain(root, d, True)
179 else:
180 hashes.append(toolchain_hash)
148 return hashes 181 return hashes
149 182
150 183
151 def SaveTimestampsAndHash(root, sha1): 184 def SaveTimestampsAndHash(root, sha1):
152 """Saves timestamps and the final hash to be able to early-out more quickly 185 """Saves timestamps and the final hash to be able to early-out more quickly
153 next time.""" 186 next time."""
154 file_list = GetFileList(os.path.join(root, sha1)) 187 file_list = GetFileList(os.path.join(root, sha1))
155 timestamps_data = { 188 timestamps_data = {
156 'files': [[f, os.path.getmtime(f)] for f in file_list], 189 'files': [[f, os.path.getmtime(f)] for f in file_list],
157 'sha1': sha1, 190 'sha1': sha1,
(...skipping 260 matching lines...)
418 toolchain_target_dir = os.path.join(target_dir, desired_hash) 451 toolchain_target_dir = os.path.join(target_dir, desired_hash)
419 452
420 abs_toolchain_target_dir = os.path.abspath(toolchain_target_dir) 453 abs_toolchain_target_dir = os.path.abspath(toolchain_target_dir)
421 454
422 got_new_toolchain = False 455 got_new_toolchain = False
423 456
424 # If the current hash doesn't match what we want in the file, nuke and pave. 457 # If the current hash doesn't match what we want in the file, nuke and pave.
425 # Typically this script is only run when the .sha1 one file is updated, but 458 # Typically this script is only run when the .sha1 one file is updated, but
426 # directly calling "gclient runhooks" will also run it, so we cache 459 # directly calling "gclient runhooks" will also run it, so we cache
427 # based on timestamps to make that case fast. 460 # based on timestamps to make that case fast.
428 current_hashes = CalculateToolchainHashes(target_dir) 461 current_hashes = CalculateToolchainHashes(target_dir, True)
429 if desired_hash not in current_hashes: 462 if desired_hash not in current_hashes:
430 should_use_gs = False 463 should_use_gs = False
431 if (HaveSrcInternalAccess() or 464 if (HaveSrcInternalAccess() or
432 LooksLikeGoogler() or 465 LooksLikeGoogler() or
433 CanAccessToolchainBucket()): 466 CanAccessToolchainBucket()):
434 should_use_gs = True 467 should_use_gs = True
435 if not CanAccessToolchainBucket(): 468 if not CanAccessToolchainBucket():
436 RequestGsAuthentication() 469 RequestGsAuthentication()
437 if not should_use_gs: 470 if not should_use_gs:
438 print('\n\n\nPlease follow the instructions at ' 471 print('\n\n\nPlease follow the instructions at '
(...skipping 33 matching lines...)
472 'wdk': os.path.join(abs_toolchain_target_dir, 'wdk'), 505 'wdk': os.path.join(abs_toolchain_target_dir, 'wdk'),
473 'runtime_dirs': [ 506 'runtime_dirs': [
474 os.path.join(abs_toolchain_target_dir, 'sys64'), 507 os.path.join(abs_toolchain_target_dir, 'sys64'),
475 os.path.join(abs_toolchain_target_dir, 'sys32'), 508 os.path.join(abs_toolchain_target_dir, 'sys32'),
476 ], 509 ],
477 } 510 }
478 with open(os.path.join(target_dir, '..', 'data.json'), 'w') as f: 511 with open(os.path.join(target_dir, '..', 'data.json'), 'w') as f:
479 json.dump(data, f) 512 json.dump(data, f)
480 513
481 if got_new_toolchain: 514 if got_new_toolchain:
482 current_hashes = CalculateToolchainHashes(target_dir) 515 current_hashes = CalculateToolchainHashes(target_dir, False)
483 if desired_hash not in current_hashes: 516 if desired_hash not in current_hashes:
484 print >> sys.stderr, ( 517 print >> sys.stderr, (
485 'Got wrong hash after pulling a new toolchain. ' 518 'Got wrong hash after pulling a new toolchain. '
486 'Wanted \'%s\', got one of \'%s\'.' % ( 519 'Wanted \'%s\', got one of \'%s\'.' % (
487 desired_hash, ', '.join(current_hashes))) 520 desired_hash, ', '.join(current_hashes)))
488 return 1 521 return 1
489 SaveTimestampsAndHash(target_dir, desired_hash) 522 SaveTimestampsAndHash(target_dir, desired_hash)
490 523
491 if options.output_json: 524 if options.output_json:
492 shutil.copyfile(os.path.join(target_dir, '..', 'data.json'), 525 shutil.copyfile(os.path.join(target_dir, '..', 'data.json'),
493 options.output_json) 526 options.output_json)
494 527
495 EnableCrashDumpCollection() 528 EnableCrashDumpCollection()
496 529
497 if os.environ.get('GYP_MSVS_VERSION') == '2015': 530 if os.environ.get('GYP_MSVS_VERSION') == '2015':
498 InstallUniversalCRTIfNeeded(abs_toolchain_target_dir) 531 InstallUniversalCRTIfNeeded(abs_toolchain_target_dir)
499 532
500 RemoveUnusedToolchains(target_dir) 533 RemoveUnusedToolchains(target_dir)
501 534
502 return 0 535 return 0
503 536
504 537
505 if __name__ == '__main__': 538 if __name__ == '__main__':
506 sys.exit(main()) 539 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine