- # -*- coding: utf-8 -*- 
- # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 
- # See https://llvm.org/LICENSE.txt for license information. 
- # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 
- """ This module is responsible for the Clang executable. 
-   
- Since Clang command line interface is so rich, but this project is using only 
- a subset of that, it makes sense to create a function specific wrapper. """ 
-   
- import subprocess 
- import re 
- from libscanbuild import run_command 
- from libscanbuild.shell import decode 
-   
- __all__ = ['get_version', 'get_arguments', 'get_checkers', 'is_ctu_capable', 
-            'get_triple_arch'] 
-   
- # regex for activated checker 
- ACTIVE_CHECKER_PATTERN = re.compile(r'^-analyzer-checker=(.*)$') 
-   
-   
- class ClangErrorException(Exception): 
-     def __init__(self, error): 
-         self.error = error 
-   
-   
- def get_version(clang): 
-     """ Returns the compiler version as string. 
-   
-     :param clang:   the compiler we are using 
-     :return:        the version string printed to stderr """ 
-   
-     output = run_command([clang, '-v']) 
-     # the relevant version info is in the first line 
-     return output[0] 
-   
-   
- def get_arguments(command, cwd): 
-     """ Capture Clang invocation. 
-   
-     :param command: the compilation command 
-     :param cwd:     the current working directory 
-     :return:        the detailed front-end invocation command """ 
-   
-     cmd = command[:] 
-     cmd.insert(1, '-###') 
-     cmd.append('-fno-color-diagnostics') 
-   
-     output = run_command(cmd, cwd=cwd) 
-     # The relevant information is in the last line of the output. 
-     # Don't check if finding last line fails, would throw exception anyway. 
-     last_line = output[-1] 
-     if re.search(r'clang(.*): error:', last_line): 
-         raise ClangErrorException(last_line) 
-     return decode(last_line) 
-   
-   
- def get_active_checkers(clang, plugins): 
-     """ Get the active checker list. 
-   
-     :param clang:   the compiler we are using 
-     :param plugins: list of plugins which was requested by the user 
-     :return:        list of checker names which are active 
-   
-     To get the default checkers we execute Clang to print how this 
-     compilation would be called. And take out the enabled checker from the 
-     arguments. For input file we specify stdin and pass only language 
-     information. """ 
-   
-     def get_active_checkers_for(language): 
-         """ Returns a list of active checkers for the given language. """ 
-   
-         load_args = [arg 
-                      for plugin in plugins 
-                      for arg in ['-Xclang', '-load', '-Xclang', plugin]] 
-         cmd = [clang, '--analyze'] + load_args + ['-x', language, '-'] 
-         return [ACTIVE_CHECKER_PATTERN.match(arg).group(1) 
-                 for arg in get_arguments(cmd, '.') 
-                 if ACTIVE_CHECKER_PATTERN.match(arg)] 
-   
-     result = set() 
-     for language in ['c', 'c++', 'objective-c', 'objective-c++']: 
-         result.update(get_active_checkers_for(language)) 
-     return frozenset(result) 
-   
-   
- def is_active(checkers): 
-     """ Returns a method, which classifies the checker active or not, 
-     based on the received checker name list. """ 
-   
-     def predicate(checker): 
-         """ Returns True if the given checker is active. """ 
-   
-         return any(pattern.match(checker) for pattern in predicate.patterns) 
-   
-     predicate.patterns = [re.compile(r'^' + a + r'(\.|$)') for a in checkers] 
-     return predicate 
-   
-   
- def parse_checkers(stream): 
-     """ Parse clang -analyzer-checker-help output. 
-   
-     Below the line 'CHECKERS:' are there the name description pairs. 
-     Many of them are in one line, but some long named checker has the 
-     name and the description in separate lines. 
-   
-     The checker name is always prefixed with two space character. The 
-     name contains no whitespaces. Then followed by newline (if it's 
-     too long) or other space characters comes the description of the 
-     checker. The description ends with a newline character. 
-   
-     :param stream:  list of lines to parse 
-     :return:        generator of tuples 
-   
-     (<checker name>, <checker description>) """ 
-   
-     lines = iter(stream) 
-     # find checkers header 
-     for line in lines: 
-         if re.match(r'^CHECKERS:', line): 
-             break 
-     # find entries 
-     state = None 
-     for line in lines: 
-         if state and not re.match(r'^\s\s\S', line): 
-             yield (state, line.strip()) 
-             state = None 
-         elif re.match(r'^\s\s\S+$', line.rstrip()): 
-             state = line.strip() 
-         else: 
-             pattern = re.compile(r'^\s\s(?P<key>\S*)\s*(?P<value>.*)') 
-             match = pattern.match(line.rstrip()) 
-             if match: 
-                 current = match.groupdict() 
-                 yield (current['key'], current['value']) 
-   
-   
- def get_checkers(clang, plugins): 
-     """ Get all the available checkers from default and from the plugins. 
-   
-     :param clang:   the compiler we are using 
-     :param plugins: list of plugins which was requested by the user 
-     :return:        a dictionary of all available checkers and its status 
-   
-     {<checker name>: (<checker description>, <is active by default>)} """ 
-   
-     load = [elem for plugin in plugins for elem in ['-load', plugin]] 
-     cmd = [clang, '-cc1'] + load + ['-analyzer-checker-help'] 
-   
-     lines = run_command(cmd) 
-   
-     is_active_checker = is_active(get_active_checkers(clang, plugins)) 
-   
-     checkers = { 
-         name: (description, is_active_checker(name)) 
-         for name, description in parse_checkers(lines) 
-     } 
-     if not checkers: 
-         raise Exception('Could not query Clang for available checkers.') 
-   
-     return checkers 
-   
-   
- def is_ctu_capable(extdef_map_cmd): 
-     """ Detects if the current (or given) clang and external definition mapping 
-     executables are CTU compatible. """ 
-   
-     try: 
-         run_command([extdef_map_cmd, '-version']) 
-     except (OSError, subprocess.CalledProcessError): 
-         return False 
-     return True 
-   
-   
- def get_triple_arch(command, cwd): 
-     """Returns the architecture part of the target triple for the given 
-     compilation command. """ 
-   
-     cmd = get_arguments(command, cwd) 
-     try: 
-         separator = cmd.index("-triple") 
-         return cmd[separator + 1] 
-     except (IndexError, ValueError): 
-         return "" 
-