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 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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()) |
OLD | NEW |