3 # scons-diff.py - diff-like utility for comparing SCons trees
5 # This supports most common diff options (with some quirks, like you can't
6 # just say -c and have it use a default value), but canonicalizes the
7 # various version strings within the file like __revision__, __build__,
8 # etc. so that you can diff trees without having to ignore changes in
19 Usage: scons-diff.py [OPTIONS] dir1 dir2
21 -c NUM, --context=NUM Print NUM lines of copied context.
22 -h, --help Print this message and exit.
23 -n Don't canonicalize SCons lines.
24 -q, --quiet Print only whether files differ.
25 -r, --recursive Recursively compare found subdirectories.
26 -s Report when two files are the same.
27 -u NUM, --unified=NUM Print NUM lines of unified context.
30 opts, args = getopt.getopt(sys.argv[1:],
32 ['context=', 'help', 'recursive', 'unified='])
41 def diff_line(left, right):
43 opts = ' ' + ' '.join(diff_options)
46 print 'diff%s %s %s' % (opts, left, right)
52 diff_options.append(o)
53 elif o in ('-h', '--help'):
57 diff_options.append(o)
61 diff_line = lambda l, r: None
62 elif o in ('-r', '--recursive'):
64 diff_options.append(o)
71 sys.stderr.write(Usage)
74 def quiet_diff(a, b, fromfile='', tofile='',
75 fromfiledate='', tofiledate='', n=3, lineterm='\n'):
77 A function with the same calling signature as difflib.context_diff
78 (diff -c) and difflib.unified_diff (diff -u) but which prints
79 output like the simple, unadorned 'diff" command.
84 return ['Files %s and %s differ\n' % (fromfile, tofile)]
86 def simple_diff(a, b, fromfile='', tofile='',
87 fromfiledate='', tofiledate='', n=3, lineterm='\n'):
89 A function with the same calling signature as difflib.context_diff
90 (diff -c) and difflib.unified_diff (diff -u) but which prints
91 output like the simple, unadorned 'diff" command.
93 sm = difflib.SequenceMatcher(None, a, b)
95 return x1+1 == x2 and str(x2) or '%s,%s' % (x1+1, x2)
97 for op, a1, a2, b1, b2 in sm.get_opcodes():
99 result.append("%sd%d\n" % (comma(a1, a2), b1))
100 result.extend(['< ' + l for l in a[a1:a2]])
102 result.append("%da%s\n" % (a1, comma(b1, b2)))
103 result.extend(['> ' + l for l in b[b1:b2]])
104 elif op == 'replace':
105 result.append("%sc%s\n" % (comma(a1, a2), comma(b1, b2)))
106 result.extend(['< ' + l for l in a[a1:a2]])
107 result.append('---\n')
108 result.extend(['> ' + l for l in b[b1:b2]])
112 '-c' : difflib.context_diff,
114 '-u' : difflib.unified_diff,
117 diff_function = diff_map.get(diff_type, simple_diff)
119 baseline_re = re.compile('(# |@REM )/home/\S+/baseline/')
120 comment_rev_re = re.compile('(# |@REM )(\S+) 0.96.[CD]\d+ \S+ \S+( knight)')
121 revision_re = re.compile('__revision__ = "[^"]*"')
122 build_re = re.compile('__build__ = "[^"]*"')
123 date_re = re.compile('__date__ = "[^"]*"')
125 def lines_read(file):
126 return open(file).readlines()
128 def lines_massage(file):
129 text = open(file).read()
130 text = baseline_re.sub('\\1', text)
131 text = comment_rev_re.sub('\\1\\2\\3', text)
132 text = revision_re.sub('__revision__ = "__FILE__"', text)
133 text = build_re.sub('__build__ = "0.96.92.DXXX"', text)
134 text = date_re.sub('__date__ = "2006/08/25 02:59:00"', text)
135 return text.splitlines(1)
141 lines_function = lines_map.get(edit_type, lines_massage)
143 def do_diff(left, right, diff_subdirs):
144 if os.path.isfile(left) and os.path.isfile(right):
145 diff_file(left, right)
146 elif not os.path.isdir(left):
147 diff_file(left, os.path.join(right, os.path.split(left)[1]))
148 elif not os.path.isdir(right):
149 diff_file(os.path.join(left, os.path.split(right)[1]), right)
151 diff_dir(left, right)
153 def diff_file(left, right):
154 l = lines_function(left)
155 r = lines_function(right)
156 d = diff_function(l, r, left, right, context)
160 sys.stderr.write('IndexError diffing %s and %s\n' % (left, right))
163 diff_line(left, right)
166 print 'Files %s and %s are identical' % (left, right)
168 def diff_dir(left, right):
169 llist = os.listdir(left)
170 rlist = os.listdir(right)
176 for x in sorted([ x for x in u.keys() if x[-4:] != '.pyc' ]):
179 do_diff(os.path.join(left, x),
180 os.path.join(right, x),
183 print 'Only in %s: %s' % (left, x)
185 print 'Only in %s: %s' % (right, x)
187 do_diff(left, right, True)
191 # indent-tabs-mode:nil
193 # vim: set expandtab tabstop=4 shiftwidth=4: