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