blob: 9a7f3918e645f777906d7e548978603c321c5cac [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
23The script performs the following checks:
Yuto Takano81528c02021-08-06 16:22:06 +010024
25- All exported and available symbols in the library object files, are explicitly
Yuto Takano159255a2021-08-06 17:00:28 +010026 declared in the header files. This uses the nm command.
Yuto Takano81528c02021-08-06 16:22:06 +010027- All macros, constants, and identifiers (function names, struct names, etc)
28 follow the required pattern.
29- Typo checking: All words that begin with MBED exist as macros or constants.
Yuto Takanofc54dfb2021-08-07 17:18:28 +010030
31Returns 0 on success, 1 on test failure, and 2 if there is a script error or a
32subprocess error. Must be run from Mbed TLS root.
Darryl Greend5802922018-05-08 15:30:59 +010033"""
Yuto Takano39639672021-08-05 19:47:48 +010034
35import argparse
Yuto Takano977e07f2021-08-09 11:56:15 +010036import glob
Yuto Takano39639672021-08-05 19:47:48 +010037import textwrap
Darryl Greend5802922018-05-08 15:30:59 +010038import os
39import sys
40import traceback
41import re
42import shutil
43import subprocess
44import logging
45
Yuto Takano81528c02021-08-06 16:22:06 +010046# Naming patterns to check against. These are defined outside the NameCheck
47# class for ease of modification.
Yuto Takanobb7dca42021-08-05 19:57:58 +010048MACRO_PATTERN = r"^(MBEDTLS|PSA)_[0-9A-Z_]*[0-9A-Z]$"
Yuto Takano81528c02021-08-06 16:22:06 +010049CONSTANTS_PATTERN = MACRO_PATTERN
Yuto Takanoc1838932021-08-05 19:52:09 +010050IDENTIFIER_PATTERN = r"^(mbedtls|psa)_[0-9a-z_]*[0-9a-z]$"
Yuto Takano39639672021-08-05 19:47:48 +010051
Yuto Takanod93fa372021-08-06 23:05:55 +010052class Match(): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010053 """
54 A class representing a match, together with its found position.
55
56 Fields:
57 * filename: the file that the match was in.
58 * line: the full line containing the match.
Yuto Takanod93fa372021-08-06 23:05:55 +010059 * pos: a tuple of (line_no, start, end) positions on the file line where the
60 match is.
Yuto Takano81528c02021-08-06 16:22:06 +010061 * name: the match itself.
62 """
Yuto Takanod93fa372021-08-06 23:05:55 +010063 def __init__(self, filename, line, pos, name):
Yuto Takano39639672021-08-05 19:47:48 +010064 self.filename = filename
65 self.line = line
66 self.pos = pos
67 self.name = name
Yuto Takano39639672021-08-05 19:47:48 +010068
Yuto Takanoa4e75122021-08-06 17:23:28 +010069 def __str__(self):
Yuto Takano381fda82021-08-06 23:37:20 +010070 ln_str = str(self.pos[0])
71 gutter_len = max(4, len(ln_str))
72 gutter = (gutter_len - len(ln_str)) * " " + ln_str
73 underline = self.pos[1] * " " + (self.pos[2] - self.pos[1]) * "^"
74
Yuto Takanoa4e75122021-08-06 17:23:28 +010075 return (
Yuto Takano381fda82021-08-06 23:37:20 +010076 " {0} |\n".format(gutter_len * " ") +
77 " {0} | {1}".format(gutter, self.line) +
Yuto Takano55614b52021-08-07 01:00:18 +010078 " {0} | {1}\n".format(gutter_len * " ", underline)
Yuto Takanoa4e75122021-08-06 17:23:28 +010079 )
Yuto Takanod93fa372021-08-06 23:05:55 +010080
81class Problem(): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010082 """
83 A parent class representing a form of static analysis error.
Yuto Takano81528c02021-08-06 16:22:06 +010084 """
Yuto Takano39639672021-08-05 19:47:48 +010085 def __init__(self):
Yuto Takanod70d4462021-08-09 12:45:51 +010086 self.quiet = False
Yuto Takano39639672021-08-05 19:47:48 +010087 self.textwrapper = textwrap.TextWrapper()
Yuto Takano81528c02021-08-06 16:22:06 +010088 self.textwrapper.width = 80
Yuto Takanoa4e75122021-08-06 17:23:28 +010089 self.textwrapper.initial_indent = " > "
Yuto Takano81528c02021-08-06 16:22:06 +010090 self.textwrapper.subsequent_indent = " "
Yuto Takano39639672021-08-05 19:47:48 +010091
Yuto Takanod93fa372021-08-06 23:05:55 +010092class SymbolNotInHeader(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010093 """
94 A problem that occurs when an exported/available symbol in the object file
95 is not explicitly declared in header files. Created with
96 NameCheck.check_symbols_declared_in_header()
97
98 Fields:
99 * symbol_name: the name of the symbol.
100 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100101 def __init__(self, symbol_name):
Yuto Takano39639672021-08-05 19:47:48 +0100102 self.symbol_name = symbol_name
103 Problem.__init__(self)
104
105 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100106 if self.quiet:
107 return "{0}".format(self.symbol_name)
108
Yuto Takano39639672021-08-05 19:47:48 +0100109 return self.textwrapper.fill(
110 "'{0}' was found as an available symbol in the output of nm, "
111 "however it was not declared in any header files."
112 .format(self.symbol_name))
113
Yuto Takanod93fa372021-08-06 23:05:55 +0100114class PatternMismatch(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100115 """
116 A problem that occurs when something doesn't match the expected pattern.
117 Created with NameCheck.check_match_pattern()
118
119 Fields:
120 * pattern: the expected regex pattern
121 * match: the Match object in question
122 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100123 def __init__(self, pattern, match):
Yuto Takano39639672021-08-05 19:47:48 +0100124 self.pattern = pattern
125 self.match = match
126 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100127
Yuto Takano39639672021-08-05 19:47:48 +0100128 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100129 if self.quiet:
Yuto Takanod70d4462021-08-09 12:45:51 +0100130 return (
131 "{0}:{1}:{3}"
132 .format(self.match.filename, self.match.pos[0], self.match.name)
133 )
Yuto Takano55614b52021-08-07 01:00:18 +0100134
Yuto Takano39639672021-08-05 19:47:48 +0100135 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100136 "{0}:{1}: '{2}' does not match the required pattern '{3}'."
137 .format(
138 self.match.filename,
Yuto Takanod93fa372021-08-06 23:05:55 +0100139 self.match.pos[0],
Yuto Takanoa4e75122021-08-06 17:23:28 +0100140 self.match.name,
Yuto Takanod70d4462021-08-09 12:45:51 +0100141 self.pattern
142 )
143 ) + "\n" + str(self.match)
Yuto Takano39639672021-08-05 19:47:48 +0100144
Yuto Takanod93fa372021-08-06 23:05:55 +0100145class Typo(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100146 """
147 A problem that occurs when a word using MBED doesn't appear to be defined as
148 constants nor enum values. Created with NameCheck.check_for_typos()
149
150 Fields:
151 * match: the Match object of the MBED name in question.
152 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100153 def __init__(self, match):
Yuto Takano39639672021-08-05 19:47:48 +0100154 self.match = match
155 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100156
Yuto Takano39639672021-08-05 19:47:48 +0100157 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100158 if self.quiet:
Yuto Takanod70d4462021-08-09 12:45:51 +0100159 return (
160 "{0}:{1}:{2}"
161 .format(self.match.filename, self.match.pos[0], self.match.name)
162 )
Yuto Takano55614b52021-08-07 01:00:18 +0100163
Yuto Takano39639672021-08-05 19:47:48 +0100164 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100165 "{0}:{1}: '{2}' looks like a typo. It was not found in any "
166 "macros or any enums. If this is not a typo, put "
167 "//no-check-names after it."
Yuto Takanod70d4462021-08-09 12:45:51 +0100168 .format(self.match.filename, self.match.pos[0], self.match.name)
169 ) + "\n" + str(self.match)
Darryl Greend5802922018-05-08 15:30:59 +0100170
Yuto Takanod93fa372021-08-06 23:05:55 +0100171class NameCheck():
Yuto Takano81528c02021-08-06 16:22:06 +0100172 """
173 Representation of the core name checking operation performed by this script.
Yuto Takano977e07f2021-08-09 11:56:15 +0100174 Shares a common logger, and a shared return code.
Yuto Takano81528c02021-08-06 16:22:06 +0100175 """
Yuto Takano977e07f2021-08-09 11:56:15 +0100176 def __init__(self, verbose=False):
Darryl Greend5802922018-05-08 15:30:59 +0100177 self.log = None
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100178 self.check_repo_path()
Darryl Greend5802922018-05-08 15:30:59 +0100179 self.return_code = 0
Yuto Takano977e07f2021-08-09 11:56:15 +0100180 self.setup_logger(verbose)
181
Yuto Takano8e9a2192021-08-09 14:48:53 +0100182 # Memo for storing "glob expression": set(filepaths)
183 self.files = {}
184
Yuto Takano977e07f2021-08-09 11:56:15 +0100185 # Globally excluded filenames
Yuto Takano8e9a2192021-08-09 14:48:53 +0100186 self.excluded_files = ["**/bn_mul", "**/compat-2.x.h"]
Yuto Takano977e07f2021-08-09 11:56:15 +0100187
188 # Will contain the parse result after a comprehensive parse
Yuto Takanod93fa372021-08-06 23:05:55 +0100189 self.parse_result = {}
Darryl Greend5802922018-05-08 15:30:59 +0100190
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100191 @staticmethod
192 def check_repo_path():
193 """
194 Check that the current working directory is the project root, and throw
195 an exception if not.
196 """
197 if not all(os.path.isdir(d) for d in ["include", "library", "tests"]):
198 raise Exception("This script must be run from Mbed TLS root")
199
Yuto Takanod70d4462021-08-09 12:45:51 +0100200 def set_return_code(self, return_code):
201 if return_code > self.return_code:
202 self.log.debug("Setting new return code to {}".format(return_code))
203 self.return_code = return_code
204
Yuto Takano39639672021-08-05 19:47:48 +0100205 def setup_logger(self, verbose=False):
206 """
207 Set up a logger and set the change the default logging level from
Yuto Takano81528c02021-08-06 16:22:06 +0100208 WARNING to INFO. Loggers are better than print statements since their
Yuto Takano39639672021-08-05 19:47:48 +0100209 verbosity can be controlled.
210 """
Darryl Greend5802922018-05-08 15:30:59 +0100211 self.log = logging.getLogger()
Yuto Takano39639672021-08-05 19:47:48 +0100212 if verbose:
213 self.log.setLevel(logging.DEBUG)
214 else:
215 self.log.setLevel(logging.INFO)
Darryl Greend5802922018-05-08 15:30:59 +0100216 self.log.addHandler(logging.StreamHandler())
217
Yuto Takano8e9a2192021-08-09 14:48:53 +0100218 def get_files(self, include_wildcards, exclude_wildcards):
Yuto Takano81528c02021-08-06 16:22:06 +0100219 """
Yuto Takano8e9a2192021-08-09 14:48:53 +0100220 Get all files that match any of the UNIX-style wildcards. While the
221 check_names script is designed only for use on UNIX/macOS (due to nm),
222 this function alone would work fine on Windows even with forward slashes
223 in the wildcard.
Yuto Takano81528c02021-08-06 16:22:06 +0100224
225 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100226 * include_wildcards: a List of shell-style wildcards to match filepaths.
227 * exclude_wildacrds: a List of shell-style wildcards to exclude.
Yuto Takano81528c02021-08-06 16:22:06 +0100228
229 Returns a List of relative filepaths.
230 """
Yuto Takano8e9a2192021-08-09 14:48:53 +0100231 accumulator = set()
Yuto Takano977e07f2021-08-09 11:56:15 +0100232
Yuto Takano8e9a2192021-08-09 14:48:53 +0100233 # exclude_wildcards may be None. Also, consider the global exclusions.
234 exclude_wildcards = (exclude_wildcards or []) + self.excluded_files
235
236 # Perform set union on the glob results. Memoise individual sets.
237 for include_wildcard in include_wildcards:
238 if include_wildcard not in self.files:
239 self.files[include_wildcard] = set(glob.glob(
240 include_wildcard,
241 recursive=True
242 ))
243
244 accumulator = accumulator.union(self.files[include_wildcard])
245
246 # Perform set difference to exclude. Also use the same memo since their
247 # behaviour is pretty much identical and it can benefit from the cache.
248 for exclude_wildcard in exclude_wildcards:
249 if exclude_wildcard not in self.files:
250 self.files[exclude_wildcard] = set(glob.glob(
251 exclude_wildcard,
252 recursive=True
253 ))
254
255 accumulator = accumulator.difference(self.files[exclude_wildcard])
256
257 return list(accumulator)
Darryl Greend5802922018-05-08 15:30:59 +0100258
Yuto Takano81528c02021-08-06 16:22:06 +0100259 def parse_names_in_source(self):
260 """
Yuto Takano977e07f2021-08-09 11:56:15 +0100261 Comprehensive function to call each parsing function and retrieve
262 various elements of the code, together with their source location.
263 Puts the parsed values in the internal variable self.parse_result, so
264 they can be used from perform_checks().
Yuto Takano81528c02021-08-06 16:22:06 +0100265 """
266 self.log.info("Parsing source code...")
Yuto Takanod24e0372021-08-06 16:42:33 +0100267 self.log.debug(
Yuto Takano50953432021-08-09 14:54:36 +0100268 "The following files are excluded from the search: {}"
Yuto Takanod24e0372021-08-06 16:42:33 +0100269 .format(str(self.excluded_files))
270 )
Yuto Takano81528c02021-08-06 16:22:06 +0100271
Yuto Takano8e9a2192021-08-09 14:48:53 +0100272 all_macros = self.parse_macros([
273 "include/mbedtls/*.h",
274 "include/psa/*.h",
275 "library/*.h",
276 "tests/include/test/drivers/*.h",
Yuto Takanod70d4462021-08-09 12:45:51 +0100277 "3rdparty/everest/include/everest/everest.h",
278 "3rdparty/everest/include/everest/x25519.h"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100279 ])
280 enum_consts = self.parse_enum_consts([
281 "include/mbedtls/*.h",
282 "library/*.h",
283 "3rdparty/everest/include/everest/everest.h",
284 "3rdparty/everest/include/everest/x25519.h"
285 ])
286 identifiers = self.parse_identifiers([
287 "include/mbedtls/*.h",
288 "include/psa/*.h",
289 "library/*.h",
290 "3rdparty/everest/include/everest/everest.h",
291 "3rdparty/everest/include/everest/x25519.h"
292 ])
293 mbed_words = self.parse_mbed_words([
294 "include/mbedtls/*.h",
295 "include/psa/*.h",
296 "library/*.h",
297 "3rdparty/everest/include/everest/everest.h",
298 "3rdparty/everest/include/everest/x25519.h",
299 "library/*.c",
Yuto Takano81528c02021-08-06 16:22:06 +0100300 "3rdparty/everest/library/everest.c",
Yuto Takanod70d4462021-08-09 12:45:51 +0100301 "3rdparty/everest/library/x25519.c"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100302 ])
Yuto Takano81528c02021-08-06 16:22:06 +0100303 symbols = self.parse_symbols()
304
305 # Remove identifier macros like mbedtls_printf or mbedtls_calloc
306 identifiers_justname = [x.name for x in identifiers]
307 actual_macros = []
308 for macro in all_macros:
309 if macro.name not in identifiers_justname:
310 actual_macros.append(macro)
311
312 self.log.debug("Found:")
Yuto Takanod70d4462021-08-09 12:45:51 +0100313 self.log.debug(" {} Total Macros".format(len(all_macros)))
Yuto Takano81528c02021-08-06 16:22:06 +0100314 self.log.debug(" {} Non-identifier Macros".format(len(actual_macros)))
315 self.log.debug(" {} Enum Constants".format(len(enum_consts)))
316 self.log.debug(" {} Identifiers".format(len(identifiers)))
317 self.log.debug(" {} Exported Symbols".format(len(symbols)))
318 self.log.info("Analysing...")
Yuto Takano81528c02021-08-06 16:22:06 +0100319 self.parse_result = {
320 "macros": actual_macros,
321 "enum_consts": enum_consts,
322 "identifiers": identifiers,
323 "symbols": symbols,
Yuto Takanod93fa372021-08-06 23:05:55 +0100324 "mbed_words": mbed_words
Yuto Takano81528c02021-08-06 16:22:06 +0100325 }
326
Yuto Takano8e9a2192021-08-09 14:48:53 +0100327 def parse_macros(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100328 """
329 Parse all macros defined by #define preprocessor directives.
330
331 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100332 * include: A List of glob expressions to look for files through.
333 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100334
335 Returns a List of Match objects for the found macros.
Yuto Takano39639672021-08-05 19:47:48 +0100336 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100337 macro_regex = re.compile(r"# *define +(?P<macro>\w+)")
338 exclusions = (
Yuto Takano39639672021-08-05 19:47:48 +0100339 "asm", "inline", "EMIT", "_CRT_SECURE_NO_DEPRECATE", "MULADDC_"
340 )
341
Yuto Takano50953432021-08-09 14:54:36 +0100342 files = self.get_files(include, exclude)
343 self.log.debug("Looking for macros in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100344
Yuto Takano50953432021-08-09 14:54:36 +0100345 macros = []
346 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100347 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100348 for line_no, line in enumerate(header):
Yuto Takanod93fa372021-08-06 23:05:55 +0100349 for macro in macro_regex.finditer(line):
Yuto Takanod70d4462021-08-09 12:45:51 +0100350 if macro.group("macro").startswith(exclusions):
351 continue
352
353 macros.append(Match(
354 header_file,
355 line,
356 (line_no, macro.start(), macro.end()),
357 macro.group("macro")))
Darryl Greend5802922018-05-08 15:30:59 +0100358
Yuto Takano39639672021-08-05 19:47:48 +0100359 return macros
Darryl Greend5802922018-05-08 15:30:59 +0100360
Yuto Takano8e9a2192021-08-09 14:48:53 +0100361 def parse_mbed_words(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100362 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100363 Parse all words in the file that begin with MBED, in and out of macros,
364 comments, anything.
Yuto Takano39639672021-08-05 19:47:48 +0100365
366 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100367 * include: A List of glob expressions to look for files through.
368 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100369
370 Returns a List of Match objects for words beginning with MBED.
Yuto Takano39639672021-08-05 19:47:48 +0100371 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100372 # Typos of TLS are common, hence the broader check below than MBEDTLS.
Yuto Takanod93fa372021-08-06 23:05:55 +0100373 mbed_regex = re.compile(r"\bMBED.+?_[A-Z0-9_]*")
374 exclusions = re.compile(r"// *no-check-names|#error")
375
Yuto Takano50953432021-08-09 14:54:36 +0100376 files = self.get_files(include, exclude)
377 self.log.debug("Looking for MBED words in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100378
Yuto Takano50953432021-08-09 14:54:36 +0100379 mbed_words = []
380 for filename in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100381 with open(filename, "r", encoding="utf-8") as fp:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100382 for line_no, line in enumerate(fp):
Yuto Takanod93fa372021-08-06 23:05:55 +0100383 if exclusions.search(line):
Yuto Takanoc62b4082021-08-05 20:17:07 +0100384 continue
Yuto Takano81528c02021-08-06 16:22:06 +0100385
Yuto Takanod93fa372021-08-06 23:05:55 +0100386 for name in mbed_regex.finditer(line):
387 mbed_words.append(Match(
Yuto Takano39639672021-08-05 19:47:48 +0100388 filename,
389 line,
Yuto Takanod93fa372021-08-06 23:05:55 +0100390 (line_no, name.start(), name.end()),
Yuto Takano39639672021-08-05 19:47:48 +0100391 name.group(0)
392 ))
393
Yuto Takanod93fa372021-08-06 23:05:55 +0100394 return mbed_words
Yuto Takano39639672021-08-05 19:47:48 +0100395
Yuto Takano8e9a2192021-08-09 14:48:53 +0100396 def parse_enum_consts(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100397 """
398 Parse all enum value constants that are declared.
399
400 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100401 * include: A List of glob expressions to look for files through.
402 * exclude: A List of glob expressions for excluding files.
Yuto Takano39639672021-08-05 19:47:48 +0100403
Yuto Takano81528c02021-08-06 16:22:06 +0100404 Returns a List of Match objects for the findings.
Yuto Takano39639672021-08-05 19:47:48 +0100405 """
Yuto Takano50953432021-08-09 14:54:36 +0100406 files = self.get_files(include, exclude)
407 self.log.debug("Looking for enum consts in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100408
Yuto Takano50953432021-08-09 14:54:36 +0100409 enum_consts = []
410 for header_file in files:
Yuto Takano39639672021-08-05 19:47:48 +0100411 # Emulate a finite state machine to parse enum declarations.
Yuto Takano81528c02021-08-06 16:22:06 +0100412 # 0 = not in enum
413 # 1 = inside enum
414 # 2 = almost inside enum
Darryl Greend5802922018-05-08 15:30:59 +0100415 state = 0
Yuto Takanoa083d152021-08-07 00:25:59 +0100416 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100417 for line_no, line in enumerate(header):
Yuto Takano13ecd992021-08-06 16:56:52 +0100418 # Match typedefs and brackets only when they are at the
419 # beginning of the line -- if they are indented, they might
420 # be sub-structures within structs, etc.
Yuto Takanod93fa372021-08-06 23:05:55 +0100421 if state == 0 and re.match(r"^(typedef +)?enum +{", line):
Darryl Greend5802922018-05-08 15:30:59 +0100422 state = 1
Yuto Takanod93fa372021-08-06 23:05:55 +0100423 elif state == 0 and re.match(r"^(typedef +)?enum", line):
Darryl Greend5802922018-05-08 15:30:59 +0100424 state = 2
Yuto Takanod93fa372021-08-06 23:05:55 +0100425 elif state == 2 and re.match(r"^{", line):
Darryl Greend5802922018-05-08 15:30:59 +0100426 state = 1
Yuto Takanod93fa372021-08-06 23:05:55 +0100427 elif state == 1 and re.match(r"^}", line):
Darryl Greend5802922018-05-08 15:30:59 +0100428 state = 0
Yuto Takanod93fa372021-08-06 23:05:55 +0100429 elif state == 1 and not re.match(r" *#", line):
Yuto Takano13ecd992021-08-06 16:56:52 +0100430 enum_const = re.match(r" *(?P<enum_const>\w+)", line)
Yuto Takanod70d4462021-08-09 12:45:51 +0100431 if not enum_const:
432 continue
433
434 enum_consts.append(Match(
435 header_file,
436 line,
437 (line_no, enum_const.start(), enum_const.end()),
438 enum_const.group("enum_const")))
Yuto Takano81528c02021-08-06 16:22:06 +0100439
Yuto Takano39639672021-08-05 19:47:48 +0100440 return enum_consts
Darryl Greend5802922018-05-08 15:30:59 +0100441
Yuto Takano8e9a2192021-08-09 14:48:53 +0100442 def parse_identifiers(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100443 """
444 Parse all lines of a header where a function identifier is declared,
Yuto Takano81528c02021-08-06 16:22:06 +0100445 based on some huersitics. Highly dependent on formatting style.
Yuto Takanod70d4462021-08-09 12:45:51 +0100446 Note: .match() checks at the beginning of the string (implicit ^), while
447 .search() checks throughout.
Darryl Greend5802922018-05-08 15:30:59 +0100448
Yuto Takano39639672021-08-05 19:47:48 +0100449 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100450 * include: A List of glob expressions to look for files through.
451 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100452
453 Returns a List of Match objects with identifiers.
Yuto Takano39639672021-08-05 19:47:48 +0100454 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100455 identifier_regex = re.compile(
456 # Match " something(a" or " *something(a". Functions.
457 # Assumptions:
458 # - function definition from return type to one of its arguments is
459 # all on one line (enforced by the previous_line concat below)
460 # - function definition line only contains alphanumeric, asterisk,
461 # underscore, and open bracket
462 r".* \**(\w+) *\( *\w|"
463 # Match "(*something)(". Flexible with spaces.
464 r".*\( *\* *(\w+) *\) *\(|"
465 # Match names of named data structures.
466 r"(?:typedef +)?(?:struct|union|enum) +(\w+)(?: *{)?$|"
467 # Match names of typedef instances, after closing bracket.
Yuto Takanod70d4462021-08-09 12:45:51 +0100468 r"}? *(\w+)[;[].*"
469 )
470 exclusion_lines = re.compile(
471 r"^("
472 r"extern +\"C\"|"
473 r"(typedef +)?(struct|union|enum)( *{)?$|"
474 r"} *;?$|"
475 r"$|"
476 r"//|"
477 r"#"
478 r")"
479 )
Yuto Takanod93fa372021-08-06 23:05:55 +0100480
Yuto Takano50953432021-08-09 14:54:36 +0100481 files = self.get_files(include, exclude)
482 self.log.debug("Looking for identifiers in {} files".format(len(files)))
483
484 identifiers = []
485 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100486 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano39639672021-08-05 19:47:48 +0100487 in_block_comment = False
Yuto Takanod70d4462021-08-09 12:45:51 +0100488 # The previous line varibale is used for concatenating lines
489 # when identifiers are formatted and spread across multiple.
Yuto Takanod93fa372021-08-06 23:05:55 +0100490 previous_line = ""
Darryl Greend5802922018-05-08 15:30:59 +0100491
Yuto Takano8f457cf2021-08-06 17:54:58 +0100492 for line_no, line in enumerate(header):
Yuto Takano81528c02021-08-06 16:22:06 +0100493 # Skip parsing this line if a block comment ends on it,
494 # but don't skip if it has just started -- there is a chance
495 # it ends on the same line.
Yuto Takano39639672021-08-05 19:47:48 +0100496 if re.search(r"/\*", line):
Yuto Takano81528c02021-08-06 16:22:06 +0100497 in_block_comment = not in_block_comment
498 if re.search(r"\*/", line):
499 in_block_comment = not in_block_comment
Yuto Takano39639672021-08-05 19:47:48 +0100500 continue
501
Yuto Takano81528c02021-08-06 16:22:06 +0100502 if in_block_comment:
Yuto Takanod93fa372021-08-06 23:05:55 +0100503 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100504 continue
505
Yuto Takanod93fa372021-08-06 23:05:55 +0100506 if exclusion_lines.match(line):
507 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100508 continue
509
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100510 # If the line contains only space-separated alphanumeric
511 # characters (or underscore, asterisk, or, open bracket),
512 # and nothing else, high chance it's a declaration that
513 # continues on the next line
514 if re.match(r"^([\w\*\(]+\s+)+$", line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100515 previous_line += line
Yuto Takano81528c02021-08-06 16:22:06 +0100516 continue
517
518 # If previous line seemed to start an unfinished declaration
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100519 # (as above), concat and treat them as one.
520 if previous_line:
521 line = previous_line.strip() + " " + line.strip()
Yuto Takanod93fa372021-08-06 23:05:55 +0100522 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100523
524 # Skip parsing if line has a space in front = hueristic to
525 # skip function argument lines (highly subject to formatting
526 # changes)
527 if line[0] == " ":
Yuto Takano39639672021-08-05 19:47:48 +0100528 continue
Yuto Takano6f38ab32021-08-05 21:07:14 +0100529
Yuto Takanod93fa372021-08-06 23:05:55 +0100530 identifier = identifier_regex.search(line)
Yuto Takano39639672021-08-05 19:47:48 +0100531
Yuto Takanod70d4462021-08-09 12:45:51 +0100532 if not identifier:
533 continue
534
535 # Find the group that matched, and append it
536 for group in identifier.groups():
537 if not group:
538 continue
539
540 identifiers.append(Match(
541 header_file,
542 line,
543 (line_no, identifier.start(), identifier.end()),
544 group))
Yuto Takano39639672021-08-05 19:47:48 +0100545
546 return identifiers
547
548 def parse_symbols(self):
549 """
550 Compile the Mbed TLS libraries, and parse the TLS, Crypto, and x509
551 object files using nm to retrieve the list of referenced symbols.
Yuto Takano81528c02021-08-06 16:22:06 +0100552 Exceptions thrown here are rethrown because they would be critical
553 errors that void several tests, and thus needs to halt the program. This
554 is explicitly done for clarity.
Yuto Takano39639672021-08-05 19:47:48 +0100555
Yuto Takano81528c02021-08-06 16:22:06 +0100556 Returns a List of unique symbols defined and used in the libraries.
557 """
558 self.log.info("Compiling...")
Yuto Takano39639672021-08-05 19:47:48 +0100559 symbols = []
560
561 # Back up the config and atomically compile with the full configratuion.
Yuto Takanod70d4462021-08-09 12:45:51 +0100562 shutil.copy(
563 "include/mbedtls/mbedtls_config.h",
564 "include/mbedtls/mbedtls_config.h.bak"
565 )
Darryl Greend5802922018-05-08 15:30:59 +0100566 try:
Yuto Takano81528c02021-08-06 16:22:06 +0100567 # Use check=True in all subprocess calls so that failures are raised
568 # as exceptions and logged.
Yuto Takano39639672021-08-05 19:47:48 +0100569 subprocess.run(
Yuto Takano81528c02021-08-06 16:22:06 +0100570 ["python3", "scripts/config.py", "full"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100571 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100572 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100573 )
574 my_environment = os.environ.copy()
575 my_environment["CFLAGS"] = "-fno-asynchronous-unwind-tables"
Yuto Takano39639672021-08-05 19:47:48 +0100576 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100577 ["make", "clean", "lib"],
578 env=my_environment,
Yuto Takanobcc3d992021-08-06 23:14:58 +0100579 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100580 stdout=subprocess.PIPE,
Darryl Greend5802922018-05-08 15:30:59 +0100581 stderr=subprocess.STDOUT,
Yuto Takano39639672021-08-05 19:47:48 +0100582 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100583 )
Yuto Takano39639672021-08-05 19:47:48 +0100584
585 # Perform object file analysis using nm
Yuto Takanod70d4462021-08-09 12:45:51 +0100586 symbols = self.parse_symbols_from_nm([
587 "library/libmbedcrypto.a",
588 "library/libmbedtls.a",
589 "library/libmbedx509.a"
590 ])
Yuto Takano39639672021-08-05 19:47:48 +0100591
592 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100593 ["make", "clean"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100594 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100595 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100596 )
597 except subprocess.CalledProcessError as error:
Yuto Takano25eeb7b2021-08-06 21:27:59 +0100598 self.log.debug(error.output)
Darryl Greend5802922018-05-08 15:30:59 +0100599 self.set_return_code(2)
Yuto Takano81528c02021-08-06 16:22:06 +0100600 raise error
Yuto Takano39639672021-08-05 19:47:48 +0100601 finally:
Yuto Takano6fececf2021-08-07 17:28:23 +0100602 # Put back the original config regardless of there being errors.
603 # Works also for keyboard interrupts.
Yuto Takanod70d4462021-08-09 12:45:51 +0100604 shutil.move(
605 "include/mbedtls/mbedtls_config.h.bak",
606 "include/mbedtls/mbedtls_config.h"
607 )
Yuto Takano39639672021-08-05 19:47:48 +0100608
609 return symbols
610
611 def parse_symbols_from_nm(self, object_files):
612 """
613 Run nm to retrieve the list of referenced symbols in each object file.
614 Does not return the position data since it is of no use.
615
Yuto Takano81528c02021-08-06 16:22:06 +0100616 Args:
617 * object_files: a List of compiled object files to search through.
618
619 Returns a List of unique symbols defined and used in any of the object
620 files.
Yuto Takano39639672021-08-05 19:47:48 +0100621 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100622 nm_undefined_regex = re.compile(r"^\S+: +U |^$|^\S+:$")
623 nm_valid_regex = re.compile(r"^\S+( [0-9A-Fa-f]+)* . _*(?P<symbol>\w+)")
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100624 exclusions = ("FStar", "Hacl")
Yuto Takano39639672021-08-05 19:47:48 +0100625
626 symbols = []
627
Yuto Takano81528c02021-08-06 16:22:06 +0100628 # Gather all outputs of nm
Yuto Takano39639672021-08-05 19:47:48 +0100629 nm_output = ""
630 for lib in object_files:
631 nm_output += subprocess.run(
632 ["nm", "-og", lib],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100633 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100634 stdout=subprocess.PIPE,
635 stderr=subprocess.STDOUT,
636 check=True
637 ).stdout
Yuto Takano81528c02021-08-06 16:22:06 +0100638
Yuto Takano39639672021-08-05 19:47:48 +0100639 for line in nm_output.splitlines():
Yuto Takanod93fa372021-08-06 23:05:55 +0100640 if not nm_undefined_regex.match(line):
641 symbol = nm_valid_regex.match(line)
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100642 if (symbol and not symbol.group("symbol").startswith(exclusions)):
Yuto Takanoe77f6992021-08-05 20:22:59 +0100643 symbols.append(symbol.group("symbol"))
Yuto Takano39639672021-08-05 19:47:48 +0100644 else:
645 self.log.error(line)
Yuto Takano81528c02021-08-06 16:22:06 +0100646
Yuto Takano39639672021-08-05 19:47:48 +0100647 return symbols
648
Yuto Takano55614b52021-08-07 01:00:18 +0100649 def perform_checks(self, quiet=False):
Yuto Takano39639672021-08-05 19:47:48 +0100650 """
651 Perform each check in order, output its PASS/FAIL status. Maintain an
652 overall test status, and output that at the end.
Yuto Takano977e07f2021-08-09 11:56:15 +0100653 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100654
655 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100656 * quiet: whether to hide detailed problem explanation.
Yuto Takano39639672021-08-05 19:47:48 +0100657 """
Yuto Takano81528c02021-08-06 16:22:06 +0100658 self.log.info("=============")
Yuto Takano39639672021-08-05 19:47:48 +0100659 problems = 0
660
Yuto Takano55614b52021-08-07 01:00:18 +0100661 problems += self.check_symbols_declared_in_header(quiet)
Yuto Takano39639672021-08-05 19:47:48 +0100662
Yuto Takanod70d4462021-08-09 12:45:51 +0100663 pattern_checks = [
664 ("macros", MACRO_PATTERN),
665 ("enum_consts", CONSTANTS_PATTERN),
666 ("identifiers", IDENTIFIER_PATTERN)
667 ]
Yuto Takano39639672021-08-05 19:47:48 +0100668 for group, check_pattern in pattern_checks:
Yuto Takano55614b52021-08-07 01:00:18 +0100669 problems += self.check_match_pattern(quiet, group, check_pattern)
Yuto Takano39639672021-08-05 19:47:48 +0100670
Yuto Takano55614b52021-08-07 01:00:18 +0100671 problems += self.check_for_typos(quiet)
Yuto Takano39639672021-08-05 19:47:48 +0100672
673 self.log.info("=============")
674 if problems > 0:
675 self.log.info("FAIL: {0} problem(s) to fix".format(str(problems)))
Yuto Takano55614b52021-08-07 01:00:18 +0100676 if quiet:
677 self.log.info("Remove --quiet to see explanations.")
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100678 else:
679 self.log.info("Use --quiet for minimal output.")
Yuto Takano39639672021-08-05 19:47:48 +0100680 else:
681 self.log.info("PASS")
Darryl Greend5802922018-05-08 15:30:59 +0100682
Yuto Takano55614b52021-08-07 01:00:18 +0100683 def check_symbols_declared_in_header(self, quiet):
Yuto Takano39639672021-08-05 19:47:48 +0100684 """
685 Perform a check that all detected symbols in the library object files
686 are properly declared in headers.
Yuto Takano977e07f2021-08-09 11:56:15 +0100687 Assumes parse_names_in_source() was called before this.
Darryl Greend5802922018-05-08 15:30:59 +0100688
Yuto Takano81528c02021-08-06 16:22:06 +0100689 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100690 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100691
692 Returns the number of problems that need fixing.
Yuto Takano39639672021-08-05 19:47:48 +0100693 """
694 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100695
Yuto Takano39639672021-08-05 19:47:48 +0100696 for symbol in self.parse_result["symbols"]:
697 found_symbol_declared = False
698 for identifier_match in self.parse_result["identifiers"]:
699 if symbol == identifier_match.name:
700 found_symbol_declared = True
701 break
Yuto Takano81528c02021-08-06 16:22:06 +0100702
Yuto Takano39639672021-08-05 19:47:48 +0100703 if not found_symbol_declared:
Yuto Takanod70d4462021-08-09 12:45:51 +0100704 problems.append(SymbolNotInHeader(symbol))
Yuto Takano39639672021-08-05 19:47:48 +0100705
Yuto Takanod70d4462021-08-09 12:45:51 +0100706 self.output_check_result(quiet, "All symbols in header", problems)
Yuto Takano39639672021-08-05 19:47:48 +0100707 return len(problems)
708
Yuto Takano55614b52021-08-07 01:00:18 +0100709 def check_match_pattern(self, quiet, group_to_check, check_pattern):
Yuto Takano81528c02021-08-06 16:22:06 +0100710 """
711 Perform a check that all items of a group conform to a regex pattern.
Yuto Takano977e07f2021-08-09 11:56:15 +0100712 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100713
714 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100715 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100716 * group_to_check: string key to index into self.parse_result.
717 * check_pattern: the regex to check against.
718
719 Returns the number of problems that need fixing.
720 """
Yuto Takano39639672021-08-05 19:47:48 +0100721 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100722
Yuto Takano39639672021-08-05 19:47:48 +0100723 for item_match in self.parse_result[group_to_check]:
724 if not re.match(check_pattern, item_match.name):
725 problems.append(PatternMismatch(check_pattern, item_match))
Yuto Takano201f9e82021-08-06 16:36:54 +0100726 # Double underscore is a reserved identifier, never to be used
Yuto Takanoc763cc32021-08-05 20:06:34 +0100727 if re.match(r".*__.*", item_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100728 problems.append(PatternMismatch("double underscore", item_match))
Yuto Takano81528c02021-08-06 16:22:06 +0100729
730 self.output_check_result(
Yuto Takanod70d4462021-08-09 12:45:51 +0100731 quiet,
Yuto Takano81528c02021-08-06 16:22:06 +0100732 "Naming patterns of {}".format(group_to_check),
Yuto Takano55614b52021-08-07 01:00:18 +0100733 problems)
Yuto Takano39639672021-08-05 19:47:48 +0100734 return len(problems)
Darryl Greend5802922018-05-08 15:30:59 +0100735
Yuto Takano55614b52021-08-07 01:00:18 +0100736 def check_for_typos(self, quiet):
Yuto Takano81528c02021-08-06 16:22:06 +0100737 """
738 Perform a check that all words in the soure code beginning with MBED are
739 either defined as macros, or as enum constants.
Yuto Takano977e07f2021-08-09 11:56:15 +0100740 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100741
742 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100743 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100744
745 Returns the number of problems that need fixing.
746 """
Yuto Takano39639672021-08-05 19:47:48 +0100747 problems = []
Yuto Takano39639672021-08-05 19:47:48 +0100748
Yuto Takanod70d4462021-08-09 12:45:51 +0100749 # Set comprehension, equivalent to a list comprehension wrapped by set()
Yuto Takanod93fa372021-08-06 23:05:55 +0100750 all_caps_names = {
751 match.name
752 for match
753 in self.parse_result["macros"] + self.parse_result["enum_consts"]}
754 typo_exclusion = re.compile(r"XXX|__|_$|^MBEDTLS_.*CONFIG_FILE$")
Yuto Takano39639672021-08-05 19:47:48 +0100755
Yuto Takanod93fa372021-08-06 23:05:55 +0100756 for name_match in self.parse_result["mbed_words"]:
Yuto Takano81528c02021-08-06 16:22:06 +0100757 found = name_match.name in all_caps_names
758
759 # Since MBEDTLS_PSA_ACCEL_XXX defines are defined by the
760 # PSA driver, they will not exist as macros. However, they
761 # should still be checked for typos using the equivalent
762 # BUILTINs that exist.
763 if "MBEDTLS_PSA_ACCEL_" in name_match.name:
764 found = name_match.name.replace(
765 "MBEDTLS_PSA_ACCEL_",
766 "MBEDTLS_PSA_BUILTIN_") in all_caps_names
767
Yuto Takanod93fa372021-08-06 23:05:55 +0100768 if not found and not typo_exclusion.search(name_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100769 problems.append(Typo(name_match))
Yuto Takano39639672021-08-05 19:47:48 +0100770
Yuto Takanod70d4462021-08-09 12:45:51 +0100771 self.output_check_result(quiet, "Likely typos", problems)
Yuto Takano81528c02021-08-06 16:22:06 +0100772 return len(problems)
773
Yuto Takanod70d4462021-08-09 12:45:51 +0100774 def output_check_result(self, quiet, name, problems):
Yuto Takano81528c02021-08-06 16:22:06 +0100775 """
776 Write out the PASS/FAIL status of a performed check depending on whether
777 there were problems.
Yuto Takanod70d4462021-08-09 12:45:51 +0100778
779 Args:
780 * quiet: whether to hide detailed problem explanation.
781 * name: the name of the test
782 * problems: a List of encountered Problems
Yuto Takano81528c02021-08-06 16:22:06 +0100783 """
Yuto Takano39639672021-08-05 19:47:48 +0100784 if problems:
Darryl Greend5802922018-05-08 15:30:59 +0100785 self.set_return_code(1)
Yuto Takano55614b52021-08-07 01:00:18 +0100786 self.log.info("{}: FAIL\n".format(name))
787 for problem in problems:
Yuto Takanod70d4462021-08-09 12:45:51 +0100788 problem.quiet = quiet
Yuto Takano55614b52021-08-07 01:00:18 +0100789 self.log.warning(str(problem))
Darryl Greend5802922018-05-08 15:30:59 +0100790 else:
Yuto Takano81528c02021-08-06 16:22:06 +0100791 self.log.info("{}: PASS".format(name))
Darryl Greend5802922018-05-08 15:30:59 +0100792
Yuto Takano39639672021-08-05 19:47:48 +0100793def main():
794 """
Yuto Takano81528c02021-08-06 16:22:06 +0100795 Perform argument parsing, and create an instance of NameCheck to begin the
796 core operation.
Yuto Takano39639672021-08-05 19:47:48 +0100797 """
Yuto Takanof005c332021-08-09 13:56:36 +0100798 parser = argparse.ArgumentParser(
Yuto Takano39639672021-08-05 19:47:48 +0100799 formatter_class=argparse.RawDescriptionHelpFormatter,
800 description=(
801 "This script confirms that the naming of all symbols and identifiers "
802 "in Mbed TLS are consistent with the house style and are also "
803 "self-consistent.\n\n"
Yuto Takanof005c332021-08-09 13:56:36 +0100804 "Expected to be run from the MbedTLS root directory.")
805 )
806 parser.add_argument(
807 "-v", "--verbose",
808 action="store_true",
809 help="show parse results"
810 )
811 parser.add_argument(
812 "-q", "--quiet",
813 action="store_true",
814 help="hide unnecessary text, explanations, and highlighs"
815 )
Darryl Greend5802922018-05-08 15:30:59 +0100816
Yuto Takanof005c332021-08-09 13:56:36 +0100817 args = parser.parse_args()
Darryl Greend5802922018-05-08 15:30:59 +0100818
Darryl Greend5802922018-05-08 15:30:59 +0100819 try:
Yuto Takano977e07f2021-08-09 11:56:15 +0100820 name_check = NameCheck(verbose=args.verbose)
Yuto Takano39639672021-08-05 19:47:48 +0100821 name_check.parse_names_in_source()
Yuto Takano55614b52021-08-07 01:00:18 +0100822 name_check.perform_checks(quiet=args.quiet)
Yuto Takano81528c02021-08-06 16:22:06 +0100823 sys.exit(name_check.return_code)
Yuto Takanod93fa372021-08-06 23:05:55 +0100824 except Exception: # pylint: disable=broad-except
Darryl Greend5802922018-05-08 15:30:59 +0100825 traceback.print_exc()
826 sys.exit(2)
827
Darryl Greend5802922018-05-08 15:30:59 +0100828if __name__ == "__main__":
Yuto Takano39639672021-08-05 19:47:48 +0100829 main()