summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Panne <svenpanne@gmail.com>2016-11-08 10:45:46 +0100
committerKim Gräsman <kim.grasman@gmail.com>2016-11-28 20:48:43 +0100
commitd53f86d8f55b659cba94788c4b42e8265e15e5b4 (patch)
tree2a0b371d99e5f2586746d4944b504b9c602ef9b2
parenteb7f5d1ef02836575aebfe5a95977f067064cee2 (diff)
Add a new option for a more tool-friendly output.
The standard output format of iwyu is not very friendly for tools like Jenkins' warnings-plugin (https://github.com/jenkinsci/warnings-plugin) or editors. This change adds a new -o/--output-format command line option with 'iwyu' being the traditional format and 'clang' the new one for mangling the output into something clang-like, enabling nice integration into emacs/Jenkins/etc. out of the box. Somehow this should probably be the default and be handled on the native side (not as a post-processing step), but the current change is probably the least controversial and easiest one for now.
-rwxr-xr-xiwyu_tool.py88
1 files changed, 80 insertions, 8 deletions
diff --git a/iwyu_tool.py b/iwyu_tool.py
index b06876b..19de45b 100755
--- a/iwyu_tool.py
+++ b/iwyu_tool.py
@@ -25,9 +25,77 @@ import sys
import json
import argparse
import subprocess
-
-
-def run_iwyu(cwd, compile_command, iwyu_args, verbose):
+import re
+
+
+def iwyu_formatter(output):
+ """ Process iwyu's output, basically a no-op. """
+ print('\n'.join(output))
+
+
+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 clang_formatter(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:1:1: note: #includes/fwd-decls are correct' % 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))
+ elif END_RE.match(line):
+ state = (GENERAL, None)
+ elif not line.strip():
+ continue
+ elif state[0] == GENERAL:
+ print(line)
+ 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(2) if match else '1'
+ print('%s:%s:1: error: remove the following line' % (state[1], line_no))
+ print(match.group(1))
+
+
+DEFAULT_FORMAT = 'iwyu'
+FORMATTERS = {
+ 'iwyu': iwyu_formatter,
+ 'clang': clang_formatter
+}
+
+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, formatter):
""" Rewrite compile_command to an IWYU command, and run it. """
compiler, _, args = compile_command.partition(' ')
if compiler.endswith('cl.exe'):
@@ -43,10 +111,10 @@ def run_iwyu(cwd, compile_command, iwyu_args, verbose):
if verbose:
print('%s:' % command)
- return subprocess.call(command, cwd=cwd, shell=True)
+ formatter(get_output(cwd, command))
-def main(compilation_db_path, source_files, verbose, iwyu_args):
+def main(compilation_db_path, source_files, verbose, formatter, iwyu_args):
""" Entry point. """
# Canonicalize compilation database path
if os.path.isdir(compilation_db_path):
@@ -87,7 +155,7 @@ def main(compilation_db_path, source_files, verbose, iwyu_args):
try:
for entry in entries:
cwd, compile_command = entry['directory'], entry['command']
- run_iwyu(cwd, compile_command, iwyu_args, verbose)
+ run_iwyu(cwd, compile_command, iwyu_args, verbose, formatter)
except OSError as why:
print('ERROR: Failed to launch include-what-you-use: %s' % why)
return 1
@@ -127,6 +195,9 @@ def _bootstrap():
parser.add_argument('-v', '--verbose', action='store_true',
help='Print IWYU commands')
+ parser.add_argument('-o', '--output-format', type=str,
+ choices=FORMATTERS.keys(), default=DEFAULT_FORMAT,
+ help='Output format (default: %s)' % DEFAULT_FORMAT)
parser.add_argument('-p', metavar='<build-path>', required=True,
help='Compilation database path', dest='dbpath')
parser.add_argument('source', nargs='*',
@@ -140,10 +211,11 @@ def _bootstrap():
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, iwyu_args))
+
+ sys.exit(main(args.dbpath, args.source, args.verbose,
+ FORMATTERS[args.output_format], iwyu_args))
if __name__ == '__main__':