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 | |
| 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 | |
| 67 | class 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 | |
| 83 | class 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 | |
| 95 | class 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 |
Daniel Dunbar | 7671591 | 2009-01-28 19:26:20 +0000 | [diff] [blame] | 109 | ln.startswith('gcc version') or |
| 110 | ln.startswith('ccc version')): |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 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 | |
| 120 | def 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 | |
| 129 | def main(): |
Daniel Dunbar | 2253dc4 | 2009-01-14 20:06:04 +0000 | [diff] [blame] | 130 | import os, sys |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 131 | |
| 132 | args = sys.argv[1:] |
Daniel Dunbar | 2253dc4 | 2009-01-14 20:06:04 +0000 | [diff] [blame] | 133 | driverA = os.getenv('DRIVER_A') or 'gcc' |
| 134 | driverB = os.getenv('DRIVER_B') or 'xcc' |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 135 | |
| 136 | infoA = captureDriverInfo(driverA, args) |
| 137 | infoB = captureDriverInfo(driverB, args) |
| 138 | |
Daniel Dunbar | c97c05f | 2009-01-17 00:50:45 +0000 | [diff] [blame] | 139 | differ = False |
| 140 | |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 141 | # Compare stdout. |
| 142 | if infoA.stdout != infoB.stdout: |
| 143 | print '-- STDOUT DIFFERS -' |
| 144 | print 'A: ',infoA.stdout |
| 145 | print 'B: ',infoB.stdout |
Daniel Dunbar | c97c05f | 2009-01-17 00:50:45 +0000 | [diff] [blame] | 146 | differ = True |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 147 | |
| 148 | # Compare stderr. |
| 149 | if infoA.stderr != infoB.stderr: |
| 150 | print '-- STDERR DIFFERS -' |
| 151 | print 'A: ',infoA.stderr |
| 152 | print 'B: ',infoB.stderr |
Daniel Dunbar | c97c05f | 2009-01-17 00:50:45 +0000 | [diff] [blame] | 153 | differ = True |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 154 | |
| 155 | # Compare commands. |
Daniel Dunbar | 163f31a | 2009-01-21 23:34:23 +0000 | [diff] [blame] | 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 | |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 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 |
Daniel Dunbar | 06172d6 | 2009-01-20 00:47:24 +0000 | [diff] [blame] | 181 | differ = True |
Daniel Dunbar | 163f31a | 2009-01-21 23:34:23 +0000 | [diff] [blame] | 182 | |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 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 |
Daniel Dunbar | c97c05f | 2009-01-17 00:50:45 +0000 | [diff] [blame] | 188 | differ = True |
| 189 | |
| 190 | if differ: |
| 191 | sys.exit(1) |
Daniel Dunbar | ab13061 | 2009-01-13 07:38:29 +0000 | [diff] [blame] | 192 | |
| 193 | if __name__ == '__main__': |
| 194 | main() |