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