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