blob: 957701433aff1a01c061a75979970071130b4f6a [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
42error error. 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 Takano381fda82021-08-06 23:37:20 +010080 ln_str = str(self.pos[0])
81 gutter_len = max(4, len(ln_str))
82 gutter = (gutter_len - len(ln_str)) * " " + ln_str
83 underline = self.pos[1] * " " + (self.pos[2] - self.pos[1]) * "^"
84
Yuto Takanoa4e75122021-08-06 17:23:28 +010085 return (
Yuto Takano381fda82021-08-06 23:37:20 +010086 " {0} |\n".format(gutter_len * " ") +
87 " {0} | {1}".format(gutter, self.line) +
Yuto Takano55614b52021-08-07 01:00:18 +010088 " {0} | {1}\n".format(gutter_len * " ", underline)
Yuto Takanoa4e75122021-08-06 17:23:28 +010089 )
Yuto Takanod93fa372021-08-06 23:05:55 +010090
91class Problem(): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010092 """
93 A parent class representing a form of static analysis error.
Yuto Takano81528c02021-08-06 16:22:06 +010094 """
Yuto Takano39639672021-08-05 19:47:48 +010095 def __init__(self):
Yuto Takanod70d4462021-08-09 12:45:51 +010096 self.quiet = False
Yuto Takano39639672021-08-05 19:47:48 +010097 self.textwrapper = textwrap.TextWrapper()
Yuto Takano81528c02021-08-06 16:22:06 +010098 self.textwrapper.width = 80
Yuto Takanoa4e75122021-08-06 17:23:28 +010099 self.textwrapper.initial_indent = " > "
Yuto Takano81528c02021-08-06 16:22:06 +0100100 self.textwrapper.subsequent_indent = " "
Yuto Takano39639672021-08-05 19:47:48 +0100101
Yuto Takanod93fa372021-08-06 23:05:55 +0100102class SymbolNotInHeader(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100103 """
104 A problem that occurs when an exported/available symbol in the object file
105 is not explicitly declared in header files. Created with
106 NameCheck.check_symbols_declared_in_header()
107
108 Fields:
109 * symbol_name: the name of the symbol.
110 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100111 def __init__(self, symbol_name):
Yuto Takano39639672021-08-05 19:47:48 +0100112 self.symbol_name = symbol_name
113 Problem.__init__(self)
114
115 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100116 if self.quiet:
117 return "{0}".format(self.symbol_name)
118
Yuto Takano39639672021-08-05 19:47:48 +0100119 return self.textwrapper.fill(
120 "'{0}' was found as an available symbol in the output of nm, "
121 "however it was not declared in any header files."
122 .format(self.symbol_name))
123
Yuto Takanod93fa372021-08-06 23:05:55 +0100124class PatternMismatch(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100125 """
126 A problem that occurs when something doesn't match the expected pattern.
127 Created with NameCheck.check_match_pattern()
128
129 Fields:
130 * pattern: the expected regex pattern
131 * match: the Match object in question
132 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100133 def __init__(self, pattern, match):
Yuto Takano39639672021-08-05 19:47:48 +0100134 self.pattern = pattern
135 self.match = match
136 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100137
Yuto Takano39639672021-08-05 19:47:48 +0100138 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100139 if self.quiet:
Yuto Takanod70d4462021-08-09 12:45:51 +0100140 return (
141 "{0}:{1}:{3}"
142 .format(self.match.filename, self.match.pos[0], self.match.name)
143 )
Yuto Takano55614b52021-08-07 01:00:18 +0100144
Yuto Takano39639672021-08-05 19:47:48 +0100145 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100146 "{0}:{1}: '{2}' does not match the required pattern '{3}'."
147 .format(
148 self.match.filename,
Yuto Takanod93fa372021-08-06 23:05:55 +0100149 self.match.pos[0],
Yuto Takanoa4e75122021-08-06 17:23:28 +0100150 self.match.name,
Yuto Takanod70d4462021-08-09 12:45:51 +0100151 self.pattern
152 )
153 ) + "\n" + str(self.match)
Yuto Takano39639672021-08-05 19:47:48 +0100154
Yuto Takanod93fa372021-08-06 23:05:55 +0100155class Typo(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100156 """
157 A problem that occurs when a word using MBED doesn't appear to be defined as
158 constants nor enum values. Created with NameCheck.check_for_typos()
159
160 Fields:
161 * match: the Match object of the MBED name in question.
162 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100163 def __init__(self, match):
Yuto Takano39639672021-08-05 19:47:48 +0100164 self.match = match
165 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100166
Yuto Takano39639672021-08-05 19:47:48 +0100167 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100168 if self.quiet:
Yuto Takanod70d4462021-08-09 12:45:51 +0100169 return (
170 "{0}:{1}:{2}"
171 .format(self.match.filename, self.match.pos[0], self.match.name)
172 )
Yuto Takano55614b52021-08-07 01:00:18 +0100173
Yuto Takano39639672021-08-05 19:47:48 +0100174 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100175 "{0}:{1}: '{2}' looks like a typo. It was not found in any "
176 "macros or any enums. If this is not a typo, put "
177 "//no-check-names after it."
Yuto Takanod70d4462021-08-09 12:45:51 +0100178 .format(self.match.filename, self.match.pos[0], self.match.name)
179 ) + "\n" + str(self.match)
Darryl Greend5802922018-05-08 15:30:59 +0100180
Yuto Takano55c6c872021-08-09 15:35:19 +0100181class CodeParser():
Yuto Takano81528c02021-08-06 16:22:06 +0100182 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100183 Class for retrieving files and parsing the code. This can be used
184 independently of the checks that NameChecker performs, for example for
185 list_internal_identifiers.py.
Yuto Takano81528c02021-08-06 16:22:06 +0100186 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100187 def __init__(self, log):
188 self.log = log
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100189 self.check_repo_path()
Yuto Takano977e07f2021-08-09 11:56:15 +0100190
Yuto Takano8e9a2192021-08-09 14:48:53 +0100191 # Memo for storing "glob expression": set(filepaths)
192 self.files = {}
193
Yuto Takano977e07f2021-08-09 11:56:15 +0100194 # Globally excluded filenames
Yuto Takano8e9a2192021-08-09 14:48:53 +0100195 self.excluded_files = ["**/bn_mul", "**/compat-2.x.h"]
Yuto Takano977e07f2021-08-09 11:56:15 +0100196
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100197 @staticmethod
198 def check_repo_path():
199 """
200 Check that the current working directory is the project root, and throw
201 an exception if not.
202 """
203 if not all(os.path.isdir(d) for d in ["include", "library", "tests"]):
204 raise Exception("This script must be run from Mbed TLS root")
205
Yuto Takano55c6c872021-08-09 15:35:19 +0100206 def comprehensive_parse(self):
Yuto Takano39639672021-08-05 19:47:48 +0100207 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100208 Comprehensive ("default") function to call each parsing function and
209 retrieve various elements of the code, together with the source location.
Darryl Greend5802922018-05-08 15:30:59 +0100210
Yuto Takano55c6c872021-08-09 15:35:19 +0100211 Returns a dict of parsed item key to the corresponding List of Matches.
Yuto Takano81528c02021-08-06 16:22:06 +0100212 """
213 self.log.info("Parsing source code...")
Yuto Takanod24e0372021-08-06 16:42:33 +0100214 self.log.debug(
Yuto Takano50953432021-08-09 14:54:36 +0100215 "The following files are excluded from the search: {}"
Yuto Takanod24e0372021-08-06 16:42:33 +0100216 .format(str(self.excluded_files))
217 )
Yuto Takano81528c02021-08-06 16:22:06 +0100218
Yuto Takano8e9a2192021-08-09 14:48:53 +0100219 all_macros = self.parse_macros([
220 "include/mbedtls/*.h",
221 "include/psa/*.h",
222 "library/*.h",
223 "tests/include/test/drivers/*.h",
Yuto Takanod70d4462021-08-09 12:45:51 +0100224 "3rdparty/everest/include/everest/everest.h",
225 "3rdparty/everest/include/everest/x25519.h"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100226 ])
227 enum_consts = self.parse_enum_consts([
228 "include/mbedtls/*.h",
229 "library/*.h",
230 "3rdparty/everest/include/everest/everest.h",
231 "3rdparty/everest/include/everest/x25519.h"
232 ])
233 identifiers = self.parse_identifiers([
234 "include/mbedtls/*.h",
235 "include/psa/*.h",
236 "library/*.h",
237 "3rdparty/everest/include/everest/everest.h",
238 "3rdparty/everest/include/everest/x25519.h"
239 ])
240 mbed_words = self.parse_mbed_words([
241 "include/mbedtls/*.h",
242 "include/psa/*.h",
243 "library/*.h",
244 "3rdparty/everest/include/everest/everest.h",
245 "3rdparty/everest/include/everest/x25519.h",
246 "library/*.c",
Yuto Takano81528c02021-08-06 16:22:06 +0100247 "3rdparty/everest/library/everest.c",
Yuto Takanod70d4462021-08-09 12:45:51 +0100248 "3rdparty/everest/library/x25519.c"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100249 ])
Yuto Takano81528c02021-08-06 16:22:06 +0100250 symbols = self.parse_symbols()
251
252 # Remove identifier macros like mbedtls_printf or mbedtls_calloc
253 identifiers_justname = [x.name for x in identifiers]
254 actual_macros = []
255 for macro in all_macros:
256 if macro.name not in identifiers_justname:
257 actual_macros.append(macro)
258
259 self.log.debug("Found:")
Yuto Takanod70d4462021-08-09 12:45:51 +0100260 self.log.debug(" {} Total Macros".format(len(all_macros)))
Yuto Takano81528c02021-08-06 16:22:06 +0100261 self.log.debug(" {} Non-identifier Macros".format(len(actual_macros)))
262 self.log.debug(" {} Enum Constants".format(len(enum_consts)))
263 self.log.debug(" {} Identifiers".format(len(identifiers)))
264 self.log.debug(" {} Exported Symbols".format(len(symbols)))
Yuto Takano55c6c872021-08-09 15:35:19 +0100265 return {
Yuto Takano81528c02021-08-06 16:22:06 +0100266 "macros": actual_macros,
267 "enum_consts": enum_consts,
268 "identifiers": identifiers,
269 "symbols": symbols,
Yuto Takanod93fa372021-08-06 23:05:55 +0100270 "mbed_words": mbed_words
Yuto Takano81528c02021-08-06 16:22:06 +0100271 }
272
Yuto Takano55c6c872021-08-09 15:35:19 +0100273 def get_files(self, include_wildcards, exclude_wildcards):
274 """
275 Get all files that match any of the UNIX-style wildcards. While the
276 check_names script is designed only for use on UNIX/macOS (due to nm),
277 this function alone would work fine on Windows even with forward slashes
278 in the wildcard.
279
280 Args:
281 * include_wildcards: a List of shell-style wildcards to match filepaths.
282 * exclude_wildcards: a List of shell-style wildcards to exclude.
283
284 Returns a List of relative filepaths.
285 """
286 accumulator = set()
287
288 # exclude_wildcards may be None. Also, consider the global exclusions.
289 exclude_wildcards = (exclude_wildcards or []) + self.excluded_files
290
291 # Perform set union on the glob results. Memoise individual sets.
292 for include_wildcard in include_wildcards:
293 if include_wildcard not in self.files:
294 self.files[include_wildcard] = set(glob.glob(
295 include_wildcard,
296 recursive=True
297 ))
298
299 accumulator = accumulator.union(self.files[include_wildcard])
300
301 # Perform set difference to exclude. Also use the same memo since their
302 # behaviour is pretty much identical and it can benefit from the cache.
303 for exclude_wildcard in exclude_wildcards:
304 if exclude_wildcard not in self.files:
305 self.files[exclude_wildcard] = set(glob.glob(
306 exclude_wildcard,
307 recursive=True
308 ))
309
310 accumulator = accumulator.difference(self.files[exclude_wildcard])
311
312 return list(accumulator)
313
Yuto Takano8e9a2192021-08-09 14:48:53 +0100314 def parse_macros(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100315 """
316 Parse all macros defined by #define preprocessor directives.
317
318 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100319 * include: A List of glob expressions to look for files through.
320 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100321
322 Returns a List of Match objects for the found macros.
Yuto Takano39639672021-08-05 19:47:48 +0100323 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100324 macro_regex = re.compile(r"# *define +(?P<macro>\w+)")
325 exclusions = (
Yuto Takano39639672021-08-05 19:47:48 +0100326 "asm", "inline", "EMIT", "_CRT_SECURE_NO_DEPRECATE", "MULADDC_"
327 )
328
Yuto Takano50953432021-08-09 14:54:36 +0100329 files = self.get_files(include, exclude)
330 self.log.debug("Looking for macros in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100331
Yuto Takano50953432021-08-09 14:54:36 +0100332 macros = []
333 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100334 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100335 for line_no, line in enumerate(header):
Yuto Takanod93fa372021-08-06 23:05:55 +0100336 for macro in macro_regex.finditer(line):
Yuto Takanod70d4462021-08-09 12:45:51 +0100337 if macro.group("macro").startswith(exclusions):
338 continue
339
340 macros.append(Match(
341 header_file,
342 line,
343 (line_no, macro.start(), macro.end()),
344 macro.group("macro")))
Darryl Greend5802922018-05-08 15:30:59 +0100345
Yuto Takano39639672021-08-05 19:47:48 +0100346 return macros
Darryl Greend5802922018-05-08 15:30:59 +0100347
Yuto Takano8e9a2192021-08-09 14:48:53 +0100348 def parse_mbed_words(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100349 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100350 Parse all words in the file that begin with MBED, in and out of macros,
351 comments, anything.
Yuto Takano39639672021-08-05 19:47:48 +0100352
353 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100354 * include: A List of glob expressions to look for files through.
355 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100356
357 Returns a List of Match objects for words beginning with MBED.
Yuto Takano39639672021-08-05 19:47:48 +0100358 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100359 # Typos of TLS are common, hence the broader check below than MBEDTLS.
Yuto Takanod93fa372021-08-06 23:05:55 +0100360 mbed_regex = re.compile(r"\bMBED.+?_[A-Z0-9_]*")
361 exclusions = re.compile(r"// *no-check-names|#error")
362
Yuto Takano50953432021-08-09 14:54:36 +0100363 files = self.get_files(include, exclude)
364 self.log.debug("Looking for MBED words in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100365
Yuto Takano50953432021-08-09 14:54:36 +0100366 mbed_words = []
367 for filename in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100368 with open(filename, "r", encoding="utf-8") as fp:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100369 for line_no, line in enumerate(fp):
Yuto Takanod93fa372021-08-06 23:05:55 +0100370 if exclusions.search(line):
Yuto Takanoc62b4082021-08-05 20:17:07 +0100371 continue
Yuto Takano81528c02021-08-06 16:22:06 +0100372
Yuto Takanod93fa372021-08-06 23:05:55 +0100373 for name in mbed_regex.finditer(line):
374 mbed_words.append(Match(
Yuto Takano39639672021-08-05 19:47:48 +0100375 filename,
376 line,
Yuto Takanod93fa372021-08-06 23:05:55 +0100377 (line_no, name.start(), name.end()),
Yuto Takano39639672021-08-05 19:47:48 +0100378 name.group(0)
379 ))
380
Yuto Takanod93fa372021-08-06 23:05:55 +0100381 return mbed_words
Yuto Takano39639672021-08-05 19:47:48 +0100382
Yuto Takano8e9a2192021-08-09 14:48:53 +0100383 def parse_enum_consts(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100384 """
385 Parse all enum value constants that are declared.
386
387 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100388 * include: A List of glob expressions to look for files through.
389 * exclude: A List of glob expressions for excluding files.
Yuto Takano39639672021-08-05 19:47:48 +0100390
Yuto Takano81528c02021-08-06 16:22:06 +0100391 Returns a List of Match objects for the findings.
Yuto Takano39639672021-08-05 19:47:48 +0100392 """
Yuto Takano50953432021-08-09 14:54:36 +0100393 files = self.get_files(include, exclude)
394 self.log.debug("Looking for enum consts in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100395
Yuto Takano50953432021-08-09 14:54:36 +0100396 enum_consts = []
397 for header_file in files:
Yuto Takano39639672021-08-05 19:47:48 +0100398 # Emulate a finite state machine to parse enum declarations.
Yuto Takano81528c02021-08-06 16:22:06 +0100399 # 0 = not in enum
400 # 1 = inside enum
401 # 2 = almost inside enum
Darryl Greend5802922018-05-08 15:30:59 +0100402 state = 0
Yuto Takanoa083d152021-08-07 00:25:59 +0100403 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100404 for line_no, line in enumerate(header):
Yuto Takano13ecd992021-08-06 16:56:52 +0100405 # Match typedefs and brackets only when they are at the
406 # beginning of the line -- if they are indented, they might
407 # be sub-structures within structs, etc.
Yuto Takanod93fa372021-08-06 23:05:55 +0100408 if state == 0 and re.match(r"^(typedef +)?enum +{", line):
Darryl Greend5802922018-05-08 15:30:59 +0100409 state = 1
Yuto Takanod93fa372021-08-06 23:05:55 +0100410 elif state == 0 and re.match(r"^(typedef +)?enum", line):
Darryl Greend5802922018-05-08 15:30:59 +0100411 state = 2
Yuto Takanod93fa372021-08-06 23:05:55 +0100412 elif state == 2 and re.match(r"^{", line):
Darryl Greend5802922018-05-08 15:30:59 +0100413 state = 1
Yuto Takanod93fa372021-08-06 23:05:55 +0100414 elif state == 1 and re.match(r"^}", line):
Darryl Greend5802922018-05-08 15:30:59 +0100415 state = 0
Yuto Takanod93fa372021-08-06 23:05:55 +0100416 elif state == 1 and not re.match(r" *#", line):
Yuto Takano13ecd992021-08-06 16:56:52 +0100417 enum_const = re.match(r" *(?P<enum_const>\w+)", line)
Yuto Takanod70d4462021-08-09 12:45:51 +0100418 if not enum_const:
419 continue
420
421 enum_consts.append(Match(
422 header_file,
423 line,
424 (line_no, enum_const.start(), enum_const.end()),
425 enum_const.group("enum_const")))
Yuto Takano81528c02021-08-06 16:22:06 +0100426
Yuto Takano39639672021-08-05 19:47:48 +0100427 return enum_consts
Darryl Greend5802922018-05-08 15:30:59 +0100428
Yuto Takano8e9a2192021-08-09 14:48:53 +0100429 def parse_identifiers(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100430 """
431 Parse all lines of a header where a function identifier is declared,
Yuto Takano81528c02021-08-06 16:22:06 +0100432 based on some huersitics. Highly dependent on formatting style.
Yuto Takanod70d4462021-08-09 12:45:51 +0100433 Note: .match() checks at the beginning of the string (implicit ^), while
434 .search() checks throughout.
Darryl Greend5802922018-05-08 15:30:59 +0100435
Yuto Takano39639672021-08-05 19:47:48 +0100436 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100437 * include: A List of glob expressions to look for files through.
438 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100439
440 Returns a List of Match objects with identifiers.
Yuto Takano39639672021-08-05 19:47:48 +0100441 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100442 identifier_regex = re.compile(
443 # Match " something(a" or " *something(a". Functions.
444 # Assumptions:
445 # - function definition from return type to one of its arguments is
Yuto Takano55c6c872021-08-09 15:35:19 +0100446 # all on one line
Yuto Takanod93fa372021-08-06 23:05:55 +0100447 # - function definition line only contains alphanumeric, asterisk,
448 # underscore, and open bracket
449 r".* \**(\w+) *\( *\w|"
Yuto Takano55c6c872021-08-09 15:35:19 +0100450 # Match "(*something)(".
Yuto Takanod93fa372021-08-06 23:05:55 +0100451 r".*\( *\* *(\w+) *\) *\(|"
452 # Match names of named data structures.
453 r"(?:typedef +)?(?:struct|union|enum) +(\w+)(?: *{)?$|"
454 # Match names of typedef instances, after closing bracket.
Yuto Takanod70d4462021-08-09 12:45:51 +0100455 r"}? *(\w+)[;[].*"
456 )
457 exclusion_lines = re.compile(
458 r"^("
459 r"extern +\"C\"|"
460 r"(typedef +)?(struct|union|enum)( *{)?$|"
461 r"} *;?$|"
462 r"$|"
463 r"//|"
464 r"#"
465 r")"
466 )
Yuto Takanod93fa372021-08-06 23:05:55 +0100467
Yuto Takano50953432021-08-09 14:54:36 +0100468 files = self.get_files(include, exclude)
469 self.log.debug("Looking for identifiers in {} files".format(len(files)))
470
471 identifiers = []
472 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100473 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano39639672021-08-05 19:47:48 +0100474 in_block_comment = False
Yuto Takano55c6c872021-08-09 15:35:19 +0100475 # The previous line variable is used for concatenating lines
Yuto Takanod70d4462021-08-09 12:45:51 +0100476 # when identifiers are formatted and spread across multiple.
Yuto Takanod93fa372021-08-06 23:05:55 +0100477 previous_line = ""
Darryl Greend5802922018-05-08 15:30:59 +0100478
Yuto Takano8f457cf2021-08-06 17:54:58 +0100479 for line_no, line in enumerate(header):
Yuto Takano81528c02021-08-06 16:22:06 +0100480 # Skip parsing this line if a block comment ends on it,
481 # but don't skip if it has just started -- there is a chance
482 # it ends on the same line.
Yuto Takano39639672021-08-05 19:47:48 +0100483 if re.search(r"/\*", line):
Yuto Takano81528c02021-08-06 16:22:06 +0100484 in_block_comment = not in_block_comment
485 if re.search(r"\*/", line):
486 in_block_comment = not in_block_comment
Yuto Takano39639672021-08-05 19:47:48 +0100487 continue
488
Yuto Takano81528c02021-08-06 16:22:06 +0100489 if in_block_comment:
Yuto Takanod93fa372021-08-06 23:05:55 +0100490 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100491 continue
492
Yuto Takanod93fa372021-08-06 23:05:55 +0100493 if exclusion_lines.match(line):
494 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100495 continue
496
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100497 # If the line contains only space-separated alphanumeric
498 # characters (or underscore, asterisk, or, open bracket),
499 # and nothing else, high chance it's a declaration that
500 # continues on the next line
501 if re.match(r"^([\w\*\(]+\s+)+$", line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100502 previous_line += line
Yuto Takano81528c02021-08-06 16:22:06 +0100503 continue
504
505 # If previous line seemed to start an unfinished declaration
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100506 # (as above), concat and treat them as one.
507 if previous_line:
508 line = previous_line.strip() + " " + line.strip()
Yuto Takanod93fa372021-08-06 23:05:55 +0100509 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100510
511 # Skip parsing if line has a space in front = hueristic to
512 # skip function argument lines (highly subject to formatting
513 # changes)
514 if line[0] == " ":
Yuto Takano39639672021-08-05 19:47:48 +0100515 continue
Yuto Takano6f38ab32021-08-05 21:07:14 +0100516
Yuto Takanod93fa372021-08-06 23:05:55 +0100517 identifier = identifier_regex.search(line)
Yuto Takano39639672021-08-05 19:47:48 +0100518
Yuto Takanod70d4462021-08-09 12:45:51 +0100519 if not identifier:
520 continue
521
522 # Find the group that matched, and append it
523 for group in identifier.groups():
524 if not group:
525 continue
526
527 identifiers.append(Match(
528 header_file,
529 line,
530 (line_no, identifier.start(), identifier.end()),
531 group))
Yuto Takano39639672021-08-05 19:47:48 +0100532
533 return identifiers
534
535 def parse_symbols(self):
536 """
537 Compile the Mbed TLS libraries, and parse the TLS, Crypto, and x509
538 object files using nm to retrieve the list of referenced symbols.
Yuto Takano81528c02021-08-06 16:22:06 +0100539 Exceptions thrown here are rethrown because they would be critical
540 errors that void several tests, and thus needs to halt the program. This
541 is explicitly done for clarity.
Yuto Takano39639672021-08-05 19:47:48 +0100542
Yuto Takano81528c02021-08-06 16:22:06 +0100543 Returns a List of unique symbols defined and used in the libraries.
544 """
545 self.log.info("Compiling...")
Yuto Takano39639672021-08-05 19:47:48 +0100546 symbols = []
547
548 # Back up the config and atomically compile with the full configratuion.
Yuto Takanod70d4462021-08-09 12:45:51 +0100549 shutil.copy(
550 "include/mbedtls/mbedtls_config.h",
551 "include/mbedtls/mbedtls_config.h.bak"
552 )
Darryl Greend5802922018-05-08 15:30:59 +0100553 try:
Yuto Takano81528c02021-08-06 16:22:06 +0100554 # Use check=True in all subprocess calls so that failures are raised
555 # as exceptions and logged.
Yuto Takano39639672021-08-05 19:47:48 +0100556 subprocess.run(
Yuto Takano81528c02021-08-06 16:22:06 +0100557 ["python3", "scripts/config.py", "full"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100558 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100559 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100560 )
561 my_environment = os.environ.copy()
562 my_environment["CFLAGS"] = "-fno-asynchronous-unwind-tables"
Yuto Takano39639672021-08-05 19:47:48 +0100563 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100564 ["make", "clean", "lib"],
565 env=my_environment,
Yuto Takanobcc3d992021-08-06 23:14:58 +0100566 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100567 stdout=subprocess.PIPE,
Darryl Greend5802922018-05-08 15:30:59 +0100568 stderr=subprocess.STDOUT,
Yuto Takano39639672021-08-05 19:47:48 +0100569 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100570 )
Yuto Takano39639672021-08-05 19:47:48 +0100571
572 # Perform object file analysis using nm
Yuto Takanod70d4462021-08-09 12:45:51 +0100573 symbols = self.parse_symbols_from_nm([
574 "library/libmbedcrypto.a",
575 "library/libmbedtls.a",
576 "library/libmbedx509.a"
577 ])
Yuto Takano39639672021-08-05 19:47:48 +0100578
579 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100580 ["make", "clean"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100581 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100582 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100583 )
584 except subprocess.CalledProcessError as error:
Yuto Takano25eeb7b2021-08-06 21:27:59 +0100585 self.log.debug(error.output)
Yuto Takano81528c02021-08-06 16:22:06 +0100586 raise error
Yuto Takano39639672021-08-05 19:47:48 +0100587 finally:
Yuto Takano6fececf2021-08-07 17:28:23 +0100588 # Put back the original config regardless of there being errors.
589 # Works also for keyboard interrupts.
Yuto Takanod70d4462021-08-09 12:45:51 +0100590 shutil.move(
591 "include/mbedtls/mbedtls_config.h.bak",
592 "include/mbedtls/mbedtls_config.h"
593 )
Yuto Takano39639672021-08-05 19:47:48 +0100594
595 return symbols
596
597 def parse_symbols_from_nm(self, object_files):
598 """
599 Run nm to retrieve the list of referenced symbols in each object file.
600 Does not return the position data since it is of no use.
601
Yuto Takano81528c02021-08-06 16:22:06 +0100602 Args:
Yuto Takano55c6c872021-08-09 15:35:19 +0100603 * object_files: a List of compiled object filepaths to search through.
Yuto Takano81528c02021-08-06 16:22:06 +0100604
605 Returns a List of unique symbols defined and used in any of the object
606 files.
Yuto Takano39639672021-08-05 19:47:48 +0100607 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100608 nm_undefined_regex = re.compile(r"^\S+: +U |^$|^\S+:$")
609 nm_valid_regex = re.compile(r"^\S+( [0-9A-Fa-f]+)* . _*(?P<symbol>\w+)")
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100610 exclusions = ("FStar", "Hacl")
Yuto Takano39639672021-08-05 19:47:48 +0100611
612 symbols = []
613
Yuto Takano81528c02021-08-06 16:22:06 +0100614 # Gather all outputs of nm
Yuto Takano39639672021-08-05 19:47:48 +0100615 nm_output = ""
616 for lib in object_files:
617 nm_output += subprocess.run(
618 ["nm", "-og", lib],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100619 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100620 stdout=subprocess.PIPE,
621 stderr=subprocess.STDOUT,
622 check=True
623 ).stdout
Yuto Takano81528c02021-08-06 16:22:06 +0100624
Yuto Takano39639672021-08-05 19:47:48 +0100625 for line in nm_output.splitlines():
Yuto Takanod93fa372021-08-06 23:05:55 +0100626 if not nm_undefined_regex.match(line):
627 symbol = nm_valid_regex.match(line)
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100628 if (symbol and not symbol.group("symbol").startswith(exclusions)):
Yuto Takanoe77f6992021-08-05 20:22:59 +0100629 symbols.append(symbol.group("symbol"))
Yuto Takano39639672021-08-05 19:47:48 +0100630 else:
631 self.log.error(line)
Yuto Takano81528c02021-08-06 16:22:06 +0100632
Yuto Takano39639672021-08-05 19:47:48 +0100633 return symbols
634
Yuto Takano55c6c872021-08-09 15:35:19 +0100635class NameChecker():
636 """
637 Representation of the core name checking operation performed by this script.
638 """
639 def __init__(self, parse_result, log):
640 self.parse_result = parse_result
641 self.log = log
642
Yuto Takano55614b52021-08-07 01:00:18 +0100643 def perform_checks(self, quiet=False):
Yuto Takano39639672021-08-05 19:47:48 +0100644 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100645 A comprehensive checker that performs each check in order, and outputs
646 a final verdict.
Yuto Takano81528c02021-08-06 16:22:06 +0100647
648 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100649 * quiet: whether to hide detailed problem explanation.
Yuto Takano39639672021-08-05 19:47:48 +0100650 """
Yuto Takano81528c02021-08-06 16:22:06 +0100651 self.log.info("=============")
Yuto Takano39639672021-08-05 19:47:48 +0100652 problems = 0
Yuto Takano55614b52021-08-07 01:00:18 +0100653 problems += self.check_symbols_declared_in_header(quiet)
Yuto Takano39639672021-08-05 19:47:48 +0100654
Yuto Takanod70d4462021-08-09 12:45:51 +0100655 pattern_checks = [
656 ("macros", MACRO_PATTERN),
657 ("enum_consts", CONSTANTS_PATTERN),
658 ("identifiers", IDENTIFIER_PATTERN)
659 ]
Yuto Takano39639672021-08-05 19:47:48 +0100660 for group, check_pattern in pattern_checks:
Yuto Takano55614b52021-08-07 01:00:18 +0100661 problems += self.check_match_pattern(quiet, group, check_pattern)
Yuto Takano39639672021-08-05 19:47:48 +0100662
Yuto Takano55614b52021-08-07 01:00:18 +0100663 problems += self.check_for_typos(quiet)
Yuto Takano39639672021-08-05 19:47:48 +0100664
665 self.log.info("=============")
666 if problems > 0:
667 self.log.info("FAIL: {0} problem(s) to fix".format(str(problems)))
Yuto Takano55614b52021-08-07 01:00:18 +0100668 if quiet:
669 self.log.info("Remove --quiet to see explanations.")
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100670 else:
671 self.log.info("Use --quiet for minimal output.")
Yuto Takano55c6c872021-08-09 15:35:19 +0100672 return 1
Yuto Takano39639672021-08-05 19:47:48 +0100673 else:
674 self.log.info("PASS")
Yuto Takano55c6c872021-08-09 15:35:19 +0100675 return 0
Darryl Greend5802922018-05-08 15:30:59 +0100676
Yuto Takano55614b52021-08-07 01:00:18 +0100677 def check_symbols_declared_in_header(self, quiet):
Yuto Takano39639672021-08-05 19:47:48 +0100678 """
679 Perform a check that all detected symbols in the library object files
680 are properly declared in headers.
Yuto Takano977e07f2021-08-09 11:56:15 +0100681 Assumes parse_names_in_source() was called before this.
Darryl Greend5802922018-05-08 15:30:59 +0100682
Yuto Takano81528c02021-08-06 16:22:06 +0100683 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100684 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100685
686 Returns the number of problems that need fixing.
Yuto Takano39639672021-08-05 19:47:48 +0100687 """
688 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100689
Yuto Takano39639672021-08-05 19:47:48 +0100690 for symbol in self.parse_result["symbols"]:
691 found_symbol_declared = False
692 for identifier_match in self.parse_result["identifiers"]:
693 if symbol == identifier_match.name:
694 found_symbol_declared = True
695 break
Yuto Takano81528c02021-08-06 16:22:06 +0100696
Yuto Takano39639672021-08-05 19:47:48 +0100697 if not found_symbol_declared:
Yuto Takanod70d4462021-08-09 12:45:51 +0100698 problems.append(SymbolNotInHeader(symbol))
Yuto Takano39639672021-08-05 19:47:48 +0100699
Yuto Takanod70d4462021-08-09 12:45:51 +0100700 self.output_check_result(quiet, "All symbols in header", problems)
Yuto Takano39639672021-08-05 19:47:48 +0100701 return len(problems)
702
Yuto Takano55614b52021-08-07 01:00:18 +0100703 def check_match_pattern(self, quiet, group_to_check, check_pattern):
Yuto Takano81528c02021-08-06 16:22:06 +0100704 """
705 Perform a check that all items of a group conform to a regex pattern.
Yuto Takano977e07f2021-08-09 11:56:15 +0100706 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100707
708 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100709 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100710 * group_to_check: string key to index into self.parse_result.
711 * check_pattern: the regex to check against.
712
713 Returns the number of problems that need fixing.
714 """
Yuto Takano39639672021-08-05 19:47:48 +0100715 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100716
Yuto Takano39639672021-08-05 19:47:48 +0100717 for item_match in self.parse_result[group_to_check]:
718 if not re.match(check_pattern, item_match.name):
719 problems.append(PatternMismatch(check_pattern, item_match))
Yuto Takano201f9e82021-08-06 16:36:54 +0100720 # Double underscore is a reserved identifier, never to be used
Yuto Takanoc763cc32021-08-05 20:06:34 +0100721 if re.match(r".*__.*", item_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100722 problems.append(PatternMismatch("double underscore", item_match))
Yuto Takano81528c02021-08-06 16:22:06 +0100723
724 self.output_check_result(
Yuto Takanod70d4462021-08-09 12:45:51 +0100725 quiet,
Yuto Takano81528c02021-08-06 16:22:06 +0100726 "Naming patterns of {}".format(group_to_check),
Yuto Takano55614b52021-08-07 01:00:18 +0100727 problems)
Yuto Takano39639672021-08-05 19:47:48 +0100728 return len(problems)
Darryl Greend5802922018-05-08 15:30:59 +0100729
Yuto Takano55614b52021-08-07 01:00:18 +0100730 def check_for_typos(self, quiet):
Yuto Takano81528c02021-08-06 16:22:06 +0100731 """
732 Perform a check that all words in the soure code beginning with MBED are
733 either defined as macros, or as enum constants.
Yuto Takano977e07f2021-08-09 11:56:15 +0100734 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100735
736 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100737 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100738
739 Returns the number of problems that need fixing.
740 """
Yuto Takano39639672021-08-05 19:47:48 +0100741 problems = []
Yuto Takano39639672021-08-05 19:47:48 +0100742
Yuto Takanod70d4462021-08-09 12:45:51 +0100743 # Set comprehension, equivalent to a list comprehension wrapped by set()
Yuto Takanod93fa372021-08-06 23:05:55 +0100744 all_caps_names = {
745 match.name
746 for match
747 in self.parse_result["macros"] + self.parse_result["enum_consts"]}
748 typo_exclusion = re.compile(r"XXX|__|_$|^MBEDTLS_.*CONFIG_FILE$")
Yuto Takano39639672021-08-05 19:47:48 +0100749
Yuto Takanod93fa372021-08-06 23:05:55 +0100750 for name_match in self.parse_result["mbed_words"]:
Yuto Takano81528c02021-08-06 16:22:06 +0100751 found = name_match.name in all_caps_names
752
753 # Since MBEDTLS_PSA_ACCEL_XXX defines are defined by the
754 # PSA driver, they will not exist as macros. However, they
755 # should still be checked for typos using the equivalent
756 # BUILTINs that exist.
757 if "MBEDTLS_PSA_ACCEL_" in name_match.name:
758 found = name_match.name.replace(
759 "MBEDTLS_PSA_ACCEL_",
760 "MBEDTLS_PSA_BUILTIN_") in all_caps_names
761
Yuto Takanod93fa372021-08-06 23:05:55 +0100762 if not found and not typo_exclusion.search(name_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100763 problems.append(Typo(name_match))
Yuto Takano39639672021-08-05 19:47:48 +0100764
Yuto Takanod70d4462021-08-09 12:45:51 +0100765 self.output_check_result(quiet, "Likely typos", problems)
Yuto Takano81528c02021-08-06 16:22:06 +0100766 return len(problems)
767
Yuto Takanod70d4462021-08-09 12:45:51 +0100768 def output_check_result(self, quiet, name, problems):
Yuto Takano81528c02021-08-06 16:22:06 +0100769 """
770 Write out the PASS/FAIL status of a performed check depending on whether
771 there were problems.
Yuto Takanod70d4462021-08-09 12:45:51 +0100772
773 Args:
774 * quiet: whether to hide detailed problem explanation.
775 * name: the name of the test
776 * problems: a List of encountered Problems
Yuto Takano81528c02021-08-06 16:22:06 +0100777 """
Yuto Takano39639672021-08-05 19:47:48 +0100778 if problems:
Yuto Takano55614b52021-08-07 01:00:18 +0100779 self.log.info("{}: FAIL\n".format(name))
780 for problem in problems:
Yuto Takanod70d4462021-08-09 12:45:51 +0100781 problem.quiet = quiet
Yuto Takano55614b52021-08-07 01:00:18 +0100782 self.log.warning(str(problem))
Darryl Greend5802922018-05-08 15:30:59 +0100783 else:
Yuto Takano81528c02021-08-06 16:22:06 +0100784 self.log.info("{}: PASS".format(name))
Darryl Greend5802922018-05-08 15:30:59 +0100785
Yuto Takano39639672021-08-05 19:47:48 +0100786def main():
787 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100788 Perform argument parsing, and create an instance of CodeParser and
789 NameChecker to begin the core operation.
Yuto Takano39639672021-08-05 19:47:48 +0100790 """
Yuto Takanof005c332021-08-09 13:56:36 +0100791 parser = argparse.ArgumentParser(
Yuto Takano39639672021-08-05 19:47:48 +0100792 formatter_class=argparse.RawDescriptionHelpFormatter,
793 description=(
794 "This script confirms that the naming of all symbols and identifiers "
795 "in Mbed TLS are consistent with the house style and are also "
796 "self-consistent.\n\n"
Yuto Takanof005c332021-08-09 13:56:36 +0100797 "Expected to be run from the MbedTLS root directory.")
798 )
799 parser.add_argument(
800 "-v", "--verbose",
801 action="store_true",
802 help="show parse results"
803 )
804 parser.add_argument(
805 "-q", "--quiet",
806 action="store_true",
807 help="hide unnecessary text, explanations, and highlighs"
808 )
Darryl Greend5802922018-05-08 15:30:59 +0100809
Yuto Takanof005c332021-08-09 13:56:36 +0100810 args = parser.parse_args()
Darryl Greend5802922018-05-08 15:30:59 +0100811
Yuto Takano55c6c872021-08-09 15:35:19 +0100812 # Configure the global logger, which is then passed to the classes below
813 log = logging.getLogger()
814 log.setLevel(logging.DEBUG if args.verbose else logging.INFO)
815 log.addHandler(logging.StreamHandler())
816
Darryl Greend5802922018-05-08 15:30:59 +0100817 try:
Yuto Takano55c6c872021-08-09 15:35:19 +0100818 code_parser = CodeParser(log)
819 parse_result = code_parser.comprehensive_parse()
Yuto Takanod93fa372021-08-06 23:05:55 +0100820 except Exception: # pylint: disable=broad-except
Darryl Greend5802922018-05-08 15:30:59 +0100821 traceback.print_exc()
822 sys.exit(2)
823
Yuto Takano55c6c872021-08-09 15:35:19 +0100824 name_checker = NameChecker(parse_result, log)
825 return_code = name_checker.perform_checks(quiet=args.quiet)
826
827 sys.exit(return_code)
828
Darryl Greend5802922018-05-08 15:30:59 +0100829if __name__ == "__main__":
Yuto Takano39639672021-08-05 19:47:48 +0100830 main()