Module: check_mk
Branch: master
Commit: 02cd19fb4408d183abdd7ce61f7448c436ea9347
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=02cd19fb4408d1…
Author: Sven Panne <sp(a)mathias-kettner.de>
Date: Tue Nov 8 11:00:50 2016 +0100
Use our own iwyu_tool variant.
---
Makefile | 4 +-
tests/iwyu_tool_jenkins.py | 214 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 216 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index 2ad28da..30f250f 100644
--- a/Makefile
+++ b/Makefile
@@ -40,7 +40,7 @@ CLANG_TIDY := clang-tidy-$(CLANG_VERSION)
SCAN_BUILD := scan-build-$(CLANG_VERSION)
CPPCHECK := cppcheck
DOXYGEN := doxygen
-IWYU_TOOL := iwyu_tool.py
+IWYU_TOOL := tests/iwyu_tool_jenkins.py
BEAR := bear
# File to pack into livestatus-$(VERSION).tar.gz
@@ -297,7 +297,7 @@ tidy: compile_commands.json
# Not really perfect rules, but better than nothing
iwyu: compile_commands.json
- @$(IWYU_TOOL) -p .
+ @$(IWYU_TOOL) --clang-style -p .
# Not really perfect rules, but better than nothing
analyze: livestatus/config.h
diff --git a/tests/iwyu_tool_jenkins.py b/tests/iwyu_tool_jenkins.py
new file mode 100755
index 0000000..ef42b65
--- /dev/null
+++ b/tests/iwyu_tool_jenkins.py
@@ -0,0 +1,214 @@
+#!/usr/bin/env python
+
+""" Driver to consume a Clang compilation database and invoke IWYU.
+
+Example usage with CMake:
+
+ # Unix systems
+ $ mkdir build && cd build
+ $ CC="clang" CXX="clang++" cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
...
+ $ iwyu_tool.py -p .
+
+ # Windows systems
+ $ mkdir build && cd build
+ $ cmake -DCMAKE_CXX_COMPILER="%VCINSTALLDIR%/bin/cl.exe" \
+ -DCMAKE_C_COMPILER="%VCINSTALLDIR%/VC/bin/cl.exe" \
+ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
+ -G Ninja ...
+ $ python iwyu_tool.py -p .
+
+See iwyu_tool.py -h for more details on command-line arguments.
+"""
+
+import os
+import sys
+import json
+import argparse
+import subprocess
+import re
+
+
+CORRECT_RE = re.compile(r'^\((.*?) has correct #includes/fwd-decls\)$')
+SHOULD_ADD_RE = re.compile(r'^(.*?) should add these lines:$')
+SHOULD_REMOVE_RE = re.compile(r'^(.*?) should remove these lines:$')
+FULL_LIST_RE = re.compile(r'The full include-list for (.*?):$')
+END_RE = re.compile(r'^---$')
+LINES_RE = re.compile(r'.*// lines ([0-9]+)-[0-9]+$')
+
+
+GENERAL, ADD, REMOVE, LIST = range(4)
+
+
+def process_output(output):
+ """ Process iwyu's output into something clang-like.
"""
+ state = (GENERAL, None)
+ for line in output:
+ match = CORRECT_RE.match(line)
+ if match:
+ print('%s has correct #includes/fwd-decls' % match.groups(1))
+ continue
+ match = SHOULD_ADD_RE.match(line)
+ if match:
+ state = (ADD, match.group(1))
+ continue
+ match = SHOULD_REMOVE_RE.match(line)
+ if match:
+ state = (REMOVE, match.group(1))
+ continue
+ match = FULL_LIST_RE.match(line)
+ if match:
+ state = (LIST, match.group(1))
+ print('%s:1:1: error: full include list follows' % state[1])
+ elif END_RE.match(line):
+ state = (GENERAL, None)
+ elif state[0] != GENERAL and not line.strip():
+ continue
+ elif state[0] == ADD:
+ print('%s:1:1: error: add the following line' % state[1])
+ print(line)
+ elif state[0] == REMOVE:
+ match = LINES_RE.match(line)
+ line_no = match.group(1) if match else '1'
+ print('%s:%s:1: error: remove the following line' % (state[1],
line_no))
+ print(line[2:])
+ else:
+ print(line)
+
+
+def get_output(cwd, command):
+ """ Run the given command and return its output as a string.
"""
+ process = subprocess.Popen(command,
+ cwd=cwd,
+ shell=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ return process.communicate()[0].decode("utf-8").splitlines()
+
+
+def run_iwyu(cwd, compile_command, iwyu_args, verbose, clang_style):
+ """ Rewrite compile_command to an IWYU command, and run it.
"""
+ compiler, _, args = compile_command.partition(' ')
+ if compiler.endswith('cl.exe'):
+ # If the compiler name is cl.exe, let IWYU be cl-compatible
+ clang_args = ['--driver-mode=cl']
+ else:
+ clang_args = []
+
+ iwyu_args = ['-Xiwyu ' + a for a in iwyu_args]
+ command = ['include-what-you-use'] + clang_args + iwyu_args
+ command = '%s %s' % (' '.join(command), args.strip())
+
+ if verbose:
+ print('%s:' % command)
+
+ output = get_output(cwd, command)
+ if clang_style:
+ process_output(output)
+ else:
+ print('\n'.join(output))
+
+
+def main(compilation_db_path, source_files, verbose, clang_style, iwyu_args):
+ """ Entry point. """
+ # Canonicalize compilation database path
+ if os.path.isdir(compilation_db_path):
+ compilation_db_path = os.path.join(compilation_db_path,
+ 'compile_commands.json')
+
+ compilation_db_path = os.path.realpath(compilation_db_path)
+ if not os.path.isfile(compilation_db_path):
+ print('ERROR: No such file or directory: \'%s\'' %
compilation_db_path)
+ return 1
+
+ # Read compilation db from disk
+ with open(compilation_db_path, 'r') as fileobj:
+ compilation_db = json.load(fileobj)
+
+ # expand symlinks
+ for entry in compilation_db:
+ entry['file'] = os.path.realpath(entry['file'])
+
+ # Cross-reference source files with compilation database
+ source_files = [os.path.realpath(s) for s in source_files]
+ if not source_files:
+ # No source files specified, analyze entire compilation database
+ entries = compilation_db
+ else:
+ # Source files specified, analyze the ones appearing in compilation db,
+ # warn for the rest.
+ entries = []
+ for source in source_files:
+ matches = [e for e in compilation_db if e['file'] == source]
+ if matches:
+ entries.extend(matches)
+ else:
+ print('WARNING: \'%s\' not found in compilation
database.' %
+ source)
+
+ # Run analysis
+ try:
+ for entry in entries:
+ cwd, compile_command = entry['directory'], entry['command']
+ run_iwyu(cwd, compile_command, iwyu_args, verbose, clang_style)
+ except OSError as why:
+ print('ERROR: Failed to launch include-what-you-use: %s' % why)
+ return 1
+
+ return 0
+
+
+def _bootstrap():
+ """ Parse arguments and dispatch to main(). """
+ # This hackery is necessary to add the forwarded IWYU args to the
+ # usage and help strings.
+ def customize_usage(parser):
+ """ Rewrite the parser's format_usage. """
+ original_format_usage = parser.format_usage
+ parser.format_usage = lambda: original_format_usage().rstrip() + \
+ ' -- [<IWYU args>]' + os.linesep
+
+ def customize_help(parser):
+ """ Rewrite the parser's format_help. """
+ original_format_help = parser.format_help
+
+ def custom_help():
+ """ Customized help string, calls the adjusted format_usage.
"""
+ helpmsg = original_format_help()
+ helplines = helpmsg.splitlines()
+ helplines[0] = parser.format_usage().rstrip()
+ return os.linesep.join(helplines) + os.linesep
+
+ parser.format_help = custom_help
+
+ # Parse arguments
+ parser = argparse.ArgumentParser(
+ description='Include-what-you-use compilation database driver.',
+ epilog='Assumes include-what-you-use is available on the PATH.')
+ customize_usage(parser)
+ customize_help(parser)
+
+ parser.add_argument('-v', '--verbose', action='store_true',
+ help='Print IWYU commands')
+ parser.add_argument('-c', '--clang-style',
action='store_true',
+ help='Use clang-style output format')
+ parser.add_argument('-p', metavar='<build-path>',
required=True,
+ help='Compilation database path', dest='dbpath')
+ parser.add_argument('source', nargs='*',
+ help='Zero or more source files to run IWYU on. '
+ 'Defaults to all in compilation database.')
+
+ def partition_args(argv):
+ """ Split around '--' into driver args and IWYU args.
"""
+ try:
+ double_dash = argv.index('--')
+ return argv[:double_dash], argv[double_dash+1:]
+ except ValueError:
+ return argv, []
+
+ argv, iwyu_args = partition_args(sys.argv[1:])
+ args = parser.parse_args(argv)
+ sys.exit(main(args.dbpath, args.source, args.verbose, args.clang_style, iwyu_args))
+
+
+if __name__ == '__main__':
+ _bootstrap()