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.
This commit is contained in:
parent
eb7f5d1ef0
commit
d53f86d8f5
84
iwyu_tool.py
84
iwyu_tool.py
|
@ -25,9 +25,77 @@ import sys
|
|||
import json
|
||||
import argparse
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
|
||||
def run_iwyu(cwd, compile_command, iwyu_args, verbose):
|
||||
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__':
|
||||
|
|
Loading…
Reference in New Issue