2019-09-21 12:14:49 +02:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
2019-09-26 20:47:04 +02:00
|
|
|
# Copyright (c) 2019 Patryk Obara <patryk.obara@gmail.com>
|
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2019-09-21 12:14:49 +02:00
|
|
|
#
|
|
|
|
# pylint: disable=invalid-name
|
|
|
|
# pylint: disable=missing-docstring
|
|
|
|
|
2019-11-23 19:48:17 -08:00
|
|
|
"""
|
|
|
|
This script counts all compiler warnings and prints a summary.
|
|
|
|
|
|
|
|
It returns success to the shell if the number or warnings encountered
|
|
|
|
is less than or equal to the desired maximum warnings. See --help
|
|
|
|
for a description of how to set the maximum.
|
|
|
|
|
|
|
|
note: new compilers include additional flag -fdiagnostics-format=[text|json],
|
|
|
|
which could be used instead of parsing using regex, but we want to preserve
|
|
|
|
human-readable output in standard log.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import argparse
|
2019-09-21 12:14:49 +02:00
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import sys
|
|
|
|
|
|
|
|
# For recognizing warnings in GCC format in stderr:
|
|
|
|
#
|
|
|
|
WARNING_PATTERN = re.compile(r'([^:]+):(\d+):\d+: warning: .* \[-W(.+)\]')
|
|
|
|
# ~~~~~ ~~~ ~~~ ~~ ~~
|
|
|
|
# ↑ ↑ ↑ ↑ ↑
|
|
|
|
# file line column message type
|
|
|
|
|
|
|
|
# For removing color when GCC is invoked with -fdiagnostics-color=always
|
|
|
|
#
|
|
|
|
ANSI_COLOR_PATTERN = re.compile(r'\x1b\[[0-9;]*[mGKH]')
|
|
|
|
|
|
|
|
|
|
|
|
def remove_colors(line):
|
|
|
|
return re.sub(ANSI_COLOR_PATTERN, '', line)
|
|
|
|
|
|
|
|
|
|
|
|
def count_warning(line, warning_types):
|
|
|
|
line = remove_colors(line)
|
|
|
|
match = WARNING_PATTERN.match(line)
|
|
|
|
if not match:
|
|
|
|
return 0
|
|
|
|
# file = match.group(1)
|
|
|
|
# line = match.group(2)
|
|
|
|
wtype = match.group(3)
|
|
|
|
count = warning_types.get(wtype) or 0
|
|
|
|
warning_types[wtype] = count + 1
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
def get_input_lines(name):
|
|
|
|
if name == '-':
|
|
|
|
return sys.stdin.readlines()
|
|
|
|
if not os.path.isfile(name):
|
|
|
|
print('{}: no such file.'.format(name))
|
|
|
|
sys.exit(2)
|
|
|
|
with open(name, 'r') as logs:
|
|
|
|
return logs.readlines()
|
|
|
|
|
|
|
|
|
2019-09-26 20:47:04 +02:00
|
|
|
def find_longest_name_length(names):
|
|
|
|
return max(len(x) for x in names)
|
|
|
|
|
|
|
|
|
|
|
|
def print_summary(issues):
|
|
|
|
summary = list(issues.items())
|
|
|
|
size = find_longest_name_length(issues.keys()) + 1
|
2019-09-21 12:14:49 +02:00
|
|
|
for warning, count in sorted(summary, key=lambda x: -x[1]):
|
2019-09-26 20:47:04 +02:00
|
|
|
print(' {text:{field_size}s}: {count}'.format(
|
|
|
|
text=warning, count=count, field_size=size))
|
2019-09-21 12:14:49 +02:00
|
|
|
print()
|
|
|
|
|
2019-11-23 19:48:17 -08:00
|
|
|
def to_int(value):
|
|
|
|
return int(value) if value is not None else None
|
|
|
|
|
|
|
|
def parse_args():
|
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
formatter_class=argparse.RawTextHelpFormatter, description=__doc__)
|
|
|
|
|
|
|
|
parser.add_argument(
|
|
|
|
'logfile',
|
|
|
|
metavar='LOGFILE',
|
|
|
|
help=("Path to the logfile, or use - to read from stdin"))
|
|
|
|
|
|
|
|
max_warnings = to_int(os.getenv('MAX_WARNINGS', None))
|
|
|
|
parser.add_argument(
|
|
|
|
'-m', '--max-warnings',
|
|
|
|
type=int,
|
|
|
|
required=not isinstance(max_warnings, int),
|
|
|
|
default=max_warnings,
|
|
|
|
help='Defines the maximum number of warnings permitted to exist\n'
|
|
|
|
'in the log file before returning a failure to the shell.\n'
|
|
|
|
'If not provided, the script will attempt to read it from\n'
|
|
|
|
'the MAX_WARNINGS environment variable, which is currently\n'
|
|
|
|
'set to: {}. If a maximum of -1 is set then success is\n'
|
|
|
|
'always returned to the shell.\n\n'.format(str(max_warnings)))
|
|
|
|
|
|
|
|
return parser.parse_args()
|
2019-09-21 12:14:49 +02:00
|
|
|
|
|
|
|
def main():
|
2019-11-23 19:48:17 -08:00
|
|
|
rcode = 0
|
2019-09-21 12:14:49 +02:00
|
|
|
total = 0
|
|
|
|
warning_types = {}
|
2019-11-23 19:48:17 -08:00
|
|
|
args = parse_args()
|
|
|
|
for line in get_input_lines(args.logfile):
|
2019-09-21 12:14:49 +02:00
|
|
|
total += count_warning(line, warning_types)
|
|
|
|
if warning_types:
|
2019-09-26 20:47:04 +02:00
|
|
|
print("Warnings grouped by type:\n")
|
2019-09-21 12:14:49 +02:00
|
|
|
print_summary(warning_types)
|
2019-11-23 19:48:17 -08:00
|
|
|
print('Total: {} warnings'.format(total), end='')
|
|
|
|
if args.max_warnings >= 0:
|
|
|
|
print(' (out of {} allowed)\n'.format(args.max_warnings))
|
|
|
|
if total > args.max_warnings:
|
|
|
|
print('Error: upper limit of allowed warnings is', args.max_warnings)
|
|
|
|
rcode = 1
|
|
|
|
else:
|
|
|
|
print('\n')
|
|
|
|
return rcode
|
2019-09-21 12:14:49 +02:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2019-11-23 19:48:17 -08:00
|
|
|
sys.exit(main())
|