1#!/usr/bin/env python3 2 3# Copyright (c) 2023 Baumer (www.baumer.com) 4# SPDX-License-Identifier: Apache-2.0 5 6"""This script converting the Zephyr coding guideline rst file to a output file, 7or print the output to the console. Which than can be used by a tool which 8needs to have that information in a specific format (e.g. for cppcheck). 9Or simply use the rule list to generate a filter to suppress all other rules 10used by default from such a tool. 11""" 12 13import sys 14import re 15import argparse 16from pathlib import Path 17 18class RuleFormatter: 19 """ 20 Base class for the different output formats 21 """ 22 def table_start_print(self, outputfile): 23 pass 24 def severity_print(self, outputfile, guideline_number, severity): 25 pass 26 def description_print(self, outputfile, guideline_number, description): 27 pass 28 def closing_print(self, outputfile): 29 pass 30 31class CppCheckFormatter(RuleFormatter): 32 """ 33 Formatter class to print the rules in a format which can be used by cppcheck 34 """ 35 def table_start_print(self, outputfile): 36 # Start search by cppcheck misra addon 37 print('Appendix A Summary of guidelines', file=outputfile) 38 39 def severity_print(self, outputfile, guideline_number, severity): 40 print('Rule ' + guideline_number + ' ' + severity, file=outputfile) 41 42 def description_print(self, outputfile, guideline_number, description): 43 print(description + '(Misra rule ' + guideline_number + ')', file=outputfile) 44 45 def closing_print(self, outputfile): 46 # Make cppcheck happy by starting the appendix 47 print('Appendix B', file=outputfile) 48 print('', file=outputfile) 49 50def convert_guidelines(args): 51 inputfile = args.input 52 outputfile = sys.stdout 53 formatter = None 54 55 # If the output is not empty, open the given file for writing 56 if args.output is not None: 57 outputfile = open(args.output, "w") 58 59 try: 60 file_stream = open(inputfile, 'rt', errors='ignore') 61 except Exception: 62 print('Error opening ' + inputfile +'.') 63 sys.exit() 64 65 # Set formatter according to the used format 66 if args.format == 'cppcheck': 67 formatter = CppCheckFormatter() 68 69 # Search for table named Main rules 70 pattern_table_start = re.compile(r'.*list-table:: Main rules') 71 # Each Rule is a new table column so start with '[tab]* - Rule' 72 # Ignore directives here 73 pattern_new_line = re.compile(r'^ \* - Rule ([0-9]+.[0-9]+).*$') 74 # Each table column start with '[tab]- ' 75 pattern_new_col = re.compile(r'^ - (.*)$') 76 77 table_start = False 78 guideline_number = '' 79 guideline_state = 0 80 guideline_list = [] 81 82 for line in file_stream: 83 84 line = line.replace('\r', '').replace('\n', '') 85 86 # Done if we find the Additional rules table start 87 if line.find('Additional rules') >= 0: 88 break 89 90 if len(line) == 0: 91 continue 92 93 if not table_start: 94 res = pattern_table_start.match(line) 95 if res: 96 table_start = True 97 formatter.table_start_print(outputfile) 98 continue 99 100 res = pattern_new_line.match(line) 101 if res: 102 guideline_state = "severity" 103 guideline_number = res.group(1) 104 guideline_list.append(guideline_number) 105 continue 106 elif guideline_number == '': 107 continue 108 109 res = pattern_new_col.match(line) 110 if res: 111 if guideline_state == "severity": 112 # Severity 113 formatter.severity_print(outputfile, guideline_number, res.group(1)) 114 guideline_state = "description" 115 continue 116 if guideline_state == "description": 117 # Description 118 formatter.description_print(outputfile, guideline_number, res.group(1)) 119 guideline_state = "None" 120 # We stop here for now, we do not handle the CERT C col 121 guideline_number = '' 122 continue 123 124 formatter.closing_print(outputfile) 125 126if __name__ == "__main__": 127 supported_formats = ['cppcheck'] 128 129 parser = argparse.ArgumentParser(allow_abbrev=False) 130 parser.add_argument( 131 "-i", "--input", metavar="RST_FILE", type=Path, required=True, 132 help="Path to rst input source file, where the guidelines are written down." 133 ) 134 parser.add_argument( 135 "-f", "--format", metavar="FORMAT", choices=supported_formats, required=True, 136 help="Format to convert guidlines to. Supported formats are: " + str(supported_formats) 137 ) 138 parser.add_argument( 139 "-o", "--output", metavar="OUTPUT_FILE", type=Path, required=False, 140 help="Path to output file, where the converted guidelines are written to. If outputfile is not specified, print to stdout." 141 ) 142 args = parser.parse_args() 143 144 convert_guidelines(args) 145