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