summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xgcc-check134
1 files changed, 134 insertions, 0 deletions
diff --git a/gcc-check b/gcc-check
new file mode 100755
index 0000000..8a233cf
--- /dev/null
+++ b/gcc-check
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+"""
+Analogous to clang-check, but for use with GCC. Intended use:
+
+ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 $srcdir
+ gcc-check path/to/source1.c path/to/source2.c
+
+Like clang-check, you can also add extra compiler options:
+
+ gcc-check -extra-arg=-Wno-missing-prototypes path/to/source.c
+"""
+import argparse
+import json
+import os
+import shlex
+import subprocess
+import sys
+
+
+def llvm_abspath(path):
+ # Deliberately preserves ".." in path="../x" for LLVM compatibility.
+ return os.path.join(os.getcwd(), path)
+
+
+# Make paths absolute (simplifying "a/../b" to "b")
+#canonicalize_path = os.path.abspath
+canonicalize_path = os.path.realpath
+
+
+def locate_compilation_database(path):
+ path = llvm_abspath(path)
+ if not os.path.isdir(path):
+ path = os.path.dirname(path)
+ while True:
+ db_path = os.path.join(path, 'compile_commands.json')
+ if os.path.exists(db_path):
+ return db_path
+ if path == '/':
+ break
+ path = os.path.dirname(path)
+ print('Could not auto-detect compilation database', file=sys.stderr)
+
+
+class DbEntry(object):
+ def __init__(self, obj):
+ def readstr(key):
+ value = obj.get(key)
+ if type(value) != str:
+ raise RuntimeError('Key %s not found' % key)
+ return value
+ self.directory = readstr('directory')
+ self.file = readstr('file')
+ self.command = readstr('command')
+ self.filepath = canonicalize_path(self.file)
+
+ def __str__(self):
+ return '{"directory": %r, "command": %r, "file": %r}' % \
+ (self.directory, self.command, self.file)
+
+ def check(self, extra_arg_before=[], extra_arg=[]):
+ args = shlex.split(self.command)
+ is_cpp = args[0].endswith('++')
+ args[0] = ['gcc', 'g++'][is_cpp]
+ args += extra_arg_before
+ args = [arg for arg in args if self._filter_cc_arg(arg, is_cpp)]
+ args += extra_arg
+ args += ['-fsyntax-only']
+ #print("Command: is_cpp=%r %r" % (is_cpp, args), file=sys.stderr)
+ subprocess.run(args, cwd=self.directory)
+
+ def _filter_cc_arg(self, arg, is_cpp):
+ # List of Clang-specific options that are not supported by GCC.
+ # (as added by Wireshark)
+ common_args = [
+ '-Qunused-arguments',
+ '-Wheader-guard',
+ '-Wcomma',
+ '-Wshorten-64-to-32',
+ '-Weverything',
+ '-Wno-documentation-unknown-command',
+ '-Wno-reserved-id-macro',
+ ]
+ c_args = [
+ ]
+ cpp_args = [
+ '-Wextra-semi',
+ ]
+ return arg not in common_args + (cpp_args if not is_cpp else c_args)
+
+
+def parse_db(entries, db_path):
+ with open(db_path) as f:
+ arr = json.load(f)
+ if type(arr) != list:
+ raise RuntimeError('Parse Error: expected array')
+ for obj in arr:
+ entry = DbEntry(obj)
+ entries[entry.filepath] = entry
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument('-p', metavar='string', dest='build_path',
+ help='Build path')
+parser.add_argument('-extra-arg', metavar='string', dest='extra_arg',
+ action='append', default=[],
+ help='Additional argument to append to the compiler command line')
+parser.add_argument('-extra-arg-before', metavar='string',
+ dest='extra_arg_before',
+ action='append', default=[],
+ help='Additional argument to prepend to the compiler command line')
+parser.add_argument('sources', nargs='+')
+
+
+def main():
+ args = parser.parse_args()
+ sources = [canonicalize_path(path) for path in args.sources]
+ build_path = args.build_path
+ if not build_path or not os.path.exists(build_path):
+ build_path = llvm_abspath(args.sources[0])
+ db_path = locate_compilation_database(build_path)
+ db = {}
+ if db_path:
+ parse_db(db, db_path)
+ for source_path in sources:
+ entry = db.get(source_path)
+ if entry is None:
+ print('Skipping %s. Compile command not found.' % source_path,
+ file=sys.stderr)
+ continue
+ entry.check(args.extra_arg_before, args.extra_arg)
+
+
+if __name__ == '__main__':
+ main()