blob: 2533f547e973c6b905995bc2c9509e48cf4e5fa6 [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):
Chris Lattnerfc8f0e12011-04-15 05:22:18 +000058 """ZipperDiff - Simple (slow) diff only accommodating inserts."""
Daniel Dunbarab130612009-01-13 07:38:29 +000059
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
Daniel Dunbarad8958c2009-09-04 18:35:09 +0000100 ln.startswith('clang 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 -'
Daniel Dunbarad8958c2009-09-04 18:35:09 +0000134 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 Dunbarc97c05f2009-01-17 00:50:45 +0000149 differ = True
Daniel Dunbarab130612009-01-13 07:38:29 +0000150
151 # Compare stderr.
152 if infoA.stderr != infoB.stderr:
153 print '-- STDERR DIFFERS -'
Daniel Dunbarad8958c2009-09-04 18:35:09 +0000154 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 Dunbarc97c05f2009-01-17 00:50:45 +0000169 differ = True
Daniel Dunbarab130612009-01-13 07:38:29 +0000170
171 # Compare commands.
Daniel Dunbar163f31a2009-01-21 23:34:23 +0000172 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 Dunbarab130612009-01-13 07:38:29 +0000182 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 Dunbar06172d62009-01-20 00:47:24 +0000197 differ = True
Daniel Dunbar163f31a2009-01-21 23:34:23 +0000198
Daniel Dunbarab130612009-01-13 07:38:29 +0000199 # 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 Dunbarc97c05f2009-01-17 00:50:45 +0000204 differ = True
205
206 if differ:
207 sys.exit(1)
Daniel Dunbarab130612009-01-13 07:38:29 +0000208
209if __name__ == '__main__':
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +0000210 main()