1*67e74705SXin Li# -*- coding: utf-8 -*- 2*67e74705SXin Li# The LLVM Compiler Infrastructure 3*67e74705SXin Li# 4*67e74705SXin Li# This file is distributed under the University of Illinois Open Source 5*67e74705SXin Li# License. See LICENSE.TXT for details. 6*67e74705SXin Li""" This module is responsible for the Clang executable. 7*67e74705SXin Li 8*67e74705SXin LiSince Clang command line interface is so rich, but this project is using only 9*67e74705SXin Lia subset of that, it makes sense to create a function specific wrapper. """ 10*67e74705SXin Li 11*67e74705SXin Liimport re 12*67e74705SXin Liimport subprocess 13*67e74705SXin Liimport logging 14*67e74705SXin Lifrom libscanbuild.shell import decode 15*67e74705SXin Li 16*67e74705SXin Li__all__ = ['get_version', 'get_arguments', 'get_checkers'] 17*67e74705SXin Li 18*67e74705SXin Li 19*67e74705SXin Lidef get_version(cmd): 20*67e74705SXin Li """ Returns the compiler version as string. """ 21*67e74705SXin Li 22*67e74705SXin Li lines = subprocess.check_output([cmd, '-v'], stderr=subprocess.STDOUT) 23*67e74705SXin Li return lines.decode('ascii').splitlines()[0] 24*67e74705SXin Li 25*67e74705SXin Li 26*67e74705SXin Lidef get_arguments(command, cwd): 27*67e74705SXin Li """ Capture Clang invocation. 28*67e74705SXin Li 29*67e74705SXin Li This method returns the front-end invocation that would be executed as 30*67e74705SXin Li a result of the given driver invocation. """ 31*67e74705SXin Li 32*67e74705SXin Li def lastline(stream): 33*67e74705SXin Li last = None 34*67e74705SXin Li for line in stream: 35*67e74705SXin Li last = line 36*67e74705SXin Li if last is None: 37*67e74705SXin Li raise Exception("output not found") 38*67e74705SXin Li return last 39*67e74705SXin Li 40*67e74705SXin Li cmd = command[:] 41*67e74705SXin Li cmd.insert(1, '-###') 42*67e74705SXin Li logging.debug('exec command in %s: %s', cwd, ' '.join(cmd)) 43*67e74705SXin Li child = subprocess.Popen(cmd, 44*67e74705SXin Li cwd=cwd, 45*67e74705SXin Li universal_newlines=True, 46*67e74705SXin Li stdout=subprocess.PIPE, 47*67e74705SXin Li stderr=subprocess.STDOUT) 48*67e74705SXin Li line = lastline(child.stdout) 49*67e74705SXin Li child.stdout.close() 50*67e74705SXin Li child.wait() 51*67e74705SXin Li if child.returncode == 0: 52*67e74705SXin Li if re.search(r'clang(.*): error:', line): 53*67e74705SXin Li raise Exception(line) 54*67e74705SXin Li return decode(line) 55*67e74705SXin Li else: 56*67e74705SXin Li raise Exception(line) 57*67e74705SXin Li 58*67e74705SXin Li 59*67e74705SXin Lidef get_active_checkers(clang, plugins): 60*67e74705SXin Li """ To get the default plugins we execute Clang to print how this 61*67e74705SXin Li compilation would be called. 62*67e74705SXin Li 63*67e74705SXin Li For input file we specify stdin and pass only language information. """ 64*67e74705SXin Li 65*67e74705SXin Li def checkers(language): 66*67e74705SXin Li """ Returns a list of active checkers for the given language. """ 67*67e74705SXin Li 68*67e74705SXin Li load = [elem 69*67e74705SXin Li for plugin in plugins 70*67e74705SXin Li for elem in ['-Xclang', '-load', '-Xclang', plugin]] 71*67e74705SXin Li cmd = [clang, '--analyze'] + load + ['-x', language, '-'] 72*67e74705SXin Li pattern = re.compile(r'^-analyzer-checker=(.*)$') 73*67e74705SXin Li return [pattern.match(arg).group(1) 74*67e74705SXin Li for arg in get_arguments(cmd, '.') if pattern.match(arg)] 75*67e74705SXin Li 76*67e74705SXin Li result = set() 77*67e74705SXin Li for language in ['c', 'c++', 'objective-c', 'objective-c++']: 78*67e74705SXin Li result.update(checkers(language)) 79*67e74705SXin Li return result 80*67e74705SXin Li 81*67e74705SXin Li 82*67e74705SXin Lidef get_checkers(clang, plugins): 83*67e74705SXin Li """ Get all the available checkers from default and from the plugins. 84*67e74705SXin Li 85*67e74705SXin Li clang -- the compiler we are using 86*67e74705SXin Li plugins -- list of plugins which was requested by the user 87*67e74705SXin Li 88*67e74705SXin Li This method returns a dictionary of all available checkers and status. 89*67e74705SXin Li 90*67e74705SXin Li {<plugin name>: (<plugin description>, <is active by default>)} """ 91*67e74705SXin Li 92*67e74705SXin Li plugins = plugins if plugins else [] 93*67e74705SXin Li 94*67e74705SXin Li def parse_checkers(stream): 95*67e74705SXin Li """ Parse clang -analyzer-checker-help output. 96*67e74705SXin Li 97*67e74705SXin Li Below the line 'CHECKERS:' are there the name description pairs. 98*67e74705SXin Li Many of them are in one line, but some long named plugins has the 99*67e74705SXin Li name and the description in separate lines. 100*67e74705SXin Li 101*67e74705SXin Li The plugin name is always prefixed with two space character. The 102*67e74705SXin Li name contains no whitespaces. Then followed by newline (if it's 103*67e74705SXin Li too long) or other space characters comes the description of the 104*67e74705SXin Li plugin. The description ends with a newline character. """ 105*67e74705SXin Li 106*67e74705SXin Li # find checkers header 107*67e74705SXin Li for line in stream: 108*67e74705SXin Li if re.match(r'^CHECKERS:', line): 109*67e74705SXin Li break 110*67e74705SXin Li # find entries 111*67e74705SXin Li state = None 112*67e74705SXin Li for line in stream: 113*67e74705SXin Li if state and not re.match(r'^\s\s\S', line): 114*67e74705SXin Li yield (state, line.strip()) 115*67e74705SXin Li state = None 116*67e74705SXin Li elif re.match(r'^\s\s\S+$', line.rstrip()): 117*67e74705SXin Li state = line.strip() 118*67e74705SXin Li else: 119*67e74705SXin Li pattern = re.compile(r'^\s\s(?P<key>\S*)\s*(?P<value>.*)') 120*67e74705SXin Li match = pattern.match(line.rstrip()) 121*67e74705SXin Li if match: 122*67e74705SXin Li current = match.groupdict() 123*67e74705SXin Li yield (current['key'], current['value']) 124*67e74705SXin Li 125*67e74705SXin Li def is_active(actives, entry): 126*67e74705SXin Li """ Returns true if plugin name is matching the active plugin names. 127*67e74705SXin Li 128*67e74705SXin Li actives -- set of active plugin names (or prefixes). 129*67e74705SXin Li entry -- the current plugin name to judge. 130*67e74705SXin Li 131*67e74705SXin Li The active plugin names are specific plugin names or prefix of some 132*67e74705SXin Li names. One example for prefix, when it say 'unix' and it shall match 133*67e74705SXin Li on 'unix.API', 'unix.Malloc' and 'unix.MallocSizeof'. """ 134*67e74705SXin Li 135*67e74705SXin Li return any(re.match(r'^' + a + r'(\.|$)', entry) for a in actives) 136*67e74705SXin Li 137*67e74705SXin Li actives = get_active_checkers(clang, plugins) 138*67e74705SXin Li 139*67e74705SXin Li load = [elem for plugin in plugins for elem in ['-load', plugin]] 140*67e74705SXin Li cmd = [clang, '-cc1'] + load + ['-analyzer-checker-help'] 141*67e74705SXin Li 142*67e74705SXin Li logging.debug('exec command: %s', ' '.join(cmd)) 143*67e74705SXin Li child = subprocess.Popen(cmd, 144*67e74705SXin Li universal_newlines=True, 145*67e74705SXin Li stdout=subprocess.PIPE, 146*67e74705SXin Li stderr=subprocess.STDOUT) 147*67e74705SXin Li checkers = { 148*67e74705SXin Li k: (v, is_active(actives, k)) 149*67e74705SXin Li for k, v in parse_checkers(child.stdout) 150*67e74705SXin Li } 151*67e74705SXin Li child.stdout.close() 152*67e74705SXin Li child.wait() 153*67e74705SXin Li if child.returncode == 0 and len(checkers): 154*67e74705SXin Li return checkers 155*67e74705SXin Li else: 156*67e74705SXin Li raise Exception('Could not query Clang for available checkers.') 157