Revision 1b57c8e01a682145f576e101c868ba84ea38ec1b authored by ah744 on 06 August 2016, 01:41:28 UTC, committed by ah744 on 06 August 2016, 01:41:28 UTC
1 parent 2eae3de
CmpDriver
``````#!/usr/bin/env python

import subprocess

def splitArgs(s):
it = iter(s)
current = ''
inQuote = False
for c in it:
if c == '"':
if inQuote:
inQuote = False
yield current + '"'
else:
inQuote = True
current = '"'
elif inQuote:
if c == '\\':
current += c
current += it.next()
else:
current += c
elif not c.isspace():
yield c

Return two lists of equal length, where some number of Nones have
been inserted into the shorter list such that sum(map(dist, a',
b')) is minimized.

Assumes dist(X, Y) -> int and non-negative.
"""

def cost(a, b):
return sum(map(dist, a + [None] * (len(b) - len(a)), b))

# Normalize so a is shortest.
if len(b) < len(a):
b, a = insertMinimumPadding(b, a, dist)
return a,b

# For each None we have to insert...
for i in range(len(b) - len(a)):
# For each position we could insert it...
current = cost(a, b)
best = None
for j in range(len(a) + 1):
a_0 = a[:j] + [None] + a[j:]
candidate = cost(a_0, b)
if best is None or candidate < best[0]:
best = (candidate, a_0, j)
a = best[1]
return a,b

class ZipperDiff(object):
"""ZipperDiff - Simple (slow) diff only accommodating inserts."""

def __init__(self, a, b):
self.a = a
self.b = b

def dist(self, a, b):
return a != b

def getDiffs(self):
for aElt,bElt in zip(a,b):
if self.dist(aElt, bElt):
yield aElt,bElt

class DriverZipperDiff(ZipperDiff):
def isTempFile(self, filename):
if filename[0] != '"' or filename[-1] != '"':
return False
return (filename.startswith('/tmp/', 1) or
filename.startswith('/var/', 1))

def dist(self, a, b):
if a and b and self.isTempFile(a) and self.isTempFile(b):
return 0
return super(DriverZipperDiff, self).dist(a,b)

class CompileInfo:
def __init__(self, out, err, res):
self.commands = []

# Standard out isn't used for much.
self.stdout = out
self.stderr = ''

# FIXME: Compare error messages as well.
for ln in err.split('\n'):
if (ln == 'Using built-in specs.' or
ln.startswith('Target: ') or
ln.startswith('Configured with: ') or
ln.startswith('gcc version') or
ln.startswith('clang version')):
pass
elif ln.strip().startswith('"'):
self.commands.append(list(splitArgs(ln)))
else:
self.stderr += ln + '\n'

self.stderr = self.stderr.strip()
self.exitCode = res

def captureDriverInfo(cmd, args):
p = subprocess.Popen([cmd,'-###'] + args,
stdin=None,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out,err = p.communicate()
res = p.wait()
return CompileInfo(out,err,res)

def main():
import os, sys

args = sys.argv[1:]
driverA = os.getenv('DRIVER_A') or 'gcc'
driverB = os.getenv('DRIVER_B') or 'clang'

infoA = captureDriverInfo(driverA, args)
infoB = captureDriverInfo(driverB, args)

differ = False

# Compare stdout.
if infoA.stdout != infoB.stdout:
print '-- STDOUT DIFFERS -'
print 'A OUTPUT: ',infoA.stdout
print 'B OUTPUT: ',infoB.stdout
print

diff = ZipperDiff(infoA.stdout.split('\n'),
infoB.stdout.split('\n'))
for i,(aElt,bElt) in enumerate(diff.getDiffs()):
if aElt is None:
print 'A missing: %s' % bElt
elif bElt is None:
print 'B missing: %s' % aElt
else:
print 'mismatch: A: %s' % aElt
print '          B: %s' % bElt

differ = True

# Compare stderr.
if infoA.stderr != infoB.stderr:
print '-- STDERR DIFFERS -'
print 'A STDERR: ',infoA.stderr
print 'B STDERR: ',infoB.stderr
print

diff = ZipperDiff(infoA.stderr.split('\n'),
infoB.stderr.split('\n'))
for i,(aElt,bElt) in enumerate(diff.getDiffs()):
if aElt is None:
print 'A missing: %s' % bElt
elif bElt is None:
print 'B missing: %s' % aElt
else:
print 'mismatch: A: %s' % aElt
print '          B: %s' % bElt

differ = True

# Compare commands.
for i,(a,b) in enumerate(map(None, infoA.commands, infoB.commands)):
if a is None:
print 'A MISSING:',' '.join(b)
differ = True
continue
elif b is None:
print 'B MISSING:',' '.join(a)
differ = True
continue

diff = DriverZipperDiff(a,b)
diffs = list(diff.getDiffs())
if diffs:
print '-- COMMAND %d DIFFERS -' % i
print 'A COMMAND:',' '.join(a)
print 'B COMMAND:',' '.join(b)
print
for i,(aElt,bElt) in enumerate(diffs):
if aElt is None:
print 'A missing: %s' % bElt
elif bElt is None:
print 'B missing: %s' % aElt
else:
print 'mismatch: A: %s' % aElt
print '          B: %s' % bElt
differ = True

# Compare result codes.
if infoA.exitCode != infoB.exitCode:
print '-- EXIT CODES DIFFER -'
print 'A: ',infoA.exitCode
print 'B: ',infoB.exitCode
differ = True

if differ:
sys.exit(1)

if __name__ == '__main__':
main()
``````

Computing file changes ...