2019-09-21 12:14:49 +02:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
2019-09-26 20:47:04 +02:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2019-09-21 12:14:49 +02:00
|
|
|
#
|
2021-01-01 00:10:26 +01:00
|
|
|
# Copyright (C) 2019-2021 Patryk Obara <patryk.obara@gmail.com>
|
|
|
|
|
2019-09-21 12:14:49 +02:00
|
|
|
# pylint: disable=invalid-name
|
|
|
|
# pylint: disable=missing-docstring
|
|
|
|
|
2019-11-23 19:48:17 -08:00
|
|
|
"""
|
2019-12-12 14:55:55 +01:00
|
|
|
Count all compiler warnings and print a summary.
|
2019-11-23 19:48:17 -08:00
|
|
|
|
|
|
|
It returns success to the shell if the number or warnings encountered
|
2019-12-12 14:26:49 +01:00
|
|
|
is less than or equal to the desired maximum warnings (default: 0).
|
|
|
|
|
|
|
|
You can override the default limit with MAX_WARNINGS environment variable or
|
|
|
|
using --max-warnings option (see the description of argument in --help for
|
|
|
|
details).
|
2019-11-23 19:48:17 -08:00
|
|
|
|
|
|
|
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:
|
|
|
|
#
|
2023-02-02 12:54:43 -08:00
|
|
|
GCC_WARN_PATTERN = re.compile(r"([^:]+):(\d+):\d+: warning: .* \[-W(.+?)\](.*)")
|
2020-07-23 12:02:59 +02:00
|
|
|
# ~~~~~ ~~~ ~~~ ~~ ~~~ ~~
|
|
|
|
# ↑ ↑ ↑ ↑ ↑ ↑
|
|
|
|
# file line column message type extra
|
2019-09-21 12:14:49 +02:00
|
|
|
|
2020-05-09 17:52:23 +02:00
|
|
|
# For recognizing warnings in MSVC format:
|
|
|
|
#
|
2023-02-02 12:54:43 -08:00
|
|
|
MSVC_WARN_PATTERN = re.compile(r".+>([^\(]+)\((\d+),\d+\): warning ([^:]+): .*")
|
2020-05-09 17:52:23 +02:00
|
|
|
# ~~ ~~~~~~ ~~~ ~~~ ~~~~~ ~~
|
|
|
|
# ↑ ↑ ↑ ↑ ↑ ↑
|
|
|
|
# project file line column code message
|
|
|
|
|
2019-09-21 12:14:49 +02:00
|
|
|
# For removing color when GCC is invoked with -fdiagnostics-color=always
|
|
|
|
#
|
2023-02-02 12:54:43 -08:00
|
|
|
ANSI_COLOR_PATTERN = re.compile(r"\x1b\[[0-9;]*[mGKH]")
|
2019-09-21 12:14:49 +02:00
|
|
|
|
2021-10-26 19:21:59 -07:00
|
|
|
# For recognizing warnings from usr/* or subprojects files
|
2023-02-02 12:54:43 -08:00
|
|
|
USR_OR_SUBPROJECTS_PATTERN = re.compile(r"^/usr/.*|.*/subprojects/.*")
|
2019-09-21 12:14:49 +02:00
|
|
|
|
2020-07-23 11:40:58 +02:00
|
|
|
|
2023-02-02 12:54:43 -08:00
|
|
|
class warning_summaries:
|
2020-07-23 11:40:58 +02:00
|
|
|
def __init__(self):
|
|
|
|
self.types = {}
|
|
|
|
self.files = {}
|
|
|
|
self.lines = set()
|
|
|
|
|
|
|
|
def count_type(self, name):
|
|
|
|
self.types[name] = self.types.get(name, 0) + 1
|
|
|
|
|
|
|
|
def count_file(self, name):
|
|
|
|
self.files[name] = self.files.get(name, 0) + 1
|
|
|
|
|
|
|
|
def list_all(self):
|
|
|
|
for line in sorted(self.lines):
|
|
|
|
print(line)
|
|
|
|
print()
|
|
|
|
|
|
|
|
def print_files(self):
|
|
|
|
print("Warnings grouped by file:\n")
|
|
|
|
print_summary(self.files)
|
|
|
|
|
|
|
|
def print_types(self):
|
|
|
|
print("Warnings grouped by type:\n")
|
|
|
|
print_summary(self.types)
|
|
|
|
|
|
|
|
|
2019-09-21 12:14:49 +02:00
|
|
|
def remove_colors(line):
|
2023-02-02 12:54:43 -08:00
|
|
|
return re.sub(ANSI_COLOR_PATTERN, "", line)
|
2019-09-21 12:14:49 +02:00
|
|
|
|
|
|
|
|
2020-07-23 12:02:59 +02:00
|
|
|
def count_warning(gcc_format, line_no, line, warnings):
|
2019-09-21 12:14:49 +02:00
|
|
|
line = remove_colors(line)
|
2020-05-09 17:52:23 +02:00
|
|
|
|
|
|
|
pattern = GCC_WARN_PATTERN if gcc_format else MSVC_WARN_PATTERN
|
|
|
|
match = pattern.match(line)
|
2019-09-21 12:14:49 +02:00
|
|
|
if not match:
|
|
|
|
return 0
|
2020-02-26 11:58:41 +01:00
|
|
|
|
2021-10-26 19:21:59 -07:00
|
|
|
# Ignore out-of-scope warnings from system and subprojects.
|
|
|
|
file = match.group(1)
|
|
|
|
if USR_OR_SUBPROJECTS_PATTERN.match(file):
|
|
|
|
return 0
|
|
|
|
|
2020-02-26 11:58:41 +01:00
|
|
|
# Some warnings (e.g. effc++) are reported multiple times, once
|
|
|
|
# for every usage; ignore duplicates.
|
|
|
|
line = line.strip()
|
2020-07-23 11:40:58 +02:00
|
|
|
if line in warnings.lines:
|
2020-02-26 11:58:41 +01:00
|
|
|
return 0
|
2020-07-23 11:40:58 +02:00
|
|
|
warnings.lines.add(line)
|
2020-02-26 11:58:41 +01:00
|
|
|
|
2020-07-23 11:40:58 +02:00
|
|
|
# wline = match.group(2)
|
2019-09-21 12:14:49 +02:00
|
|
|
wtype = match.group(3)
|
2020-07-23 12:02:59 +02:00
|
|
|
|
|
|
|
if pattern == GCC_WARN_PATTERN and match.group(4):
|
2023-02-02 12:54:43 -08:00
|
|
|
print(
|
|
|
|
"Log file is corrupted: extra characters in line", line_no, file=sys.stderr
|
|
|
|
)
|
2020-07-23 12:02:59 +02:00
|
|
|
|
2019-12-11 00:18:38 +01:00
|
|
|
_, fname = os.path.split(file)
|
2020-07-23 11:40:58 +02:00
|
|
|
warnings.count_type(wtype)
|
|
|
|
warnings.count_file(fname)
|
2019-09-21 12:14:49 +02:00
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
def get_input_lines(name):
|
2023-02-02 12:54:43 -08:00
|
|
|
if name == "-":
|
2019-09-21 12:14:49 +02:00
|
|
|
return sys.stdin.readlines()
|
|
|
|
if not os.path.isfile(name):
|
2023-02-02 12:54:43 -08:00
|
|
|
print("{}: no such file.".format(name))
|
2019-09-21 12:14:49 +02:00
|
|
|
sys.exit(2)
|
2023-02-02 12:54:43 -08:00
|
|
|
with open(name, "r", encoding="utf-8") as logs:
|
2019-09-21 12:14:49 +02:00
|
|
|
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):
|
|
|
|
size = find_longest_name_length(issues.keys()) + 1
|
2019-12-12 16:20:50 +01:00
|
|
|
items = list(issues.items())
|
|
|
|
for name, count in sorted(items, key=lambda x: (x[1], x[0]), reverse=True):
|
2023-02-02 12:54:43 -08:00
|
|
|
print(
|
|
|
|
" {text:{field_size}s}: {count}".format(
|
|
|
|
text=name, count=count, field_size=size
|
|
|
|
)
|
|
|
|
)
|
2019-09-21 12:14:49 +02:00
|
|
|
print()
|
|
|
|
|
2019-11-23 19:48:17 -08:00
|
|
|
|
|
|
|
def parse_args():
|
|
|
|
parser = argparse.ArgumentParser(
|
2023-02-02 12:54:43 -08:00
|
|
|
formatter_class=argparse.RawTextHelpFormatter, description=__doc__
|
|
|
|
)
|
2019-11-23 19:48:17 -08:00
|
|
|
|
|
|
|
parser.add_argument(
|
2023-02-02 12:54:43 -08:00
|
|
|
"logfile",
|
|
|
|
metavar="LOGFILE",
|
|
|
|
help="Path to the logfile, or use - to read from stdin",
|
|
|
|
)
|
2019-11-23 19:48:17 -08:00
|
|
|
|
2023-02-02 12:54:43 -08:00
|
|
|
max_warnings = int(os.getenv("MAX_WARNINGS", "0"))
|
2019-11-23 19:48:17 -08:00
|
|
|
parser.add_argument(
|
2023-02-02 12:54:43 -08:00
|
|
|
"-m",
|
|
|
|
"--max-warnings",
|
2019-11-23 19:48:17 -08:00
|
|
|
type=int,
|
|
|
|
default=max_warnings,
|
2023-02-02 12:54:43 -08:00
|
|
|
help="Override the maximum number of warnings.\n"
|
|
|
|
"Use value -1 to disable the check.",
|
|
|
|
)
|
2019-11-23 19:48:17 -08:00
|
|
|
|
2019-12-11 00:18:38 +01:00
|
|
|
parser.add_argument(
|
2023-02-02 12:54:43 -08:00
|
|
|
"-f", "--files", action="store_true", help="Group warnings by filename."
|
|
|
|
)
|
2019-12-11 00:18:38 +01:00
|
|
|
|
2019-12-12 14:55:55 +01:00
|
|
|
parser.add_argument(
|
2023-02-02 12:54:43 -08:00
|
|
|
"-l", "--list", action="store_true", help="Display sorted list of all warnings."
|
|
|
|
)
|
2019-12-12 14:55:55 +01:00
|
|
|
|
2020-05-09 17:52:23 +02:00
|
|
|
parser.add_argument(
|
2023-02-02 12:54:43 -08:00
|
|
|
"--msvc", action="store_true", help="Look for warnings using MSVC format."
|
|
|
|
)
|
2020-05-09 17:52:23 +02:00
|
|
|
|
2019-11-23 19:48:17 -08:00
|
|
|
return parser.parse_args()
|
2019-09-21 12:14:49 +02:00
|
|
|
|
2019-12-12 14:55:55 +01:00
|
|
|
|
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
|
2020-07-23 11:40:58 +02:00
|
|
|
warnings = warning_summaries()
|
2019-11-23 19:48:17 -08:00
|
|
|
args = parse_args()
|
2020-05-09 17:52:23 +02:00
|
|
|
use_gcc_format = not args.msvc
|
2020-07-23 12:02:59 +02:00
|
|
|
line_no = 1
|
2019-11-23 19:48:17 -08:00
|
|
|
for line in get_input_lines(args.logfile):
|
2020-07-23 12:02:59 +02:00
|
|
|
total += count_warning(use_gcc_format, line_no, line, warnings)
|
|
|
|
line_no += 1
|
2019-12-12 14:55:55 +01:00
|
|
|
if args.list:
|
2020-07-23 11:40:58 +02:00
|
|
|
warnings.list_all()
|
|
|
|
if args.files and warnings.files:
|
|
|
|
warnings.print_files()
|
|
|
|
if warnings.types:
|
|
|
|
warnings.print_types()
|
2023-02-02 12:54:43 -08:00
|
|
|
print("Total: {} warnings".format(total), end="")
|
2019-11-23 19:48:17 -08:00
|
|
|
if args.max_warnings >= 0:
|
2023-02-02 12:54:43 -08:00
|
|
|
print(" (out of {} allowed)\n".format(args.max_warnings))
|
2019-11-23 19:48:17 -08:00
|
|
|
if total > args.max_warnings:
|
2023-02-02 12:54:43 -08:00
|
|
|
print("Error: upper limit of allowed warnings is", args.max_warnings)
|
2019-11-23 19:48:17 -08:00
|
|
|
rcode = 1
|
|
|
|
else:
|
2023-02-02 12:54:43 -08:00
|
|
|
print("\n")
|
2019-11-23 19:48:17 -08:00
|
|
|
return rcode
|
2019-09-21 12:14:49 +02:00
|
|
|
|
2023-02-02 12:54:43 -08:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2019-11-23 19:48:17 -08:00
|
|
|
sys.exit(main())
|