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