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