blob: ad75809d754c5082aeb431abcb97aa9f6f5b9b67 [file] [log] [blame]
Eli Friedman77a1fe92009-07-10 20:15:12 +00001#!/usr/bin/env python
Daniel Dunbarab130612009-01-13 07:38:29 +00002
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
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +000036 def cost(a, b):
37 return sum(map(dist, a + [None] * (len(b) - len(a)), b))
Daniel Dunbarab130612009-01-13 07:38:29 +000038
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +000039 # Normalize so a is shortest.
40 if len(b) < len(a):
41 b, a = insertMinimumPadding(b, a, dist)
42 return a,b
Daniel Dunbarab130612009-01-13 07:38:29 +000043
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +000044 # 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 Dunbarab130612009-01-13 07:38:29 +000056
57class ZipperDiff(object):
58 """ZipperDiff - Simple (slow) diff only accomodating inserts."""
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
73class 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
85class 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 Dunbar76715912009-01-28 19:26:20 +000099 ln.startswith('gcc version') or
100 ln.startswith('ccc version')):
Daniel Dunbarab130612009-01-13 07:38:29 +0000101 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
110def 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
119def main():
Daniel Dunbar2253dc42009-01-14 20:06:04 +0000120 import os, sys
Daniel Dunbarab130612009-01-13 07:38:29 +0000121
122 args = sys.argv[1:]
Daniel Dunbar2253dc42009-01-14 20:06:04 +0000123 driverA = os.getenv('DRIVER_A') or 'gcc'
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +0000124 driverB = os.getenv('DRIVER_B') or 'clang'
Daniel Dunbarab130612009-01-13 07:38:29 +0000125
126 infoA = captureDriverInfo(driverA, args)
127 infoB = captureDriverInfo(driverB, args)
128
Daniel Dunbarc97c05f2009-01-17 00:50:45 +0000129 differ = False
130
Daniel Dunbarab130612009-01-13 07:38:29 +0000131 # Compare stdout.
132 if infoA.stdout != infoB.stdout:
133 print '-- STDOUT DIFFERS -'
134 print 'A: ',infoA.stdout
135 print 'B: ',infoB.stdout
Daniel Dunbarc97c05f2009-01-17 00:50:45 +0000136 differ = True
Daniel Dunbarab130612009-01-13 07:38:29 +0000137
138 # Compare stderr.
139 if infoA.stderr != infoB.stderr:
140 print '-- STDERR DIFFERS -'
141 print 'A: ',infoA.stderr
142 print 'B: ',infoB.stderr
Daniel Dunbarc97c05f2009-01-17 00:50:45 +0000143 differ = True
Daniel Dunbarab130612009-01-13 07:38:29 +0000144
145 # Compare commands.
Daniel Dunbar163f31a2009-01-21 23:34:23 +0000146 for i,(a,b) in enumerate(map(None, infoA.commands, infoB.commands)):
147 if a is None:
148 print 'A MISSING:',' '.join(b)
149 differ = True
150 continue
151 elif b is None:
152 print 'B MISSING:',' '.join(a)
153 differ = True
154 continue
155
Daniel Dunbarab130612009-01-13 07:38:29 +0000156 diff = DriverZipperDiff(a,b)
157 diffs = list(diff.getDiffs())
158 if diffs:
159 print '-- COMMAND %d DIFFERS -' % i
160 print 'A COMMAND:',' '.join(a)
161 print 'B COMMAND:',' '.join(b)
162 print
163 for i,(aElt,bElt) in enumerate(diffs):
164 if aElt is None:
165 print 'A missing: %s' % bElt
166 elif bElt is None:
167 print 'B missing: %s' % aElt
168 else:
169 print 'mismatch: A: %s' % aElt
170 print ' B: %s' % bElt
Daniel Dunbar06172d62009-01-20 00:47:24 +0000171 differ = True
Daniel Dunbar163f31a2009-01-21 23:34:23 +0000172
Daniel Dunbarab130612009-01-13 07:38:29 +0000173 # Compare result codes.
174 if infoA.exitCode != infoB.exitCode:
175 print '-- EXIT CODES DIFFER -'
176 print 'A: ',infoA.exitCode
177 print 'B: ',infoB.exitCode
Daniel Dunbarc97c05f2009-01-17 00:50:45 +0000178 differ = True
179
180 if differ:
181 sys.exit(1)
Daniel Dunbarab130612009-01-13 07:38:29 +0000182
183if __name__ == '__main__':
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +0000184 main()