Eli Friedman | 77a1fe9 | 2009-07-10 20:15:12 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 2 | |
| 3 | import subprocess |
| 4 | |
| 5 | def 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 | |
| 26 | def 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 | |
Daniel Dunbar | 8f5fdbe | 2009-09-04 17:41:47 +0000 | [diff] [blame] | 36 | def cost(a, b): |
| 37 | return sum(map(dist, a + [None] * (len(b) - len(a)), b)) |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 38 | |
Daniel Dunbar | 8f5fdbe | 2009-09-04 17:41:47 +0000 | [diff] [blame] | 39 | # Normalize so a is shortest. |
| 40 | if len(b) < len(a): |
| 41 | b, a = insertMinimumPadding(b, a, dist) |
| 42 | return a,b |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 43 | |
Daniel Dunbar | 8f5fdbe | 2009-09-04 17:41:47 +0000 | [diff] [blame] | 44 | # For each None we have to insert... |
| 45 | for i in range(len(b) - len(a)): |
| 46 | # For each position we could insert it... |
| 47 | current = cost(a, b) |
| 48 | best = None |
| 49 | for j in range(len(a) + 1): |
| 50 | a_0 = a[:j] + [None] + a[j:] |
| 51 | candidate = cost(a_0, b) |
| 52 | if best is None or candidate < best[0]: |
| 53 | best = (candidate, a_0, j) |
| 54 | a = best[1] |
| 55 | return a,b |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 56 | |
| 57 | class ZipperDiff(object): |
Chris Lattner | fc8f0e1 | 2011-04-15 05:22:18 +0000 | [diff] [blame] | 58 | """ZipperDiff - Simple (slow) diff only accommodating inserts.""" |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 59 | |
| 60 | def __init__(self, a, b): |
| 61 | self.a = a |
| 62 | self.b = b |
| 63 | |
| 64 | def dist(self, a, b): |
| 65 | return a != b |
| 66 | |
| 67 | def getDiffs(self): |
| 68 | a,b = insertMinimumPadding(self.a, self.b, self.dist) |
| 69 | for aElt,bElt in zip(a,b): |
| 70 | if self.dist(aElt, bElt): |
| 71 | yield aElt,bElt |
| 72 | |
| 73 | class DriverZipperDiff(ZipperDiff): |
| 74 | def isTempFile(self, filename): |
| 75 | if filename[0] != '"' or filename[-1] != '"': |
| 76 | return False |
| 77 | return (filename.startswith('/tmp/', 1) or |
| 78 | filename.startswith('/var/', 1)) |
| 79 | |
| 80 | def dist(self, a, b): |
| 81 | if a and b and self.isTempFile(a) and self.isTempFile(b): |
| 82 | return 0 |
| 83 | return super(DriverZipperDiff, self).dist(a,b) |
| 84 | |
| 85 | class CompileInfo: |
| 86 | def __init__(self, out, err, res): |
| 87 | self.commands = [] |
| 88 | |
| 89 | # Standard out isn't used for much. |
| 90 | self.stdout = out |
| 91 | self.stderr = '' |
| 92 | |
| 93 | # FIXME: Compare error messages as well. |
| 94 | for ln in err.split('\n'): |
| 95 | if (ln == 'Using built-in specs.' or |
| 96 | ln.startswith('Target: ') or |
| 97 | ln.startswith('Configured with: ') or |
| 98 | ln.startswith('Thread model: ') or |
Daniel Dunbar | 7671591 | 2009-01-28 19:26:20 +0000 | [diff] [blame] | 99 | ln.startswith('gcc version') or |
Daniel Dunbar | ad8958c | 2009-09-04 18:35:09 +0000 | [diff] [blame] | 100 | ln.startswith('clang version')): |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 101 | pass |
| 102 | elif ln.strip().startswith('"'): |
| 103 | self.commands.append(list(splitArgs(ln))) |
| 104 | else: |
| 105 | self.stderr += ln + '\n' |
| 106 | |
| 107 | self.stderr = self.stderr.strip() |
| 108 | self.exitCode = res |
| 109 | |
| 110 | def captureDriverInfo(cmd, args): |
| 111 | p = subprocess.Popen([cmd,'-###'] + args, |
| 112 | stdin=None, |
| 113 | stdout=subprocess.PIPE, |
| 114 | stderr=subprocess.PIPE) |
| 115 | out,err = p.communicate() |
| 116 | res = p.wait() |
| 117 | return CompileInfo(out,err,res) |
| 118 | |
| 119 | def main(): |
Daniel Dunbar | 2253dc4 | 2009-01-14 20:06:04 +0000 | [diff] [blame] | 120 | import os, sys |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 121 | |
| 122 | args = sys.argv[1:] |
Daniel Dunbar | 2253dc4 | 2009-01-14 20:06:04 +0000 | [diff] [blame] | 123 | driverA = os.getenv('DRIVER_A') or 'gcc' |
Daniel Dunbar | 8f5fdbe | 2009-09-04 17:41:47 +0000 | [diff] [blame] | 124 | driverB = os.getenv('DRIVER_B') or 'clang' |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 125 | |
| 126 | infoA = captureDriverInfo(driverA, args) |
| 127 | infoB = captureDriverInfo(driverB, args) |
| 128 | |
Daniel Dunbar | c97c05f | 2009-01-17 00:50:45 +0000 | [diff] [blame] | 129 | differ = False |
| 130 | |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 131 | # Compare stdout. |
| 132 | if infoA.stdout != infoB.stdout: |
| 133 | print '-- STDOUT DIFFERS -' |
Daniel Dunbar | ad8958c | 2009-09-04 18:35:09 +0000 | [diff] [blame] | 134 | print 'A OUTPUT: ',infoA.stdout |
| 135 | print 'B OUTPUT: ',infoB.stdout |
| 136 | print |
| 137 | |
| 138 | diff = ZipperDiff(infoA.stdout.split('\n'), |
| 139 | infoB.stdout.split('\n')) |
| 140 | for i,(aElt,bElt) in enumerate(diff.getDiffs()): |
| 141 | if aElt is None: |
| 142 | print 'A missing: %s' % bElt |
| 143 | elif bElt is None: |
| 144 | print 'B missing: %s' % aElt |
| 145 | else: |
| 146 | print 'mismatch: A: %s' % aElt |
| 147 | print ' B: %s' % bElt |
| 148 | |
Daniel Dunbar | c97c05f | 2009-01-17 00:50:45 +0000 | [diff] [blame] | 149 | differ = True |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 150 | |
| 151 | # Compare stderr. |
| 152 | if infoA.stderr != infoB.stderr: |
| 153 | print '-- STDERR DIFFERS -' |
Daniel Dunbar | ad8958c | 2009-09-04 18:35:09 +0000 | [diff] [blame] | 154 | print 'A STDERR: ',infoA.stderr |
| 155 | print 'B STDERR: ',infoB.stderr |
| 156 | print |
| 157 | |
| 158 | diff = ZipperDiff(infoA.stderr.split('\n'), |
| 159 | infoB.stderr.split('\n')) |
| 160 | for i,(aElt,bElt) in enumerate(diff.getDiffs()): |
| 161 | if aElt is None: |
| 162 | print 'A missing: %s' % bElt |
| 163 | elif bElt is None: |
| 164 | print 'B missing: %s' % aElt |
| 165 | else: |
| 166 | print 'mismatch: A: %s' % aElt |
| 167 | print ' B: %s' % bElt |
| 168 | |
Daniel Dunbar | c97c05f | 2009-01-17 00:50:45 +0000 | [diff] [blame] | 169 | differ = True |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 170 | |
| 171 | # Compare commands. |
Daniel Dunbar | 163f31a | 2009-01-21 23:34:23 +0000 | [diff] [blame] | 172 | for i,(a,b) in enumerate(map(None, infoA.commands, infoB.commands)): |
| 173 | if a is None: |
| 174 | print 'A MISSING:',' '.join(b) |
| 175 | differ = True |
| 176 | continue |
| 177 | elif b is None: |
| 178 | print 'B MISSING:',' '.join(a) |
| 179 | differ = True |
| 180 | continue |
| 181 | |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 182 | diff = DriverZipperDiff(a,b) |
| 183 | diffs = list(diff.getDiffs()) |
| 184 | if diffs: |
| 185 | print '-- COMMAND %d DIFFERS -' % i |
| 186 | print 'A COMMAND:',' '.join(a) |
| 187 | print 'B COMMAND:',' '.join(b) |
| 188 | print |
| 189 | for i,(aElt,bElt) in enumerate(diffs): |
| 190 | if aElt is None: |
| 191 | print 'A missing: %s' % bElt |
| 192 | elif bElt is None: |
| 193 | print 'B missing: %s' % aElt |
| 194 | else: |
| 195 | print 'mismatch: A: %s' % aElt |
| 196 | print ' B: %s' % bElt |
Daniel Dunbar | 06172d6 | 2009-01-20 00:47:24 +0000 | [diff] [blame] | 197 | differ = True |
Daniel Dunbar | 163f31a | 2009-01-21 23:34:23 +0000 | [diff] [blame] | 198 | |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 199 | # Compare result codes. |
| 200 | if infoA.exitCode != infoB.exitCode: |
| 201 | print '-- EXIT CODES DIFFER -' |
| 202 | print 'A: ',infoA.exitCode |
| 203 | print 'B: ',infoB.exitCode |
Daniel Dunbar | c97c05f | 2009-01-17 00:50:45 +0000 | [diff] [blame] | 204 | differ = True |
| 205 | |
| 206 | if differ: |
| 207 | sys.exit(1) |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 208 | |
| 209 | if __name__ == '__main__': |
Daniel Dunbar | 8f5fdbe | 2009-09-04 17:41:47 +0000 | [diff] [blame] | 210 | main() |