[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / clang / utils / CmpDriver
blob12ce7a3250f66b71d0c01b72cd58d2009b84c6ab
1 #!/usr/bin/env python
3 """
4 A simple utility that compares tool invocations and exit codes issued by
5 compiler drivers that support -### (e.g. gcc and clang).
6 """
8 import subprocess
10 def 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
31 def insertMinimumPadding(a, b, dist):
32 """insertMinimumPadding(a,b) -> (a',b')
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.
38 Assumes dist(X, Y) -> int and non-negative.
39 """
41 def cost(a, b):
42 return sum(map(dist, a + [None] * (len(b) - len(a)), b))
44 # Normalize so a is shortest.
45 if len(b) < len(a):
46 b, a = insertMinimumPadding(b, a, dist)
47 return a,b
49 # 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
62 class ZipperDiff(object):
63 """ZipperDiff - Simple (slow) diff only accommodating inserts."""
65 def __init__(self, a, b):
66 self.a = a
67 self.b = b
69 def dist(self, a, b):
70 return a != b
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
78 class 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))
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)
90 class CompileInfo:
91 def __init__(self, out, err, res):
92 self.commands = []
94 # Standard out isn't used for much.
95 self.stdout = out
96 self.stderr = ''
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
104 ln.startswith('gcc version') or
105 ln.startswith('clang version')):
106 pass
107 elif ln.strip().startswith('"'):
108 self.commands.append(list(splitArgs(ln)))
109 else:
110 self.stderr += ln + '\n'
112 self.stderr = self.stderr.strip()
113 self.exitCode = res
115 def 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)
124 def main():
125 import os, sys
127 args = sys.argv[1:]
128 driverA = os.getenv('DRIVER_A') or 'gcc'
129 driverB = os.getenv('DRIVER_B') or 'clang'
131 infoA = captureDriverInfo(driverA, args)
132 infoB = captureDriverInfo(driverB, args)
134 differ = False
136 # Compare stdout.
137 if infoA.stdout != infoB.stdout:
138 print '-- STDOUT DIFFERS -'
139 print 'A OUTPUT: ',infoA.stdout
140 print 'B OUTPUT: ',infoB.stdout
141 print
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
154 differ = True
156 # Compare stderr.
157 if infoA.stderr != infoB.stderr:
158 print '-- STDERR DIFFERS -'
159 print 'A STDERR: ',infoA.stderr
160 print 'B STDERR: ',infoB.stderr
161 print
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
174 differ = True
176 # Compare commands.
177 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
187 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
202 differ = True
204 # Compare result codes.
205 if infoA.exitCode != infoB.exitCode:
206 print '-- EXIT CODES DIFFER -'
207 print 'A: ',infoA.exitCode
208 print 'B: ',infoB.exitCode
209 differ = True
211 if differ:
212 sys.exit(1)
214 if __name__ == '__main__':
215 main()