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