]> granicus.if.org Git - clang/blob - utils/CmpDriver
Delay emitting members of dllexport classes until the class is fully parsed (PR23542)
[clang] / utils / CmpDriver
1 #!/usr/bin/env python
2
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 """
7
8 import subprocess
9
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
30
31 def 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     
41     def cost(a, b):
42         return sum(map(dist, a + [None] * (len(b) - len(a)), b))
43
44     # Normalize so a is shortest.
45     if len(b) < len(a):
46         b, a = insertMinimumPadding(b, a, dist)
47         return a,b
48
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
61
62 class ZipperDiff(object):
63     """ZipperDiff - Simple (slow) diff only accommodating inserts."""
64     
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
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))
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
90 class 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
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'
111         
112         self.stderr = self.stderr.strip()
113         self.exitCode = res
114
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)
123
124 def main():
125     import os, sys
126
127     args = sys.argv[1:]
128     driverA = os.getenv('DRIVER_A') or 'gcc'
129     driverB = os.getenv('DRIVER_B') or 'clang'
130
131     infoA = captureDriverInfo(driverA, args)
132     infoB = captureDriverInfo(driverB, args)
133
134     differ = False
135
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
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
154         differ = True
155
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
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
174         differ = True
175
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
186
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
203     
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
210
211     if differ:
212         sys.exit(1)
213
214 if __name__ == '__main__':
215     main()