blob: 16d5aba8b5318c965d1aee9cc951a43f865de6e9 [file] [log] [blame]
Yuto Takano39639672021-08-05 19:47:48 +01001#!/usr/bin/env python3
2#
3# Copyright The Mbed TLS Contributors
4# SPDX-License-Identifier: Apache-2.0
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
Darryl Greend5802922018-05-08 15:30:59 +010018"""
Yuto Takano39639672021-08-05 19:47:48 +010019This script confirms that the naming of all symbols and identifiers in Mbed TLS
Yuto Takano159255a2021-08-06 17:00:28 +010020are consistent with the house style and are also self-consistent. It only runs
21on Linux and macOS since it depends on nm.
22
Yuto Takano55c6c872021-08-09 15:35:19 +010023It contains two major Python classes, CodeParser and NameChecker. They both have
24a comprehensive "run-all" function (comprehensive_parse() and perform_checks())
25but the individual functions can also be used for specific needs.
26
27CodeParser makes heavy use of regular expressions to parse the code, and is
28dependent on the current code formatting. Many Python C parser libraries require
29preprocessed C code, which means no macro parsing. Compiler tools are also not
30very helpful when we want the exact location in the original source (which
31becomes impossible when e.g. comments are stripped).
32
33NameChecker performs the following checks:
Yuto Takano81528c02021-08-06 16:22:06 +010034
35- All exported and available symbols in the library object files, are explicitly
Yuto Takano159255a2021-08-06 17:00:28 +010036 declared in the header files. This uses the nm command.
Yuto Takano81528c02021-08-06 16:22:06 +010037- All macros, constants, and identifiers (function names, struct names, etc)
Yuto Takano55c6c872021-08-09 15:35:19 +010038 follow the required regex pattern.
Yuto Takano81528c02021-08-06 16:22:06 +010039- Typo checking: All words that begin with MBED exist as macros or constants.
Yuto Takanofc54dfb2021-08-07 17:18:28 +010040
Yuto Takano55c6c872021-08-09 15:35:19 +010041The script returns 0 on success, 1 on test failure, and 2 if there is a script
Yuto Takano8246eb82021-08-16 10:37:24 +010042error. It must be run from Mbed TLS root.
Darryl Greend5802922018-05-08 15:30:59 +010043"""
Yuto Takano39639672021-08-05 19:47:48 +010044
45import argparse
Yuto Takano977e07f2021-08-09 11:56:15 +010046import glob
Yuto Takano39639672021-08-05 19:47:48 +010047import textwrap
Darryl Greend5802922018-05-08 15:30:59 +010048import os
49import sys
50import traceback
51import re
52import shutil
53import subprocess
54import logging
55
Yuto Takano81528c02021-08-06 16:22:06 +010056# Naming patterns to check against. These are defined outside the NameCheck
57# class for ease of modification.
Yuto Takanobb7dca42021-08-05 19:57:58 +010058MACRO_PATTERN = r"^(MBEDTLS|PSA)_[0-9A-Z_]*[0-9A-Z]$"
Yuto Takano81528c02021-08-06 16:22:06 +010059CONSTANTS_PATTERN = MACRO_PATTERN
Yuto Takanoc1838932021-08-05 19:52:09 +010060IDENTIFIER_PATTERN = r"^(mbedtls|psa)_[0-9a-z_]*[0-9a-z]$"
Yuto Takano39639672021-08-05 19:47:48 +010061
Yuto Takanod93fa372021-08-06 23:05:55 +010062class Match(): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010063 """
64 A class representing a match, together with its found position.
65
66 Fields:
67 * filename: the file that the match was in.
68 * line: the full line containing the match.
Yuto Takanod93fa372021-08-06 23:05:55 +010069 * pos: a tuple of (line_no, start, end) positions on the file line where the
70 match is.
Yuto Takano81528c02021-08-06 16:22:06 +010071 * name: the match itself.
72 """
Yuto Takanod93fa372021-08-06 23:05:55 +010073 def __init__(self, filename, line, pos, name):
Yuto Takano39639672021-08-05 19:47:48 +010074 self.filename = filename
75 self.line = line
76 self.pos = pos
77 self.name = name
Yuto Takano39639672021-08-05 19:47:48 +010078
Yuto Takanoa4e75122021-08-06 17:23:28 +010079 def __str__(self):
Yuto Takanofb86ac72021-08-16 10:32:40 +010080 """
81 Return a formatted code listing representation of the erroneous line.
82 """
83 gutter = format(self.pos[0], "4d")
Yuto Takano381fda82021-08-06 23:37:20 +010084 underline = self.pos[1] * " " + (self.pos[2] - self.pos[1]) * "^"
85
Yuto Takanoa4e75122021-08-06 17:23:28 +010086 return (
Yuto Takanofb86ac72021-08-16 10:32:40 +010087 " {0} |\n".format(" " * len(gutter)) +
Yuto Takano381fda82021-08-06 23:37:20 +010088 " {0} | {1}".format(gutter, self.line) +
Yuto Takanofb86ac72021-08-16 10:32:40 +010089 " {0} | {1}\n".format(" " * len(gutter), underline)
Yuto Takanoa4e75122021-08-06 17:23:28 +010090 )
Yuto Takanod93fa372021-08-06 23:05:55 +010091
92class Problem(): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010093 """
94 A parent class representing a form of static analysis error.
Yuto Takano81528c02021-08-06 16:22:06 +010095 """
Yuto Takano5473be22021-08-17 10:14:01 +010096 # Class variable to control the quietness of all problems
97 quiet = False
Yuto Takano39639672021-08-05 19:47:48 +010098 def __init__(self):
99 self.textwrapper = textwrap.TextWrapper()
Yuto Takano81528c02021-08-06 16:22:06 +0100100 self.textwrapper.width = 80
Yuto Takanoa4e75122021-08-06 17:23:28 +0100101 self.textwrapper.initial_indent = " > "
Yuto Takano81528c02021-08-06 16:22:06 +0100102 self.textwrapper.subsequent_indent = " "
Yuto Takano39639672021-08-05 19:47:48 +0100103
Yuto Takanod93fa372021-08-06 23:05:55 +0100104class SymbolNotInHeader(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100105 """
106 A problem that occurs when an exported/available symbol in the object file
107 is not explicitly declared in header files. Created with
108 NameCheck.check_symbols_declared_in_header()
109
110 Fields:
111 * symbol_name: the name of the symbol.
112 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100113 def __init__(self, symbol_name):
Yuto Takano39639672021-08-05 19:47:48 +0100114 self.symbol_name = symbol_name
115 Problem.__init__(self)
116
117 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100118 if self.quiet:
119 return "{0}".format(self.symbol_name)
120
Yuto Takano39639672021-08-05 19:47:48 +0100121 return self.textwrapper.fill(
122 "'{0}' was found as an available symbol in the output of nm, "
123 "however it was not declared in any header files."
124 .format(self.symbol_name))
125
Yuto Takanod93fa372021-08-06 23:05:55 +0100126class PatternMismatch(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100127 """
128 A problem that occurs when something doesn't match the expected pattern.
129 Created with NameCheck.check_match_pattern()
130
131 Fields:
132 * pattern: the expected regex pattern
133 * match: the Match object in question
134 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100135 def __init__(self, pattern, match):
Yuto Takano39639672021-08-05 19:47:48 +0100136 self.pattern = pattern
137 self.match = match
138 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100139
Yuto Takano39639672021-08-05 19:47:48 +0100140 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100141 if self.quiet:
Yuto Takanod70d4462021-08-09 12:45:51 +0100142 return (
Yuto Takano206b0222021-08-10 11:30:43 +0100143 "{0}:{1}:{2}"
Yuto Takanod70d4462021-08-09 12:45:51 +0100144 .format(self.match.filename, self.match.pos[0], self.match.name)
145 )
Yuto Takano55614b52021-08-07 01:00:18 +0100146
Yuto Takano39639672021-08-05 19:47:48 +0100147 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100148 "{0}:{1}: '{2}' does not match the required pattern '{3}'."
149 .format(
150 self.match.filename,
Yuto Takanod93fa372021-08-06 23:05:55 +0100151 self.match.pos[0],
Yuto Takanoa4e75122021-08-06 17:23:28 +0100152 self.match.name,
Yuto Takanod70d4462021-08-09 12:45:51 +0100153 self.pattern
154 )
155 ) + "\n" + str(self.match)
Yuto Takano39639672021-08-05 19:47:48 +0100156
Yuto Takanod93fa372021-08-06 23:05:55 +0100157class Typo(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100158 """
159 A problem that occurs when a word using MBED doesn't appear to be defined as
160 constants nor enum values. Created with NameCheck.check_for_typos()
161
162 Fields:
163 * match: the Match object of the MBED name in question.
164 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100165 def __init__(self, match):
Yuto Takano39639672021-08-05 19:47:48 +0100166 self.match = match
167 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100168
Yuto Takano39639672021-08-05 19:47:48 +0100169 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100170 if self.quiet:
Yuto Takanod70d4462021-08-09 12:45:51 +0100171 return (
172 "{0}:{1}:{2}"
173 .format(self.match.filename, self.match.pos[0], self.match.name)
174 )
Yuto Takano55614b52021-08-07 01:00:18 +0100175
Yuto Takano39639672021-08-05 19:47:48 +0100176 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100177 "{0}:{1}: '{2}' looks like a typo. It was not found in any "
178 "macros or any enums. If this is not a typo, put "
179 "//no-check-names after it."
Yuto Takanod70d4462021-08-09 12:45:51 +0100180 .format(self.match.filename, self.match.pos[0], self.match.name)
181 ) + "\n" + str(self.match)
Darryl Greend5802922018-05-08 15:30:59 +0100182
Yuto Takano55c6c872021-08-09 15:35:19 +0100183class CodeParser():
Yuto Takano81528c02021-08-06 16:22:06 +0100184 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100185 Class for retrieving files and parsing the code. This can be used
186 independently of the checks that NameChecker performs, for example for
187 list_internal_identifiers.py.
Yuto Takano81528c02021-08-06 16:22:06 +0100188 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100189 def __init__(self, log):
190 self.log = log
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100191 self.check_repo_path()
Yuto Takano977e07f2021-08-09 11:56:15 +0100192
Yuto Takano8e9a2192021-08-09 14:48:53 +0100193 # Memo for storing "glob expression": set(filepaths)
194 self.files = {}
195
Yuto Takano977e07f2021-08-09 11:56:15 +0100196 # Globally excluded filenames
Yuto Takano8e9a2192021-08-09 14:48:53 +0100197 self.excluded_files = ["**/bn_mul", "**/compat-2.x.h"]
Yuto Takano977e07f2021-08-09 11:56:15 +0100198
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100199 @staticmethod
200 def check_repo_path():
201 """
202 Check that the current working directory is the project root, and throw
203 an exception if not.
204 """
205 if not all(os.path.isdir(d) for d in ["include", "library", "tests"]):
206 raise Exception("This script must be run from Mbed TLS root")
207
Yuto Takano55c6c872021-08-09 15:35:19 +0100208 def comprehensive_parse(self):
Yuto Takano39639672021-08-05 19:47:48 +0100209 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100210 Comprehensive ("default") function to call each parsing function and
211 retrieve various elements of the code, together with the source location.
Darryl Greend5802922018-05-08 15:30:59 +0100212
Yuto Takano55c6c872021-08-09 15:35:19 +0100213 Returns a dict of parsed item key to the corresponding List of Matches.
Yuto Takano81528c02021-08-06 16:22:06 +0100214 """
215 self.log.info("Parsing source code...")
Yuto Takanod24e0372021-08-06 16:42:33 +0100216 self.log.debug(
Yuto Takano50953432021-08-09 14:54:36 +0100217 "The following files are excluded from the search: {}"
Yuto Takanod24e0372021-08-06 16:42:33 +0100218 .format(str(self.excluded_files))
219 )
Yuto Takano81528c02021-08-06 16:22:06 +0100220
Yuto Takano8e9a2192021-08-09 14:48:53 +0100221 all_macros = self.parse_macros([
222 "include/mbedtls/*.h",
223 "include/psa/*.h",
224 "library/*.h",
225 "tests/include/test/drivers/*.h",
Yuto Takanod70d4462021-08-09 12:45:51 +0100226 "3rdparty/everest/include/everest/everest.h",
227 "3rdparty/everest/include/everest/x25519.h"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100228 ])
229 enum_consts = self.parse_enum_consts([
230 "include/mbedtls/*.h",
231 "library/*.h",
232 "3rdparty/everest/include/everest/everest.h",
233 "3rdparty/everest/include/everest/x25519.h"
234 ])
235 identifiers = self.parse_identifiers([
236 "include/mbedtls/*.h",
237 "include/psa/*.h",
238 "library/*.h",
239 "3rdparty/everest/include/everest/everest.h",
240 "3rdparty/everest/include/everest/x25519.h"
241 ])
242 mbed_words = self.parse_mbed_words([
243 "include/mbedtls/*.h",
244 "include/psa/*.h",
245 "library/*.h",
246 "3rdparty/everest/include/everest/everest.h",
247 "3rdparty/everest/include/everest/x25519.h",
248 "library/*.c",
Yuto Takano81528c02021-08-06 16:22:06 +0100249 "3rdparty/everest/library/everest.c",
Yuto Takanod70d4462021-08-09 12:45:51 +0100250 "3rdparty/everest/library/x25519.c"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100251 ])
Yuto Takano81528c02021-08-06 16:22:06 +0100252 symbols = self.parse_symbols()
253
254 # Remove identifier macros like mbedtls_printf or mbedtls_calloc
255 identifiers_justname = [x.name for x in identifiers]
256 actual_macros = []
257 for macro in all_macros:
258 if macro.name not in identifiers_justname:
259 actual_macros.append(macro)
260
261 self.log.debug("Found:")
Yuto Takano9d9c6dc2021-08-16 10:43:45 +0100262 # Aligns the counts on the assumption that none exceeds 4 digits
263 self.log.debug(" {:4} Total Macros".format(len(all_macros)))
264 self.log.debug(" {:4} Non-identifier Macros".format(len(actual_macros)))
265 self.log.debug(" {:4} Enum Constants".format(len(enum_consts)))
266 self.log.debug(" {:4} Identifiers".format(len(identifiers)))
267 self.log.debug(" {:4} Exported Symbols".format(len(symbols)))
Yuto Takano55c6c872021-08-09 15:35:19 +0100268 return {
Yuto Takano81528c02021-08-06 16:22:06 +0100269 "macros": actual_macros,
270 "enum_consts": enum_consts,
271 "identifiers": identifiers,
272 "symbols": symbols,
Yuto Takanod93fa372021-08-06 23:05:55 +0100273 "mbed_words": mbed_words
Yuto Takano81528c02021-08-06 16:22:06 +0100274 }
275
Yuto Takano55c6c872021-08-09 15:35:19 +0100276 def get_files(self, include_wildcards, exclude_wildcards):
277 """
278 Get all files that match any of the UNIX-style wildcards. While the
279 check_names script is designed only for use on UNIX/macOS (due to nm),
280 this function alone would work fine on Windows even with forward slashes
281 in the wildcard.
282
283 Args:
284 * include_wildcards: a List of shell-style wildcards to match filepaths.
285 * exclude_wildcards: a List of shell-style wildcards to exclude.
286
287 Returns a List of relative filepaths.
288 """
289 accumulator = set()
290
291 # exclude_wildcards may be None. Also, consider the global exclusions.
292 exclude_wildcards = (exclude_wildcards or []) + self.excluded_files
293
Yuto Takano6adb2872021-08-16 11:38:34 +0100294 # Internal function to hit the memoisation cache or add to it the result
295 # of a glob operation. Used both for inclusion and exclusion since the
296 # only difference between them is whether they perform set union or
297 # difference on the return value of this function.
298 def hit_cache(wildcard):
299 if wildcard not in self.files:
300 self.files[wildcard] = set(glob.glob(wildcard, recursive=True))
301 return self.files[wildcard]
302
Yuto Takano55c6c872021-08-09 15:35:19 +0100303 for include_wildcard in include_wildcards:
Yuto Takano6adb2872021-08-16 11:38:34 +0100304 accumulator = accumulator.union(hit_cache(include_wildcard))
Yuto Takano55c6c872021-08-09 15:35:19 +0100305
Yuto Takano55c6c872021-08-09 15:35:19 +0100306 for exclude_wildcard in exclude_wildcards:
Yuto Takano6adb2872021-08-16 11:38:34 +0100307 accumulator = accumulator.difference(hit_cache(exclude_wildcard))
Yuto Takano55c6c872021-08-09 15:35:19 +0100308
309 return list(accumulator)
310
Yuto Takano8e9a2192021-08-09 14:48:53 +0100311 def parse_macros(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100312 """
313 Parse all macros defined by #define preprocessor directives.
314
315 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100316 * include: A List of glob expressions to look for files through.
317 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100318
319 Returns a List of Match objects for the found macros.
Yuto Takano39639672021-08-05 19:47:48 +0100320 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100321 macro_regex = re.compile(r"# *define +(?P<macro>\w+)")
322 exclusions = (
Yuto Takano39639672021-08-05 19:47:48 +0100323 "asm", "inline", "EMIT", "_CRT_SECURE_NO_DEPRECATE", "MULADDC_"
324 )
325
Yuto Takano50953432021-08-09 14:54:36 +0100326 files = self.get_files(include, exclude)
327 self.log.debug("Looking for macros in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100328
Yuto Takano50953432021-08-09 14:54:36 +0100329 macros = []
330 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100331 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100332 for line_no, line in enumerate(header):
Yuto Takanod93fa372021-08-06 23:05:55 +0100333 for macro in macro_regex.finditer(line):
Yuto Takanod70d4462021-08-09 12:45:51 +0100334 if macro.group("macro").startswith(exclusions):
335 continue
336
337 macros.append(Match(
338 header_file,
339 line,
340 (line_no, macro.start(), macro.end()),
341 macro.group("macro")))
Darryl Greend5802922018-05-08 15:30:59 +0100342
Yuto Takano39639672021-08-05 19:47:48 +0100343 return macros
Darryl Greend5802922018-05-08 15:30:59 +0100344
Yuto Takano8e9a2192021-08-09 14:48:53 +0100345 def parse_mbed_words(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100346 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100347 Parse all words in the file that begin with MBED, in and out of macros,
348 comments, anything.
Yuto Takano39639672021-08-05 19:47:48 +0100349
350 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100351 * include: A List of glob expressions to look for files through.
352 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100353
354 Returns a List of Match objects for words beginning with MBED.
Yuto Takano39639672021-08-05 19:47:48 +0100355 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100356 # Typos of TLS are common, hence the broader check below than MBEDTLS.
Yuto Takanod93fa372021-08-06 23:05:55 +0100357 mbed_regex = re.compile(r"\bMBED.+?_[A-Z0-9_]*")
358 exclusions = re.compile(r"// *no-check-names|#error")
359
Yuto Takano50953432021-08-09 14:54:36 +0100360 files = self.get_files(include, exclude)
361 self.log.debug("Looking for MBED words in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100362
Yuto Takano50953432021-08-09 14:54:36 +0100363 mbed_words = []
364 for filename in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100365 with open(filename, "r", encoding="utf-8") as fp:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100366 for line_no, line in enumerate(fp):
Yuto Takanod93fa372021-08-06 23:05:55 +0100367 if exclusions.search(line):
Yuto Takanoc62b4082021-08-05 20:17:07 +0100368 continue
Yuto Takano81528c02021-08-06 16:22:06 +0100369
Yuto Takanod93fa372021-08-06 23:05:55 +0100370 for name in mbed_regex.finditer(line):
371 mbed_words.append(Match(
Yuto Takano39639672021-08-05 19:47:48 +0100372 filename,
373 line,
Yuto Takanod93fa372021-08-06 23:05:55 +0100374 (line_no, name.start(), name.end()),
Yuto Takano39639672021-08-05 19:47:48 +0100375 name.group(0)
376 ))
377
Yuto Takanod93fa372021-08-06 23:05:55 +0100378 return mbed_words
Yuto Takano39639672021-08-05 19:47:48 +0100379
Yuto Takano8e9a2192021-08-09 14:48:53 +0100380 def parse_enum_consts(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100381 """
382 Parse all enum value constants that are declared.
383
384 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100385 * include: A List of glob expressions to look for files through.
386 * exclude: A List of glob expressions for excluding files.
Yuto Takano39639672021-08-05 19:47:48 +0100387
Yuto Takano81528c02021-08-06 16:22:06 +0100388 Returns a List of Match objects for the findings.
Yuto Takano39639672021-08-05 19:47:48 +0100389 """
Yuto Takano50953432021-08-09 14:54:36 +0100390 files = self.get_files(include, exclude)
391 self.log.debug("Looking for enum consts in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100392
Yuto Takano50953432021-08-09 14:54:36 +0100393 enum_consts = []
394 for header_file in files:
Yuto Takano39639672021-08-05 19:47:48 +0100395 # Emulate a finite state machine to parse enum declarations.
Yuto Takano81528c02021-08-06 16:22:06 +0100396 # 0 = not in enum
397 # 1 = inside enum
398 # 2 = almost inside enum
Darryl Greend5802922018-05-08 15:30:59 +0100399 state = 0
Yuto Takanoa083d152021-08-07 00:25:59 +0100400 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100401 for line_no, line in enumerate(header):
Yuto Takano13ecd992021-08-06 16:56:52 +0100402 # Match typedefs and brackets only when they are at the
403 # beginning of the line -- if they are indented, they might
404 # be sub-structures within structs, etc.
Yuto Takano90bc0262021-08-16 11:34:10 +0100405 if state == 0 and re.search(r"^(typedef +)?enum +{", line):
Darryl Greend5802922018-05-08 15:30:59 +0100406 state = 1
Yuto Takano90bc0262021-08-16 11:34:10 +0100407 elif state == 0 and re.search(r"^(typedef +)?enum", line):
Darryl Greend5802922018-05-08 15:30:59 +0100408 state = 2
Yuto Takano90bc0262021-08-16 11:34:10 +0100409 elif state == 2 and re.search(r"^{", line):
Darryl Greend5802922018-05-08 15:30:59 +0100410 state = 1
Yuto Takano90bc0262021-08-16 11:34:10 +0100411 elif state == 1 and re.search(r"^}", line):
Darryl Greend5802922018-05-08 15:30:59 +0100412 state = 0
Yuto Takano90bc0262021-08-16 11:34:10 +0100413 elif state == 1 and not re.search(r"^ *#", line):
414 enum_const = re.search(r"^ *(?P<enum_const>\w+)", line)
Yuto Takanod70d4462021-08-09 12:45:51 +0100415 if not enum_const:
416 continue
417
418 enum_consts.append(Match(
419 header_file,
420 line,
421 (line_no, enum_const.start(), enum_const.end()),
422 enum_const.group("enum_const")))
Yuto Takano81528c02021-08-06 16:22:06 +0100423
Yuto Takano39639672021-08-05 19:47:48 +0100424 return enum_consts
Darryl Greend5802922018-05-08 15:30:59 +0100425
Yuto Takano8e9a2192021-08-09 14:48:53 +0100426 def parse_identifiers(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100427 """
Yuto Takano8246eb82021-08-16 10:37:24 +0100428 Parse all lines of a header where a function/enum/struct/union/typedef
429 identifier is declared, based on some heuristics. Highly dependent on
430 formatting style.
Darryl Greend5802922018-05-08 15:30:59 +0100431
Yuto Takano39639672021-08-05 19:47:48 +0100432 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100433 * include: A List of glob expressions to look for files through.
434 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100435
436 Returns a List of Match objects with identifiers.
Yuto Takano39639672021-08-05 19:47:48 +0100437 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100438 identifier_regex = re.compile(
439 # Match " something(a" or " *something(a". Functions.
440 # Assumptions:
441 # - function definition from return type to one of its arguments is
Yuto Takano55c6c872021-08-09 15:35:19 +0100442 # all on one line
Yuto Takanod93fa372021-08-06 23:05:55 +0100443 # - function definition line only contains alphanumeric, asterisk,
444 # underscore, and open bracket
445 r".* \**(\w+) *\( *\w|"
Yuto Takano55c6c872021-08-09 15:35:19 +0100446 # Match "(*something)(".
Yuto Takanod93fa372021-08-06 23:05:55 +0100447 r".*\( *\* *(\w+) *\) *\(|"
448 # Match names of named data structures.
449 r"(?:typedef +)?(?:struct|union|enum) +(\w+)(?: *{)?$|"
450 # Match names of typedef instances, after closing bracket.
Yuto Takanod70d4462021-08-09 12:45:51 +0100451 r"}? *(\w+)[;[].*"
452 )
453 exclusion_lines = re.compile(
454 r"^("
Yuto Takano90bc0262021-08-16 11:34:10 +0100455 r"extern +\"C\"|"
456 r"(typedef +)?(struct|union|enum)( *{)?$|"
457 r"} *;?$|"
458 r"$|"
459 r"//|"
460 r"#"
Yuto Takanod70d4462021-08-09 12:45:51 +0100461 r")"
462 )
Yuto Takanod93fa372021-08-06 23:05:55 +0100463
Yuto Takano50953432021-08-09 14:54:36 +0100464 files = self.get_files(include, exclude)
465 self.log.debug("Looking for identifiers in {} files".format(len(files)))
466
467 identifiers = []
468 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100469 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano39639672021-08-05 19:47:48 +0100470 in_block_comment = False
Yuto Takano55c6c872021-08-09 15:35:19 +0100471 # The previous line variable is used for concatenating lines
Yuto Takanod70d4462021-08-09 12:45:51 +0100472 # when identifiers are formatted and spread across multiple.
Yuto Takanod93fa372021-08-06 23:05:55 +0100473 previous_line = ""
Darryl Greend5802922018-05-08 15:30:59 +0100474
Yuto Takano8f457cf2021-08-06 17:54:58 +0100475 for line_no, line in enumerate(header):
Yuto Takano81528c02021-08-06 16:22:06 +0100476 # Skip parsing this line if a block comment ends on it,
477 # but don't skip if it has just started -- there is a chance
478 # it ends on the same line.
Yuto Takano39639672021-08-05 19:47:48 +0100479 if re.search(r"/\*", line):
Yuto Takano81528c02021-08-06 16:22:06 +0100480 in_block_comment = not in_block_comment
481 if re.search(r"\*/", line):
482 in_block_comment = not in_block_comment
Yuto Takano39639672021-08-05 19:47:48 +0100483 continue
484
Yuto Takano81528c02021-08-06 16:22:06 +0100485 if in_block_comment:
Yuto Takanod93fa372021-08-06 23:05:55 +0100486 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100487 continue
488
Yuto Takano90bc0262021-08-16 11:34:10 +0100489 if exclusion_lines.search(line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100490 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100491 continue
492
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100493 # If the line contains only space-separated alphanumeric
494 # characters (or underscore, asterisk, or, open bracket),
495 # and nothing else, high chance it's a declaration that
496 # continues on the next line
Yuto Takano90bc0262021-08-16 11:34:10 +0100497 if re.search(r"^([\w\*\(]+\s+)+$", line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100498 previous_line += line
Yuto Takano81528c02021-08-06 16:22:06 +0100499 continue
500
501 # If previous line seemed to start an unfinished declaration
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100502 # (as above), concat and treat them as one.
503 if previous_line:
Yuto Takano90bc0262021-08-16 11:34:10 +0100504 line = previous_line.strip() + " " + line.strip() + "\n"
Yuto Takanod93fa372021-08-06 23:05:55 +0100505 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100506
Yuto Takano8246eb82021-08-16 10:37:24 +0100507 # Skip parsing if line has a space in front = heuristic to
Yuto Takano81528c02021-08-06 16:22:06 +0100508 # skip function argument lines (highly subject to formatting
509 # changes)
510 if line[0] == " ":
Yuto Takano39639672021-08-05 19:47:48 +0100511 continue
Yuto Takano6f38ab32021-08-05 21:07:14 +0100512
Yuto Takanod93fa372021-08-06 23:05:55 +0100513 identifier = identifier_regex.search(line)
Yuto Takano39639672021-08-05 19:47:48 +0100514
Yuto Takanod70d4462021-08-09 12:45:51 +0100515 if not identifier:
516 continue
517
518 # Find the group that matched, and append it
519 for group in identifier.groups():
520 if not group:
521 continue
522
523 identifiers.append(Match(
524 header_file,
525 line,
526 (line_no, identifier.start(), identifier.end()),
527 group))
Yuto Takano39639672021-08-05 19:47:48 +0100528
529 return identifiers
530
531 def parse_symbols(self):
532 """
533 Compile the Mbed TLS libraries, and parse the TLS, Crypto, and x509
534 object files using nm to retrieve the list of referenced symbols.
Yuto Takano81528c02021-08-06 16:22:06 +0100535 Exceptions thrown here are rethrown because they would be critical
536 errors that void several tests, and thus needs to halt the program. This
537 is explicitly done for clarity.
Yuto Takano39639672021-08-05 19:47:48 +0100538
Yuto Takano81528c02021-08-06 16:22:06 +0100539 Returns a List of unique symbols defined and used in the libraries.
540 """
541 self.log.info("Compiling...")
Yuto Takano39639672021-08-05 19:47:48 +0100542 symbols = []
543
544 # Back up the config and atomically compile with the full configratuion.
Yuto Takanod70d4462021-08-09 12:45:51 +0100545 shutil.copy(
546 "include/mbedtls/mbedtls_config.h",
547 "include/mbedtls/mbedtls_config.h.bak"
548 )
Darryl Greend5802922018-05-08 15:30:59 +0100549 try:
Yuto Takano81528c02021-08-06 16:22:06 +0100550 # Use check=True in all subprocess calls so that failures are raised
551 # as exceptions and logged.
Yuto Takano39639672021-08-05 19:47:48 +0100552 subprocess.run(
Yuto Takano81528c02021-08-06 16:22:06 +0100553 ["python3", "scripts/config.py", "full"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100554 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100555 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100556 )
557 my_environment = os.environ.copy()
558 my_environment["CFLAGS"] = "-fno-asynchronous-unwind-tables"
Yuto Takano39639672021-08-05 19:47:48 +0100559 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100560 ["make", "clean", "lib"],
561 env=my_environment,
Yuto Takanobcc3d992021-08-06 23:14:58 +0100562 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100563 stdout=subprocess.PIPE,
Darryl Greend5802922018-05-08 15:30:59 +0100564 stderr=subprocess.STDOUT,
Yuto Takano39639672021-08-05 19:47:48 +0100565 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100566 )
Yuto Takano39639672021-08-05 19:47:48 +0100567
568 # Perform object file analysis using nm
Yuto Takanod70d4462021-08-09 12:45:51 +0100569 symbols = self.parse_symbols_from_nm([
570 "library/libmbedcrypto.a",
571 "library/libmbedtls.a",
572 "library/libmbedx509.a"
573 ])
Yuto Takano39639672021-08-05 19:47:48 +0100574
575 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100576 ["make", "clean"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100577 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100578 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100579 )
580 except subprocess.CalledProcessError as error:
Yuto Takano25eeb7b2021-08-06 21:27:59 +0100581 self.log.debug(error.output)
Yuto Takano81528c02021-08-06 16:22:06 +0100582 raise error
Yuto Takano39639672021-08-05 19:47:48 +0100583 finally:
Yuto Takano6fececf2021-08-07 17:28:23 +0100584 # Put back the original config regardless of there being errors.
585 # Works also for keyboard interrupts.
Yuto Takanod70d4462021-08-09 12:45:51 +0100586 shutil.move(
587 "include/mbedtls/mbedtls_config.h.bak",
588 "include/mbedtls/mbedtls_config.h"
589 )
Yuto Takano39639672021-08-05 19:47:48 +0100590
591 return symbols
592
593 def parse_symbols_from_nm(self, object_files):
594 """
595 Run nm to retrieve the list of referenced symbols in each object file.
596 Does not return the position data since it is of no use.
597
Yuto Takano81528c02021-08-06 16:22:06 +0100598 Args:
Yuto Takano55c6c872021-08-09 15:35:19 +0100599 * object_files: a List of compiled object filepaths to search through.
Yuto Takano81528c02021-08-06 16:22:06 +0100600
601 Returns a List of unique symbols defined and used in any of the object
602 files.
Yuto Takano39639672021-08-05 19:47:48 +0100603 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100604 nm_undefined_regex = re.compile(r"^\S+: +U |^$|^\S+:$")
605 nm_valid_regex = re.compile(r"^\S+( [0-9A-Fa-f]+)* . _*(?P<symbol>\w+)")
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100606 exclusions = ("FStar", "Hacl")
Yuto Takano39639672021-08-05 19:47:48 +0100607
608 symbols = []
609
Yuto Takano81528c02021-08-06 16:22:06 +0100610 # Gather all outputs of nm
Yuto Takano39639672021-08-05 19:47:48 +0100611 nm_output = ""
612 for lib in object_files:
613 nm_output += subprocess.run(
614 ["nm", "-og", lib],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100615 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100616 stdout=subprocess.PIPE,
617 stderr=subprocess.STDOUT,
618 check=True
619 ).stdout
Yuto Takano81528c02021-08-06 16:22:06 +0100620
Yuto Takano39639672021-08-05 19:47:48 +0100621 for line in nm_output.splitlines():
Yuto Takano90bc0262021-08-16 11:34:10 +0100622 if not nm_undefined_regex.search(line):
623 symbol = nm_valid_regex.search(line)
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100624 if (symbol and not symbol.group("symbol").startswith(exclusions)):
Yuto Takanoe77f6992021-08-05 20:22:59 +0100625 symbols.append(symbol.group("symbol"))
Yuto Takano39639672021-08-05 19:47:48 +0100626 else:
627 self.log.error(line)
Yuto Takano81528c02021-08-06 16:22:06 +0100628
Yuto Takano39639672021-08-05 19:47:48 +0100629 return symbols
630
Yuto Takano55c6c872021-08-09 15:35:19 +0100631class NameChecker():
632 """
633 Representation of the core name checking operation performed by this script.
634 """
635 def __init__(self, parse_result, log):
636 self.parse_result = parse_result
637 self.log = log
638
Yuto Takano55614b52021-08-07 01:00:18 +0100639 def perform_checks(self, quiet=False):
Yuto Takano39639672021-08-05 19:47:48 +0100640 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100641 A comprehensive checker that performs each check in order, and outputs
642 a final verdict.
Yuto Takano81528c02021-08-06 16:22:06 +0100643
644 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100645 * quiet: whether to hide detailed problem explanation.
Yuto Takano39639672021-08-05 19:47:48 +0100646 """
Yuto Takano81528c02021-08-06 16:22:06 +0100647 self.log.info("=============")
Yuto Takano5473be22021-08-17 10:14:01 +0100648 Problem.quiet = quiet
Yuto Takano39639672021-08-05 19:47:48 +0100649 problems = 0
Yuto Takano5473be22021-08-17 10:14:01 +0100650 problems += self.check_symbols_declared_in_header()
Yuto Takano39639672021-08-05 19:47:48 +0100651
Yuto Takanod70d4462021-08-09 12:45:51 +0100652 pattern_checks = [
653 ("macros", MACRO_PATTERN),
654 ("enum_consts", CONSTANTS_PATTERN),
655 ("identifiers", IDENTIFIER_PATTERN)
656 ]
Yuto Takano39639672021-08-05 19:47:48 +0100657 for group, check_pattern in pattern_checks:
Yuto Takano5473be22021-08-17 10:14:01 +0100658 problems += self.check_match_pattern(group, check_pattern)
Yuto Takano39639672021-08-05 19:47:48 +0100659
Yuto Takano5473be22021-08-17 10:14:01 +0100660 problems += self.check_for_typos()
Yuto Takano39639672021-08-05 19:47:48 +0100661
662 self.log.info("=============")
663 if problems > 0:
664 self.log.info("FAIL: {0} problem(s) to fix".format(str(problems)))
Yuto Takano55614b52021-08-07 01:00:18 +0100665 if quiet:
666 self.log.info("Remove --quiet to see explanations.")
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100667 else:
668 self.log.info("Use --quiet for minimal output.")
Yuto Takano55c6c872021-08-09 15:35:19 +0100669 return 1
Yuto Takano39639672021-08-05 19:47:48 +0100670 else:
671 self.log.info("PASS")
Yuto Takano55c6c872021-08-09 15:35:19 +0100672 return 0
Darryl Greend5802922018-05-08 15:30:59 +0100673
Yuto Takano5473be22021-08-17 10:14:01 +0100674 def check_symbols_declared_in_header(self):
Yuto Takano39639672021-08-05 19:47:48 +0100675 """
676 Perform a check that all detected symbols in the library object files
677 are properly declared in headers.
Yuto Takano977e07f2021-08-09 11:56:15 +0100678 Assumes parse_names_in_source() was called before this.
Darryl Greend5802922018-05-08 15:30:59 +0100679
Yuto Takano81528c02021-08-06 16:22:06 +0100680 Returns the number of problems that need fixing.
Yuto Takano39639672021-08-05 19:47:48 +0100681 """
682 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100683
Yuto Takano39639672021-08-05 19:47:48 +0100684 for symbol in self.parse_result["symbols"]:
685 found_symbol_declared = False
686 for identifier_match in self.parse_result["identifiers"]:
687 if symbol == identifier_match.name:
688 found_symbol_declared = True
689 break
Yuto Takano81528c02021-08-06 16:22:06 +0100690
Yuto Takano39639672021-08-05 19:47:48 +0100691 if not found_symbol_declared:
Yuto Takanod70d4462021-08-09 12:45:51 +0100692 problems.append(SymbolNotInHeader(symbol))
Yuto Takano39639672021-08-05 19:47:48 +0100693
Yuto Takano5473be22021-08-17 10:14:01 +0100694 self.output_check_result("All symbols in header", problems)
Yuto Takano39639672021-08-05 19:47:48 +0100695 return len(problems)
696
Yuto Takano5473be22021-08-17 10:14:01 +0100697 def check_match_pattern(self, group_to_check, check_pattern):
Yuto Takano81528c02021-08-06 16:22:06 +0100698 """
699 Perform a check that all items of a group conform to a regex pattern.
Yuto Takano977e07f2021-08-09 11:56:15 +0100700 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100701
702 Args:
Yuto Takano81528c02021-08-06 16:22:06 +0100703 * group_to_check: string key to index into self.parse_result.
704 * check_pattern: the regex to check against.
705
706 Returns the number of problems that need fixing.
707 """
Yuto Takano39639672021-08-05 19:47:48 +0100708 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100709
Yuto Takano39639672021-08-05 19:47:48 +0100710 for item_match in self.parse_result[group_to_check]:
Yuto Takano90bc0262021-08-16 11:34:10 +0100711 if not re.search(check_pattern, item_match.name):
Yuto Takano39639672021-08-05 19:47:48 +0100712 problems.append(PatternMismatch(check_pattern, item_match))
Yuto Takano90bc0262021-08-16 11:34:10 +0100713 # Double underscore should not be used for names
714 if re.search(r".*__.*", item_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100715 problems.append(PatternMismatch("double underscore", item_match))
Yuto Takano81528c02021-08-06 16:22:06 +0100716
717 self.output_check_result(
718 "Naming patterns of {}".format(group_to_check),
Yuto Takano55614b52021-08-07 01:00:18 +0100719 problems)
Yuto Takano39639672021-08-05 19:47:48 +0100720 return len(problems)
Darryl Greend5802922018-05-08 15:30:59 +0100721
Yuto Takano5473be22021-08-17 10:14:01 +0100722 def check_for_typos(self):
Yuto Takano81528c02021-08-06 16:22:06 +0100723 """
724 Perform a check that all words in the soure code beginning with MBED are
725 either defined as macros, or as enum constants.
Yuto Takano977e07f2021-08-09 11:56:15 +0100726 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100727
Yuto Takano81528c02021-08-06 16:22:06 +0100728 Returns the number of problems that need fixing.
729 """
Yuto Takano39639672021-08-05 19:47:48 +0100730 problems = []
Yuto Takano39639672021-08-05 19:47:48 +0100731
Yuto Takanod70d4462021-08-09 12:45:51 +0100732 # Set comprehension, equivalent to a list comprehension wrapped by set()
Yuto Takanod93fa372021-08-06 23:05:55 +0100733 all_caps_names = {
734 match.name
735 for match
736 in self.parse_result["macros"] + self.parse_result["enum_consts"]}
737 typo_exclusion = re.compile(r"XXX|__|_$|^MBEDTLS_.*CONFIG_FILE$")
Yuto Takano39639672021-08-05 19:47:48 +0100738
Yuto Takanod93fa372021-08-06 23:05:55 +0100739 for name_match in self.parse_result["mbed_words"]:
Yuto Takano81528c02021-08-06 16:22:06 +0100740 found = name_match.name in all_caps_names
741
742 # Since MBEDTLS_PSA_ACCEL_XXX defines are defined by the
743 # PSA driver, they will not exist as macros. However, they
744 # should still be checked for typos using the equivalent
745 # BUILTINs that exist.
746 if "MBEDTLS_PSA_ACCEL_" in name_match.name:
747 found = name_match.name.replace(
748 "MBEDTLS_PSA_ACCEL_",
749 "MBEDTLS_PSA_BUILTIN_") in all_caps_names
750
Yuto Takanod93fa372021-08-06 23:05:55 +0100751 if not found and not typo_exclusion.search(name_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100752 problems.append(Typo(name_match))
Yuto Takano39639672021-08-05 19:47:48 +0100753
Yuto Takano5473be22021-08-17 10:14:01 +0100754 self.output_check_result("Likely typos", problems)
Yuto Takano81528c02021-08-06 16:22:06 +0100755 return len(problems)
756
Yuto Takano5473be22021-08-17 10:14:01 +0100757 def output_check_result(self, name, problems):
Yuto Takano81528c02021-08-06 16:22:06 +0100758 """
759 Write out the PASS/FAIL status of a performed check depending on whether
760 there were problems.
Yuto Takanod70d4462021-08-09 12:45:51 +0100761
762 Args:
Yuto Takanod70d4462021-08-09 12:45:51 +0100763 * name: the name of the test
764 * problems: a List of encountered Problems
Yuto Takano81528c02021-08-06 16:22:06 +0100765 """
Yuto Takano39639672021-08-05 19:47:48 +0100766 if problems:
Yuto Takano55614b52021-08-07 01:00:18 +0100767 self.log.info("{}: FAIL\n".format(name))
768 for problem in problems:
769 self.log.warning(str(problem))
Darryl Greend5802922018-05-08 15:30:59 +0100770 else:
Yuto Takano81528c02021-08-06 16:22:06 +0100771 self.log.info("{}: PASS".format(name))
Darryl Greend5802922018-05-08 15:30:59 +0100772
Yuto Takano39639672021-08-05 19:47:48 +0100773def main():
774 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100775 Perform argument parsing, and create an instance of CodeParser and
776 NameChecker to begin the core operation.
Yuto Takano39639672021-08-05 19:47:48 +0100777 """
Yuto Takanof005c332021-08-09 13:56:36 +0100778 parser = argparse.ArgumentParser(
Yuto Takano39639672021-08-05 19:47:48 +0100779 formatter_class=argparse.RawDescriptionHelpFormatter,
780 description=(
781 "This script confirms that the naming of all symbols and identifiers "
782 "in Mbed TLS are consistent with the house style and are also "
783 "self-consistent.\n\n"
Yuto Takanof005c332021-08-09 13:56:36 +0100784 "Expected to be run from the MbedTLS root directory.")
785 )
786 parser.add_argument(
787 "-v", "--verbose",
788 action="store_true",
789 help="show parse results"
790 )
791 parser.add_argument(
792 "-q", "--quiet",
793 action="store_true",
794 help="hide unnecessary text, explanations, and highlighs"
795 )
Darryl Greend5802922018-05-08 15:30:59 +0100796
Yuto Takanof005c332021-08-09 13:56:36 +0100797 args = parser.parse_args()
Darryl Greend5802922018-05-08 15:30:59 +0100798
Yuto Takano55c6c872021-08-09 15:35:19 +0100799 # Configure the global logger, which is then passed to the classes below
800 log = logging.getLogger()
801 log.setLevel(logging.DEBUG if args.verbose else logging.INFO)
802 log.addHandler(logging.StreamHandler())
803
Darryl Greend5802922018-05-08 15:30:59 +0100804 try:
Yuto Takano55c6c872021-08-09 15:35:19 +0100805 code_parser = CodeParser(log)
806 parse_result = code_parser.comprehensive_parse()
Yuto Takanod93fa372021-08-06 23:05:55 +0100807 except Exception: # pylint: disable=broad-except
Darryl Greend5802922018-05-08 15:30:59 +0100808 traceback.print_exc()
809 sys.exit(2)
810
Yuto Takano55c6c872021-08-09 15:35:19 +0100811 name_checker = NameChecker(parse_result, log)
812 return_code = name_checker.perform_checks(quiet=args.quiet)
813
814 sys.exit(return_code)
815
Darryl Greend5802922018-05-08 15:30:59 +0100816if __name__ == "__main__":
Yuto Takano39639672021-08-05 19:47:48 +0100817 main()