From 72c578fd4b0b4a5a43e18594339ac4ff26c376dc Mon Sep 17 00:00:00 2001 From: Luca Falavigna Date: Sat, 2 Jan 2010 20:56:27 +0100 Subject: Imported Upstream version 1.2.0.d20091224 --- bin/scons-diff.py | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 bin/scons-diff.py (limited to 'bin/scons-diff.py') diff --git a/bin/scons-diff.py b/bin/scons-diff.py new file mode 100644 index 0000000..11d1bda --- /dev/null +++ b/bin/scons-diff.py @@ -0,0 +1,195 @@ +#!/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 in ('-n'): + diff_options.append(o) + edit_type = o + elif o in ('-q'): + diff_type = o + diff_line = lambda l, r: None + elif o in ('-r', '--recursive'): + recursive = True + diff_options.append(o) + elif o in ('-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(map(lambda l: '< ' + l, a[a1:a2])) + elif op == 'insert': + result.append("%da%s\n" % (a1, comma(b1, b2))) + result.extend(map(lambda l: '> ' + l, b[b1:b2])) + elif op == 'replace': + result.append("%sc%s\n" % (comma(a1, a2), comma(b1, b2))) + result.extend(map(lambda l: '< ' + l, a[a1:a2])) + result.append('---\n') + result.extend(map(lambda l: '> ' + l, 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__ = "bin/scons-diff.py"', 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 + clist = [ x for x in u.keys() if x[-4:] != '.pyc' ] + clist.sort() + for x in clist: + 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: -- cgit v1.2.3