mirror of
https://github.com/Relintai/scons_gd.git
synced 2025-02-10 16:40:14 +01:00
193 lines
5.9 KiB
Python
193 lines
5.9 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# scons-diff.py - diff-like utility for comparing SCons trees
|
|
#
|
|
# This supports most common diff options (with some quirks, like you can't
|
|
# just say -c and have it use a default value), but canonicalizes the
|
|
# various version strings within the file like __revision__, __build__,
|
|
# etc. so that you can diff trees without having to ignore changes in
|
|
# version lines.
|
|
#
|
|
import difflib
|
|
import getopt
|
|
import os.path
|
|
import re
|
|
import sys
|
|
|
|
Usage = """\
|
|
Usage: scons-diff.py [OPTIONS] dir1 dir2
|
|
Options:
|
|
-c NUM, --context=NUM Print NUM lines of copied context.
|
|
-h, --help Print this message and exit.
|
|
-n Don't canonicalize SCons lines.
|
|
-q, --quiet Print only whether files differ.
|
|
-r, --recursive Recursively compare found subdirectories.
|
|
-s Report when two files are the same.
|
|
-u NUM, --unified=NUM Print NUM lines of unified context.
|
|
"""
|
|
|
|
opts, args = getopt.getopt(sys.argv[1:],
|
|
'c:dhnqrsu:',
|
|
['context=', 'help', 'recursive', 'unified='])
|
|
|
|
diff_type = None
|
|
edit_type = None
|
|
context = 2
|
|
recursive = False
|
|
report_same = False
|
|
diff_options = []
|
|
|
|
def diff_line(left, right):
|
|
if diff_options:
|
|
opts = ' ' + ' '.join(diff_options)
|
|
else:
|
|
opts = ''
|
|
print('diff%s %s %s' % (opts, left, right))
|
|
|
|
for o, a in opts:
|
|
if o in ('-c', '-u'):
|
|
diff_type = o
|
|
context = int(a)
|
|
diff_options.append(o)
|
|
elif o in ('-h', '--help'):
|
|
print(Usage)
|
|
sys.exit(0)
|
|
elif o == '-n':
|
|
diff_options.append(o)
|
|
edit_type = o
|
|
elif o == '-q':
|
|
diff_type = o
|
|
diff_line = lambda l, r: None
|
|
elif o in ('-r', '--recursive'):
|
|
recursive = True
|
|
diff_options.append(o)
|
|
elif o == '-s':
|
|
report_same = True
|
|
|
|
try:
|
|
left, right = args
|
|
except ValueError:
|
|
sys.stderr.write(Usage)
|
|
sys.exit(1)
|
|
|
|
def quiet_diff(a, b, fromfile='', tofile='',
|
|
fromfiledate='', tofiledate='', n=3, lineterm='\n'):
|
|
"""
|
|
A function with the same calling signature as difflib.context_diff
|
|
(diff -c) and difflib.unified_diff (diff -u) but which prints
|
|
output like the simple, unadorned 'diff" command.
|
|
"""
|
|
if a == b:
|
|
return []
|
|
else:
|
|
return ['Files %s and %s differ\n' % (fromfile, tofile)]
|
|
|
|
def simple_diff(a, b, fromfile='', tofile='',
|
|
fromfiledate='', tofiledate='', n=3, lineterm='\n'):
|
|
"""
|
|
A function with the same calling signature as difflib.context_diff
|
|
(diff -c) and difflib.unified_diff (diff -u) but which prints
|
|
output like the simple, unadorned 'diff" command.
|
|
"""
|
|
sm = difflib.SequenceMatcher(None, a, b)
|
|
def comma(x1, x2):
|
|
return x1+1 == x2 and str(x2) or '%s,%s' % (x1+1, x2)
|
|
result = []
|
|
for op, a1, a2, b1, b2 in sm.get_opcodes():
|
|
if op == 'delete':
|
|
result.append("%sd%d\n" % (comma(a1, a2), b1))
|
|
result.extend(['< ' + l for l in a[a1:a2]])
|
|
elif op == 'insert':
|
|
result.append("%da%s\n" % (a1, comma(b1, b2)))
|
|
result.extend(['> ' + l for l in b[b1:b2]])
|
|
elif op == 'replace':
|
|
result.append("%sc%s\n" % (comma(a1, a2), comma(b1, b2)))
|
|
result.extend(['< ' + l for l in a[a1:a2]])
|
|
result.append('---\n')
|
|
result.extend(['> ' + l for l in b[b1:b2]])
|
|
return result
|
|
|
|
diff_map = {
|
|
'-c' : difflib.context_diff,
|
|
'-q' : quiet_diff,
|
|
'-u' : difflib.unified_diff,
|
|
}
|
|
|
|
diff_function = diff_map.get(diff_type, simple_diff)
|
|
|
|
baseline_re = re.compile('(# |@REM )/home/\S+/baseline/')
|
|
comment_rev_re = re.compile('(# |@REM )(\S+) 0.96.[CD]\d+ \S+ \S+( knight)')
|
|
revision_re = re.compile('__revision__ = "[^"]*"')
|
|
build_re = re.compile('__build__ = "[^"]*"')
|
|
date_re = re.compile('__date__ = "[^"]*"')
|
|
|
|
def lines_read(file):
|
|
return open(file).readlines()
|
|
|
|
def lines_massage(file):
|
|
text = open(file).read()
|
|
text = baseline_re.sub('\\1', text)
|
|
text = comment_rev_re.sub('\\1\\2\\3', text)
|
|
text = revision_re.sub('__revision__ = "__FILE__"', text)
|
|
text = build_re.sub('__build__ = "0.96.92.DXXX"', text)
|
|
text = date_re.sub('__date__ = "2006/08/25 02:59:00"', text)
|
|
return text.splitlines(1)
|
|
|
|
lines_map = {
|
|
'-n' : lines_read,
|
|
}
|
|
|
|
lines_function = lines_map.get(edit_type, lines_massage)
|
|
|
|
def do_diff(left, right, diff_subdirs):
|
|
if os.path.isfile(left) and os.path.isfile(right):
|
|
diff_file(left, right)
|
|
elif not os.path.isdir(left):
|
|
diff_file(left, os.path.join(right, os.path.split(left)[1]))
|
|
elif not os.path.isdir(right):
|
|
diff_file(os.path.join(left, os.path.split(right)[1]), right)
|
|
elif diff_subdirs:
|
|
diff_dir(left, right)
|
|
|
|
def diff_file(left, right):
|
|
l = lines_function(left)
|
|
r = lines_function(right)
|
|
d = diff_function(l, r, left, right, context)
|
|
try:
|
|
text = ''.join(d)
|
|
except IndexError:
|
|
sys.stderr.write('IndexError diffing %s and %s\n' % (left, right))
|
|
else:
|
|
if text:
|
|
diff_line(left, right)
|
|
print(text)
|
|
elif report_same:
|
|
print('Files %s and %s are identical' % (left, right))
|
|
|
|
def diff_dir(left, right):
|
|
llist = os.listdir(left)
|
|
rlist = os.listdir(right)
|
|
u = {}
|
|
for l in llist:
|
|
u[l] = 1
|
|
for r in rlist:
|
|
u[r] = 1
|
|
for x in sorted([ x for x in list(u.keys()) if x[-4:] != '.pyc' ]):
|
|
if x in llist:
|
|
if x in rlist:
|
|
do_diff(os.path.join(left, x),
|
|
os.path.join(right, x),
|
|
recursive)
|
|
else:
|
|
print('Only in %s: %s' % (left, x))
|
|
else:
|
|
print('Only in %s: %s' % (right, x))
|
|
|
|
do_diff(left, right, True)
|
|
|
|
# Local Variables:
|
|
# tab-width:4
|
|
# indent-tabs-mode:nil
|
|
# End:
|
|
# vim: set expandtab tabstop=4 shiftwidth=4:
|