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

Side by Side Diff: scripts/slave/recipe_modules/isolate/resources/compare_build_artifacts.py

Issue 695123002: Add specialized .isolated differ that prints unified diff. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 6 years, 1 month 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 | Annotate | Revision Log
« 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 2014 The Chromium Authors. All rights reserved. 2 # Copyright 2014 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 """Compare the artifacts from two builds.""" 6 """Compare the artifacts from two builds."""
6 7
8 import difflib
7 import json 9 import json
8 import optparse 10 import optparse
9 import os 11 import os
10 import sys 12 import sys
11 13
12 14
13 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 15 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
14 16
15 17
16 def get_files_to_compare(build_dir): 18 def get_files_to_compare(build_dir):
17 """Get the list of files to compare.""" 19 """Get the list of files to compare."""
18 allowed = frozenset( 20 allowed = frozenset(
19 ('', '.app', '.dll', '.dylib', '.exe', '.nexe', '.so')) 21 ('', '.app', '.dll', '.dylib', '.exe', '.nexe', '.so'))
20 22
21 def check(f): 23 def check(f):
22 if not os.path.isfile(f): 24 if not os.path.isfile(f):
23 return False 25 return False
24 if os.path.basename(f).startswith('.'): 26 if os.path.basename(f).startswith('.'):
25 return False 27 return False
26 ext = os.path.splitext(f)[1] 28 ext = os.path.splitext(f)[1]
27 if ext == '.isolated': 29 if ext == '.isolated':
28 return True 30 return True
29 return ext in allowed and os.access(f, os.X_OK) 31 return ext in allowed and os.access(f, os.X_OK)
30 32
31 return set(f for f in os.listdir(build_dir) if 33 return set(f for f in os.listdir(build_dir) if
32 check(os.path.join(build_dir, f))) 34 check(os.path.join(build_dir, f)))
33 35
34 36
35 def compare_files(first_filepath, second_filepath): 37 def compare_files(first_filepath, second_filepath):
36 """Compare two binaries and return the number of differences between them. 38 """Compares two binaries and return the number of differences between them.
37 39
38 Returns -1 if the files have a different size. 40 Returns None if the files are equal, a string otherwise.
39 """ 41 """
40 if os.stat(first_filepath).st_size != os.stat(second_filepath).st_size: 42 file_len = os.stat(first_filepath).st_size
41 return -1 43 if file_len != os.stat(second_filepath).st_size:
44 return 'different size: %d != %d' % (
45 file_len, os.stat(second_filepath).st_size)
42 46
43 # Read the files by chunks of 1MB.
44 chunk_size = 1024 * 1024 47 chunk_size = 1024 * 1024
45 diffs = 0 48 diffs = 0
46 with open(first_filepath, 'rb') as lhs: 49 with open(first_filepath, 'rb') as lhs:
47 with open(second_filepath, 'rb') as rhs: 50 with open(second_filepath, 'rb') as rhs:
48 while True: 51 while True:
49 lhs_data = lhs.read(chunk_size) 52 lhs_data = lhs.read(chunk_size)
50 rhs_data = rhs.read(chunk_size) 53 rhs_data = rhs.read(chunk_size)
51 if not lhs_data: 54 if not lhs_data:
52 break 55 break
53 diffs += sum(l != r for l, r in zip(lhs_data, rhs_data)) 56 diffs += sum(l != r for l, r in zip(lhs_data, rhs_data))
57 if not diffs:
58 return None
54 59
55 return diffs 60 result = '%d out of %d bytes are different (%.2f%%)' % (
61 diffs, file_len, 100.0 * diffs / file_len)
62
63 if diffs and first_filepath.endswith('.isolated'):
64 # Unpack the files.
65 with open(first_filepath, 'rb') as f:
66 lhs = json.dumps(
67 json.load(f), indent=2, sort_keys=True,
68 separators=(',', ': ')).splitlines()
69 with open(second_filepath, 'rb') as f:
70 rhs = json.dumps(
71 json.load(f), indent=2, sort_keys=True,
72 separators=(',', ': ')).splitlines()
73
74 result += '\n' + '\n'.join(
75 line for line in difflib.unified_diff(lhs, rhs)
76 if not line.startswith(('---', '+++')))
77 return result
56 78
57 79
58 def compare_build_artifacts(first_dir, second_dir): 80 def compare_build_artifacts(first_dir, second_dir):
59 """Compare the artifacts from two distinct builds.""" 81 """Compare the artifacts from two distinct builds."""
60 if not os.path.isdir(first_dir): 82 if not os.path.isdir(first_dir):
61 print >> sys.stderr, '%s isn\'t a valid directory.' % first_dir 83 print >> sys.stderr, '%s isn\'t a valid directory.' % first_dir
62 return 1 84 return 1
63 if not os.path.isdir(second_dir): 85 if not os.path.isdir(second_dir):
64 print >> sys.stderr, '%s isn\'t a valid directory.' % second_dir 86 print >> sys.stderr, '%s isn\'t a valid directory.' % second_dir
65 return 1 87 return 1
66 88
67 with open(os.path.join(BASE_DIR, 'deterministic_build_blacklist.json')) as f: 89 with open(os.path.join(BASE_DIR, 'deterministic_build_blacklist.json')) as f:
68 blacklist = frozenset(json.load(f)) 90 blacklist = frozenset(json.load(f))
69 91
70 res = 0 92 res = 0
71 first_list = get_files_to_compare(first_dir) - blacklist 93 first_list = get_files_to_compare(first_dir) - blacklist
72 second_list = get_files_to_compare(second_dir) - blacklist 94 second_list = get_files_to_compare(second_dir) - blacklist
73 95
74 diff = first_list.symmetric_difference(second_list) 96 diff = first_list.symmetric_difference(second_list)
75 if diff: 97 if diff:
76 print >> sys.stderr, 'Different list of files in both directories' 98 print >> sys.stderr, 'Different list of files in both directories'
77 print >> sys.stderr, '\n'.join(' ' + i for i in sorted(diff)) 99 print >> sys.stderr, '\n'.join(' ' + i for i in sorted(diff))
78 res += len(diff) 100 res += len(diff)
79 101
80 max_filepath_len = max(len(n) for n in first_list & second_list) 102 max_filepath_len = max(len(n) for n in first_list & second_list)
81 for f in sorted(first_list & second_list): 103 for f in sorted(first_list & second_list):
82 first_file = os.path.join(first_dir, f) 104 first_file = os.path.join(first_dir, f)
83 second_file = os.path.join(second_dir, f) 105 second_file = os.path.join(second_dir, f)
84 files_diffs = compare_files(first_file, second_file) 106 result = compare_files(first_file, second_file)
85 if not files_diffs: 107 if not result:
86 result = 'equal' 108 result = 'equal'
87 else: 109 else:
88 file_len = os.stat(first_file).st_size 110 result = 'DIFFERENT: %s' % result
89 difference = ''
90 if files_diffs == -1:
91 difference = 'different size: %d != %d' % (file_len,
92 os.stat(second_file).st_size)
93 else:
94 difference = '%d out of %d bytes are different (%.2f%%)' % (
95 files_diffs, file_len, 100.0 * files_diffs / file_len)
96 result = 'DIFFERENT: %s' % difference
97 res += 1 111 res += 1
98 print('%-*s: %s' % (max_filepath_len, f, result)) 112 print('%-*s: %s' % (max_filepath_len, f, result))
99 113
100 print '%d files are equal, %d are different.' % ( 114 print '%d files are equal, %d are different.' % (
101 len(first_list & second_list) - res, res) 115 len(first_list & second_list) - res, res)
102 116
103 return 0 if res == 0 else 1 117 return 0 if res == 0 else 1
104 118
105 119
106 def main(): 120 def main():
107 parser = optparse.OptionParser(usage='%prog [options]') 121 parser = optparse.OptionParser(usage='%prog [options]')
108 parser.add_option('--first-build-dir', help='The first build directory.') 122 parser.add_option(
109 parser.add_option('--second-build-dir', help='The second build directory.') 123 '-f', '--first-build-dir', help='The first build directory.')
124 parser.add_option(
125 '-s', '--second-build-dir', help='The second build directory.')
110 options, _ = parser.parse_args() 126 options, _ = parser.parse_args()
111 127
112 if not options.first_build_dir: 128 if not options.first_build_dir:
113 parser.error('--first-build-dir is required') 129 parser.error('--first-build-dir is required')
114 if not options.second_build_dir: 130 if not options.second_build_dir:
115 parser.error('--second-build-dir is required') 131 parser.error('--second-build-dir is required')
116 132
117 return compare_build_artifacts(options.first_build_dir, 133 return compare_build_artifacts(options.first_build_dir,
118 options.second_build_dir) 134 options.second_build_dir)
119 135
120 136
121 if __name__ == '__main__': 137 if __name__ == '__main__':
122 sys.exit(main()) 138 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