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