# -*- coding: utf-8 -*- # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. """ This module is responsible for to parse a compiler invocation. """ import re import os __all__ = ['Action', 'classify_parameters', 'classify_source'] class Action(object): """ Enumeration class for compiler action. """ Link, Compile, Ignored = range(3) def classify_parameters(command): """ Parses the command line arguments of the given invocation. """ # result value of this method. # some value are preset, some will be set only when found. result = { 'action': Action.Link, 'files': [], 'output': None, 'compile_options': [], 'c++': is_cplusplus_compiler(command[0]) # archs_seen # language } # data structure to ignore compiler parameters. # key: parameter name, value: number of parameters to ignore afterwards. ignored = { '-g': 0, '-fsyntax-only': 0, '-save-temps': 0, '-install_name': 1, '-exported_symbols_list': 1, '-current_version': 1, '-compatibility_version': 1, '-init': 1, '-e': 1, '-seg1addr': 1, '-bundle_loader': 1, '-multiply_defined': 1, '-sectorder': 3, '--param': 1, '--serialize-diagnostics': 1 } args = iter(command[1:]) for arg in args: # compiler action parameters are the most important ones... if arg in {'-E', '-S', '-cc1', '-M', '-MM', '-###'}: result.update({'action': Action.Ignored}) elif arg == '-c': result.update({'action': max(result['action'], Action.Compile)}) # arch flags are taken... elif arg == '-arch': archs = result.get('archs_seen', []) result.update({'archs_seen': archs + [next(args)]}) # explicit language option taken... elif arg == '-x': result.update({'language': next(args)}) # output flag taken... elif arg == '-o': result.update({'output': next(args)}) # warning disable options are taken... elif re.match(r'^-Wno-', arg): result['compile_options'].append(arg) # warning options are ignored... elif re.match(r'^-[mW].+', arg): pass # some preprocessor parameters are ignored... elif arg in {'-MD', '-MMD', '-MG', '-MP'}: pass elif arg in {'-MF', '-MT', '-MQ'}: next(args) # linker options are ignored... elif arg in {'-static', '-shared', '-s', '-rdynamic'} or \ re.match(r'^-[lL].+', arg): pass elif arg in {'-l', '-L', '-u', '-z', '-T', '-Xlinker'}: next(args) # some other options are ignored... elif arg in ignored.keys(): for _ in range(ignored[arg]): next(args) # parameters which looks source file are taken... elif re.match(r'^[^-].+', arg) and classify_source(arg): result['files'].append(arg) # and consider everything else as compile option. else: result['compile_options'].append(arg) return result def classify_source(filename, cplusplus=False): """ Return the language from file name extension. """ mapping = { '.c': 'c++' if cplusplus else 'c', '.i': 'c++-cpp-output' if cplusplus else 'c-cpp-output', '.ii': 'c++-cpp-output', '.m': 'objective-c', '.mi': 'objective-c-cpp-output', '.mm': 'objective-c++', '.mii': 'objective-c++-cpp-output', '.C': 'c++', '.cc': 'c++', '.CC': 'c++', '.cp': 'c++', '.cpp': 'c++', '.cxx': 'c++', '.c++': 'c++', '.C++': 'c++', '.txx': 'c++' } __, extension = os.path.splitext(os.path.basename(filename)) return mapping.get(extension) def is_cplusplus_compiler(name): """ Returns true when the compiler name refer to a C++ compiler. """ match = re.match(r'^([^/]*/)*(\w*-)*(\w+\+\+)(-(\d+(\.\d+){0,3}))?$', name) return False if match is None else True