summaryrefslogtreecommitdiffstats
path: root/tools/scan-build-py/libscanbuild/command.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/scan-build-py/libscanbuild/command.py')
-rw-r--r--tools/scan-build-py/libscanbuild/command.py133
1 files changed, 133 insertions, 0 deletions
diff --git a/tools/scan-build-py/libscanbuild/command.py b/tools/scan-build-py/libscanbuild/command.py
new file mode 100644
index 0000000000..69ca3393f9
--- /dev/null
+++ b/tools/scan-build-py/libscanbuild/command.py
@@ -0,0 +1,133 @@
+# -*- 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