blob: 12ce7a3250f66b71d0c01b72cd58d2009b84c6ab [file] [log] [blame]
Eli Friedman77a1fe92009-07-10 20:15:12 +00001#!/usr/bin/env python
Daniel Dunbarab130612009-01-13 07:38:29 +00002
Stephen Hines6bcf27b2014-05-29 04:14:42 -07003"""
4A simple utility that compares tool invocations and exit codes issued by
5compiler drivers that support -### (e.g. gcc and clang).
6"""
7
Daniel Dunbarab130612009-01-13 07:38:29 +00008import subprocess
9
10def splitArgs(s):
11 it = iter(s)
12 current = ''
13 inQuote = False
14 for c in it:
15 if c == '"':
16 if inQuote:
17 inQuote = False
18 yield current + '"'
19 else:
20 inQuote = True
21 current = '"'
22 elif inQuote:
23 if c == '\\':
24 current += c
25 current += it.next()
26 else:
27 current += c
28 elif not c.isspace():
29 yield c
30
31def insertMinimumPadding(a, b, dist):
32 """insertMinimumPadding(a,b) -> (a',b')
33
34 Return two lists of equal length, where some number of Nones have
35 been inserted into the shorter list such that sum(map(dist, a',
36 b')) is minimized.
37
38 Assumes dist(X, Y) -> int and non-negative.
39 """
40
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +000041 def cost(a, b):
42 return sum(map(dist, a + [None] * (len(b) - len(a)), b))
Daniel Dunbarab130612009-01-13 07:38:29 +000043
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +000044 # Normalize so a is shortest.
45 if len(b) < len(a):
46 b, a = insertMinimumPadding(b, a, dist)
47 return a,b
Daniel Dunbarab130612009-01-13 07:38:29 +000048
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +000049 # For each None we have to insert...
50 for i in range(len(b) - len(a)):
51 # For each position we could insert it...
52 current = cost(a, b)
53 best = None
54 for j in range(len(a) + 1):
55 a_0 = a[:j] + [None] + a[j:]
56 candidate = cost(a_0, b)
57 if best is None or candidate < best[0]:
58 best = (candidate, a_0, j)
59 a = best[1]
60 return a,b
Daniel Dunbarab130612009-01-13 07:38:29 +000061
62class ZipperDiff(object):
Chris Lattnerfc8f0e12011-04-15 05:22:18 +000063 """ZipperDiff - Simple (slow) diff only accommodating inserts."""
Daniel Dunbarab130612009-01-13 07:38:29 +000064
65 def __init__(self, a, b):
66 self.a = a
67 self.b = b
68
69 def dist(self, a, b):
70 return a != b
71
72 def getDiffs(self):
73 a,b = insertMinimumPadding(self.a, self.b, self.dist)
74 for aElt,bElt in zip(a,b):
75 if self.dist(aElt, bElt):
76 yield aElt,bElt
77
78class DriverZipperDiff(ZipperDiff):
79 def isTempFile(self, filename):
80 if filename[0] != '"' or filename[-1] != '"':
81 return False
82 return (filename.startswith('/tmp/', 1) or
83 filename.startswith('/var/', 1))
84
85 def dist(self, a, b):
86 if a and b and self.isTempFile(a) and self.isTempFile(b):
87 return 0
88 return super(DriverZipperDiff, self).dist(a,b)
89
90class CompileInfo:
91 def __init__(self, out, err, res):
92 self.commands = []
93
94 # Standard out isn't used for much.
95 self.stdout = out
96 self.stderr = ''
97
98 # FIXME: Compare error messages as well.
99 for ln in err.split('\n'):
100 if (ln == 'Using built-in specs.' or
101 ln.startswith('Target: ') or
102 ln.startswith('Configured with: ') or
103 ln.startswith('Thread model: ') or
Daniel Dunbar76715912009-01-28 19:26:20 +0000104 ln.startswith('gcc version') or
Daniel Dunbarad8958c2009-09-04 18:35:09 +0000105 ln.startswith('clang version')):
Daniel Dunbarab130612009-01-13 07:38:29 +0000106 pass
107 elif ln.strip().startswith('"'):
108 self.commands.append(list(splitArgs(ln)))
109 else:
110 self.stderr += ln + '\n'
111
112 self.stderr = self.stderr.strip()
113 self.exitCode = res
114
115def captureDriverInfo(cmd, args):
116 p = subprocess.Popen([cmd,'-###'] + args,
117 stdin=None,
118 stdout=subprocess.PIPE,
119 stderr=subprocess.PIPE)
120 out,err = p.communicate()
121 res = p.wait()
122 return CompileInfo(out,err,res)
123
124def main():
Daniel Dunbar2253dc42009-01-14 20:06:04 +0000125 import os, sys
Daniel Dunbarab130612009-01-13 07:38:29 +0000126
127 args = sys.argv[1:]
Daniel Dunbar2253dc42009-01-14 20:06:04 +0000128 driverA = os.getenv('DRIVER_A') or 'gcc'
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +0000129 driverB = os.getenv('DRIVER_B') or 'clang'
Daniel Dunbarab130612009-01-13 07:38:29 +0000130
131 infoA = captureDriverInfo(driverA, args)
132 infoB = captureDriverInfo(driverB, args)
133
Daniel Dunbarc97c05f2009-01-17 00:50:45 +0000134 differ = False
135
Daniel Dunbarab130612009-01-13 07:38:29 +0000136 # Compare stdout.
137 if infoA.stdout != infoB.stdout:
138 print '-- STDOUT DIFFERS -'
Daniel Dunbarad8958c2009-09-04 18:35:09 +0000139 print 'A OUTPUT: ',infoA.stdout
140 print 'B OUTPUT: ',infoB.stdout
141 print
142
143 diff = ZipperDiff(infoA.stdout.split('\n'),
144 infoB.stdout.split('\n'))
145 for i,(aElt,bElt) in enumerate(diff.getDiffs()):
146 if aElt is None:
147 print 'A missing: %s' % bElt
148 elif bElt is None:
149 print 'B missing: %s' % aElt
150 else:
151 print 'mismatch: A: %s' % aElt
152 print ' B: %s' % bElt
153
Daniel Dunbarc97c05f2009-01-17 00:50:45 +0000154 differ = True
Daniel Dunbarab130612009-01-13 07:38:29 +0000155
156 # Compare stderr.
157 if infoA.stderr != infoB.stderr:
158 print '-- STDERR DIFFERS -'
Daniel Dunbarad8958c2009-09-04 18:35:09 +0000159 print 'A STDERR: ',infoA.stderr
160 print 'B STDERR: ',infoB.stderr
161 print
162
163 diff = ZipperDiff(infoA.stderr.split('\n'),
164 infoB.stderr.split('\n'))
165 for i,(aElt,bElt) in enumerate(diff.getDiffs()):
166 if aElt is None:
167 print 'A missing: %s' % bElt
168 elif bElt is None:
169 print 'B missing: %s' % aElt
170 else:
171 print 'mismatch: A: %s' % aElt
172 print ' B: %s' % bElt
173
Daniel Dunbarc97c05f2009-01-17 00:50:45 +0000174 differ = True
Daniel Dunbarab130612009-01-13 07:38:29 +0000175
176 # Compare commands.
Daniel Dunbar163f31a2009-01-21 23:34:23 +0000177 for i,(a,b) in enumerate(map(None, infoA.commands, infoB.commands)):
178 if a is None:
179 print 'A MISSING:',' '.join(b)
180 differ = True
181 continue
182 elif b is None:
183 print 'B MISSING:',' '.join(a)
184 differ = True
185 continue
186
Daniel Dunbarab130612009-01-13 07:38:29 +0000187 diff = DriverZipperDiff(a,b)
188 diffs = list(diff.getDiffs())
189 if diffs:
190 print '-- COMMAND %d DIFFERS -' % i
191 print 'A COMMAND:',' '.join(a)
192 print 'B COMMAND:',' '.join(b)
193 print
194 for i,(aElt,bElt) in enumerate(diffs):
195 if aElt is None:
196 print 'A missing: %s' % bElt
197 elif bElt is None:
198 print 'B missing: %s' % aElt
199 else:
200 print 'mismatch: A: %s' % aElt
201 print ' B: %s' % bElt
Daniel Dunbar06172d62009-01-20 00:47:24 +0000202 differ = True
Daniel Dunbar163f31a2009-01-21 23:34:23 +0000203
Daniel Dunbarab130612009-01-13 07:38:29 +0000204 # Compare result codes.
205 if infoA.exitCode != infoB.exitCode:
206 print '-- EXIT CODES DIFFER -'
207 print 'A: ',infoA.exitCode
208 print 'B: ',infoB.exitCode
Daniel Dunbarc97c05f2009-01-17 00:50:45 +0000209 differ = True
210
211 if differ:
212 sys.exit(1)
Daniel Dunbarab130612009-01-13 07:38:29 +0000213
214if __name__ == '__main__':
Daniel Dunbar8f5fdbe2009-09-04 17:41:47 +0000215 main()