xref: /netbsd-src/external/apache2/llvm/dist/clang/utils/CmpDriver (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1*7330f729Sjoerg#!/usr/bin/env python
2*7330f729Sjoerg
3*7330f729Sjoerg"""
4*7330f729SjoergA simple utility that compares tool invocations and exit codes issued by
5*7330f729Sjoergcompiler drivers that support -### (e.g. gcc and clang).
6*7330f729Sjoerg"""
7*7330f729Sjoerg
8*7330f729Sjoergimport subprocess
9*7330f729Sjoerg
10*7330f729Sjoergdef splitArgs(s):
11*7330f729Sjoerg    it = iter(s)
12*7330f729Sjoerg    current = ''
13*7330f729Sjoerg    inQuote = False
14*7330f729Sjoerg    for c in it:
15*7330f729Sjoerg        if c == '"':
16*7330f729Sjoerg            if inQuote:
17*7330f729Sjoerg                inQuote = False
18*7330f729Sjoerg                yield current + '"'
19*7330f729Sjoerg            else:
20*7330f729Sjoerg                inQuote = True
21*7330f729Sjoerg                current = '"'
22*7330f729Sjoerg        elif inQuote:
23*7330f729Sjoerg            if c == '\\':
24*7330f729Sjoerg                current += c
25*7330f729Sjoerg                current += it.next()
26*7330f729Sjoerg            else:
27*7330f729Sjoerg                current += c
28*7330f729Sjoerg        elif not c.isspace():
29*7330f729Sjoerg            yield c
30*7330f729Sjoerg
31*7330f729Sjoergdef insertMinimumPadding(a, b, dist):
32*7330f729Sjoerg    """insertMinimumPadding(a,b) -> (a',b')
33*7330f729Sjoerg
34*7330f729Sjoerg    Return two lists of equal length, where some number of Nones have
35*7330f729Sjoerg    been inserted into the shorter list such that sum(map(dist, a',
36*7330f729Sjoerg    b')) is minimized.
37*7330f729Sjoerg
38*7330f729Sjoerg    Assumes dist(X, Y) -> int and non-negative.
39*7330f729Sjoerg    """
40*7330f729Sjoerg
41*7330f729Sjoerg    def cost(a, b):
42*7330f729Sjoerg        return sum(map(dist, a + [None] * (len(b) - len(a)), b))
43*7330f729Sjoerg
44*7330f729Sjoerg    # Normalize so a is shortest.
45*7330f729Sjoerg    if len(b) < len(a):
46*7330f729Sjoerg        b, a = insertMinimumPadding(b, a, dist)
47*7330f729Sjoerg        return a,b
48*7330f729Sjoerg
49*7330f729Sjoerg    # For each None we have to insert...
50*7330f729Sjoerg    for i in range(len(b) - len(a)):
51*7330f729Sjoerg        # For each position we could insert it...
52*7330f729Sjoerg        current = cost(a, b)
53*7330f729Sjoerg        best = None
54*7330f729Sjoerg        for j in range(len(a) + 1):
55*7330f729Sjoerg            a_0 = a[:j] + [None] + a[j:]
56*7330f729Sjoerg            candidate = cost(a_0, b)
57*7330f729Sjoerg            if best is None or candidate < best[0]:
58*7330f729Sjoerg                best = (candidate, a_0, j)
59*7330f729Sjoerg        a = best[1]
60*7330f729Sjoerg    return a,b
61*7330f729Sjoerg
62*7330f729Sjoergclass ZipperDiff(object):
63*7330f729Sjoerg    """ZipperDiff - Simple (slow) diff only accommodating inserts."""
64*7330f729Sjoerg
65*7330f729Sjoerg    def __init__(self, a, b):
66*7330f729Sjoerg        self.a = a
67*7330f729Sjoerg        self.b = b
68*7330f729Sjoerg
69*7330f729Sjoerg    def dist(self, a, b):
70*7330f729Sjoerg        return a != b
71*7330f729Sjoerg
72*7330f729Sjoerg    def getDiffs(self):
73*7330f729Sjoerg        a,b =  insertMinimumPadding(self.a, self.b, self.dist)
74*7330f729Sjoerg        for aElt,bElt in zip(a,b):
75*7330f729Sjoerg            if self.dist(aElt, bElt):
76*7330f729Sjoerg                yield aElt,bElt
77*7330f729Sjoerg
78*7330f729Sjoergclass DriverZipperDiff(ZipperDiff):
79*7330f729Sjoerg    def isTempFile(self, filename):
80*7330f729Sjoerg        if filename[0] != '"' or filename[-1] != '"':
81*7330f729Sjoerg            return False
82*7330f729Sjoerg        return (filename.startswith('/tmp/', 1) or
83*7330f729Sjoerg                filename.startswith('/var/', 1))
84*7330f729Sjoerg
85*7330f729Sjoerg    def dist(self, a, b):
86*7330f729Sjoerg        if a and b and self.isTempFile(a) and self.isTempFile(b):
87*7330f729Sjoerg            return 0
88*7330f729Sjoerg        return super(DriverZipperDiff, self).dist(a,b)
89*7330f729Sjoerg
90*7330f729Sjoergclass CompileInfo:
91*7330f729Sjoerg    def __init__(self, out, err, res):
92*7330f729Sjoerg        self.commands = []
93*7330f729Sjoerg
94*7330f729Sjoerg        # Standard out isn't used for much.
95*7330f729Sjoerg        self.stdout = out
96*7330f729Sjoerg        self.stderr = ''
97*7330f729Sjoerg
98*7330f729Sjoerg        # FIXME: Compare error messages as well.
99*7330f729Sjoerg        for ln in err.split('\n'):
100*7330f729Sjoerg            if (ln == 'Using built-in specs.' or
101*7330f729Sjoerg                ln.startswith('Target: ') or
102*7330f729Sjoerg                ln.startswith('Configured with: ') or
103*7330f729Sjoerg                ln.startswith('Thread model: ') or
104*7330f729Sjoerg                ln.startswith('gcc version') or
105*7330f729Sjoerg                ln.startswith('clang version')):
106*7330f729Sjoerg                pass
107*7330f729Sjoerg            elif ln.strip().startswith('"'):
108*7330f729Sjoerg                self.commands.append(list(splitArgs(ln)))
109*7330f729Sjoerg            else:
110*7330f729Sjoerg                self.stderr += ln + '\n'
111*7330f729Sjoerg
112*7330f729Sjoerg        self.stderr = self.stderr.strip()
113*7330f729Sjoerg        self.exitCode = res
114*7330f729Sjoerg
115*7330f729Sjoergdef captureDriverInfo(cmd, args):
116*7330f729Sjoerg    p = subprocess.Popen([cmd,'-###'] + args,
117*7330f729Sjoerg                         stdin=None,
118*7330f729Sjoerg                         stdout=subprocess.PIPE,
119*7330f729Sjoerg                         stderr=subprocess.PIPE)
120*7330f729Sjoerg    out,err = p.communicate()
121*7330f729Sjoerg    res = p.wait()
122*7330f729Sjoerg    return CompileInfo(out,err,res)
123*7330f729Sjoerg
124*7330f729Sjoergdef main():
125*7330f729Sjoerg    import os, sys
126*7330f729Sjoerg
127*7330f729Sjoerg    args = sys.argv[1:]
128*7330f729Sjoerg    driverA = os.getenv('DRIVER_A') or 'gcc'
129*7330f729Sjoerg    driverB = os.getenv('DRIVER_B') or 'clang'
130*7330f729Sjoerg
131*7330f729Sjoerg    infoA = captureDriverInfo(driverA, args)
132*7330f729Sjoerg    infoB = captureDriverInfo(driverB, args)
133*7330f729Sjoerg
134*7330f729Sjoerg    differ = False
135*7330f729Sjoerg
136*7330f729Sjoerg    # Compare stdout.
137*7330f729Sjoerg    if infoA.stdout != infoB.stdout:
138*7330f729Sjoerg        print '-- STDOUT DIFFERS -'
139*7330f729Sjoerg        print 'A OUTPUT: ',infoA.stdout
140*7330f729Sjoerg        print 'B OUTPUT: ',infoB.stdout
141*7330f729Sjoerg        print
142*7330f729Sjoerg
143*7330f729Sjoerg        diff = ZipperDiff(infoA.stdout.split('\n'),
144*7330f729Sjoerg                          infoB.stdout.split('\n'))
145*7330f729Sjoerg        for i,(aElt,bElt) in enumerate(diff.getDiffs()):
146*7330f729Sjoerg            if aElt is None:
147*7330f729Sjoerg                print 'A missing: %s' % bElt
148*7330f729Sjoerg            elif bElt is None:
149*7330f729Sjoerg                print 'B missing: %s' % aElt
150*7330f729Sjoerg            else:
151*7330f729Sjoerg                print 'mismatch: A: %s' % aElt
152*7330f729Sjoerg                print '          B: %s' % bElt
153*7330f729Sjoerg
154*7330f729Sjoerg        differ = True
155*7330f729Sjoerg
156*7330f729Sjoerg    # Compare stderr.
157*7330f729Sjoerg    if infoA.stderr != infoB.stderr:
158*7330f729Sjoerg        print '-- STDERR DIFFERS -'
159*7330f729Sjoerg        print 'A STDERR: ',infoA.stderr
160*7330f729Sjoerg        print 'B STDERR: ',infoB.stderr
161*7330f729Sjoerg        print
162*7330f729Sjoerg
163*7330f729Sjoerg        diff = ZipperDiff(infoA.stderr.split('\n'),
164*7330f729Sjoerg                          infoB.stderr.split('\n'))
165*7330f729Sjoerg        for i,(aElt,bElt) in enumerate(diff.getDiffs()):
166*7330f729Sjoerg            if aElt is None:
167*7330f729Sjoerg                print 'A missing: %s' % bElt
168*7330f729Sjoerg            elif bElt is None:
169*7330f729Sjoerg                print 'B missing: %s' % aElt
170*7330f729Sjoerg            else:
171*7330f729Sjoerg                print 'mismatch: A: %s' % aElt
172*7330f729Sjoerg                print '          B: %s' % bElt
173*7330f729Sjoerg
174*7330f729Sjoerg        differ = True
175*7330f729Sjoerg
176*7330f729Sjoerg    # Compare commands.
177*7330f729Sjoerg    for i,(a,b) in enumerate(map(None, infoA.commands, infoB.commands)):
178*7330f729Sjoerg        if a is None:
179*7330f729Sjoerg            print 'A MISSING:',' '.join(b)
180*7330f729Sjoerg            differ = True
181*7330f729Sjoerg            continue
182*7330f729Sjoerg        elif b is None:
183*7330f729Sjoerg            print 'B MISSING:',' '.join(a)
184*7330f729Sjoerg            differ = True
185*7330f729Sjoerg            continue
186*7330f729Sjoerg
187*7330f729Sjoerg        diff = DriverZipperDiff(a,b)
188*7330f729Sjoerg        diffs = list(diff.getDiffs())
189*7330f729Sjoerg        if diffs:
190*7330f729Sjoerg            print '-- COMMAND %d DIFFERS -' % i
191*7330f729Sjoerg            print 'A COMMAND:',' '.join(a)
192*7330f729Sjoerg            print 'B COMMAND:',' '.join(b)
193*7330f729Sjoerg            print
194*7330f729Sjoerg            for i,(aElt,bElt) in enumerate(diffs):
195*7330f729Sjoerg                if aElt is None:
196*7330f729Sjoerg                    print 'A missing: %s' % bElt
197*7330f729Sjoerg                elif bElt is None:
198*7330f729Sjoerg                    print 'B missing: %s' % aElt
199*7330f729Sjoerg                else:
200*7330f729Sjoerg                    print 'mismatch: A: %s' % aElt
201*7330f729Sjoerg                    print '          B: %s' % bElt
202*7330f729Sjoerg            differ = True
203*7330f729Sjoerg
204*7330f729Sjoerg    # Compare result codes.
205*7330f729Sjoerg    if infoA.exitCode != infoB.exitCode:
206*7330f729Sjoerg        print '-- EXIT CODES DIFFER -'
207*7330f729Sjoerg        print 'A: ',infoA.exitCode
208*7330f729Sjoerg        print 'B: ',infoB.exitCode
209*7330f729Sjoerg        differ = True
210*7330f729Sjoerg
211*7330f729Sjoerg    if differ:
212*7330f729Sjoerg        sys.exit(1)
213*7330f729Sjoerg
214*7330f729Sjoergif __name__ == '__main__':
215*7330f729Sjoerg    main()
216