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