#!/usr/bin/python3 # SPDX-License-Identifier: GPL-2.0-or-later # # Copyright (C) 2019-2021 Patryk Obara """ This script prints a summary snippet of information out of reports created by scan-build or analyze-build for Clang's static code analysis. It returns success to the shell if the number or bugs encountered is less than or equal to the desired maximum bugs. See --help for a description of how to set the maximum. If the count exceeds the maximum then the script will return a status of 1 (failure), otherwise the script returns 0 (success). """ # This script depends on BeautifulSoup module, if you're distribution is # missing it, you can use pipenv to install it for virtualenv spanning only # this repo: pipenv install beautifulsoup4 html5lib # pylint: disable=invalid-name # pylint: disable=missing-docstring import os import argparse import sys from bs4 import BeautifulSoup def summary_values(summary_table): if not summary_table: return for row in summary_table.find_all('tr'): description = row.find('td', 'SUMM_DESC') value = row.find('td', 'Q') if description is None or value is None: continue yield description.get_text(), int(value.get_text()) def read_soup(index_html): with open(index_html, encoding='utf-8') as index: soup = BeautifulSoup(index, 'html5lib') tables = soup.find_all('table') summary = tables[1] return dict(summary_values(summary)) 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 for warning, count in sorted(summary, key=lambda x: -x[1]): print(' {text:{field_size}s}: {count}'.format( text=warning, count=count, field_size=size)) print() 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( 'report', metavar='REPORT', help=("Path to the HTML report file")) max_bugs = to_int(os.getenv('MAX_BUGS', None)) parser.add_argument( '-m', '--max-bugs', type=int, required=not isinstance(max_bugs, int), default=max_bugs, help='Defines the maximum number of bugs permitted to exist\n' 'in the report before returning a failure to the shell.\n' 'If not provided, the script will attempt to read it from\n' 'the MAX_BUGS 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_bugs))) return parser.parse_args() def main(): rcode = 0 args = parse_args() bug_types = read_soup(args.report) total = bug_types.pop('All Bugs') if bug_types: print("Bugs grouped by type:\n") print_summary(bug_types) print('Total: {} bugs'.format(total), end='') if args.max_bugs >= 0: print(' (out of {} allowed)\n'.format(args.max_bugs)) if total > args.max_bugs: print('Error: upper limit of allowed bugs is', args.max_bugs) rcode = 1 else: print('\n') return rcode if __name__ == '__main__': sys.exit(main())