OLD | NEW |
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import difflib | 5 import difflib |
6 import hashlib | 6 import hashlib |
7 import itertools | 7 import itertools |
8 import json | 8 import json |
9 import os | 9 import os |
10 import sys | 10 import sys |
11 import zipfile | 11 import zipfile |
12 | 12 |
13 | 13 |
14 # When set and a difference is detected, a diff of what changed is printed. | 14 # When set and a difference is detected, a diff of what changed is printed. |
15 _PRINT_MD5_DIFFS = int(os.environ.get('PRINT_MD5_DIFFS', 0)) | 15 _PRINT_MD5_DIFFS = int(os.environ.get('PRINT_MD5_DIFFS', 0)) |
16 | 16 |
| 17 # An escape hatch that causes all targets to be rebuilt. |
| 18 _FORCE_REBUILD = int(os.environ.get('FORCE_REBUILD', 0)) |
| 19 |
17 | 20 |
18 def CallAndRecordIfStale( | 21 def CallAndRecordIfStale( |
19 function, record_path=None, input_paths=None, input_strings=None, | 22 function, record_path=None, input_paths=None, input_strings=None, |
20 output_paths=None, force=False, pass_changes=False): | 23 output_paths=None, force=False, pass_changes=False): |
21 """Calls function if outputs are stale. | 24 """Calls function if outputs are stale. |
22 | 25 |
23 Outputs are considered stale if: | 26 Outputs are considered stale if: |
24 - any output_paths are missing, or | 27 - any output_paths are missing, or |
25 - the contents of any file within input_paths has changed, or | 28 - the contents of any file within input_paths has changed, or |
26 - the contents of input_strings has changed. | 29 - the contents of input_strings has changed. |
(...skipping 26 matching lines...) Expand all Loading... |
53 new_metadata.AddStrings(input_strings) | 56 new_metadata.AddStrings(input_strings) |
54 | 57 |
55 for path in input_paths: | 58 for path in input_paths: |
56 if _IsZipFile(path): | 59 if _IsZipFile(path): |
57 entries = _ExtractZipEntries(path) | 60 entries = _ExtractZipEntries(path) |
58 new_metadata.AddZipFile(path, entries) | 61 new_metadata.AddZipFile(path, entries) |
59 else: | 62 else: |
60 new_metadata.AddFile(path, _Md5ForPath(path)) | 63 new_metadata.AddFile(path, _Md5ForPath(path)) |
61 | 64 |
62 old_metadata = None | 65 old_metadata = None |
| 66 force = force or _FORCE_REBUILD |
63 missing_outputs = [x for x in output_paths if force or not os.path.exists(x)] | 67 missing_outputs = [x for x in output_paths if force or not os.path.exists(x)] |
64 # When outputs are missing, don't bother gathering change information. | 68 # When outputs are missing, don't bother gathering change information. |
65 if not missing_outputs and os.path.exists(record_path): | 69 if not missing_outputs and os.path.exists(record_path): |
66 with open(record_path, 'r') as jsonfile: | 70 with open(record_path, 'r') as jsonfile: |
67 try: | 71 try: |
68 old_metadata = _Metadata.FromFile(jsonfile) | 72 old_metadata = _Metadata.FromFile(jsonfile) |
69 except: # pylint: disable=bare-except | 73 except: # pylint: disable=bare-except |
70 pass # Not yet using new file format. | 74 pass # Not yet using new file format. |
71 | 75 |
72 changes = Changes(old_metadata, new_metadata, force, missing_outputs) | 76 changes = Changes(old_metadata, new_metadata, force, missing_outputs) |
73 if not changes.HasChanges(): | 77 if not changes.HasChanges(): |
74 return | 78 return |
75 | 79 |
76 if _PRINT_MD5_DIFFS: | 80 if _PRINT_MD5_DIFFS: |
77 print '=' * 80 | 81 print '=' * 80 |
78 print 'Target is stale: %s' % record_path | 82 print 'Target is stale: %s' % record_path |
79 print changes.DescribeDifference() | 83 print changes.DescribeDifference() |
80 print '=' * 80 | 84 print '=' * 80 |
81 | 85 |
82 # Delete the old metdata beforehand since failures leave it in an | |
83 # inderterminate state. | |
84 if old_metadata: | |
85 os.unlink(record_path) | |
86 | |
87 args = (changes,) if pass_changes else () | 86 args = (changes,) if pass_changes else () |
88 function(*args) | 87 function(*args) |
89 | 88 |
90 with open(record_path, 'w') as f: | 89 with open(record_path, 'w') as f: |
91 new_metadata.ToFile(f) | 90 new_metadata.ToFile(f) |
92 | 91 |
93 | 92 |
94 class Changes(object): | 93 class Changes(object): |
95 """Provides and API for querying what changed between runs.""" | 94 """Provides and API for querying what changed between runs.""" |
96 | 95 |
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
390 | 389 |
391 def _ExtractZipEntries(path): | 390 def _ExtractZipEntries(path): |
392 """Returns a list of (path, CRC32) of all files within |path|.""" | 391 """Returns a list of (path, CRC32) of all files within |path|.""" |
393 entries = [] | 392 entries = [] |
394 with zipfile.ZipFile(path) as zip_file: | 393 with zipfile.ZipFile(path) as zip_file: |
395 for zip_info in zip_file.infolist(): | 394 for zip_info in zip_file.infolist(): |
396 # Skip directories and empty files. | 395 # Skip directories and empty files. |
397 if zip_info.CRC: | 396 if zip_info.CRC: |
398 entries.append((zip_info.filename, zip_info.CRC)) | 397 entries.append((zip_info.filename, zip_info.CRC)) |
399 return entries | 398 return entries |
OLD | NEW |