blob: 192e708782c10953a31f193ba1c222eea0756bc6 [file] [log] [blame]
Laszlo Nagybc687582016-01-12 22:38:41 +00001# -*- coding: utf-8 -*-
2# The LLVM Compiler Infrastructure
3#
4# This file is distributed under the University of Illinois Open Source
5# License. See LICENSE.TXT for details.
6""" This module is responsible for the Clang executable.
7
8Since Clang command line interface is so rich, but this project is using only
9a subset of that, it makes sense to create a function specific wrapper. """
10
11import re
Laszlo Nagy46fc18a2017-01-28 22:48:26 +000012from libscanbuild import run_command
Laszlo Nagybc687582016-01-12 22:38:41 +000013from libscanbuild.shell import decode
14
15__all__ = ['get_version', 'get_arguments', 'get_checkers']
16
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000017# regex for activated checker
18ACTIVE_CHECKER_PATTERN = re.compile(r'^-analyzer-checker=(.*)$')
Laszlo Nagybc687582016-01-12 22:38:41 +000019
Laszlo Nagybc687582016-01-12 22:38:41 +000020
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000021def get_version(clang):
22 """ Returns the compiler version as string.
23
24 :param clang: the compiler we are using
25 :return: the version string printed to stderr """
26
Laszlo Nagy46fc18a2017-01-28 22:48:26 +000027 output = run_command([clang, '-v'])
28 # the relevant version info is in the first line
29 return output[0]
Laszlo Nagybc687582016-01-12 22:38:41 +000030
31
32def get_arguments(command, cwd):
33 """ Capture Clang invocation.
34
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000035 :param command: the compilation command
36 :param cwd: the current working directory
37 :return: the detailed front-end invocation command """
Laszlo Nagybc687582016-01-12 22:38:41 +000038
39 cmd = command[:]
40 cmd.insert(1, '-###')
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000041
Laszlo Nagy46fc18a2017-01-28 22:48:26 +000042 output = run_command(cmd, cwd=cwd)
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000043 # The relevant information is in the last line of the output.
44 # Don't check if finding last line fails, would throw exception anyway.
Laszlo Nagy46fc18a2017-01-28 22:48:26 +000045 last_line = output[-1]
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000046 if re.search(r'clang(.*): error:', last_line):
47 raise Exception(last_line)
48 return decode(last_line)
Laszlo Nagybc687582016-01-12 22:38:41 +000049
50
51def get_active_checkers(clang, plugins):
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000052 """ Get the active checker list.
Laszlo Nagybc687582016-01-12 22:38:41 +000053
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000054 :param clang: the compiler we are using
55 :param plugins: list of plugins which was requested by the user
56 :return: list of checker names which are active
Laszlo Nagybc687582016-01-12 22:38:41 +000057
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000058 To get the default checkers we execute Clang to print how this
59 compilation would be called. And take out the enabled checker from the
60 arguments. For input file we specify stdin and pass only language
61 information. """
62
63 def get_active_checkers_for(language):
Laszlo Nagybc687582016-01-12 22:38:41 +000064 """ Returns a list of active checkers for the given language. """
65
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000066 load_args = [arg
67 for plugin in plugins
68 for arg in ['-Xclang', '-load', '-Xclang', plugin]]
69 cmd = [clang, '--analyze'] + load_args + ['-x', language, '-']
70 return [ACTIVE_CHECKER_PATTERN.match(arg).group(1)
71 for arg in get_arguments(cmd, '.')
72 if ACTIVE_CHECKER_PATTERN.match(arg)]
Laszlo Nagybc687582016-01-12 22:38:41 +000073
74 result = set()
75 for language in ['c', 'c++', 'objective-c', 'objective-c++']:
Laszlo Nagy4f6a1752016-09-24 00:20:59 +000076 result.update(get_active_checkers_for(language))
77 return frozenset(result)
78
79
80def is_active(checkers):
81 """ Returns a method, which classifies the checker active or not,
82 based on the received checker name list. """
83
84 def predicate(checker):
85 """ Returns True if the given checker is active. """
86
87 return any(pattern.match(checker) for pattern in predicate.patterns)
88
89 predicate.patterns = [re.compile(r'^' + a + r'(\.|$)') for a in checkers]
90 return predicate
91
92
93def parse_checkers(stream):
94 """ Parse clang -analyzer-checker-help output.
95
96 Below the line 'CHECKERS:' are there the name description pairs.
97 Many of them are in one line, but some long named checker has the
98 name and the description in separate lines.
99
100 The checker name is always prefixed with two space character. The
101 name contains no whitespaces. Then followed by newline (if it's
102 too long) or other space characters comes the description of the
103 checker. The description ends with a newline character.
104
105 :param stream: list of lines to parse
106 :return: generator of tuples
107
108 (<checker name>, <checker description>) """
109
110 lines = iter(stream)
111 # find checkers header
112 for line in lines:
113 if re.match(r'^CHECKERS:', line):
114 break
115 # find entries
116 state = None
117 for line in lines:
118 if state and not re.match(r'^\s\s\S', line):
119 yield (state, line.strip())
120 state = None
121 elif re.match(r'^\s\s\S+$', line.rstrip()):
122 state = line.strip()
123 else:
124 pattern = re.compile(r'^\s\s(?P<key>\S*)\s*(?P<value>.*)')
125 match = pattern.match(line.rstrip())
126 if match:
127 current = match.groupdict()
128 yield (current['key'], current['value'])
Laszlo Nagybc687582016-01-12 22:38:41 +0000129
130
131def get_checkers(clang, plugins):
132 """ Get all the available checkers from default and from the plugins.
133
Laszlo Nagy4f6a1752016-09-24 00:20:59 +0000134 :param clang: the compiler we are using
135 :param plugins: list of plugins which was requested by the user
136 :return: a dictionary of all available checkers and its status
Laszlo Nagybc687582016-01-12 22:38:41 +0000137
Laszlo Nagy4f6a1752016-09-24 00:20:59 +0000138 {<checker name>: (<checker description>, <is active by default>)} """
Laszlo Nagybc687582016-01-12 22:38:41 +0000139
140 load = [elem for plugin in plugins for elem in ['-load', plugin]]
141 cmd = [clang, '-cc1'] + load + ['-analyzer-checker-help']
142
Laszlo Nagy46fc18a2017-01-28 22:48:26 +0000143 lines = run_command(cmd)
Laszlo Nagy4f6a1752016-09-24 00:20:59 +0000144
145 is_active_checker = is_active(get_active_checkers(clang, plugins))
146
Laszlo Nagybc687582016-01-12 22:38:41 +0000147 checkers = {
Laszlo Nagy4f6a1752016-09-24 00:20:59 +0000148 name: (description, is_active_checker(name))
149 for name, description in parse_checkers(lines)
Laszlo Nagybc687582016-01-12 22:38:41 +0000150 }
Laszlo Nagy4f6a1752016-09-24 00:20:59 +0000151 if not checkers:
Laszlo Nagybc687582016-01-12 22:38:41 +0000152 raise Exception('Could not query Clang for available checkers.')
Laszlo Nagy4f6a1752016-09-24 00:20:59 +0000153
154 return checkers