Simon Hein | 169de2c | 2023-07-25 10:35:11 +0200 | [diff] [blame] | 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, |
| 7 | or print the output to the console. Which than can be used by a tool which |
| 8 | needs to have that information in a specific format (e.g. for cppcheck). |
| 9 | Or simply use the rule list to generate a filter to suppress all other rules |
| 10 | used by default from such a tool. |
| 11 | """ |
| 12 | |
| 13 | import sys |
| 14 | import re |
| 15 | import argparse |
| 16 | from pathlib import Path |
| 17 | |
| 18 | class 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 | |
| 31 | class 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 | |
| 50 | def 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 | |
| 126 | if __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) |