Add the cmake files for running static code analysis with the Polyspace tools in the west build. The analysis leverages the compilation database. Options for the analysis are documented in doc/develop/sca/polyspace.rst. Analysis results are printed as command line output and provided as CSV. Manually tested on v4.0.0 with various sample applications. Signed-off-by: Martin Becker <mbecker@mathworks.com>
85 lines
2.6 KiB
Python
Executable file
85 lines
2.6 KiB
Python
Executable file
#!/usr/bin/python3
|
|
"""
|
|
Print Polyspace results on terminal, for easy review.
|
|
Copyright (C) 2020-2024 The MathWorks, Inc.
|
|
"""
|
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
from collections import Counter
|
|
|
|
|
|
def _parse_findings(filename: str, ignore_metrics=True):
|
|
"""Parse CSV file"""
|
|
csv_sep = '\t' # Polyspace default separator
|
|
|
|
def consume_header(oneline):
|
|
parts = oneline.split(csv_sep)
|
|
header.extend([p.strip() for p in parts])
|
|
|
|
def consume_data(oneline):
|
|
columns = oneline.split(csv_sep)
|
|
return dict(zip(header, columns, strict=True))
|
|
|
|
findings = []
|
|
header = []
|
|
with open(filename, encoding="latin-1") as fp:
|
|
for lno, line in enumerate(fp):
|
|
if lno == 0:
|
|
consume_header(line.rstrip())
|
|
else:
|
|
onefind = consume_data(line.rstrip())
|
|
if onefind and (not ignore_metrics or onefind.get('Family', '') != 'Code Metric'):
|
|
findings.append(onefind)
|
|
# --
|
|
return findings
|
|
|
|
|
|
def print_sorted(mydict, maxlines=0):
|
|
"""Print a dict sorted by value, largest first"""
|
|
|
|
for lno, cnt_and_fil in enumerate(
|
|
sorted(((cnt, fil) for fil, cnt in mydict.items()), reverse=True), start=1
|
|
):
|
|
print(f" {cnt_and_fil[0]} issues in {cnt_and_fil[1]}")
|
|
if lno >= maxlines and maxlines != 0:
|
|
break
|
|
|
|
|
|
def main(argv):
|
|
# 1. command line arguments as required by your script
|
|
parser = argparse.ArgumentParser(description='Print results to console', allow_abbrev=False)
|
|
parser.add_argument('file', type=str, help='output file from polyspace-results-export')
|
|
parser.add_argument('--details', action='store_true', help='print details')
|
|
args = parser.parse_args()
|
|
|
|
# 2. parsing the Polyspace files -> report
|
|
findings = _parse_findings(args.file)
|
|
print("-= Polyspace Static Code Analysis results =-")
|
|
|
|
# 3. print details
|
|
if args.details:
|
|
for f in findings:
|
|
print(
|
|
f"{f.get('File', 'unknown file')}:"
|
|
+ f"{f.get('Line', '?')}:"
|
|
+ f"{f.get('Col', '?')}: "
|
|
+ f"{f.get('Family', '')} {f.get('Check', 'Defect')}"
|
|
)
|
|
|
|
# 3. summary by category and file
|
|
print("By type:")
|
|
family = Counter([f.get('Family', 'Defect') for f in findings])
|
|
print_sorted(family)
|
|
print("Top 10 files:")
|
|
files = Counter([os.path.basename(f.get('File', 'Unknown')) for f in findings])
|
|
print_sorted(files, 10)
|
|
print(f"SCA found {len(findings)} issues in total")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv[1:])
|