Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(363)

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: Address Scott's comments. Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« 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...) Expand 10 before | Expand all | Expand 10 after
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',
80 'win_sdk\\debuggers\\x86\\sym\\',
81 'win_sdk\\debuggers\\x64\\sym\\']
Nico 2016/05/27 14:45:57 this won't work on non-windows. it looks like this
Sébastien Marchand 2016/05/27 15:16:22 These files get created by Windbg/WER, so they sho
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 p in paths:
78 # they crash. 85 if any(ignored_dir in p 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(p)
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...) Expand all
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.
Nico 2016/06/22 15:09:11 On my windows box, this just spent several minutes
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):
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:
Nico 2016/07/08 14:38:30 Do you really want to compare toolchain_hash and d
Sébastien Marchand 2016/07/08 14:44:22 It's not a bug, it's a feature :). In the past it
176 print ('The hash of a version of the toolchain has an unexpected value ('
177 '%s instead of %s)%s.' % (toolchain_hash, d,
178 ', removing it' if remove_corrupt_toolchains else ''))
179 if remove_corrupt_toolchains:
180 RemoveToolchain(root, d, True)
181 else:
182 hashes.append(toolchain_hash)
148 return hashes 183 return hashes
149 184
150 185
151 def SaveTimestampsAndHash(root, sha1): 186 def SaveTimestampsAndHash(root, sha1):
152 """Saves timestamps and the final hash to be able to early-out more quickly 187 """Saves timestamps and the final hash to be able to early-out more quickly
153 next time.""" 188 next time."""
154 file_list = GetFileList(os.path.join(root, sha1)) 189 file_list = GetFileList(os.path.join(root, sha1))
155 timestamps_data = { 190 timestamps_data = {
156 'files': [[f, os.path.getmtime(f)] for f in file_list], 191 'files': [[f, os.path.getmtime(f)] for f in file_list],
157 'sha1': sha1, 192 'sha1': sha1,
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 toolchain_target_dir = os.path.join(target_dir, desired_hash) 453 toolchain_target_dir = os.path.join(target_dir, desired_hash)
419 454
420 abs_toolchain_target_dir = os.path.abspath(toolchain_target_dir) 455 abs_toolchain_target_dir = os.path.abspath(toolchain_target_dir)
421 456
422 got_new_toolchain = False 457 got_new_toolchain = False
423 458
424 # If the current hash doesn't match what we want in the file, nuke and pave. 459 # 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 460 # 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 461 # directly calling "gclient runhooks" will also run it, so we cache
427 # based on timestamps to make that case fast. 462 # based on timestamps to make that case fast.
428 current_hashes = CalculateToolchainHashes(target_dir) 463 current_hashes = CalculateToolchainHashes(target_dir, True)
429 if desired_hash not in current_hashes: 464 if desired_hash not in current_hashes:
430 should_use_gs = False 465 should_use_gs = False
431 if (HaveSrcInternalAccess() or 466 if (HaveSrcInternalAccess() or
432 LooksLikeGoogler() or 467 LooksLikeGoogler() or
433 CanAccessToolchainBucket()): 468 CanAccessToolchainBucket()):
434 should_use_gs = True 469 should_use_gs = True
435 if not CanAccessToolchainBucket(): 470 if not CanAccessToolchainBucket():
436 RequestGsAuthentication() 471 RequestGsAuthentication()
437 if not should_use_gs: 472 if not should_use_gs:
438 print('\n\n\nPlease follow the instructions at ' 473 print('\n\n\nPlease follow the instructions at '
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
472 'wdk': os.path.join(abs_toolchain_target_dir, 'wdk'), 507 'wdk': os.path.join(abs_toolchain_target_dir, 'wdk'),
473 'runtime_dirs': [ 508 'runtime_dirs': [
474 os.path.join(abs_toolchain_target_dir, 'sys64'), 509 os.path.join(abs_toolchain_target_dir, 'sys64'),
475 os.path.join(abs_toolchain_target_dir, 'sys32'), 510 os.path.join(abs_toolchain_target_dir, 'sys32'),
476 ], 511 ],
477 } 512 }
478 with open(os.path.join(target_dir, '..', 'data.json'), 'w') as f: 513 with open(os.path.join(target_dir, '..', 'data.json'), 'w') as f:
479 json.dump(data, f) 514 json.dump(data, f)
480 515
481 if got_new_toolchain: 516 if got_new_toolchain:
482 current_hashes = CalculateToolchainHashes(target_dir) 517 current_hashes = CalculateToolchainHashes(target_dir, False)
483 if desired_hash not in current_hashes: 518 if desired_hash not in current_hashes:
484 print >> sys.stderr, ( 519 print >> sys.stderr, (
485 'Got wrong hash after pulling a new toolchain. ' 520 'Got wrong hash after pulling a new toolchain. '
486 'Wanted \'%s\', got one of \'%s\'.' % ( 521 'Wanted \'%s\', got one of \'%s\'.' % (
487 desired_hash, ', '.join(current_hashes))) 522 desired_hash, ', '.join(current_hashes)))
488 return 1 523 return 1
489 SaveTimestampsAndHash(target_dir, desired_hash) 524 SaveTimestampsAndHash(target_dir, desired_hash)
490 525
491 if options.output_json: 526 if options.output_json:
492 shutil.copyfile(os.path.join(target_dir, '..', 'data.json'), 527 shutil.copyfile(os.path.join(target_dir, '..', 'data.json'),
493 options.output_json) 528 options.output_json)
494 529
495 EnableCrashDumpCollection() 530 EnableCrashDumpCollection()
496 531
497 if os.environ.get('GYP_MSVS_VERSION') == '2015': 532 if os.environ.get('GYP_MSVS_VERSION') == '2015':
498 InstallUniversalCRTIfNeeded(abs_toolchain_target_dir) 533 InstallUniversalCRTIfNeeded(abs_toolchain_target_dir)
499 534
500 RemoveUnusedToolchains(target_dir) 535 RemoveUnusedToolchains(target_dir)
501 536
502 return 0 537 return 0
503 538
504 539
505 if __name__ == '__main__': 540 if __name__ == '__main__':
506 sys.exit(main()) 541 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
This is Rietveld 408576698