diff --git a/ci/cppcheck-misra.sh b/ci/cppcheck-misra.sh index 96e0c9cda223ce6b1bae4c330fff5e7a072ff600..529f96aee9be670dcce09b11b086dcc853ee7513 100755 --- a/ci/cppcheck-misra.sh +++ b/ci/cppcheck-misra.sh @@ -14,17 +14,11 @@ curl https://raw.githubusercontent.com/danmar/cppcheck/f4b5b156d720c712f6ce99f6e # generate dump files (XML representations of AST etc.) for all headers, source files etc. echo -e "\u001b[34;1mGenerating dump files...\u001b[0m" -for file in $(find inc/ src/ -type f \( -iname "*.cpp" -or -iname "*.hpp" \)) -do - cppcheck --dump $file -done +find inc/ src/ -type f \( -iname "*.cpp" -or -iname "*.hpp" \) | xargs cppcheck --dump # run the MISRA checks against the dumps and send the results to a file echo -e "\u001b[34;1mRunning MISRA C(2012) rule compliance tests...\u001b[0m" -for file in $(find inc/ src/ -type f -name "*.dump") -do - python ci/misra.py $file >> ci/report.msr 2>&1 -done +find inc/ src/ -type f -name "*.dump" | xargs python3 ci/misra.py >> ci/report.msr 2>&1 # pre-process the generated report to remove all useless strings echo -e "\u001b[34;1mPre-processing report...\u001b[0m" @@ -32,4 +26,4 @@ sed -i -r 's/(.*Script.*)|(.*Checking.*)|(.*MISRA.*)//gm; /(^$)/d; s/(\s\(.*\)\s # run the summarizer for a nice, clean summary of errors echo -e "\u001b[34;1mSummarizing results...\u001b[0m" -python ci/summarizer.py ci/report.msr +python3 ci/summarizer.py ci/report.msr diff --git a/ci/summarizer.py b/ci/summarizer.py index 60090ef370632aa97336ac39cc519c3989223f14..b7a3aa17a360de5f1bc83720b68a4608493a65d9 100755 --- a/ci/summarizer.py +++ b/ci/summarizer.py @@ -1,68 +1,80 @@ -#!/bin/env python +#!/bin/env python3 from sys import argv from sys import exit from collections import Counter -''' +""" Naive parser and pretty printer for the MISRA reports by cppcheck -''' +""" -script, reportfile = argv -def analyze(): - ''' - A really dumb parser for the pre-processed report generated by cppcheck - ''' - errorsMap = {} # keys: filenames, values: list of tuples containing rule violated and line - file = open(reportfile, 'r') - fileLines = file.readlines() - cppcheckNumOfErrors = len(fileLines) # number of errors in the original report (most of these are duplicates) - linesSeen = set() # contains the unique lines from the file +red = "\033[91m" +yellow = "\033[93m" +green = "\033[92m" +bold = "\033[1m" +end = "\033[0m" + +class Summarizer(object): + def __init__(self, report_name): + with open(report_name, 'r') as f: + self.file_lines = f.readlines() + f.close() + + def analyze(self): + """ + A really dumb parser for the pre-processed report generated by cppcheck + """ + + errors_map = {} # dictionary containing filenames, rule violations and line where the violation occurred - if len(fileLines) == 0: - print("\033[1m\033[92mStatic analysis for MISRA compliance complete. No errors found.") - return 0 - else: - for line in fileLines: # remove duplicate lines - if line not in linesSeen: - linesSeen.add(line) + lines_seen = set() # contains the unique lines from the file + + if len(self.file_lines) == 0: + print(bold + green + "Static analysis for MISRA compliance complete. No errors found." + end) + return 0 + else: + for line in self.file_lines: # remove duplicate lines + if line not in lines_seen: + lines_seen.add(line) - for line in linesSeen: # after cleaning up the duplicates, split the line into parts - lineContents = line.split(':') - fileName = lineContents[0] # first part is the filename (index 0) - error = (lineContents[1], lineContents[2].strip('\n')) # index 1: line number, index 2: number of violated rule + line_contents = line.split(':') + file_name = line_contents[0] # first part is the filename (index 0) + error = (line_contents[1], line_contents[2].strip('\n')) # index 1 is the line number, index 2 is the number of violated rule - if fileName not in errorsMap.keys(): - errorsMap[fileName] = list() # create a new list for the new filename and append the tuple in it - errorsMap[fileName].append(error) - else: - errorsMap[fileName].append(error) # do not append if it already exists + if file_name not in errors_map.keys(): + errors_map[file_name] = list() # create a new list for the new filename and append the tuple in it + errors_map[file_name].append(error) + else: + errors_map[file_name].append(error) # do not append if it already exists - return errorsMap # return the completed error dictionary + return errors_map # return the completed error dictionary + + + def pretty_print(self, errors): + """ + Pretty-prints the contents of the error dictionary with colors and stuff + """ + print(bold + red + "=================================================\n" + end) + print(bold + red + " Static analysis results: Error Summary \n" + end) + for key in errors: + for error in errors[key]: + name_string = f"{bold}{red}File {yellow}{key}{red}" + rule_violated_string = f"violates rule {yellow}#{error[1]}{red} of the MISRA C 2012 standard" + line_number_string = f"at line {yellow}{error[0]}{end}" -def prettyprint(errors): - ''' - Prettyprints the contents of the error dictionary with colors and stuff - ''' - print("\033[1m\033[91m=================================================\n") - print("\033[1m Static analysis results: Error Summary \n") - for key in errors: - for error in errors[key]: - print("\033[1mFile \033[93m{0}\033[91m violates rule \033[93m#{1}\033[91m of the MISRA C 2012 standard at line \033[93m{2}\033[91m".format(key, error[1], error[0])) - print "" - print "" - print("\033[1m=================================================\033[0m\n") + print(f"{name_string.ljust(75)} {rule_violated_string} {line_number_string}") + print("") + print("") + print(bold + red +"=================================================" + end) -def run(): - errors = analyze() + +if __name__ == "__main__": + s = Summarizer(argv[1]) + errors = s.analyze() if isinstance(errors, dict): - prettyprint(errors) + s.pretty_print(errors) exit(127) elif isinstance(errors, int) and errors == 0: exit(0) - - -if __name__ == "__main__": - run()