Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Blame | Last modification | View Log | Download | RSS feed

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