diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index fe372f45..6ff0a397 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -20,8 +20,10 @@ jobs: set -x $CXX --version ./autogen.sh - ./configure - make -j "$(nproc)" + ./configure CXXFLAGS="-Wall -fdiagnostics-color=always" + make -j "$(nproc)" |& tee build.log + - name: Summarize warnings + run: python3 ./scripts/count-warnings.py build.log build_gcc8: name: "GCC 8 (ubuntu-18.04)" @@ -39,8 +41,10 @@ jobs: set -x $CXX --version ./autogen.sh - ./configure - make -j "$(nproc)" + ./configure CXXFLAGS="-Wall -fdiagnostics-color=always" + make -j "$(nproc)" |& tee build.log + - name: Summarize warnings + run: python3 ./scripts/count-warnings.py build.log build_ubuntu: name: "GCC" @@ -58,8 +62,10 @@ jobs: set -x g++ --version ./autogen.sh - ./configure - make -j "$(nproc)" + ./configure CXXFLAGS="-Wall -fdiagnostics-color=always" + make -j "$(nproc)" |& tee build.log + - name: Summarize warnings + run: python3 ./scripts/count-warnings.py build.log build_clang: name: "Clang 6 (ubuntu-18.04)" @@ -77,8 +83,10 @@ jobs: set -x $CXX --version ./autogen.sh - ./configure - make -j "$(nproc)" + ./configure CXXFLAGS="-Wall" + make -j "$(nproc)" |& tee build.log + - name: Summarize warnings + run: python3 ./scripts/count-warnings.py build.log build_macos: name: "Clang (macOS-10.14)" @@ -93,5 +101,7 @@ jobs: set -x clang++ --version ./autogen.sh - ./configure - make -j "$(sysctl -n hw.physicalcpu)" + ./configure CXXFLAGS="-Wall" + make -j "$(sysctl -n hw.physicalcpu)" 2>&1 | tee build.log + - name: Summarize warnings + run: python3 ./scripts/count-warnings.py build.log diff --git a/scripts/count-warnings.py b/scripts/count-warnings.py new file mode 100644 index 00000000..54aa06a8 --- /dev/null +++ b/scripts/count-warnings.py @@ -0,0 +1,88 @@ +#!/usr/bin/python3 + +# Copyright (c) 2019 Patryk Obara + +# This script counts and all warnings and prints a summary. +# +# Usage: ./count-warnings.py build.log +# Usage: cat "*.log" | ./count-warnings.py - +# +# 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. + +# pylint: disable=invalid-name +# pylint: disable=missing-docstring + +import os +import re +import sys + +# Maximum allowed number of warnings; if build will include more warnings, +# then script will return with status 1. Simply change this line if you +# want to set a different limit. +# +MAX_WARNINGS = 357 + +# 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() + + +def print_summary(warning_types): + print("Warnings grouped by type:\n") + summary = list(warning_types.items()) + for warning, count in sorted(summary, key=lambda x: -x[1]): + print(' {:28s}: {}'.format(warning, count)) + print() + + +def main(): + total = 0 + warning_types = {} + for line in get_input_lines(sys.argv[1]): + total += count_warning(line, warning_types) + if warning_types: + print_summary(warning_types) + print('Total: {} warnings\n'.format(total)) + if total > MAX_WARNINGS: + print('Error: upper limit of warnings is', MAX_WARNINGS) + sys.exit(1) + + +if __name__ == '__main__': + main()