blob: 10ed5bba393e158f47f82cf25af7cf6f85faf02c [file] [log] [blame]
Yuto Takano39639672021-08-05 19:47:48 +01001#!/usr/bin/env python3
2#
3# Copyright The Mbed TLS Contributors
4# SPDX-License-Identifier: Apache-2.0
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
Darryl Greend5802922018-05-08 15:30:59 +010018"""
Yuto Takano39639672021-08-05 19:47:48 +010019This script confirms that the naming of all symbols and identifiers in Mbed TLS
Yuto Takano159255a2021-08-06 17:00:28 +010020are consistent with the house style and are also self-consistent. It only runs
21on Linux and macOS since it depends on nm.
22
Yuto Takano55c6c872021-08-09 15:35:19 +010023It contains two major Python classes, CodeParser and NameChecker. They both have
24a comprehensive "run-all" function (comprehensive_parse() and perform_checks())
25but the individual functions can also be used for specific needs.
26
27CodeParser makes heavy use of regular expressions to parse the code, and is
28dependent on the current code formatting. Many Python C parser libraries require
29preprocessed C code, which means no macro parsing. Compiler tools are also not
30very helpful when we want the exact location in the original source (which
31becomes impossible when e.g. comments are stripped).
32
33NameChecker performs the following checks:
Yuto Takano81528c02021-08-06 16:22:06 +010034
35- All exported and available symbols in the library object files, are explicitly
Yuto Takano159255a2021-08-06 17:00:28 +010036 declared in the header files. This uses the nm command.
Yuto Takano81528c02021-08-06 16:22:06 +010037- All macros, constants, and identifiers (function names, struct names, etc)
Yuto Takano55c6c872021-08-09 15:35:19 +010038 follow the required regex pattern.
Yuto Takano81528c02021-08-06 16:22:06 +010039- Typo checking: All words that begin with MBED exist as macros or constants.
Yuto Takanofc54dfb2021-08-07 17:18:28 +010040
Yuto Takano55c6c872021-08-09 15:35:19 +010041The script returns 0 on success, 1 on test failure, and 2 if there is a script
Yuto Takano8246eb82021-08-16 10:37:24 +010042error. It must be run from Mbed TLS root.
Darryl Greend5802922018-05-08 15:30:59 +010043"""
Yuto Takano39639672021-08-05 19:47:48 +010044
45import argparse
Yuto Takano977e07f2021-08-09 11:56:15 +010046import glob
Yuto Takano39639672021-08-05 19:47:48 +010047import textwrap
Darryl Greend5802922018-05-08 15:30:59 +010048import os
49import sys
50import traceback
51import re
52import shutil
53import subprocess
54import logging
55
Yuto Takano81528c02021-08-06 16:22:06 +010056# Naming patterns to check against. These are defined outside the NameCheck
57# class for ease of modification.
Yuto Takanobb7dca42021-08-05 19:57:58 +010058MACRO_PATTERN = r"^(MBEDTLS|PSA)_[0-9A-Z_]*[0-9A-Z]$"
Yuto Takano81528c02021-08-06 16:22:06 +010059CONSTANTS_PATTERN = MACRO_PATTERN
Yuto Takanoc1838932021-08-05 19:52:09 +010060IDENTIFIER_PATTERN = r"^(mbedtls|psa)_[0-9a-z_]*[0-9a-z]$"
Yuto Takano39639672021-08-05 19:47:48 +010061
Yuto Takanod93fa372021-08-06 23:05:55 +010062class Match(): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010063 """
64 A class representing a match, together with its found position.
65
66 Fields:
67 * filename: the file that the match was in.
68 * line: the full line containing the match.
Yuto Takanod93fa372021-08-06 23:05:55 +010069 * pos: a tuple of (line_no, start, end) positions on the file line where the
70 match is.
Yuto Takano81528c02021-08-06 16:22:06 +010071 * name: the match itself.
72 """
Yuto Takanod93fa372021-08-06 23:05:55 +010073 def __init__(self, filename, line, pos, name):
Yuto Takano39639672021-08-05 19:47:48 +010074 self.filename = filename
75 self.line = line
76 self.pos = pos
77 self.name = name
Yuto Takano39639672021-08-05 19:47:48 +010078
Yuto Takanoa4e75122021-08-06 17:23:28 +010079 def __str__(self):
Yuto Takanofb86ac72021-08-16 10:32:40 +010080 """
81 Return a formatted code listing representation of the erroneous line.
82 """
83 gutter = format(self.pos[0], "4d")
Yuto Takano381fda82021-08-06 23:37:20 +010084 underline = self.pos[1] * " " + (self.pos[2] - self.pos[1]) * "^"
85
Yuto Takanoa4e75122021-08-06 17:23:28 +010086 return (
Yuto Takanofb86ac72021-08-16 10:32:40 +010087 " {0} |\n".format(" " * len(gutter)) +
Yuto Takano381fda82021-08-06 23:37:20 +010088 " {0} | {1}".format(gutter, self.line) +
Yuto Takanofb86ac72021-08-16 10:32:40 +010089 " {0} | {1}\n".format(" " * len(gutter), underline)
Yuto Takanoa4e75122021-08-06 17:23:28 +010090 )
Yuto Takanod93fa372021-08-06 23:05:55 +010091
92class Problem(): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010093 """
94 A parent class representing a form of static analysis error.
Yuto Takano81528c02021-08-06 16:22:06 +010095 """
Yuto Takano39639672021-08-05 19:47:48 +010096 def __init__(self):
Yuto Takanod70d4462021-08-09 12:45:51 +010097 self.quiet = False
Yuto Takano39639672021-08-05 19:47:48 +010098 self.textwrapper = textwrap.TextWrapper()
Yuto Takano81528c02021-08-06 16:22:06 +010099 self.textwrapper.width = 80
Yuto Takanoa4e75122021-08-06 17:23:28 +0100100 self.textwrapper.initial_indent = " > "
Yuto Takano81528c02021-08-06 16:22:06 +0100101 self.textwrapper.subsequent_indent = " "
Yuto Takano39639672021-08-05 19:47:48 +0100102
Yuto Takanod93fa372021-08-06 23:05:55 +0100103class SymbolNotInHeader(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100104 """
105 A problem that occurs when an exported/available symbol in the object file
106 is not explicitly declared in header files. Created with
107 NameCheck.check_symbols_declared_in_header()
108
109 Fields:
110 * symbol_name: the name of the symbol.
111 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100112 def __init__(self, symbol_name):
Yuto Takano39639672021-08-05 19:47:48 +0100113 self.symbol_name = symbol_name
114 Problem.__init__(self)
115
116 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100117 if self.quiet:
118 return "{0}".format(self.symbol_name)
119
Yuto Takano39639672021-08-05 19:47:48 +0100120 return self.textwrapper.fill(
121 "'{0}' was found as an available symbol in the output of nm, "
122 "however it was not declared in any header files."
123 .format(self.symbol_name))
124
Yuto Takanod93fa372021-08-06 23:05:55 +0100125class PatternMismatch(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100126 """
127 A problem that occurs when something doesn't match the expected pattern.
128 Created with NameCheck.check_match_pattern()
129
130 Fields:
131 * pattern: the expected regex pattern
132 * match: the Match object in question
133 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100134 def __init__(self, pattern, match):
Yuto Takano39639672021-08-05 19:47:48 +0100135 self.pattern = pattern
136 self.match = match
137 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100138
Yuto Takano39639672021-08-05 19:47:48 +0100139 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100140 if self.quiet:
Yuto Takanod70d4462021-08-09 12:45:51 +0100141 return (
Yuto Takano206b0222021-08-10 11:30:43 +0100142 "{0}:{1}:{2}"
Yuto Takanod70d4462021-08-09 12:45:51 +0100143 .format(self.match.filename, self.match.pos[0], self.match.name)
144 )
Yuto Takano55614b52021-08-07 01:00:18 +0100145
Yuto Takano39639672021-08-05 19:47:48 +0100146 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100147 "{0}:{1}: '{2}' does not match the required pattern '{3}'."
148 .format(
149 self.match.filename,
Yuto Takanod93fa372021-08-06 23:05:55 +0100150 self.match.pos[0],
Yuto Takanoa4e75122021-08-06 17:23:28 +0100151 self.match.name,
Yuto Takanod70d4462021-08-09 12:45:51 +0100152 self.pattern
153 )
154 ) + "\n" + str(self.match)
Yuto Takano39639672021-08-05 19:47:48 +0100155
Yuto Takanod93fa372021-08-06 23:05:55 +0100156class Typo(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100157 """
158 A problem that occurs when a word using MBED doesn't appear to be defined as
159 constants nor enum values. Created with NameCheck.check_for_typos()
160
161 Fields:
162 * match: the Match object of the MBED name in question.
163 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100164 def __init__(self, match):
Yuto Takano39639672021-08-05 19:47:48 +0100165 self.match = match
166 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100167
Yuto Takano39639672021-08-05 19:47:48 +0100168 def __str__(self):
Yuto Takano55614b52021-08-07 01:00:18 +0100169 if self.quiet:
Yuto Takanod70d4462021-08-09 12:45:51 +0100170 return (
171 "{0}:{1}:{2}"
172 .format(self.match.filename, self.match.pos[0], self.match.name)
173 )
Yuto Takano55614b52021-08-07 01:00:18 +0100174
Yuto Takano39639672021-08-05 19:47:48 +0100175 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100176 "{0}:{1}: '{2}' looks like a typo. It was not found in any "
177 "macros or any enums. If this is not a typo, put "
178 "//no-check-names after it."
Yuto Takanod70d4462021-08-09 12:45:51 +0100179 .format(self.match.filename, self.match.pos[0], self.match.name)
180 ) + "\n" + str(self.match)
Darryl Greend5802922018-05-08 15:30:59 +0100181
Yuto Takano55c6c872021-08-09 15:35:19 +0100182class CodeParser():
Yuto Takano81528c02021-08-06 16:22:06 +0100183 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100184 Class for retrieving files and parsing the code. This can be used
185 independently of the checks that NameChecker performs, for example for
186 list_internal_identifiers.py.
Yuto Takano81528c02021-08-06 16:22:06 +0100187 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100188 def __init__(self, log):
189 self.log = log
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100190 self.check_repo_path()
Yuto Takano977e07f2021-08-09 11:56:15 +0100191
Yuto Takano8e9a2192021-08-09 14:48:53 +0100192 # Memo for storing "glob expression": set(filepaths)
193 self.files = {}
194
Yuto Takano977e07f2021-08-09 11:56:15 +0100195 # Globally excluded filenames
Yuto Takano8e9a2192021-08-09 14:48:53 +0100196 self.excluded_files = ["**/bn_mul", "**/compat-2.x.h"]
Yuto Takano977e07f2021-08-09 11:56:15 +0100197
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100198 @staticmethod
199 def check_repo_path():
200 """
201 Check that the current working directory is the project root, and throw
202 an exception if not.
203 """
204 if not all(os.path.isdir(d) for d in ["include", "library", "tests"]):
205 raise Exception("This script must be run from Mbed TLS root")
206
Yuto Takano55c6c872021-08-09 15:35:19 +0100207 def comprehensive_parse(self):
Yuto Takano39639672021-08-05 19:47:48 +0100208 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100209 Comprehensive ("default") function to call each parsing function and
210 retrieve various elements of the code, together with the source location.
Darryl Greend5802922018-05-08 15:30:59 +0100211
Yuto Takano55c6c872021-08-09 15:35:19 +0100212 Returns a dict of parsed item key to the corresponding List of Matches.
Yuto Takano81528c02021-08-06 16:22:06 +0100213 """
214 self.log.info("Parsing source code...")
Yuto Takanod24e0372021-08-06 16:42:33 +0100215 self.log.debug(
Yuto Takano50953432021-08-09 14:54:36 +0100216 "The following files are excluded from the search: {}"
Yuto Takanod24e0372021-08-06 16:42:33 +0100217 .format(str(self.excluded_files))
218 )
Yuto Takano81528c02021-08-06 16:22:06 +0100219
Yuto Takano8e9a2192021-08-09 14:48:53 +0100220 all_macros = self.parse_macros([
221 "include/mbedtls/*.h",
222 "include/psa/*.h",
223 "library/*.h",
224 "tests/include/test/drivers/*.h",
Yuto Takanod70d4462021-08-09 12:45:51 +0100225 "3rdparty/everest/include/everest/everest.h",
226 "3rdparty/everest/include/everest/x25519.h"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100227 ])
228 enum_consts = self.parse_enum_consts([
229 "include/mbedtls/*.h",
230 "library/*.h",
231 "3rdparty/everest/include/everest/everest.h",
232 "3rdparty/everest/include/everest/x25519.h"
233 ])
234 identifiers = self.parse_identifiers([
235 "include/mbedtls/*.h",
236 "include/psa/*.h",
237 "library/*.h",
238 "3rdparty/everest/include/everest/everest.h",
239 "3rdparty/everest/include/everest/x25519.h"
240 ])
241 mbed_words = self.parse_mbed_words([
242 "include/mbedtls/*.h",
243 "include/psa/*.h",
244 "library/*.h",
245 "3rdparty/everest/include/everest/everest.h",
246 "3rdparty/everest/include/everest/x25519.h",
247 "library/*.c",
Yuto Takano81528c02021-08-06 16:22:06 +0100248 "3rdparty/everest/library/everest.c",
Yuto Takanod70d4462021-08-09 12:45:51 +0100249 "3rdparty/everest/library/x25519.c"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100250 ])
Yuto Takano81528c02021-08-06 16:22:06 +0100251 symbols = self.parse_symbols()
252
253 # Remove identifier macros like mbedtls_printf or mbedtls_calloc
254 identifiers_justname = [x.name for x in identifiers]
255 actual_macros = []
256 for macro in all_macros:
257 if macro.name not in identifiers_justname:
258 actual_macros.append(macro)
259
260 self.log.debug("Found:")
Yuto Takano9d9c6dc2021-08-16 10:43:45 +0100261 # Aligns the counts on the assumption that none exceeds 4 digits
262 self.log.debug(" {:4} Total Macros".format(len(all_macros)))
263 self.log.debug(" {:4} Non-identifier Macros".format(len(actual_macros)))
264 self.log.debug(" {:4} Enum Constants".format(len(enum_consts)))
265 self.log.debug(" {:4} Identifiers".format(len(identifiers)))
266 self.log.debug(" {:4} Exported Symbols".format(len(symbols)))
Yuto Takano55c6c872021-08-09 15:35:19 +0100267 return {
Yuto Takano81528c02021-08-06 16:22:06 +0100268 "macros": actual_macros,
269 "enum_consts": enum_consts,
270 "identifiers": identifiers,
271 "symbols": symbols,
Yuto Takanod93fa372021-08-06 23:05:55 +0100272 "mbed_words": mbed_words
Yuto Takano81528c02021-08-06 16:22:06 +0100273 }
274
Yuto Takano55c6c872021-08-09 15:35:19 +0100275 def get_files(self, include_wildcards, exclude_wildcards):
276 """
277 Get all files that match any of the UNIX-style wildcards. While the
278 check_names script is designed only for use on UNIX/macOS (due to nm),
279 this function alone would work fine on Windows even with forward slashes
280 in the wildcard.
281
282 Args:
283 * include_wildcards: a List of shell-style wildcards to match filepaths.
284 * exclude_wildcards: a List of shell-style wildcards to exclude.
285
286 Returns a List of relative filepaths.
287 """
288 accumulator = set()
289
290 # exclude_wildcards may be None. Also, consider the global exclusions.
291 exclude_wildcards = (exclude_wildcards or []) + self.excluded_files
292
Yuto Takano6adb2872021-08-16 11:38:34 +0100293 # Internal function to hit the memoisation cache or add to it the result
294 # of a glob operation. Used both for inclusion and exclusion since the
295 # only difference between them is whether they perform set union or
296 # difference on the return value of this function.
297 def hit_cache(wildcard):
298 if wildcard not in self.files:
299 self.files[wildcard] = set(glob.glob(wildcard, recursive=True))
300 return self.files[wildcard]
301
Yuto Takano55c6c872021-08-09 15:35:19 +0100302 for include_wildcard in include_wildcards:
Yuto Takano6adb2872021-08-16 11:38:34 +0100303 accumulator = accumulator.union(hit_cache(include_wildcard))
Yuto Takano55c6c872021-08-09 15:35:19 +0100304
Yuto Takano55c6c872021-08-09 15:35:19 +0100305 for exclude_wildcard in exclude_wildcards:
Yuto Takano6adb2872021-08-16 11:38:34 +0100306 accumulator = accumulator.difference(hit_cache(exclude_wildcard))
Yuto Takano55c6c872021-08-09 15:35:19 +0100307
308 return list(accumulator)
309
Yuto Takano8e9a2192021-08-09 14:48:53 +0100310 def parse_macros(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100311 """
312 Parse all macros defined by #define preprocessor directives.
313
314 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100315 * include: A List of glob expressions to look for files through.
316 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100317
318 Returns a List of Match objects for the found macros.
Yuto Takano39639672021-08-05 19:47:48 +0100319 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100320 macro_regex = re.compile(r"# *define +(?P<macro>\w+)")
321 exclusions = (
Yuto Takano39639672021-08-05 19:47:48 +0100322 "asm", "inline", "EMIT", "_CRT_SECURE_NO_DEPRECATE", "MULADDC_"
323 )
324
Yuto Takano50953432021-08-09 14:54:36 +0100325 files = self.get_files(include, exclude)
326 self.log.debug("Looking for macros in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100327
Yuto Takano50953432021-08-09 14:54:36 +0100328 macros = []
329 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100330 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100331 for line_no, line in enumerate(header):
Yuto Takanod93fa372021-08-06 23:05:55 +0100332 for macro in macro_regex.finditer(line):
Yuto Takanod70d4462021-08-09 12:45:51 +0100333 if macro.group("macro").startswith(exclusions):
334 continue
335
336 macros.append(Match(
337 header_file,
338 line,
339 (line_no, macro.start(), macro.end()),
340 macro.group("macro")))
Darryl Greend5802922018-05-08 15:30:59 +0100341
Yuto Takano39639672021-08-05 19:47:48 +0100342 return macros
Darryl Greend5802922018-05-08 15:30:59 +0100343
Yuto Takano8e9a2192021-08-09 14:48:53 +0100344 def parse_mbed_words(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100345 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100346 Parse all words in the file that begin with MBED, in and out of macros,
347 comments, anything.
Yuto Takano39639672021-08-05 19:47:48 +0100348
349 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100350 * include: A List of glob expressions to look for files through.
351 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100352
353 Returns a List of Match objects for words beginning with MBED.
Yuto Takano39639672021-08-05 19:47:48 +0100354 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100355 # Typos of TLS are common, hence the broader check below than MBEDTLS.
Yuto Takanod93fa372021-08-06 23:05:55 +0100356 mbed_regex = re.compile(r"\bMBED.+?_[A-Z0-9_]*")
357 exclusions = re.compile(r"// *no-check-names|#error")
358
Yuto Takano50953432021-08-09 14:54:36 +0100359 files = self.get_files(include, exclude)
360 self.log.debug("Looking for MBED words in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100361
Yuto Takano50953432021-08-09 14:54:36 +0100362 mbed_words = []
363 for filename in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100364 with open(filename, "r", encoding="utf-8") as fp:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100365 for line_no, line in enumerate(fp):
Yuto Takanod93fa372021-08-06 23:05:55 +0100366 if exclusions.search(line):
Yuto Takanoc62b4082021-08-05 20:17:07 +0100367 continue
Yuto Takano81528c02021-08-06 16:22:06 +0100368
Yuto Takanod93fa372021-08-06 23:05:55 +0100369 for name in mbed_regex.finditer(line):
370 mbed_words.append(Match(
Yuto Takano39639672021-08-05 19:47:48 +0100371 filename,
372 line,
Yuto Takanod93fa372021-08-06 23:05:55 +0100373 (line_no, name.start(), name.end()),
Yuto Takano39639672021-08-05 19:47:48 +0100374 name.group(0)
375 ))
376
Yuto Takanod93fa372021-08-06 23:05:55 +0100377 return mbed_words
Yuto Takano39639672021-08-05 19:47:48 +0100378
Yuto Takano8e9a2192021-08-09 14:48:53 +0100379 def parse_enum_consts(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100380 """
381 Parse all enum value constants that are declared.
382
383 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100384 * include: A List of glob expressions to look for files through.
385 * exclude: A List of glob expressions for excluding files.
Yuto Takano39639672021-08-05 19:47:48 +0100386
Yuto Takano81528c02021-08-06 16:22:06 +0100387 Returns a List of Match objects for the findings.
Yuto Takano39639672021-08-05 19:47:48 +0100388 """
Yuto Takano50953432021-08-09 14:54:36 +0100389 files = self.get_files(include, exclude)
390 self.log.debug("Looking for enum consts in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100391
Yuto Takano50953432021-08-09 14:54:36 +0100392 enum_consts = []
393 for header_file in files:
Yuto Takano39639672021-08-05 19:47:48 +0100394 # Emulate a finite state machine to parse enum declarations.
Yuto Takano81528c02021-08-06 16:22:06 +0100395 # 0 = not in enum
396 # 1 = inside enum
397 # 2 = almost inside enum
Darryl Greend5802922018-05-08 15:30:59 +0100398 state = 0
Yuto Takanoa083d152021-08-07 00:25:59 +0100399 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100400 for line_no, line in enumerate(header):
Yuto Takano13ecd992021-08-06 16:56:52 +0100401 # Match typedefs and brackets only when they are at the
402 # beginning of the line -- if they are indented, they might
403 # be sub-structures within structs, etc.
Yuto Takano90bc0262021-08-16 11:34:10 +0100404 if state == 0 and re.search(r"^(typedef +)?enum +{", line):
Darryl Greend5802922018-05-08 15:30:59 +0100405 state = 1
Yuto Takano90bc0262021-08-16 11:34:10 +0100406 elif state == 0 and re.search(r"^(typedef +)?enum", line):
Darryl Greend5802922018-05-08 15:30:59 +0100407 state = 2
Yuto Takano90bc0262021-08-16 11:34:10 +0100408 elif state == 2 and re.search(r"^{", line):
Darryl Greend5802922018-05-08 15:30:59 +0100409 state = 1
Yuto Takano90bc0262021-08-16 11:34:10 +0100410 elif state == 1 and re.search(r"^}", line):
Darryl Greend5802922018-05-08 15:30:59 +0100411 state = 0
Yuto Takano90bc0262021-08-16 11:34:10 +0100412 elif state == 1 and not re.search(r"^ *#", line):
413 enum_const = re.search(r"^ *(?P<enum_const>\w+)", line)
Yuto Takanod70d4462021-08-09 12:45:51 +0100414 if not enum_const:
415 continue
416
417 enum_consts.append(Match(
418 header_file,
419 line,
420 (line_no, enum_const.start(), enum_const.end()),
421 enum_const.group("enum_const")))
Yuto Takano81528c02021-08-06 16:22:06 +0100422
Yuto Takano39639672021-08-05 19:47:48 +0100423 return enum_consts
Darryl Greend5802922018-05-08 15:30:59 +0100424
Yuto Takano8e9a2192021-08-09 14:48:53 +0100425 def parse_identifiers(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100426 """
Yuto Takano8246eb82021-08-16 10:37:24 +0100427 Parse all lines of a header where a function/enum/struct/union/typedef
428 identifier is declared, based on some heuristics. Highly dependent on
429 formatting style.
Darryl Greend5802922018-05-08 15:30:59 +0100430
Yuto Takano39639672021-08-05 19:47:48 +0100431 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100432 * include: A List of glob expressions to look for files through.
433 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100434
435 Returns a List of Match objects with identifiers.
Yuto Takano39639672021-08-05 19:47:48 +0100436 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100437 identifier_regex = re.compile(
438 # Match " something(a" or " *something(a". Functions.
439 # Assumptions:
440 # - function definition from return type to one of its arguments is
Yuto Takano55c6c872021-08-09 15:35:19 +0100441 # all on one line
Yuto Takanod93fa372021-08-06 23:05:55 +0100442 # - function definition line only contains alphanumeric, asterisk,
443 # underscore, and open bracket
444 r".* \**(\w+) *\( *\w|"
Yuto Takano55c6c872021-08-09 15:35:19 +0100445 # Match "(*something)(".
Yuto Takanod93fa372021-08-06 23:05:55 +0100446 r".*\( *\* *(\w+) *\) *\(|"
447 # Match names of named data structures.
448 r"(?:typedef +)?(?:struct|union|enum) +(\w+)(?: *{)?$|"
449 # Match names of typedef instances, after closing bracket.
Yuto Takanod70d4462021-08-09 12:45:51 +0100450 r"}? *(\w+)[;[].*"
451 )
452 exclusion_lines = re.compile(
453 r"^("
Yuto Takano90bc0262021-08-16 11:34:10 +0100454 r"extern +\"C\"|"
455 r"(typedef +)?(struct|union|enum)( *{)?$|"
456 r"} *;?$|"
457 r"$|"
458 r"//|"
459 r"#"
Yuto Takanod70d4462021-08-09 12:45:51 +0100460 r")"
461 )
Yuto Takanod93fa372021-08-06 23:05:55 +0100462
Yuto Takano50953432021-08-09 14:54:36 +0100463 files = self.get_files(include, exclude)
464 self.log.debug("Looking for identifiers in {} files".format(len(files)))
465
466 identifiers = []
467 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100468 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano39639672021-08-05 19:47:48 +0100469 in_block_comment = False
Yuto Takano55c6c872021-08-09 15:35:19 +0100470 # The previous line variable is used for concatenating lines
Yuto Takanod70d4462021-08-09 12:45:51 +0100471 # when identifiers are formatted and spread across multiple.
Yuto Takanod93fa372021-08-06 23:05:55 +0100472 previous_line = ""
Darryl Greend5802922018-05-08 15:30:59 +0100473
Yuto Takano8f457cf2021-08-06 17:54:58 +0100474 for line_no, line in enumerate(header):
Yuto Takano81528c02021-08-06 16:22:06 +0100475 # Skip parsing this line if a block comment ends on it,
476 # but don't skip if it has just started -- there is a chance
477 # it ends on the same line.
Yuto Takano39639672021-08-05 19:47:48 +0100478 if re.search(r"/\*", line):
Yuto Takano81528c02021-08-06 16:22:06 +0100479 in_block_comment = not in_block_comment
480 if re.search(r"\*/", line):
481 in_block_comment = not in_block_comment
Yuto Takano39639672021-08-05 19:47:48 +0100482 continue
483
Yuto Takano81528c02021-08-06 16:22:06 +0100484 if in_block_comment:
Yuto Takanod93fa372021-08-06 23:05:55 +0100485 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100486 continue
487
Yuto Takano90bc0262021-08-16 11:34:10 +0100488 if exclusion_lines.search(line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100489 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100490 continue
491
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100492 # If the line contains only space-separated alphanumeric
493 # characters (or underscore, asterisk, or, open bracket),
494 # and nothing else, high chance it's a declaration that
495 # continues on the next line
Yuto Takano90bc0262021-08-16 11:34:10 +0100496 if re.search(r"^([\w\*\(]+\s+)+$", line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100497 previous_line += line
Yuto Takano81528c02021-08-06 16:22:06 +0100498 continue
499
500 # If previous line seemed to start an unfinished declaration
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100501 # (as above), concat and treat them as one.
502 if previous_line:
Yuto Takano90bc0262021-08-16 11:34:10 +0100503 line = previous_line.strip() + " " + line.strip() + "\n"
Yuto Takanod93fa372021-08-06 23:05:55 +0100504 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100505
Yuto Takano8246eb82021-08-16 10:37:24 +0100506 # Skip parsing if line has a space in front = heuristic to
Yuto Takano81528c02021-08-06 16:22:06 +0100507 # skip function argument lines (highly subject to formatting
508 # changes)
509 if line[0] == " ":
Yuto Takano39639672021-08-05 19:47:48 +0100510 continue
Yuto Takano6f38ab32021-08-05 21:07:14 +0100511
Yuto Takanod93fa372021-08-06 23:05:55 +0100512 identifier = identifier_regex.search(line)
Yuto Takano39639672021-08-05 19:47:48 +0100513
Yuto Takanod70d4462021-08-09 12:45:51 +0100514 if not identifier:
515 continue
516
517 # Find the group that matched, and append it
518 for group in identifier.groups():
519 if not group:
520 continue
521
522 identifiers.append(Match(
523 header_file,
524 line,
525 (line_no, identifier.start(), identifier.end()),
526 group))
Yuto Takano39639672021-08-05 19:47:48 +0100527
528 return identifiers
529
530 def parse_symbols(self):
531 """
532 Compile the Mbed TLS libraries, and parse the TLS, Crypto, and x509
533 object files using nm to retrieve the list of referenced symbols.
Yuto Takano81528c02021-08-06 16:22:06 +0100534 Exceptions thrown here are rethrown because they would be critical
535 errors that void several tests, and thus needs to halt the program. This
536 is explicitly done for clarity.
Yuto Takano39639672021-08-05 19:47:48 +0100537
Yuto Takano81528c02021-08-06 16:22:06 +0100538 Returns a List of unique symbols defined and used in the libraries.
539 """
540 self.log.info("Compiling...")
Yuto Takano39639672021-08-05 19:47:48 +0100541 symbols = []
542
543 # Back up the config and atomically compile with the full configratuion.
Yuto Takanod70d4462021-08-09 12:45:51 +0100544 shutil.copy(
545 "include/mbedtls/mbedtls_config.h",
546 "include/mbedtls/mbedtls_config.h.bak"
547 )
Darryl Greend5802922018-05-08 15:30:59 +0100548 try:
Yuto Takano81528c02021-08-06 16:22:06 +0100549 # Use check=True in all subprocess calls so that failures are raised
550 # as exceptions and logged.
Yuto Takano39639672021-08-05 19:47:48 +0100551 subprocess.run(
Yuto Takano81528c02021-08-06 16:22:06 +0100552 ["python3", "scripts/config.py", "full"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100553 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100554 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100555 )
556 my_environment = os.environ.copy()
557 my_environment["CFLAGS"] = "-fno-asynchronous-unwind-tables"
Yuto Takano39639672021-08-05 19:47:48 +0100558 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100559 ["make", "clean", "lib"],
560 env=my_environment,
Yuto Takanobcc3d992021-08-06 23:14:58 +0100561 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100562 stdout=subprocess.PIPE,
Darryl Greend5802922018-05-08 15:30:59 +0100563 stderr=subprocess.STDOUT,
Yuto Takano39639672021-08-05 19:47:48 +0100564 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100565 )
Yuto Takano39639672021-08-05 19:47:48 +0100566
567 # Perform object file analysis using nm
Yuto Takanod70d4462021-08-09 12:45:51 +0100568 symbols = self.parse_symbols_from_nm([
569 "library/libmbedcrypto.a",
570 "library/libmbedtls.a",
571 "library/libmbedx509.a"
572 ])
Yuto Takano39639672021-08-05 19:47:48 +0100573
574 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100575 ["make", "clean"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100576 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100577 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100578 )
579 except subprocess.CalledProcessError as error:
Yuto Takano25eeb7b2021-08-06 21:27:59 +0100580 self.log.debug(error.output)
Yuto Takano81528c02021-08-06 16:22:06 +0100581 raise error
Yuto Takano39639672021-08-05 19:47:48 +0100582 finally:
Yuto Takano6fececf2021-08-07 17:28:23 +0100583 # Put back the original config regardless of there being errors.
584 # Works also for keyboard interrupts.
Yuto Takanod70d4462021-08-09 12:45:51 +0100585 shutil.move(
586 "include/mbedtls/mbedtls_config.h.bak",
587 "include/mbedtls/mbedtls_config.h"
588 )
Yuto Takano39639672021-08-05 19:47:48 +0100589
590 return symbols
591
592 def parse_symbols_from_nm(self, object_files):
593 """
594 Run nm to retrieve the list of referenced symbols in each object file.
595 Does not return the position data since it is of no use.
596
Yuto Takano81528c02021-08-06 16:22:06 +0100597 Args:
Yuto Takano55c6c872021-08-09 15:35:19 +0100598 * object_files: a List of compiled object filepaths to search through.
Yuto Takano81528c02021-08-06 16:22:06 +0100599
600 Returns a List of unique symbols defined and used in any of the object
601 files.
Yuto Takano39639672021-08-05 19:47:48 +0100602 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100603 nm_undefined_regex = re.compile(r"^\S+: +U |^$|^\S+:$")
604 nm_valid_regex = re.compile(r"^\S+( [0-9A-Fa-f]+)* . _*(?P<symbol>\w+)")
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100605 exclusions = ("FStar", "Hacl")
Yuto Takano39639672021-08-05 19:47:48 +0100606
607 symbols = []
608
Yuto Takano81528c02021-08-06 16:22:06 +0100609 # Gather all outputs of nm
Yuto Takano39639672021-08-05 19:47:48 +0100610 nm_output = ""
611 for lib in object_files:
612 nm_output += subprocess.run(
613 ["nm", "-og", lib],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100614 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100615 stdout=subprocess.PIPE,
616 stderr=subprocess.STDOUT,
617 check=True
618 ).stdout
Yuto Takano81528c02021-08-06 16:22:06 +0100619
Yuto Takano39639672021-08-05 19:47:48 +0100620 for line in nm_output.splitlines():
Yuto Takano90bc0262021-08-16 11:34:10 +0100621 if not nm_undefined_regex.search(line):
622 symbol = nm_valid_regex.search(line)
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100623 if (symbol and not symbol.group("symbol").startswith(exclusions)):
Yuto Takanoe77f6992021-08-05 20:22:59 +0100624 symbols.append(symbol.group("symbol"))
Yuto Takano39639672021-08-05 19:47:48 +0100625 else:
626 self.log.error(line)
Yuto Takano81528c02021-08-06 16:22:06 +0100627
Yuto Takano39639672021-08-05 19:47:48 +0100628 return symbols
629
Yuto Takano55c6c872021-08-09 15:35:19 +0100630class NameChecker():
631 """
632 Representation of the core name checking operation performed by this script.
633 """
634 def __init__(self, parse_result, log):
635 self.parse_result = parse_result
636 self.log = log
637
Yuto Takano55614b52021-08-07 01:00:18 +0100638 def perform_checks(self, quiet=False):
Yuto Takano39639672021-08-05 19:47:48 +0100639 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100640 A comprehensive checker that performs each check in order, and outputs
641 a final verdict.
Yuto Takano81528c02021-08-06 16:22:06 +0100642
643 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100644 * quiet: whether to hide detailed problem explanation.
Yuto Takano39639672021-08-05 19:47:48 +0100645 """
Yuto Takano81528c02021-08-06 16:22:06 +0100646 self.log.info("=============")
Yuto Takano39639672021-08-05 19:47:48 +0100647 problems = 0
Yuto Takano55614b52021-08-07 01:00:18 +0100648 problems += self.check_symbols_declared_in_header(quiet)
Yuto Takano39639672021-08-05 19:47:48 +0100649
Yuto Takanod70d4462021-08-09 12:45:51 +0100650 pattern_checks = [
651 ("macros", MACRO_PATTERN),
652 ("enum_consts", CONSTANTS_PATTERN),
653 ("identifiers", IDENTIFIER_PATTERN)
654 ]
Yuto Takano39639672021-08-05 19:47:48 +0100655 for group, check_pattern in pattern_checks:
Yuto Takano55614b52021-08-07 01:00:18 +0100656 problems += self.check_match_pattern(quiet, group, check_pattern)
Yuto Takano39639672021-08-05 19:47:48 +0100657
Yuto Takano55614b52021-08-07 01:00:18 +0100658 problems += self.check_for_typos(quiet)
Yuto Takano39639672021-08-05 19:47:48 +0100659
660 self.log.info("=============")
661 if problems > 0:
662 self.log.info("FAIL: {0} problem(s) to fix".format(str(problems)))
Yuto Takano55614b52021-08-07 01:00:18 +0100663 if quiet:
664 self.log.info("Remove --quiet to see explanations.")
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100665 else:
666 self.log.info("Use --quiet for minimal output.")
Yuto Takano55c6c872021-08-09 15:35:19 +0100667 return 1
Yuto Takano39639672021-08-05 19:47:48 +0100668 else:
669 self.log.info("PASS")
Yuto Takano55c6c872021-08-09 15:35:19 +0100670 return 0
Darryl Greend5802922018-05-08 15:30:59 +0100671
Yuto Takano55614b52021-08-07 01:00:18 +0100672 def check_symbols_declared_in_header(self, quiet):
Yuto Takano39639672021-08-05 19:47:48 +0100673 """
674 Perform a check that all detected symbols in the library object files
675 are properly declared in headers.
Yuto Takano977e07f2021-08-09 11:56:15 +0100676 Assumes parse_names_in_source() was called before this.
Darryl Greend5802922018-05-08 15:30:59 +0100677
Yuto Takano81528c02021-08-06 16:22:06 +0100678 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100679 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100680
681 Returns the number of problems that need fixing.
Yuto Takano39639672021-08-05 19:47:48 +0100682 """
683 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100684
Yuto Takano39639672021-08-05 19:47:48 +0100685 for symbol in self.parse_result["symbols"]:
686 found_symbol_declared = False
687 for identifier_match in self.parse_result["identifiers"]:
688 if symbol == identifier_match.name:
689 found_symbol_declared = True
690 break
Yuto Takano81528c02021-08-06 16:22:06 +0100691
Yuto Takano39639672021-08-05 19:47:48 +0100692 if not found_symbol_declared:
Yuto Takanod70d4462021-08-09 12:45:51 +0100693 problems.append(SymbolNotInHeader(symbol))
Yuto Takano39639672021-08-05 19:47:48 +0100694
Yuto Takanod70d4462021-08-09 12:45:51 +0100695 self.output_check_result(quiet, "All symbols in header", problems)
Yuto Takano39639672021-08-05 19:47:48 +0100696 return len(problems)
697
Yuto Takano55614b52021-08-07 01:00:18 +0100698 def check_match_pattern(self, quiet, group_to_check, check_pattern):
Yuto Takano81528c02021-08-06 16:22:06 +0100699 """
700 Perform a check that all items of a group conform to a regex pattern.
Yuto Takano977e07f2021-08-09 11:56:15 +0100701 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100702
703 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100704 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100705 * group_to_check: string key to index into self.parse_result.
706 * check_pattern: the regex to check against.
707
708 Returns the number of problems that need fixing.
709 """
Yuto Takano39639672021-08-05 19:47:48 +0100710 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100711
Yuto Takano39639672021-08-05 19:47:48 +0100712 for item_match in self.parse_result[group_to_check]:
Yuto Takano90bc0262021-08-16 11:34:10 +0100713 if not re.search(check_pattern, item_match.name):
Yuto Takano39639672021-08-05 19:47:48 +0100714 problems.append(PatternMismatch(check_pattern, item_match))
Yuto Takano90bc0262021-08-16 11:34:10 +0100715 # Double underscore should not be used for names
716 if re.search(r".*__.*", item_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100717 problems.append(PatternMismatch("double underscore", item_match))
Yuto Takano81528c02021-08-06 16:22:06 +0100718
719 self.output_check_result(
Yuto Takanod70d4462021-08-09 12:45:51 +0100720 quiet,
Yuto Takano81528c02021-08-06 16:22:06 +0100721 "Naming patterns of {}".format(group_to_check),
Yuto Takano55614b52021-08-07 01:00:18 +0100722 problems)
Yuto Takano39639672021-08-05 19:47:48 +0100723 return len(problems)
Darryl Greend5802922018-05-08 15:30:59 +0100724
Yuto Takano55614b52021-08-07 01:00:18 +0100725 def check_for_typos(self, quiet):
Yuto Takano81528c02021-08-06 16:22:06 +0100726 """
727 Perform a check that all words in the soure code beginning with MBED are
728 either defined as macros, or as enum constants.
Yuto Takano977e07f2021-08-09 11:56:15 +0100729 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100730
731 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100732 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100733
734 Returns the number of problems that need fixing.
735 """
Yuto Takano39639672021-08-05 19:47:48 +0100736 problems = []
Yuto Takano39639672021-08-05 19:47:48 +0100737
Yuto Takanod70d4462021-08-09 12:45:51 +0100738 # Set comprehension, equivalent to a list comprehension wrapped by set()
Yuto Takanod93fa372021-08-06 23:05:55 +0100739 all_caps_names = {
740 match.name
741 for match
742 in self.parse_result["macros"] + self.parse_result["enum_consts"]}
743 typo_exclusion = re.compile(r"XXX|__|_$|^MBEDTLS_.*CONFIG_FILE$")
Yuto Takano39639672021-08-05 19:47:48 +0100744
Yuto Takanod93fa372021-08-06 23:05:55 +0100745 for name_match in self.parse_result["mbed_words"]:
Yuto Takano81528c02021-08-06 16:22:06 +0100746 found = name_match.name in all_caps_names
747
748 # Since MBEDTLS_PSA_ACCEL_XXX defines are defined by the
749 # PSA driver, they will not exist as macros. However, they
750 # should still be checked for typos using the equivalent
751 # BUILTINs that exist.
752 if "MBEDTLS_PSA_ACCEL_" in name_match.name:
753 found = name_match.name.replace(
754 "MBEDTLS_PSA_ACCEL_",
755 "MBEDTLS_PSA_BUILTIN_") in all_caps_names
756
Yuto Takanod93fa372021-08-06 23:05:55 +0100757 if not found and not typo_exclusion.search(name_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100758 problems.append(Typo(name_match))
Yuto Takano39639672021-08-05 19:47:48 +0100759
Yuto Takanod70d4462021-08-09 12:45:51 +0100760 self.output_check_result(quiet, "Likely typos", problems)
Yuto Takano81528c02021-08-06 16:22:06 +0100761 return len(problems)
762
Yuto Takanod70d4462021-08-09 12:45:51 +0100763 def output_check_result(self, quiet, name, problems):
Yuto Takano81528c02021-08-06 16:22:06 +0100764 """
765 Write out the PASS/FAIL status of a performed check depending on whether
766 there were problems.
Yuto Takanod70d4462021-08-09 12:45:51 +0100767
768 Args:
769 * quiet: whether to hide detailed problem explanation.
770 * name: the name of the test
771 * problems: a List of encountered Problems
Yuto Takano81528c02021-08-06 16:22:06 +0100772 """
Yuto Takano39639672021-08-05 19:47:48 +0100773 if problems:
Yuto Takano55614b52021-08-07 01:00:18 +0100774 self.log.info("{}: FAIL\n".format(name))
775 for problem in problems:
Yuto Takanod70d4462021-08-09 12:45:51 +0100776 problem.quiet = quiet
Yuto Takano55614b52021-08-07 01:00:18 +0100777 self.log.warning(str(problem))
Darryl Greend5802922018-05-08 15:30:59 +0100778 else:
Yuto Takano81528c02021-08-06 16:22:06 +0100779 self.log.info("{}: PASS".format(name))
Darryl Greend5802922018-05-08 15:30:59 +0100780
Yuto Takano39639672021-08-05 19:47:48 +0100781def main():
782 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100783 Perform argument parsing, and create an instance of CodeParser and
784 NameChecker to begin the core operation.
Yuto Takano39639672021-08-05 19:47:48 +0100785 """
Yuto Takanof005c332021-08-09 13:56:36 +0100786 parser = argparse.ArgumentParser(
Yuto Takano39639672021-08-05 19:47:48 +0100787 formatter_class=argparse.RawDescriptionHelpFormatter,
788 description=(
789 "This script confirms that the naming of all symbols and identifiers "
790 "in Mbed TLS are consistent with the house style and are also "
791 "self-consistent.\n\n"
Yuto Takanof005c332021-08-09 13:56:36 +0100792 "Expected to be run from the MbedTLS root directory.")
793 )
794 parser.add_argument(
795 "-v", "--verbose",
796 action="store_true",
797 help="show parse results"
798 )
799 parser.add_argument(
800 "-q", "--quiet",
801 action="store_true",
802 help="hide unnecessary text, explanations, and highlighs"
803 )
Darryl Greend5802922018-05-08 15:30:59 +0100804
Yuto Takanof005c332021-08-09 13:56:36 +0100805 args = parser.parse_args()
Darryl Greend5802922018-05-08 15:30:59 +0100806
Yuto Takano55c6c872021-08-09 15:35:19 +0100807 # Configure the global logger, which is then passed to the classes below
808 log = logging.getLogger()
809 log.setLevel(logging.DEBUG if args.verbose else logging.INFO)
810 log.addHandler(logging.StreamHandler())
811
Darryl Greend5802922018-05-08 15:30:59 +0100812 try:
Yuto Takano55c6c872021-08-09 15:35:19 +0100813 code_parser = CodeParser(log)
814 parse_result = code_parser.comprehensive_parse()
Yuto Takanod93fa372021-08-06 23:05:55 +0100815 except Exception: # pylint: disable=broad-except
Darryl Greend5802922018-05-08 15:30:59 +0100816 traceback.print_exc()
817 sys.exit(2)
818
Yuto Takano55c6c872021-08-09 15:35:19 +0100819 name_checker = NameChecker(parse_result, log)
820 return_code = name_checker.perform_checks(quiet=args.quiet)
821
822 sys.exit(return_code)
823
Darryl Greend5802922018-05-08 15:30:59 +0100824if __name__ == "__main__":
Yuto Takano39639672021-08-05 19:47:48 +0100825 main()