| #!/usr/bin/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 |
| |
| def insertMinimumPadding(a, b, dist): |
| """insertMinimumPadding(a,b) -> (a',b') |
| |
| 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. |
| """ |
| |
| # Yay for simplicity over complexity. |
| |
| def extend(aElt, bElt, solution): |
| d0,(a0,b0) = solution |
| return d0 + dist(aElt,bElt), (([aElt]+a0),([bElt]+b0)) |
| |
| def f(a, b): |
| if len(a) == len(b): |
| return (sum(map(dist, a, b)), (a, b)) |
| |
| if not a or not b: |
| if not a: |
| a += [None] * len(b) |
| else: |
| b += [None] * len(a) |
| return (sum(map(dist, a, b)), (a, b)) |
| |
| if int(dist(a[0], b[0])) == 0: |
| # Non-negative condition implies maximum is satisfied |
| # taking this. |
| return extend(a[0], b[0], f(a[1:], b[1:])) |
| |
| if len(a) < len(b): |
| return min(f([None] + a, b), |
| extend(a[0], b[0], f(a[1:], b[1:]))) |
| else: |
| return min(f(a, [None] + b), |
| extend(a[0], b[0], f(a[1:], b[1:]))) |
| |
| return f(a, b)[1] |
| |
| class ZipperDiff(object): |
| """ZipperDiff - Simple (slow) diff only accomodating inserts.""" |
| |
| def __init__(self, a, b): |
| self.a = a |
| self.b = b |
| |
| def dist(self, a, b): |
| return a != b |
| |
| def getDiffs(self): |
| a,b = insertMinimumPadding(self.a, self.b, self.dist) |
| 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('Thread model: ') or |
| ln.startswith('gcc 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 'xcc' |
| |
| infoA = captureDriverInfo(driverA, args) |
| infoB = captureDriverInfo(driverB, args) |
| |
| differ = False |
| |
| # Compare stdout. |
| if infoA.stdout != infoB.stdout: |
| print '-- STDOUT DIFFERS -' |
| print 'A: ',infoA.stdout |
| print 'B: ',infoB.stdout |
| differ = True |
| |
| # Compare stderr. |
| if infoA.stderr != infoB.stderr: |
| print '-- STDERR DIFFERS -' |
| print 'A: ',infoA.stderr |
| print 'B: ',infoB.stderr |
| differ = True |
| |
| # Compare commands. |
| for i,(a,b) in enumerate(zip(infoA.commands, infoB.commands)): |
| 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() |