blob: ecb00454a6676d62b31976b59f5f3d5e9539b3ef [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
Yuto Takanob1417b42021-08-17 10:30:20 +010052import enum
Darryl Greend5802922018-05-08 15:30:59 +010053import shutil
54import subprocess
55import logging
56
Yuto Takano81528c02021-08-06 16:22:06 +010057# Naming patterns to check against. These are defined outside the NameCheck
58# class for ease of modification.
Yuto Takanobb7dca42021-08-05 19:57:58 +010059MACRO_PATTERN = r"^(MBEDTLS|PSA)_[0-9A-Z_]*[0-9A-Z]$"
Yuto Takano81528c02021-08-06 16:22:06 +010060CONSTANTS_PATTERN = MACRO_PATTERN
Yuto Takanoc1838932021-08-05 19:52:09 +010061IDENTIFIER_PATTERN = r"^(mbedtls|psa)_[0-9a-z_]*[0-9a-z]$"
Yuto Takano39639672021-08-05 19:47:48 +010062
Yuto Takanod93fa372021-08-06 23:05:55 +010063class Match(): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010064 """
65 A class representing a match, together with its found position.
66
67 Fields:
68 * filename: the file that the match was in.
69 * line: the full line containing the match.
Yuto Takanod93fa372021-08-06 23:05:55 +010070 * pos: a tuple of (line_no, start, end) positions on the file line where the
71 match is.
Yuto Takano81528c02021-08-06 16:22:06 +010072 * name: the match itself.
73 """
Yuto Takanod93fa372021-08-06 23:05:55 +010074 def __init__(self, filename, line, pos, name):
Yuto Takano39639672021-08-05 19:47:48 +010075 self.filename = filename
76 self.line = line
77 self.pos = pos
78 self.name = name
Yuto Takano39639672021-08-05 19:47:48 +010079
Yuto Takanoa4e75122021-08-06 17:23:28 +010080 def __str__(self):
Yuto Takanofb86ac72021-08-16 10:32:40 +010081 """
82 Return a formatted code listing representation of the erroneous line.
83 """
84 gutter = format(self.pos[0], "4d")
Yuto Takano381fda82021-08-06 23:37:20 +010085 underline = self.pos[1] * " " + (self.pos[2] - self.pos[1]) * "^"
86
Yuto Takanoa4e75122021-08-06 17:23:28 +010087 return (
Yuto Takanofb86ac72021-08-16 10:32:40 +010088 " {0} |\n".format(" " * len(gutter)) +
Yuto Takano381fda82021-08-06 23:37:20 +010089 " {0} | {1}".format(gutter, self.line) +
Yuto Takanofb86ac72021-08-16 10:32:40 +010090 " {0} | {1}\n".format(" " * len(gutter), underline)
Yuto Takanoa4e75122021-08-06 17:23:28 +010091 )
Yuto Takanod93fa372021-08-06 23:05:55 +010092
93class Problem(): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010094 """
95 A parent class representing a form of static analysis error.
Yuto Takano81528c02021-08-06 16:22:06 +010096 """
Yuto Takano5473be22021-08-17 10:14:01 +010097 # Class variable to control the quietness of all problems
98 quiet = False
Yuto Takano39639672021-08-05 19:47:48 +010099 def __init__(self):
100 self.textwrapper = textwrap.TextWrapper()
Yuto Takano81528c02021-08-06 16:22:06 +0100101 self.textwrapper.width = 80
Yuto Takanoa4e75122021-08-06 17:23:28 +0100102 self.textwrapper.initial_indent = " > "
Yuto Takano81528c02021-08-06 16:22:06 +0100103 self.textwrapper.subsequent_indent = " "
Yuto Takano39639672021-08-05 19:47:48 +0100104
Yuto Takanod93fa372021-08-06 23:05:55 +0100105class SymbolNotInHeader(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100106 """
107 A problem that occurs when an exported/available symbol in the object file
108 is not explicitly declared in header files. Created with
109 NameCheck.check_symbols_declared_in_header()
110
111 Fields:
112 * symbol_name: the name of the symbol.
113 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100114 def __init__(self, symbol_name):
Yuto Takano39639672021-08-05 19:47:48 +0100115 self.symbol_name = symbol_name
116 Problem.__init__(self)
117
118 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100119 if self.quiet:
120 return "{0}".format(self.symbol_name)
121
Yuto Takano39639672021-08-05 19:47:48 +0100122 return self.textwrapper.fill(
123 "'{0}' was found as an available symbol in the output of nm, "
124 "however it was not declared in any header files."
125 .format(self.symbol_name))
126
Yuto Takanod93fa372021-08-06 23:05:55 +0100127class PatternMismatch(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100128 """
129 A problem that occurs when something doesn't match the expected pattern.
130 Created with NameCheck.check_match_pattern()
131
132 Fields:
133 * pattern: the expected regex pattern
134 * match: the Match object in question
135 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100136 def __init__(self, pattern, match):
Yuto Takano39639672021-08-05 19:47:48 +0100137 self.pattern = pattern
138 self.match = match
139 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100140
Yuto Takano39639672021-08-05 19:47:48 +0100141 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100142 if self.quiet:
Yuto Takanod70d4462021-08-09 12:45:51 +0100143 return (
Yuto Takano206b0222021-08-10 11:30:43 +0100144 "{0}:{1}:{2}"
Yuto Takanod70d4462021-08-09 12:45:51 +0100145 .format(self.match.filename, self.match.pos[0], self.match.name)
146 )
Yuto Takano55614b52021-08-07 01:00:18 +0100147
Yuto Takano39639672021-08-05 19:47:48 +0100148 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100149 "{0}:{1}: '{2}' does not match the required pattern '{3}'."
150 .format(
151 self.match.filename,
Yuto Takanod93fa372021-08-06 23:05:55 +0100152 self.match.pos[0],
Yuto Takanoa4e75122021-08-06 17:23:28 +0100153 self.match.name,
Yuto Takanod70d4462021-08-09 12:45:51 +0100154 self.pattern
155 )
156 ) + "\n" + str(self.match)
Yuto Takano39639672021-08-05 19:47:48 +0100157
Yuto Takanod93fa372021-08-06 23:05:55 +0100158class Typo(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100159 """
160 A problem that occurs when a word using MBED doesn't appear to be defined as
161 constants nor enum values. Created with NameCheck.check_for_typos()
162
163 Fields:
164 * match: the Match object of the MBED name in question.
165 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100166 def __init__(self, match):
Yuto Takano39639672021-08-05 19:47:48 +0100167 self.match = match
168 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100169
Yuto Takano39639672021-08-05 19:47:48 +0100170 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100171 if self.quiet:
Yuto Takanod70d4462021-08-09 12:45:51 +0100172 return (
173 "{0}:{1}:{2}"
174 .format(self.match.filename, self.match.pos[0], self.match.name)
175 )
Yuto Takano55614b52021-08-07 01:00:18 +0100176
Yuto Takano39639672021-08-05 19:47:48 +0100177 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100178 "{0}:{1}: '{2}' looks like a typo. It was not found in any "
179 "macros or any enums. If this is not a typo, put "
180 "//no-check-names after it."
Yuto Takanod70d4462021-08-09 12:45:51 +0100181 .format(self.match.filename, self.match.pos[0], self.match.name)
182 ) + "\n" + str(self.match)
Darryl Greend5802922018-05-08 15:30:59 +0100183
Yuto Takano55c6c872021-08-09 15:35:19 +0100184class CodeParser():
Yuto Takano81528c02021-08-06 16:22:06 +0100185 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100186 Class for retrieving files and parsing the code. This can be used
187 independently of the checks that NameChecker performs, for example for
188 list_internal_identifiers.py.
Yuto Takano81528c02021-08-06 16:22:06 +0100189 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100190 def __init__(self, log):
191 self.log = log
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100192 self.check_repo_path()
Yuto Takano977e07f2021-08-09 11:56:15 +0100193
Yuto Takano8e9a2192021-08-09 14:48:53 +0100194 # Memo for storing "glob expression": set(filepaths)
195 self.files = {}
196
Yuto Takano977e07f2021-08-09 11:56:15 +0100197 # Globally excluded filenames
Yuto Takano8e9a2192021-08-09 14:48:53 +0100198 self.excluded_files = ["**/bn_mul", "**/compat-2.x.h"]
Yuto Takano977e07f2021-08-09 11:56:15 +0100199
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100200 @staticmethod
201 def check_repo_path():
202 """
203 Check that the current working directory is the project root, and throw
204 an exception if not.
205 """
206 if not all(os.path.isdir(d) for d in ["include", "library", "tests"]):
207 raise Exception("This script must be run from Mbed TLS root")
208
Yuto Takano55c6c872021-08-09 15:35:19 +0100209 def comprehensive_parse(self):
Yuto Takano39639672021-08-05 19:47:48 +0100210 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100211 Comprehensive ("default") function to call each parsing function and
212 retrieve various elements of the code, together with the source location.
Darryl Greend5802922018-05-08 15:30:59 +0100213
Yuto Takano55c6c872021-08-09 15:35:19 +0100214 Returns a dict of parsed item key to the corresponding List of Matches.
Yuto Takano81528c02021-08-06 16:22:06 +0100215 """
216 self.log.info("Parsing source code...")
Yuto Takanod24e0372021-08-06 16:42:33 +0100217 self.log.debug(
Yuto Takano50953432021-08-09 14:54:36 +0100218 "The following files are excluded from the search: {}"
Yuto Takanod24e0372021-08-06 16:42:33 +0100219 .format(str(self.excluded_files))
220 )
Yuto Takano81528c02021-08-06 16:22:06 +0100221
Yuto Takano8e9a2192021-08-09 14:48:53 +0100222 all_macros = self.parse_macros([
223 "include/mbedtls/*.h",
224 "include/psa/*.h",
225 "library/*.h",
226 "tests/include/test/drivers/*.h",
Yuto Takanod70d4462021-08-09 12:45:51 +0100227 "3rdparty/everest/include/everest/everest.h",
228 "3rdparty/everest/include/everest/x25519.h"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100229 ])
230 enum_consts = self.parse_enum_consts([
231 "include/mbedtls/*.h",
232 "library/*.h",
233 "3rdparty/everest/include/everest/everest.h",
234 "3rdparty/everest/include/everest/x25519.h"
235 ])
236 identifiers = self.parse_identifiers([
237 "include/mbedtls/*.h",
238 "include/psa/*.h",
239 "library/*.h",
240 "3rdparty/everest/include/everest/everest.h",
241 "3rdparty/everest/include/everest/x25519.h"
242 ])
243 mbed_words = self.parse_mbed_words([
244 "include/mbedtls/*.h",
245 "include/psa/*.h",
246 "library/*.h",
247 "3rdparty/everest/include/everest/everest.h",
248 "3rdparty/everest/include/everest/x25519.h",
249 "library/*.c",
Yuto Takano81528c02021-08-06 16:22:06 +0100250 "3rdparty/everest/library/everest.c",
Yuto Takanod70d4462021-08-09 12:45:51 +0100251 "3rdparty/everest/library/x25519.c"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100252 ])
Yuto Takano81528c02021-08-06 16:22:06 +0100253 symbols = self.parse_symbols()
254
255 # Remove identifier macros like mbedtls_printf or mbedtls_calloc
256 identifiers_justname = [x.name for x in identifiers]
257 actual_macros = []
258 for macro in all_macros:
259 if macro.name not in identifiers_justname:
260 actual_macros.append(macro)
261
262 self.log.debug("Found:")
Yuto Takano9d9c6dc2021-08-16 10:43:45 +0100263 # Aligns the counts on the assumption that none exceeds 4 digits
264 self.log.debug(" {:4} Total Macros".format(len(all_macros)))
265 self.log.debug(" {:4} Non-identifier Macros".format(len(actual_macros)))
266 self.log.debug(" {:4} Enum Constants".format(len(enum_consts)))
267 self.log.debug(" {:4} Identifiers".format(len(identifiers)))
268 self.log.debug(" {:4} Exported Symbols".format(len(symbols)))
Yuto Takano55c6c872021-08-09 15:35:19 +0100269 return {
Yuto Takano81528c02021-08-06 16:22:06 +0100270 "macros": actual_macros,
271 "enum_consts": enum_consts,
272 "identifiers": identifiers,
273 "symbols": symbols,
Yuto Takanod93fa372021-08-06 23:05:55 +0100274 "mbed_words": mbed_words
Yuto Takano81528c02021-08-06 16:22:06 +0100275 }
276
Yuto Takano55c6c872021-08-09 15:35:19 +0100277 def get_files(self, include_wildcards, exclude_wildcards):
278 """
279 Get all files that match any of the UNIX-style wildcards. While the
280 check_names script is designed only for use on UNIX/macOS (due to nm),
281 this function alone would work fine on Windows even with forward slashes
282 in the wildcard.
283
284 Args:
285 * include_wildcards: a List of shell-style wildcards to match filepaths.
286 * exclude_wildcards: a List of shell-style wildcards to exclude.
287
288 Returns a List of relative filepaths.
289 """
290 accumulator = set()
291
292 # exclude_wildcards may be None. Also, consider the global exclusions.
293 exclude_wildcards = (exclude_wildcards or []) + self.excluded_files
294
Yuto Takano6adb2872021-08-16 11:38:34 +0100295 # Internal function to hit the memoisation cache or add to it the result
296 # of a glob operation. Used both for inclusion and exclusion since the
297 # only difference between them is whether they perform set union or
298 # difference on the return value of this function.
299 def hit_cache(wildcard):
300 if wildcard not in self.files:
301 self.files[wildcard] = set(glob.glob(wildcard, recursive=True))
302 return self.files[wildcard]
303
Yuto Takano55c6c872021-08-09 15:35:19 +0100304 for include_wildcard in include_wildcards:
Yuto Takano6adb2872021-08-16 11:38:34 +0100305 accumulator = accumulator.union(hit_cache(include_wildcard))
Yuto Takano55c6c872021-08-09 15:35:19 +0100306
Yuto Takano55c6c872021-08-09 15:35:19 +0100307 for exclude_wildcard in exclude_wildcards:
Yuto Takano6adb2872021-08-16 11:38:34 +0100308 accumulator = accumulator.difference(hit_cache(exclude_wildcard))
Yuto Takano55c6c872021-08-09 15:35:19 +0100309
310 return list(accumulator)
311
Yuto Takano8e9a2192021-08-09 14:48:53 +0100312 def parse_macros(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100313 """
314 Parse all macros defined by #define preprocessor directives.
315
316 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100317 * include: A List of glob expressions to look for files through.
318 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100319
320 Returns a List of Match objects for the found macros.
Yuto Takano39639672021-08-05 19:47:48 +0100321 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100322 macro_regex = re.compile(r"# *define +(?P<macro>\w+)")
323 exclusions = (
Yuto Takano39639672021-08-05 19:47:48 +0100324 "asm", "inline", "EMIT", "_CRT_SECURE_NO_DEPRECATE", "MULADDC_"
325 )
326
Yuto Takano50953432021-08-09 14:54:36 +0100327 files = self.get_files(include, exclude)
328 self.log.debug("Looking for macros in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100329
Yuto Takano50953432021-08-09 14:54:36 +0100330 macros = []
331 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100332 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100333 for line_no, line in enumerate(header):
Yuto Takanod93fa372021-08-06 23:05:55 +0100334 for macro in macro_regex.finditer(line):
Yuto Takanod70d4462021-08-09 12:45:51 +0100335 if macro.group("macro").startswith(exclusions):
336 continue
337
338 macros.append(Match(
339 header_file,
340 line,
341 (line_no, macro.start(), macro.end()),
342 macro.group("macro")))
Darryl Greend5802922018-05-08 15:30:59 +0100343
Yuto Takano39639672021-08-05 19:47:48 +0100344 return macros
Darryl Greend5802922018-05-08 15:30:59 +0100345
Yuto Takano8e9a2192021-08-09 14:48:53 +0100346 def parse_mbed_words(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100347 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100348 Parse all words in the file that begin with MBED, in and out of macros,
349 comments, anything.
Yuto Takano39639672021-08-05 19:47:48 +0100350
351 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100352 * include: A List of glob expressions to look for files through.
353 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100354
355 Returns a List of Match objects for words beginning with MBED.
Yuto Takano39639672021-08-05 19:47:48 +0100356 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100357 # Typos of TLS are common, hence the broader check below than MBEDTLS.
Yuto Takanod93fa372021-08-06 23:05:55 +0100358 mbed_regex = re.compile(r"\bMBED.+?_[A-Z0-9_]*")
359 exclusions = re.compile(r"// *no-check-names|#error")
360
Yuto Takano50953432021-08-09 14:54:36 +0100361 files = self.get_files(include, exclude)
362 self.log.debug("Looking for MBED words in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100363
Yuto Takano50953432021-08-09 14:54:36 +0100364 mbed_words = []
365 for filename in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100366 with open(filename, "r", encoding="utf-8") as fp:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100367 for line_no, line in enumerate(fp):
Yuto Takanod93fa372021-08-06 23:05:55 +0100368 if exclusions.search(line):
Yuto Takanoc62b4082021-08-05 20:17:07 +0100369 continue
Yuto Takano81528c02021-08-06 16:22:06 +0100370
Yuto Takanod93fa372021-08-06 23:05:55 +0100371 for name in mbed_regex.finditer(line):
372 mbed_words.append(Match(
Yuto Takano39639672021-08-05 19:47:48 +0100373 filename,
374 line,
Yuto Takanod93fa372021-08-06 23:05:55 +0100375 (line_no, name.start(), name.end()),
Yuto Takano39639672021-08-05 19:47:48 +0100376 name.group(0)
377 ))
378
Yuto Takanod93fa372021-08-06 23:05:55 +0100379 return mbed_words
Yuto Takano39639672021-08-05 19:47:48 +0100380
Yuto Takano8e9a2192021-08-09 14:48:53 +0100381 def parse_enum_consts(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100382 """
383 Parse all enum value constants that are declared.
384
385 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100386 * include: A List of glob expressions to look for files through.
387 * exclude: A List of glob expressions for excluding files.
Yuto Takano39639672021-08-05 19:47:48 +0100388
Yuto Takano81528c02021-08-06 16:22:06 +0100389 Returns a List of Match objects for the findings.
Yuto Takano39639672021-08-05 19:47:48 +0100390 """
Yuto Takano50953432021-08-09 14:54:36 +0100391 files = self.get_files(include, exclude)
392 self.log.debug("Looking for enum consts in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100393
Yuto Takanob1417b42021-08-17 10:30:20 +0100394 # Emulate a finite state machine to parse enum declarations.
395 # OUTSIDE_KEYWORD = outside the enum keyword
396 # IN_BRACES = inside enum opening braces
397 # IN_BETWEEN = between enum keyword and opening braces
398 states = enum.Enum("FSM", ["OUTSIDE_KEYWORD", "IN_BRACES", "IN_BETWEEN"])
Yuto Takano50953432021-08-09 14:54:36 +0100399 enum_consts = []
400 for header_file in files:
Yuto Takanob1417b42021-08-17 10:30:20 +0100401 state = states.OUTSIDE_KEYWORD
Yuto Takanoa083d152021-08-07 00:25:59 +0100402 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100403 for line_no, line in enumerate(header):
Yuto Takano13ecd992021-08-06 16:56:52 +0100404 # Match typedefs and brackets only when they are at the
405 # beginning of the line -- if they are indented, they might
406 # be sub-structures within structs, etc.
Yuto Takanob1417b42021-08-17 10:30:20 +0100407 if (state == states.OUTSIDE_KEYWORD and
408 re.search(r"^(typedef +)?enum +{", line)):
409 state = states.IN_BRACES
410 elif (state == states.OUTSIDE_KEYWORD and
411 re.search(r"^(typedef +)?enum", line)):
412 state = states.IN_BETWEEN
413 elif (state == states.IN_BETWEEN and
414 re.search(r"^{", line)):
415 state = states.IN_BRACES
416 elif (state == states.IN_BRACES and
417 re.search(r"^}", line)):
418 state = states.OUTSIDE_KEYWORD
419 elif (state == states.IN_BRACES and
420 not re.search(r"^ *#", line)):
Yuto Takano90bc0262021-08-16 11:34:10 +0100421 enum_const = re.search(r"^ *(?P<enum_const>\w+)", line)
Yuto Takanod70d4462021-08-09 12:45:51 +0100422 if not enum_const:
423 continue
424
425 enum_consts.append(Match(
426 header_file,
427 line,
Yuto Takanob1417b42021-08-17 10:30:20 +0100428 (line_no,
429 enum_const.start("enum_const"),
430 enum_const.end("enum_const")),
Yuto Takanod70d4462021-08-09 12:45:51 +0100431 enum_const.group("enum_const")))
Yuto Takano81528c02021-08-06 16:22:06 +0100432
Yuto Takano39639672021-08-05 19:47:48 +0100433 return enum_consts
Darryl Greend5802922018-05-08 15:30:59 +0100434
Yuto Takano8e9a2192021-08-09 14:48:53 +0100435 def parse_identifiers(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100436 """
Yuto Takano8246eb82021-08-16 10:37:24 +0100437 Parse all lines of a header where a function/enum/struct/union/typedef
Yuto Takanob1417b42021-08-17 10:30:20 +0100438 identifier is declared, based on some regex and heuristics. Highly
439 dependent on formatting style.
Darryl Greend5802922018-05-08 15:30:59 +0100440
Yuto Takano39639672021-08-05 19:47:48 +0100441 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100442 * include: A List of glob expressions to look for files through.
443 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100444
445 Returns a List of Match objects with identifiers.
Yuto Takano39639672021-08-05 19:47:48 +0100446 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100447 identifier_regex = re.compile(
448 # Match " something(a" or " *something(a". Functions.
449 # Assumptions:
450 # - function definition from return type to one of its arguments is
Yuto Takano55c6c872021-08-09 15:35:19 +0100451 # all on one line
Yuto Takanod93fa372021-08-06 23:05:55 +0100452 # - function definition line only contains alphanumeric, asterisk,
453 # underscore, and open bracket
454 r".* \**(\w+) *\( *\w|"
Yuto Takano55c6c872021-08-09 15:35:19 +0100455 # Match "(*something)(".
Yuto Takanod93fa372021-08-06 23:05:55 +0100456 r".*\( *\* *(\w+) *\) *\(|"
457 # Match names of named data structures.
458 r"(?:typedef +)?(?:struct|union|enum) +(\w+)(?: *{)?$|"
459 # Match names of typedef instances, after closing bracket.
Yuto Takanod70d4462021-08-09 12:45:51 +0100460 r"}? *(\w+)[;[].*"
461 )
462 exclusion_lines = re.compile(
463 r"^("
Yuto Takano90bc0262021-08-16 11:34:10 +0100464 r"extern +\"C\"|"
465 r"(typedef +)?(struct|union|enum)( *{)?$|"
466 r"} *;?$|"
467 r"$|"
468 r"//|"
469 r"#"
Yuto Takanod70d4462021-08-09 12:45:51 +0100470 r")"
471 )
Yuto Takanod93fa372021-08-06 23:05:55 +0100472
Yuto Takano50953432021-08-09 14:54:36 +0100473 files = self.get_files(include, exclude)
474 self.log.debug("Looking for identifiers in {} files".format(len(files)))
475
476 identifiers = []
477 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100478 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano39639672021-08-05 19:47:48 +0100479 in_block_comment = False
Yuto Takano55c6c872021-08-09 15:35:19 +0100480 # The previous line variable is used for concatenating lines
Yuto Takanob1417b42021-08-17 10:30:20 +0100481 # when identifiers are formatted and spread across multiple
482 # lines.
Yuto Takanod93fa372021-08-06 23:05:55 +0100483 previous_line = ""
Darryl Greend5802922018-05-08 15:30:59 +0100484
Yuto Takano8f457cf2021-08-06 17:54:58 +0100485 for line_no, line in enumerate(header):
Yuto Takano81528c02021-08-06 16:22:06 +0100486 # Skip parsing this line if a block comment ends on it,
487 # but don't skip if it has just started -- there is a chance
488 # it ends on the same line.
Yuto Takano39639672021-08-05 19:47:48 +0100489 if re.search(r"/\*", line):
Yuto Takano81528c02021-08-06 16:22:06 +0100490 in_block_comment = not in_block_comment
491 if re.search(r"\*/", line):
492 in_block_comment = not in_block_comment
Yuto Takano39639672021-08-05 19:47:48 +0100493 continue
494
Yuto Takano81528c02021-08-06 16:22:06 +0100495 if in_block_comment:
Yuto Takanod93fa372021-08-06 23:05:55 +0100496 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100497 continue
498
Yuto Takano90bc0262021-08-16 11:34:10 +0100499 if exclusion_lines.search(line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100500 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100501 continue
502
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100503 # If the line contains only space-separated alphanumeric
504 # characters (or underscore, asterisk, or, open bracket),
505 # and nothing else, high chance it's a declaration that
506 # continues on the next line
Yuto Takano90bc0262021-08-16 11:34:10 +0100507 if re.search(r"^([\w\*\(]+\s+)+$", line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100508 previous_line += line
Yuto Takano81528c02021-08-06 16:22:06 +0100509 continue
510
511 # If previous line seemed to start an unfinished declaration
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100512 # (as above), concat and treat them as one.
513 if previous_line:
Yuto Takano90bc0262021-08-16 11:34:10 +0100514 line = previous_line.strip() + " " + line.strip() + "\n"
Yuto Takanod93fa372021-08-06 23:05:55 +0100515 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100516
Yuto Takano8246eb82021-08-16 10:37:24 +0100517 # Skip parsing if line has a space in front = heuristic to
Yuto Takano81528c02021-08-06 16:22:06 +0100518 # skip function argument lines (highly subject to formatting
519 # changes)
520 if line[0] == " ":
Yuto Takano39639672021-08-05 19:47:48 +0100521 continue
Yuto Takano6f38ab32021-08-05 21:07:14 +0100522
Yuto Takanod93fa372021-08-06 23:05:55 +0100523 identifier = identifier_regex.search(line)
Yuto Takano39639672021-08-05 19:47:48 +0100524
Yuto Takanod70d4462021-08-09 12:45:51 +0100525 if not identifier:
526 continue
527
528 # Find the group that matched, and append it
529 for group in identifier.groups():
530 if not group:
531 continue
532
533 identifiers.append(Match(
534 header_file,
535 line,
536 (line_no, identifier.start(), identifier.end()),
537 group))
Yuto Takano39639672021-08-05 19:47:48 +0100538
539 return identifiers
540
541 def parse_symbols(self):
542 """
543 Compile the Mbed TLS libraries, and parse the TLS, Crypto, and x509
544 object files using nm to retrieve the list of referenced symbols.
Yuto Takano81528c02021-08-06 16:22:06 +0100545 Exceptions thrown here are rethrown because they would be critical
546 errors that void several tests, and thus needs to halt the program. This
547 is explicitly done for clarity.
Yuto Takano39639672021-08-05 19:47:48 +0100548
Yuto Takano81528c02021-08-06 16:22:06 +0100549 Returns a List of unique symbols defined and used in the libraries.
550 """
551 self.log.info("Compiling...")
Yuto Takano39639672021-08-05 19:47:48 +0100552 symbols = []
553
554 # Back up the config and atomically compile with the full configratuion.
Yuto Takanod70d4462021-08-09 12:45:51 +0100555 shutil.copy(
556 "include/mbedtls/mbedtls_config.h",
557 "include/mbedtls/mbedtls_config.h.bak"
558 )
Darryl Greend5802922018-05-08 15:30:59 +0100559 try:
Yuto Takano81528c02021-08-06 16:22:06 +0100560 # Use check=True in all subprocess calls so that failures are raised
561 # as exceptions and logged.
Yuto Takano39639672021-08-05 19:47:48 +0100562 subprocess.run(
Yuto Takano81528c02021-08-06 16:22:06 +0100563 ["python3", "scripts/config.py", "full"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100564 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100565 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100566 )
567 my_environment = os.environ.copy()
568 my_environment["CFLAGS"] = "-fno-asynchronous-unwind-tables"
Yuto Takano39639672021-08-05 19:47:48 +0100569 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100570 ["make", "clean", "lib"],
571 env=my_environment,
Yuto Takanobcc3d992021-08-06 23:14:58 +0100572 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100573 stdout=subprocess.PIPE,
Darryl Greend5802922018-05-08 15:30:59 +0100574 stderr=subprocess.STDOUT,
Yuto Takano39639672021-08-05 19:47:48 +0100575 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100576 )
Yuto Takano39639672021-08-05 19:47:48 +0100577
578 # Perform object file analysis using nm
Yuto Takanod70d4462021-08-09 12:45:51 +0100579 symbols = self.parse_symbols_from_nm([
580 "library/libmbedcrypto.a",
581 "library/libmbedtls.a",
582 "library/libmbedx509.a"
583 ])
Yuto Takano39639672021-08-05 19:47:48 +0100584
585 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100586 ["make", "clean"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100587 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100588 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100589 )
590 except subprocess.CalledProcessError as error:
Yuto Takano25eeb7b2021-08-06 21:27:59 +0100591 self.log.debug(error.output)
Yuto Takano81528c02021-08-06 16:22:06 +0100592 raise error
Yuto Takano39639672021-08-05 19:47:48 +0100593 finally:
Yuto Takano6fececf2021-08-07 17:28:23 +0100594 # Put back the original config regardless of there being errors.
595 # Works also for keyboard interrupts.
Yuto Takanod70d4462021-08-09 12:45:51 +0100596 shutil.move(
597 "include/mbedtls/mbedtls_config.h.bak",
598 "include/mbedtls/mbedtls_config.h"
599 )
Yuto Takano39639672021-08-05 19:47:48 +0100600
601 return symbols
602
603 def parse_symbols_from_nm(self, object_files):
604 """
605 Run nm to retrieve the list of referenced symbols in each object file.
606 Does not return the position data since it is of no use.
607
Yuto Takano81528c02021-08-06 16:22:06 +0100608 Args:
Yuto Takano55c6c872021-08-09 15:35:19 +0100609 * object_files: a List of compiled object filepaths to search through.
Yuto Takano81528c02021-08-06 16:22:06 +0100610
611 Returns a List of unique symbols defined and used in any of the object
612 files.
Yuto Takano39639672021-08-05 19:47:48 +0100613 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100614 nm_undefined_regex = re.compile(r"^\S+: +U |^$|^\S+:$")
615 nm_valid_regex = re.compile(r"^\S+( [0-9A-Fa-f]+)* . _*(?P<symbol>\w+)")
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100616 exclusions = ("FStar", "Hacl")
Yuto Takano39639672021-08-05 19:47:48 +0100617
618 symbols = []
619
Yuto Takano81528c02021-08-06 16:22:06 +0100620 # Gather all outputs of nm
Yuto Takano39639672021-08-05 19:47:48 +0100621 nm_output = ""
622 for lib in object_files:
623 nm_output += subprocess.run(
624 ["nm", "-og", lib],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100625 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100626 stdout=subprocess.PIPE,
627 stderr=subprocess.STDOUT,
628 check=True
629 ).stdout
Yuto Takano81528c02021-08-06 16:22:06 +0100630
Yuto Takano39639672021-08-05 19:47:48 +0100631 for line in nm_output.splitlines():
Yuto Takano90bc0262021-08-16 11:34:10 +0100632 if not nm_undefined_regex.search(line):
633 symbol = nm_valid_regex.search(line)
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100634 if (symbol and not symbol.group("symbol").startswith(exclusions)):
Yuto Takanoe77f6992021-08-05 20:22:59 +0100635 symbols.append(symbol.group("symbol"))
Yuto Takano39639672021-08-05 19:47:48 +0100636 else:
637 self.log.error(line)
Yuto Takano81528c02021-08-06 16:22:06 +0100638
Yuto Takano39639672021-08-05 19:47:48 +0100639 return symbols
640
Yuto Takano55c6c872021-08-09 15:35:19 +0100641class NameChecker():
642 """
643 Representation of the core name checking operation performed by this script.
644 """
645 def __init__(self, parse_result, log):
646 self.parse_result = parse_result
647 self.log = log
648
Yuto Takano55614b52021-08-07 01:00:18 +0100649 def perform_checks(self, quiet=False):
Yuto Takano39639672021-08-05 19:47:48 +0100650 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100651 A comprehensive checker that performs each check in order, and outputs
652 a final verdict.
Yuto Takano81528c02021-08-06 16:22:06 +0100653
654 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100655 * quiet: whether to hide detailed problem explanation.
Yuto Takano39639672021-08-05 19:47:48 +0100656 """
Yuto Takano81528c02021-08-06 16:22:06 +0100657 self.log.info("=============")
Yuto Takano5473be22021-08-17 10:14:01 +0100658 Problem.quiet = quiet
Yuto Takano39639672021-08-05 19:47:48 +0100659 problems = 0
Yuto Takano5473be22021-08-17 10:14:01 +0100660 problems += self.check_symbols_declared_in_header()
Yuto Takano39639672021-08-05 19:47:48 +0100661
Yuto Takanod70d4462021-08-09 12:45:51 +0100662 pattern_checks = [
663 ("macros", MACRO_PATTERN),
664 ("enum_consts", CONSTANTS_PATTERN),
665 ("identifiers", IDENTIFIER_PATTERN)
666 ]
Yuto Takano39639672021-08-05 19:47:48 +0100667 for group, check_pattern in pattern_checks:
Yuto Takano5473be22021-08-17 10:14:01 +0100668 problems += self.check_match_pattern(group, check_pattern)
Yuto Takano39639672021-08-05 19:47:48 +0100669
Yuto Takano5473be22021-08-17 10:14:01 +0100670 problems += self.check_for_typos()
Yuto Takano39639672021-08-05 19:47:48 +0100671
672 self.log.info("=============")
673 if problems > 0:
674 self.log.info("FAIL: {0} problem(s) to fix".format(str(problems)))
Yuto Takano55614b52021-08-07 01:00:18 +0100675 if quiet:
676 self.log.info("Remove --quiet to see explanations.")
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100677 else:
678 self.log.info("Use --quiet for minimal output.")
Yuto Takano55c6c872021-08-09 15:35:19 +0100679 return 1
Yuto Takano39639672021-08-05 19:47:48 +0100680 else:
681 self.log.info("PASS")
Yuto Takano55c6c872021-08-09 15:35:19 +0100682 return 0
Darryl Greend5802922018-05-08 15:30:59 +0100683
Yuto Takano5473be22021-08-17 10:14:01 +0100684 def check_symbols_declared_in_header(self):
Yuto Takano39639672021-08-05 19:47:48 +0100685 """
686 Perform a check that all detected symbols in the library object files
687 are properly declared in headers.
Yuto Takano977e07f2021-08-09 11:56:15 +0100688 Assumes parse_names_in_source() was called before this.
Darryl Greend5802922018-05-08 15:30:59 +0100689
Yuto Takano81528c02021-08-06 16:22:06 +0100690 Returns the number of problems that need fixing.
Yuto Takano39639672021-08-05 19:47:48 +0100691 """
692 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100693
Yuto Takano39639672021-08-05 19:47:48 +0100694 for symbol in self.parse_result["symbols"]:
695 found_symbol_declared = False
696 for identifier_match in self.parse_result["identifiers"]:
697 if symbol == identifier_match.name:
698 found_symbol_declared = True
699 break
Yuto Takano81528c02021-08-06 16:22:06 +0100700
Yuto Takano39639672021-08-05 19:47:48 +0100701 if not found_symbol_declared:
Yuto Takanod70d4462021-08-09 12:45:51 +0100702 problems.append(SymbolNotInHeader(symbol))
Yuto Takano39639672021-08-05 19:47:48 +0100703
Yuto Takano5473be22021-08-17 10:14:01 +0100704 self.output_check_result("All symbols in header", problems)
Yuto Takano39639672021-08-05 19:47:48 +0100705 return len(problems)
706
Yuto Takano5473be22021-08-17 10:14:01 +0100707 def check_match_pattern(self, group_to_check, check_pattern):
Yuto Takano81528c02021-08-06 16:22:06 +0100708 """
709 Perform a check that all items of a group conform to a regex pattern.
Yuto Takano977e07f2021-08-09 11:56:15 +0100710 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100711
712 Args:
Yuto Takano81528c02021-08-06 16:22:06 +0100713 * group_to_check: string key to index into self.parse_result.
714 * check_pattern: the regex to check against.
715
716 Returns the number of problems that need fixing.
717 """
Yuto Takano39639672021-08-05 19:47:48 +0100718 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100719
Yuto Takano39639672021-08-05 19:47:48 +0100720 for item_match in self.parse_result[group_to_check]:
Yuto Takano90bc0262021-08-16 11:34:10 +0100721 if not re.search(check_pattern, item_match.name):
Yuto Takano39639672021-08-05 19:47:48 +0100722 problems.append(PatternMismatch(check_pattern, item_match))
Yuto Takano90bc0262021-08-16 11:34:10 +0100723 # Double underscore should not be used for names
724 if re.search(r".*__.*", item_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100725 problems.append(PatternMismatch("double underscore", item_match))
Yuto Takano81528c02021-08-06 16:22:06 +0100726
727 self.output_check_result(
728 "Naming patterns of {}".format(group_to_check),
Yuto Takano55614b52021-08-07 01:00:18 +0100729 problems)
Yuto Takano39639672021-08-05 19:47:48 +0100730 return len(problems)
Darryl Greend5802922018-05-08 15:30:59 +0100731
Yuto Takano5473be22021-08-17 10:14:01 +0100732 def check_for_typos(self):
Yuto Takano81528c02021-08-06 16:22:06 +0100733 """
734 Perform a check that all words in the soure code beginning with MBED are
735 either defined as macros, or as enum constants.
Yuto Takano977e07f2021-08-09 11:56:15 +0100736 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100737
Yuto Takano81528c02021-08-06 16:22:06 +0100738 Returns the number of problems that need fixing.
739 """
Yuto Takano39639672021-08-05 19:47:48 +0100740 problems = []
Yuto Takano39639672021-08-05 19:47:48 +0100741
Yuto Takanod70d4462021-08-09 12:45:51 +0100742 # Set comprehension, equivalent to a list comprehension wrapped by set()
Yuto Takanod93fa372021-08-06 23:05:55 +0100743 all_caps_names = {
744 match.name
745 for match
746 in self.parse_result["macros"] + self.parse_result["enum_consts"]}
747 typo_exclusion = re.compile(r"XXX|__|_$|^MBEDTLS_.*CONFIG_FILE$")
Yuto Takano39639672021-08-05 19:47:48 +0100748
Yuto Takanod93fa372021-08-06 23:05:55 +0100749 for name_match in self.parse_result["mbed_words"]:
Yuto Takano81528c02021-08-06 16:22:06 +0100750 found = name_match.name in all_caps_names
751
752 # Since MBEDTLS_PSA_ACCEL_XXX defines are defined by the
753 # PSA driver, they will not exist as macros. However, they
754 # should still be checked for typos using the equivalent
755 # BUILTINs that exist.
756 if "MBEDTLS_PSA_ACCEL_" in name_match.name:
757 found = name_match.name.replace(
758 "MBEDTLS_PSA_ACCEL_",
759 "MBEDTLS_PSA_BUILTIN_") in all_caps_names
760
Yuto Takanod93fa372021-08-06 23:05:55 +0100761 if not found and not typo_exclusion.search(name_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100762 problems.append(Typo(name_match))
Yuto Takano39639672021-08-05 19:47:48 +0100763
Yuto Takano5473be22021-08-17 10:14:01 +0100764 self.output_check_result("Likely typos", problems)
Yuto Takano81528c02021-08-06 16:22:06 +0100765 return len(problems)
766
Yuto Takano5473be22021-08-17 10:14:01 +0100767 def output_check_result(self, name, problems):
Yuto Takano81528c02021-08-06 16:22:06 +0100768 """
769 Write out the PASS/FAIL status of a performed check depending on whether
770 there were problems.
Yuto Takanod70d4462021-08-09 12:45:51 +0100771
772 Args:
Yuto Takanod70d4462021-08-09 12:45:51 +0100773 * name: the name of the test
774 * problems: a List of encountered Problems
Yuto Takano81528c02021-08-06 16:22:06 +0100775 """
Yuto Takano39639672021-08-05 19:47:48 +0100776 if problems:
Yuto Takano55614b52021-08-07 01:00:18 +0100777 self.log.info("{}: FAIL\n".format(name))
778 for problem in problems:
779 self.log.warning(str(problem))
Darryl Greend5802922018-05-08 15:30:59 +0100780 else:
Yuto Takano81528c02021-08-06 16:22:06 +0100781 self.log.info("{}: PASS".format(name))
Darryl Greend5802922018-05-08 15:30:59 +0100782
Yuto Takano39639672021-08-05 19:47:48 +0100783def main():
784 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100785 Perform argument parsing, and create an instance of CodeParser and
786 NameChecker to begin the core operation.
Yuto Takano39639672021-08-05 19:47:48 +0100787 """
Yuto Takanof005c332021-08-09 13:56:36 +0100788 parser = argparse.ArgumentParser(
Yuto Takano39639672021-08-05 19:47:48 +0100789 formatter_class=argparse.RawDescriptionHelpFormatter,
790 description=(
791 "This script confirms that the naming of all symbols and identifiers "
792 "in Mbed TLS are consistent with the house style and are also "
793 "self-consistent.\n\n"
Yuto Takanof005c332021-08-09 13:56:36 +0100794 "Expected to be run from the MbedTLS root directory.")
795 )
796 parser.add_argument(
797 "-v", "--verbose",
798 action="store_true",
799 help="show parse results"
800 )
801 parser.add_argument(
802 "-q", "--quiet",
803 action="store_true",
804 help="hide unnecessary text, explanations, and highlighs"
805 )
Darryl Greend5802922018-05-08 15:30:59 +0100806
Yuto Takanof005c332021-08-09 13:56:36 +0100807 args = parser.parse_args()
Darryl Greend5802922018-05-08 15:30:59 +0100808
Yuto Takano55c6c872021-08-09 15:35:19 +0100809 # Configure the global logger, which is then passed to the classes below
810 log = logging.getLogger()
811 log.setLevel(logging.DEBUG if args.verbose else logging.INFO)
812 log.addHandler(logging.StreamHandler())
813
Darryl Greend5802922018-05-08 15:30:59 +0100814 try:
Yuto Takano55c6c872021-08-09 15:35:19 +0100815 code_parser = CodeParser(log)
816 parse_result = code_parser.comprehensive_parse()
Yuto Takanod93fa372021-08-06 23:05:55 +0100817 except Exception: # pylint: disable=broad-except
Darryl Greend5802922018-05-08 15:30:59 +0100818 traceback.print_exc()
819 sys.exit(2)
820
Yuto Takano55c6c872021-08-09 15:35:19 +0100821 name_checker = NameChecker(parse_result, log)
822 return_code = name_checker.perform_checks(quiet=args.quiet)
823
824 sys.exit(return_code)
825
Darryl Greend5802922018-05-08 15:30:59 +0100826if __name__ == "__main__":
Yuto Takano39639672021-08-05 19:47:48 +0100827 main()