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