Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14 pmbaty 1
# -*- coding: utf-8 -*-
2
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3
# See https://llvm.org/LICENSE.txt for license information.
4
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5
""" This module is responsible for the Clang executable.
6
 
7
Since Clang command line interface is so rich, but this project is using only
8
a subset of that, it makes sense to create a function specific wrapper. """
9
 
10
import subprocess
11
import re
12
from libscanbuild import run_command
13
from libscanbuild.shell import decode
14
 
15
__all__ = ['get_version', 'get_arguments', 'get_checkers', 'is_ctu_capable',
16
           'get_triple_arch']
17
 
18
# regex for activated checker
19
ACTIVE_CHECKER_PATTERN = re.compile(r'^-analyzer-checker=(.*)$')
20
 
21
 
22
class ClangErrorException(Exception):
23
    def __init__(self, error):
24
        self.error = error
25
 
26
 
27
def get_version(clang):
28
    """ Returns the compiler version as string.
29
 
30
    :param clang:   the compiler we are using
31
    :return:        the version string printed to stderr """
32
 
33
    output = run_command([clang, '-v'])
34
    # the relevant version info is in the first line
35
    return output[0]
36
 
37
 
38
def get_arguments(command, cwd):
39
    """ Capture Clang invocation.
40
 
41
    :param command: the compilation command
42
    :param cwd:     the current working directory
43
    :return:        the detailed front-end invocation command """
44
 
45
    cmd = command[:]
46
    cmd.insert(1, '-###')
47
    cmd.append('-fno-color-diagnostics')
48
 
49
    output = run_command(cmd, cwd=cwd)
50
    # The relevant information is in the last line of the output.
51
    # Don't check if finding last line fails, would throw exception anyway.
52
    last_line = output[-1]
53
    if re.search(r'clang(.*): error:', last_line):
54
        raise ClangErrorException(last_line)
55
    return decode(last_line)
56
 
57
 
58
def get_active_checkers(clang, plugins):
59
    """ Get the active checker list.
60
 
61
    :param clang:   the compiler we are using
62
    :param plugins: list of plugins which was requested by the user
63
    :return:        list of checker names which are active
64
 
65
    To get the default checkers we execute Clang to print how this
66
    compilation would be called. And take out the enabled checker from the
67
    arguments. For input file we specify stdin and pass only language
68
    information. """
69
 
70
    def get_active_checkers_for(language):
71
        """ Returns a list of active checkers for the given language. """
72
 
73
        load_args = [arg
74
                     for plugin in plugins
75
                     for arg in ['-Xclang', '-load', '-Xclang', plugin]]
76
        cmd = [clang, '--analyze'] + load_args + ['-x', language, '-']
77
        return [ACTIVE_CHECKER_PATTERN.match(arg).group(1)
78
                for arg in get_arguments(cmd, '.')
79
                if ACTIVE_CHECKER_PATTERN.match(arg)]
80
 
81
    result = set()
82
    for language in ['c', 'c++', 'objective-c', 'objective-c++']:
83
        result.update(get_active_checkers_for(language))
84
    return frozenset(result)
85
 
86
 
87
def is_active(checkers):
88
    """ Returns a method, which classifies the checker active or not,
89
    based on the received checker name list. """
90
 
91
    def predicate(checker):
92
        """ Returns True if the given checker is active. """
93
 
94
        return any(pattern.match(checker) for pattern in predicate.patterns)
95
 
96
    predicate.patterns = [re.compile(r'^' + a + r'(\.|$)') for a in checkers]
97
    return predicate
98
 
99
 
100
def parse_checkers(stream):
101
    """ Parse clang -analyzer-checker-help output.
102
 
103
    Below the line 'CHECKERS:' are there the name description pairs.
104
    Many of them are in one line, but some long named checker has the
105
    name and the description in separate lines.
106
 
107
    The checker name is always prefixed with two space character. The
108
    name contains no whitespaces. Then followed by newline (if it's
109
    too long) or other space characters comes the description of the
110
    checker. The description ends with a newline character.
111
 
112
    :param stream:  list of lines to parse
113
    :return:        generator of tuples
114
 
115
    (<checker name>, <checker description>) """
116
 
117
    lines = iter(stream)
118
    # find checkers header
119
    for line in lines:
120
        if re.match(r'^CHECKERS:', line):
121
            break
122
    # find entries
123
    state = None
124
    for line in lines:
125
        if state and not re.match(r'^\s\s\S', line):
126
            yield (state, line.strip())
127
            state = None
128
        elif re.match(r'^\s\s\S+$', line.rstrip()):
129
            state = line.strip()
130
        else:
131
            pattern = re.compile(r'^\s\s(?P<key>\S*)\s*(?P<value>.*)')
132
            match = pattern.match(line.rstrip())
133
            if match:
134
                current = match.groupdict()
135
                yield (current['key'], current['value'])
136
 
137
 
138
def get_checkers(clang, plugins):
139
    """ Get all the available checkers from default and from the plugins.
140
 
141
    :param clang:   the compiler we are using
142
    :param plugins: list of plugins which was requested by the user
143
    :return:        a dictionary of all available checkers and its status
144
 
145
    {<checker name>: (<checker description>, <is active by default>)} """
146
 
147
    load = [elem for plugin in plugins for elem in ['-load', plugin]]
148
    cmd = [clang, '-cc1'] + load + ['-analyzer-checker-help']
149
 
150
    lines = run_command(cmd)
151
 
152
    is_active_checker = is_active(get_active_checkers(clang, plugins))
153
 
154
    checkers = {
155
        name: (description, is_active_checker(name))
156
        for name, description in parse_checkers(lines)
157
    }
158
    if not checkers:
159
        raise Exception('Could not query Clang for available checkers.')
160
 
161
    return checkers
162
 
163
 
164
def is_ctu_capable(extdef_map_cmd):
165
    """ Detects if the current (or given) clang and external definition mapping
166
    executables are CTU compatible. """
167
 
168
    try:
169
        run_command([extdef_map_cmd, '-version'])
170
    except (OSError, subprocess.CalledProcessError):
171
        return False
172
    return True
173
 
174
 
175
def get_triple_arch(command, cwd):
176
    """Returns the architecture part of the target triple for the given
177
    compilation command. """
178
 
179
    cmd = get_arguments(command, cwd)
180
    try:
181
        separator = cmd.index("-triple")
182
        return cmd[separator + 1]
183
    except (IndexError, ValueError):
184
        return ""