1*e5dd7070Spatrick#!/usr/bin/env python 2*e5dd7070Spatrick 3*e5dd7070Spatrick""" 4*e5dd7070SpatrickA simple utility that compares tool invocations and exit codes issued by 5*e5dd7070Spatrickcompiler drivers that support -### (e.g. gcc and clang). 6*e5dd7070Spatrick""" 7*e5dd7070Spatrick 8*e5dd7070Spatrickimport subprocess 9*e5dd7070Spatrick 10*e5dd7070Spatrickdef splitArgs(s): 11*e5dd7070Spatrick it = iter(s) 12*e5dd7070Spatrick current = '' 13*e5dd7070Spatrick inQuote = False 14*e5dd7070Spatrick for c in it: 15*e5dd7070Spatrick if c == '"': 16*e5dd7070Spatrick if inQuote: 17*e5dd7070Spatrick inQuote = False 18*e5dd7070Spatrick yield current + '"' 19*e5dd7070Spatrick else: 20*e5dd7070Spatrick inQuote = True 21*e5dd7070Spatrick current = '"' 22*e5dd7070Spatrick elif inQuote: 23*e5dd7070Spatrick if c == '\\': 24*e5dd7070Spatrick current += c 25*e5dd7070Spatrick current += it.next() 26*e5dd7070Spatrick else: 27*e5dd7070Spatrick current += c 28*e5dd7070Spatrick elif not c.isspace(): 29*e5dd7070Spatrick yield c 30*e5dd7070Spatrick 31*e5dd7070Spatrickdef insertMinimumPadding(a, b, dist): 32*e5dd7070Spatrick """insertMinimumPadding(a,b) -> (a',b') 33*e5dd7070Spatrick 34*e5dd7070Spatrick Return two lists of equal length, where some number of Nones have 35*e5dd7070Spatrick been inserted into the shorter list such that sum(map(dist, a', 36*e5dd7070Spatrick b')) is minimized. 37*e5dd7070Spatrick 38*e5dd7070Spatrick Assumes dist(X, Y) -> int and non-negative. 39*e5dd7070Spatrick """ 40*e5dd7070Spatrick 41*e5dd7070Spatrick def cost(a, b): 42*e5dd7070Spatrick return sum(map(dist, a + [None] * (len(b) - len(a)), b)) 43*e5dd7070Spatrick 44*e5dd7070Spatrick # Normalize so a is shortest. 45*e5dd7070Spatrick if len(b) < len(a): 46*e5dd7070Spatrick b, a = insertMinimumPadding(b, a, dist) 47*e5dd7070Spatrick return a,b 48*e5dd7070Spatrick 49*e5dd7070Spatrick # For each None we have to insert... 50*e5dd7070Spatrick for i in range(len(b) - len(a)): 51*e5dd7070Spatrick # For each position we could insert it... 52*e5dd7070Spatrick current = cost(a, b) 53*e5dd7070Spatrick best = None 54*e5dd7070Spatrick for j in range(len(a) + 1): 55*e5dd7070Spatrick a_0 = a[:j] + [None] + a[j:] 56*e5dd7070Spatrick candidate = cost(a_0, b) 57*e5dd7070Spatrick if best is None or candidate < best[0]: 58*e5dd7070Spatrick best = (candidate, a_0, j) 59*e5dd7070Spatrick a = best[1] 60*e5dd7070Spatrick return a,b 61*e5dd7070Spatrick 62*e5dd7070Spatrickclass ZipperDiff(object): 63*e5dd7070Spatrick """ZipperDiff - Simple (slow) diff only accommodating inserts.""" 64*e5dd7070Spatrick 65*e5dd7070Spatrick def __init__(self, a, b): 66*e5dd7070Spatrick self.a = a 67*e5dd7070Spatrick self.b = b 68*e5dd7070Spatrick 69*e5dd7070Spatrick def dist(self, a, b): 70*e5dd7070Spatrick return a != b 71*e5dd7070Spatrick 72*e5dd7070Spatrick def getDiffs(self): 73*e5dd7070Spatrick a,b = insertMinimumPadding(self.a, self.b, self.dist) 74*e5dd7070Spatrick for aElt,bElt in zip(a,b): 75*e5dd7070Spatrick if self.dist(aElt, bElt): 76*e5dd7070Spatrick yield aElt,bElt 77*e5dd7070Spatrick 78*e5dd7070Spatrickclass DriverZipperDiff(ZipperDiff): 79*e5dd7070Spatrick def isTempFile(self, filename): 80*e5dd7070Spatrick if filename[0] != '"' or filename[-1] != '"': 81*e5dd7070Spatrick return False 82*e5dd7070Spatrick return (filename.startswith('/tmp/', 1) or 83*e5dd7070Spatrick filename.startswith('/var/', 1)) 84*e5dd7070Spatrick 85*e5dd7070Spatrick def dist(self, a, b): 86*e5dd7070Spatrick if a and b and self.isTempFile(a) and self.isTempFile(b): 87*e5dd7070Spatrick return 0 88*e5dd7070Spatrick return super(DriverZipperDiff, self).dist(a,b) 89*e5dd7070Spatrick 90*e5dd7070Spatrickclass CompileInfo: 91*e5dd7070Spatrick def __init__(self, out, err, res): 92*e5dd7070Spatrick self.commands = [] 93*e5dd7070Spatrick 94*e5dd7070Spatrick # Standard out isn't used for much. 95*e5dd7070Spatrick self.stdout = out 96*e5dd7070Spatrick self.stderr = '' 97*e5dd7070Spatrick 98*e5dd7070Spatrick # FIXME: Compare error messages as well. 99*e5dd7070Spatrick for ln in err.split('\n'): 100*e5dd7070Spatrick if (ln == 'Using built-in specs.' or 101*e5dd7070Spatrick ln.startswith('Target: ') or 102*e5dd7070Spatrick ln.startswith('Configured with: ') or 103*e5dd7070Spatrick ln.startswith('Thread model: ') or 104*e5dd7070Spatrick ln.startswith('gcc version') or 105*e5dd7070Spatrick ln.startswith('clang version')): 106*e5dd7070Spatrick pass 107*e5dd7070Spatrick elif ln.strip().startswith('"'): 108*e5dd7070Spatrick self.commands.append(list(splitArgs(ln))) 109*e5dd7070Spatrick else: 110*e5dd7070Spatrick self.stderr += ln + '\n' 111*e5dd7070Spatrick 112*e5dd7070Spatrick self.stderr = self.stderr.strip() 113*e5dd7070Spatrick self.exitCode = res 114*e5dd7070Spatrick 115*e5dd7070Spatrickdef captureDriverInfo(cmd, args): 116*e5dd7070Spatrick p = subprocess.Popen([cmd,'-###'] + args, 117*e5dd7070Spatrick stdin=None, 118*e5dd7070Spatrick stdout=subprocess.PIPE, 119*e5dd7070Spatrick stderr=subprocess.PIPE) 120*e5dd7070Spatrick out,err = p.communicate() 121*e5dd7070Spatrick res = p.wait() 122*e5dd7070Spatrick return CompileInfo(out,err,res) 123*e5dd7070Spatrick 124*e5dd7070Spatrickdef main(): 125*e5dd7070Spatrick import os, sys 126*e5dd7070Spatrick 127*e5dd7070Spatrick args = sys.argv[1:] 128*e5dd7070Spatrick driverA = os.getenv('DRIVER_A') or 'gcc' 129*e5dd7070Spatrick driverB = os.getenv('DRIVER_B') or 'clang' 130*e5dd7070Spatrick 131*e5dd7070Spatrick infoA = captureDriverInfo(driverA, args) 132*e5dd7070Spatrick infoB = captureDriverInfo(driverB, args) 133*e5dd7070Spatrick 134*e5dd7070Spatrick differ = False 135*e5dd7070Spatrick 136*e5dd7070Spatrick # Compare stdout. 137*e5dd7070Spatrick if infoA.stdout != infoB.stdout: 138*e5dd7070Spatrick print '-- STDOUT DIFFERS -' 139*e5dd7070Spatrick print 'A OUTPUT: ',infoA.stdout 140*e5dd7070Spatrick print 'B OUTPUT: ',infoB.stdout 141*e5dd7070Spatrick print 142*e5dd7070Spatrick 143*e5dd7070Spatrick diff = ZipperDiff(infoA.stdout.split('\n'), 144*e5dd7070Spatrick infoB.stdout.split('\n')) 145*e5dd7070Spatrick for i,(aElt,bElt) in enumerate(diff.getDiffs()): 146*e5dd7070Spatrick if aElt is None: 147*e5dd7070Spatrick print 'A missing: %s' % bElt 148*e5dd7070Spatrick elif bElt is None: 149*e5dd7070Spatrick print 'B missing: %s' % aElt 150*e5dd7070Spatrick else: 151*e5dd7070Spatrick print 'mismatch: A: %s' % aElt 152*e5dd7070Spatrick print ' B: %s' % bElt 153*e5dd7070Spatrick 154*e5dd7070Spatrick differ = True 155*e5dd7070Spatrick 156*e5dd7070Spatrick # Compare stderr. 157*e5dd7070Spatrick if infoA.stderr != infoB.stderr: 158*e5dd7070Spatrick print '-- STDERR DIFFERS -' 159*e5dd7070Spatrick print 'A STDERR: ',infoA.stderr 160*e5dd7070Spatrick print 'B STDERR: ',infoB.stderr 161*e5dd7070Spatrick print 162*e5dd7070Spatrick 163*e5dd7070Spatrick diff = ZipperDiff(infoA.stderr.split('\n'), 164*e5dd7070Spatrick infoB.stderr.split('\n')) 165*e5dd7070Spatrick for i,(aElt,bElt) in enumerate(diff.getDiffs()): 166*e5dd7070Spatrick if aElt is None: 167*e5dd7070Spatrick print 'A missing: %s' % bElt 168*e5dd7070Spatrick elif bElt is None: 169*e5dd7070Spatrick print 'B missing: %s' % aElt 170*e5dd7070Spatrick else: 171*e5dd7070Spatrick print 'mismatch: A: %s' % aElt 172*e5dd7070Spatrick print ' B: %s' % bElt 173*e5dd7070Spatrick 174*e5dd7070Spatrick differ = True 175*e5dd7070Spatrick 176*e5dd7070Spatrick # Compare commands. 177*e5dd7070Spatrick for i,(a,b) in enumerate(map(None, infoA.commands, infoB.commands)): 178*e5dd7070Spatrick if a is None: 179*e5dd7070Spatrick print 'A MISSING:',' '.join(b) 180*e5dd7070Spatrick differ = True 181*e5dd7070Spatrick continue 182*e5dd7070Spatrick elif b is None: 183*e5dd7070Spatrick print 'B MISSING:',' '.join(a) 184*e5dd7070Spatrick differ = True 185*e5dd7070Spatrick continue 186*e5dd7070Spatrick 187*e5dd7070Spatrick diff = DriverZipperDiff(a,b) 188*e5dd7070Spatrick diffs = list(diff.getDiffs()) 189*e5dd7070Spatrick if diffs: 190*e5dd7070Spatrick print '-- COMMAND %d DIFFERS -' % i 191*e5dd7070Spatrick print 'A COMMAND:',' '.join(a) 192*e5dd7070Spatrick print 'B COMMAND:',' '.join(b) 193*e5dd7070Spatrick print 194*e5dd7070Spatrick for i,(aElt,bElt) in enumerate(diffs): 195*e5dd7070Spatrick if aElt is None: 196*e5dd7070Spatrick print 'A missing: %s' % bElt 197*e5dd7070Spatrick elif bElt is None: 198*e5dd7070Spatrick print 'B missing: %s' % aElt 199*e5dd7070Spatrick else: 200*e5dd7070Spatrick print 'mismatch: A: %s' % aElt 201*e5dd7070Spatrick print ' B: %s' % bElt 202*e5dd7070Spatrick differ = True 203*e5dd7070Spatrick 204*e5dd7070Spatrick # Compare result codes. 205*e5dd7070Spatrick if infoA.exitCode != infoB.exitCode: 206*e5dd7070Spatrick print '-- EXIT CODES DIFFER -' 207*e5dd7070Spatrick print 'A: ',infoA.exitCode 208*e5dd7070Spatrick print 'B: ',infoB.exitCode 209*e5dd7070Spatrick differ = True 210*e5dd7070Spatrick 211*e5dd7070Spatrick if differ: 212*e5dd7070Spatrick sys.exit(1) 213*e5dd7070Spatrick 214*e5dd7070Spatrickif __name__ == '__main__': 215*e5dd7070Spatrick main() 216