brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.6 KiB · 6b9e3ca Raw
142 lines · python
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-exception5""" This module is responsible for to parse a compiler invocation. """6 7import re8import os9import collections10 11__all__ = ["split_command", "classify_source", "compiler_language"]12 13# Ignored compiler options map for compilation database creation.14# The map is used in `split_command` method. (Which does ignore and classify15# parameters.) Please note, that these are not the only parameters which16# might be ignored.17#18# Keys are the option name, value number of options to skip19IGNORED_FLAGS = {20    # compiling only flag, ignored because the creator of compilation21    # database will explicitly set it.22    "-c": 0,23    # preprocessor macros, ignored because would cause duplicate entries in24    # the output (the only difference would be these flags). this is actual25    # finding from users, who suffered longer execution time caused by the26    # duplicates.27    "-MD": 0,28    "-MMD": 0,29    "-MG": 0,30    "-MP": 0,31    "-MF": 1,32    "-MT": 1,33    "-MQ": 1,34    # linker options, ignored because for compilation database will contain35    # compilation commands only. so, the compiler would ignore these flags36    # anyway. the benefit to get rid of them is to make the output more37    # readable.38    "-static": 0,39    "-shared": 0,40    "-s": 0,41    "-rdynamic": 0,42    "-l": 1,43    "-L": 1,44    "-u": 1,45    "-z": 1,46    "-T": 1,47    "-Xlinker": 1,48}49 50# Known C/C++ compiler executable name patterns51COMPILER_PATTERNS = frozenset(52    [53        re.compile(r"^(intercept-|analyze-|)c(c|\+\+)$"),54        re.compile(r"^([^-]*-)*[mg](cc|\+\+)(-\d+(\.\d+){0,2})?$"),55        re.compile(r"^([^-]*-)*clang(\+\+)?(-\d+(\.\d+){0,2})?$"),56        re.compile(r"^llvm-g(cc|\+\+)$"),57    ]58)59 60 61def split_command(command):62    """Returns a value when the command is a compilation, None otherwise.63 64    The value on success is a named tuple with the following attributes:65 66        files:    list of source files67        flags:    list of compile options68        compiler: string value of 'c' or 'c++'"""69 70    # the result of this method71    result = collections.namedtuple("Compilation", ["compiler", "flags", "files"])72    result.compiler = compiler_language(command)73    result.flags = []74    result.files = []75    # quit right now, if the program was not a C/C++ compiler76    if not result.compiler:77        return None78    # iterate on the compile options79    args = iter(command[1:])80    for arg in args:81        # quit when compilation pass is not involved82        if arg in {"-E", "-S", "-cc1", "-M", "-MM", "-###"}:83            return None84        # ignore some flags85        elif arg in IGNORED_FLAGS:86            count = IGNORED_FLAGS[arg]87            for _ in range(count):88                next(args)89        elif re.match(r"^-(l|L|Wl,).+", arg):90            pass91        # some parameters could look like filename, take as compile option92        elif arg in {"-D", "-I"}:93            result.flags.extend([arg, next(args)])94        # parameter which looks source file is taken...95        elif re.match(r"^[^-].+", arg) and classify_source(arg):96            result.files.append(arg)97        # and consider everything else as compile option.98        else:99            result.flags.append(arg)100    # do extra check on number of source files101    return result if result.files else None102 103 104def classify_source(filename, c_compiler=True):105    """Return the language from file name extension."""106 107    mapping = {108        ".c": "c" if c_compiler else "c++",109        ".i": "c-cpp-output" if c_compiler else "c++-cpp-output",110        ".ii": "c++-cpp-output",111        ".m": "objective-c",112        ".mi": "objective-c-cpp-output",113        ".mm": "objective-c++",114        ".mii": "objective-c++-cpp-output",115        ".C": "c++",116        ".cc": "c++",117        ".CC": "c++",118        ".cp": "c++",119        ".cpp": "c++",120        ".cxx": "c++",121        ".c++": "c++",122        ".C++": "c++",123        ".txx": "c++",124    }125 126    __, extension = os.path.splitext(os.path.basename(filename))127    return mapping.get(extension)128 129 130def compiler_language(command):131    """A predicate to decide the command is a compiler call or not.132 133    Returns 'c' or 'c++' when it match. None otherwise."""134 135    cplusplus = re.compile(r"^(.+)(\+\+)(-.+|)$")136 137    if command:138        executable = os.path.basename(command[0])139        if any(pattern.match(executable) for pattern in COMPILER_PATTERNS):140            return "c++" if cplusplus.match(executable) else "c"141    return None142