blob: 69ca3393f955edaf3035d7051b4508520dc94899 [file] [log] [blame]
# -*- 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