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