blob: ce03b8a661344e9e36bddee4c414f8d7d3aeb396 [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
182 # Globally excluded filenames
Yuto Takano81528c02021-08-06 16:22:06 +0100183 self.excluded_files = ["bn_mul", "compat-2.x.h"]
Yuto Takano977e07f2021-08-09 11:56:15 +0100184
185 # Will contain the parse result after a comprehensive parse
Yuto Takanod93fa372021-08-06 23:05:55 +0100186 self.parse_result = {}
Darryl Greend5802922018-05-08 15:30:59 +0100187
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100188 @staticmethod
189 def check_repo_path():
190 """
191 Check that the current working directory is the project root, and throw
192 an exception if not.
193 """
194 if not all(os.path.isdir(d) for d in ["include", "library", "tests"]):
195 raise Exception("This script must be run from Mbed TLS root")
196
Yuto Takanod70d4462021-08-09 12:45:51 +0100197 def set_return_code(self, return_code):
198 if return_code > self.return_code:
199 self.log.debug("Setting new return code to {}".format(return_code))
200 self.return_code = return_code
201
Yuto Takano39639672021-08-05 19:47:48 +0100202 def setup_logger(self, verbose=False):
203 """
204 Set up a logger and set the change the default logging level from
Yuto Takano81528c02021-08-06 16:22:06 +0100205 WARNING to INFO. Loggers are better than print statements since their
Yuto Takano39639672021-08-05 19:47:48 +0100206 verbosity can be controlled.
207 """
Darryl Greend5802922018-05-08 15:30:59 +0100208 self.log = logging.getLogger()
Yuto Takano39639672021-08-05 19:47:48 +0100209 if verbose:
210 self.log.setLevel(logging.DEBUG)
211 else:
212 self.log.setLevel(logging.INFO)
Darryl Greend5802922018-05-08 15:30:59 +0100213 self.log.addHandler(logging.StreamHandler())
214
Yuto Takano977e07f2021-08-09 11:56:15 +0100215 def get_files(self, wildcard):
Yuto Takano81528c02021-08-06 16:22:06 +0100216 """
Yuto Takano977e07f2021-08-09 11:56:15 +0100217 Get all files that match a UNIX-style wildcard recursively. While the
218 script is designed only for use on UNIX/macOS (due to nm), this function
219 would work fine on Windows even with forward slashes in the wildcard.
Yuto Takano81528c02021-08-06 16:22:06 +0100220
221 Args:
Yuto Takano977e07f2021-08-09 11:56:15 +0100222 * wildcard: shell-style wildcards to match filepaths against.
Yuto Takano81528c02021-08-06 16:22:06 +0100223
224 Returns a List of relative filepaths.
225 """
Yuto Takano977e07f2021-08-09 11:56:15 +0100226 accumulator = []
227
228 for filepath in glob.iglob(wildcard, recursive=True):
229 if os.path.basename(filepath) not in self.excluded_files:
230 accumulator.append(filepath)
231 return accumulator
Darryl Greend5802922018-05-08 15:30:59 +0100232
Yuto Takano81528c02021-08-06 16:22:06 +0100233 def parse_names_in_source(self):
234 """
Yuto Takano977e07f2021-08-09 11:56:15 +0100235 Comprehensive function to call each parsing function and retrieve
236 various elements of the code, together with their source location.
237 Puts the parsed values in the internal variable self.parse_result, so
238 they can be used from perform_checks().
Yuto Takano81528c02021-08-06 16:22:06 +0100239 """
240 self.log.info("Parsing source code...")
Yuto Takanod24e0372021-08-06 16:42:33 +0100241 self.log.debug(
Yuto Takanod70d4462021-08-09 12:45:51 +0100242 "The following filenames are excluded from the search: {}"
Yuto Takanod24e0372021-08-06 16:42:33 +0100243 .format(str(self.excluded_files))
244 )
Yuto Takano81528c02021-08-06 16:22:06 +0100245
Yuto Takano977e07f2021-08-09 11:56:15 +0100246 m_headers = self.get_files("include/mbedtls/*.h")
247 p_headers = self.get_files("include/psa/*.h")
Yuto Takanod70d4462021-08-09 12:45:51 +0100248 t_headers = [
249 "3rdparty/everest/include/everest/everest.h",
250 "3rdparty/everest/include/everest/x25519.h"
251 ]
Yuto Takano977e07f2021-08-09 11:56:15 +0100252 d_headers = self.get_files("tests/include/test/drivers/*.h")
253 l_headers = self.get_files("library/*.h")
254 libraries = self.get_files("library/*.c") + [
Yuto Takano81528c02021-08-06 16:22:06 +0100255 "3rdparty/everest/library/everest.c",
Yuto Takanod70d4462021-08-09 12:45:51 +0100256 "3rdparty/everest/library/x25519.c"
257 ]
Yuto Takano81528c02021-08-06 16:22:06 +0100258
259 all_macros = self.parse_macros(
Yuto Takanod70d4462021-08-09 12:45:51 +0100260 m_headers + p_headers + t_headers + l_headers + d_headers
261 )
Yuto Takano81528c02021-08-06 16:22:06 +0100262 enum_consts = self.parse_enum_consts(
Yuto Takanod70d4462021-08-09 12:45:51 +0100263 m_headers + l_headers + t_headers
264 )
Yuto Takano81528c02021-08-06 16:22:06 +0100265 identifiers = self.parse_identifiers(
Yuto Takanod70d4462021-08-09 12:45:51 +0100266 m_headers + p_headers + t_headers + l_headers
267 )
Yuto Takanod93fa372021-08-06 23:05:55 +0100268 mbed_words = self.parse_mbed_words(
Yuto Takanod70d4462021-08-09 12:45:51 +0100269 m_headers + p_headers + t_headers + l_headers + libraries
270 )
Yuto Takano81528c02021-08-06 16:22:06 +0100271 symbols = self.parse_symbols()
272
273 # Remove identifier macros like mbedtls_printf or mbedtls_calloc
274 identifiers_justname = [x.name for x in identifiers]
275 actual_macros = []
276 for macro in all_macros:
277 if macro.name not in identifiers_justname:
278 actual_macros.append(macro)
279
280 self.log.debug("Found:")
Yuto Takanod70d4462021-08-09 12:45:51 +0100281 self.log.debug(" {} Total Macros".format(len(all_macros)))
Yuto Takano81528c02021-08-06 16:22:06 +0100282 self.log.debug(" {} Non-identifier Macros".format(len(actual_macros)))
283 self.log.debug(" {} Enum Constants".format(len(enum_consts)))
284 self.log.debug(" {} Identifiers".format(len(identifiers)))
285 self.log.debug(" {} Exported Symbols".format(len(symbols)))
286 self.log.info("Analysing...")
287
288 self.parse_result = {
289 "macros": actual_macros,
290 "enum_consts": enum_consts,
291 "identifiers": identifiers,
292 "symbols": symbols,
Yuto Takanod93fa372021-08-06 23:05:55 +0100293 "mbed_words": mbed_words
Yuto Takano81528c02021-08-06 16:22:06 +0100294 }
295
Yuto Takanod70d4462021-08-09 12:45:51 +0100296 def parse_macros(self, files):
Yuto Takano39639672021-08-05 19:47:48 +0100297 """
298 Parse all macros defined by #define preprocessor directives.
299
300 Args:
Yuto Takanod70d4462021-08-09 12:45:51 +0100301 * files: A List of filepaths to look through.
Yuto Takano81528c02021-08-06 16:22:06 +0100302
303 Returns a List of Match objects for the found macros.
Yuto Takano39639672021-08-05 19:47:48 +0100304 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100305 macro_regex = re.compile(r"# *define +(?P<macro>\w+)")
306 exclusions = (
Yuto Takano39639672021-08-05 19:47:48 +0100307 "asm", "inline", "EMIT", "_CRT_SECURE_NO_DEPRECATE", "MULADDC_"
308 )
309
Yuto Takanod70d4462021-08-09 12:45:51 +0100310 self.log.debug("Looking for macros in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100311
312 macros = []
313
Yuto Takanod70d4462021-08-09 12:45:51 +0100314 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100315 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100316 for line_no, line in enumerate(header):
Yuto Takanod93fa372021-08-06 23:05:55 +0100317 for macro in macro_regex.finditer(line):
Yuto Takanod70d4462021-08-09 12:45:51 +0100318 if macro.group("macro").startswith(exclusions):
319 continue
320
321 macros.append(Match(
322 header_file,
323 line,
324 (line_no, macro.start(), macro.end()),
325 macro.group("macro")))
Darryl Greend5802922018-05-08 15:30:59 +0100326
Yuto Takano39639672021-08-05 19:47:48 +0100327 return macros
Darryl Greend5802922018-05-08 15:30:59 +0100328
Yuto Takanod93fa372021-08-06 23:05:55 +0100329 def parse_mbed_words(self, files):
Yuto Takano39639672021-08-05 19:47:48 +0100330 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100331 Parse all words in the file that begin with MBED, in and out of macros,
332 comments, anything.
Yuto Takano39639672021-08-05 19:47:48 +0100333
334 Args:
Yuto Takano81528c02021-08-06 16:22:06 +0100335 * files: a List of filepaths to look through.
336
337 Returns a List of Match objects for words beginning with MBED.
Yuto Takano39639672021-08-05 19:47:48 +0100338 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100339 # Typos of TLS are common, hence the broader check below than MBEDTLS.
Yuto Takanod93fa372021-08-06 23:05:55 +0100340 mbed_regex = re.compile(r"\bMBED.+?_[A-Z0-9_]*")
341 exclusions = re.compile(r"// *no-check-names|#error")
342
Yuto Takano201f9e82021-08-06 16:36:54 +0100343 self.log.debug("Looking for MBED names in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100344
345 mbed_words = []
346
Yuto Takanobb7dca42021-08-05 19:57:58 +0100347 for filename in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100348 with open(filename, "r", encoding="utf-8") as fp:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100349 for line_no, line in enumerate(fp):
Yuto Takanod93fa372021-08-06 23:05:55 +0100350 if exclusions.search(line):
Yuto Takanoc62b4082021-08-05 20:17:07 +0100351 continue
Yuto Takano81528c02021-08-06 16:22:06 +0100352
Yuto Takanod93fa372021-08-06 23:05:55 +0100353 for name in mbed_regex.finditer(line):
354 mbed_words.append(Match(
Yuto Takano39639672021-08-05 19:47:48 +0100355 filename,
356 line,
Yuto Takanod93fa372021-08-06 23:05:55 +0100357 (line_no, name.start(), name.end()),
Yuto Takano39639672021-08-05 19:47:48 +0100358 name.group(0)
359 ))
360
Yuto Takanod93fa372021-08-06 23:05:55 +0100361 return mbed_words
Yuto Takano39639672021-08-05 19:47:48 +0100362
Yuto Takanod70d4462021-08-09 12:45:51 +0100363 def parse_enum_consts(self, files):
Yuto Takano39639672021-08-05 19:47:48 +0100364 """
365 Parse all enum value constants that are declared.
366
367 Args:
Yuto Takanod70d4462021-08-09 12:45:51 +0100368 * files: A List of filepaths to look through.
Yuto Takano39639672021-08-05 19:47:48 +0100369
Yuto Takano81528c02021-08-06 16:22:06 +0100370 Returns a List of Match objects for the findings.
Yuto Takano39639672021-08-05 19:47:48 +0100371 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100372 self.log.debug(
373 "Looking for enum consts in {} files"
374 .format(len(files))
375 )
Yuto Takano39639672021-08-05 19:47:48 +0100376
377 enum_consts = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100378
Yuto Takanod70d4462021-08-09 12:45:51 +0100379 for header_file in files:
Yuto Takano39639672021-08-05 19:47:48 +0100380 # Emulate a finite state machine to parse enum declarations.
Yuto Takano81528c02021-08-06 16:22:06 +0100381 # 0 = not in enum
382 # 1 = inside enum
383 # 2 = almost inside enum
Darryl Greend5802922018-05-08 15:30:59 +0100384 state = 0
Yuto Takanoa083d152021-08-07 00:25:59 +0100385 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100386 for line_no, line in enumerate(header):
Yuto Takano13ecd992021-08-06 16:56:52 +0100387 # Match typedefs and brackets only when they are at the
388 # beginning of the line -- if they are indented, they might
389 # be sub-structures within structs, etc.
Yuto Takanod93fa372021-08-06 23:05:55 +0100390 if state == 0 and re.match(r"^(typedef +)?enum +{", line):
Darryl Greend5802922018-05-08 15:30:59 +0100391 state = 1
Yuto Takanod93fa372021-08-06 23:05:55 +0100392 elif state == 0 and re.match(r"^(typedef +)?enum", line):
Darryl Greend5802922018-05-08 15:30:59 +0100393 state = 2
Yuto Takanod93fa372021-08-06 23:05:55 +0100394 elif state == 2 and re.match(r"^{", line):
Darryl Greend5802922018-05-08 15:30:59 +0100395 state = 1
Yuto Takanod93fa372021-08-06 23:05:55 +0100396 elif state == 1 and re.match(r"^}", line):
Darryl Greend5802922018-05-08 15:30:59 +0100397 state = 0
Yuto Takanod93fa372021-08-06 23:05:55 +0100398 elif state == 1 and not re.match(r" *#", line):
Yuto Takano13ecd992021-08-06 16:56:52 +0100399 enum_const = re.match(r" *(?P<enum_const>\w+)", line)
Yuto Takanod70d4462021-08-09 12:45:51 +0100400 if not enum_const:
401 continue
402
403 enum_consts.append(Match(
404 header_file,
405 line,
406 (line_no, enum_const.start(), enum_const.end()),
407 enum_const.group("enum_const")))
Yuto Takano81528c02021-08-06 16:22:06 +0100408
Yuto Takano39639672021-08-05 19:47:48 +0100409 return enum_consts
Darryl Greend5802922018-05-08 15:30:59 +0100410
Yuto Takanod70d4462021-08-09 12:45:51 +0100411 def parse_identifiers(self, files):
Yuto Takano39639672021-08-05 19:47:48 +0100412 """
413 Parse all lines of a header where a function identifier is declared,
Yuto Takano81528c02021-08-06 16:22:06 +0100414 based on some huersitics. Highly dependent on formatting style.
Yuto Takanod70d4462021-08-09 12:45:51 +0100415 Note: .match() checks at the beginning of the string (implicit ^), while
416 .search() checks throughout.
Darryl Greend5802922018-05-08 15:30:59 +0100417
Yuto Takano39639672021-08-05 19:47:48 +0100418 Args:
Yuto Takanod70d4462021-08-09 12:45:51 +0100419 * files: A List of filepaths to look through.
Yuto Takano81528c02021-08-06 16:22:06 +0100420
421 Returns a List of Match objects with identifiers.
Yuto Takano39639672021-08-05 19:47:48 +0100422 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100423 identifier_regex = re.compile(
424 # Match " something(a" or " *something(a". Functions.
425 # Assumptions:
426 # - function definition from return type to one of its arguments is
427 # all on one line (enforced by the previous_line concat below)
428 # - function definition line only contains alphanumeric, asterisk,
429 # underscore, and open bracket
430 r".* \**(\w+) *\( *\w|"
431 # Match "(*something)(". Flexible with spaces.
432 r".*\( *\* *(\w+) *\) *\(|"
433 # Match names of named data structures.
434 r"(?:typedef +)?(?:struct|union|enum) +(\w+)(?: *{)?$|"
435 # Match names of typedef instances, after closing bracket.
Yuto Takanod70d4462021-08-09 12:45:51 +0100436 r"}? *(\w+)[;[].*"
437 )
438 exclusion_lines = re.compile(
439 r"^("
440 r"extern +\"C\"|"
441 r"(typedef +)?(struct|union|enum)( *{)?$|"
442 r"} *;?$|"
443 r"$|"
444 r"//|"
445 r"#"
446 r")"
447 )
Yuto Takanod93fa372021-08-06 23:05:55 +0100448
Yuto Takanod70d4462021-08-09 12:45:51 +0100449 self.log.debug(
450 "Looking for identifiers in {} files"
451 .format(len(files))
452 )
Darryl Greend5802922018-05-08 15:30:59 +0100453
Yuto Takano39639672021-08-05 19:47:48 +0100454 identifiers = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100455
Yuto Takanod70d4462021-08-09 12:45:51 +0100456 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100457 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano39639672021-08-05 19:47:48 +0100458 in_block_comment = False
Yuto Takanod70d4462021-08-09 12:45:51 +0100459 # The previous line varibale is used for concatenating lines
460 # when identifiers are formatted and spread across multiple.
Yuto Takanod93fa372021-08-06 23:05:55 +0100461 previous_line = ""
Darryl Greend5802922018-05-08 15:30:59 +0100462
Yuto Takano8f457cf2021-08-06 17:54:58 +0100463 for line_no, line in enumerate(header):
Yuto Takano81528c02021-08-06 16:22:06 +0100464 # Skip parsing this line if a block comment ends on it,
465 # but don't skip if it has just started -- there is a chance
466 # it ends on the same line.
Yuto Takano39639672021-08-05 19:47:48 +0100467 if re.search(r"/\*", line):
Yuto Takano81528c02021-08-06 16:22:06 +0100468 in_block_comment = not in_block_comment
469 if re.search(r"\*/", line):
470 in_block_comment = not in_block_comment
Yuto Takano39639672021-08-05 19:47:48 +0100471 continue
472
Yuto Takano81528c02021-08-06 16:22:06 +0100473 if in_block_comment:
Yuto Takanod93fa372021-08-06 23:05:55 +0100474 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100475 continue
476
Yuto Takanod93fa372021-08-06 23:05:55 +0100477 if exclusion_lines.match(line):
478 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100479 continue
480
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100481 # If the line contains only space-separated alphanumeric
482 # characters (or underscore, asterisk, or, open bracket),
483 # and nothing else, high chance it's a declaration that
484 # continues on the next line
485 if re.match(r"^([\w\*\(]+\s+)+$", line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100486 previous_line += line
Yuto Takano81528c02021-08-06 16:22:06 +0100487 continue
488
489 # If previous line seemed to start an unfinished declaration
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100490 # (as above), concat and treat them as one.
491 if previous_line:
492 line = previous_line.strip() + " " + line.strip()
Yuto Takanod93fa372021-08-06 23:05:55 +0100493 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100494
495 # Skip parsing if line has a space in front = hueristic to
496 # skip function argument lines (highly subject to formatting
497 # changes)
498 if line[0] == " ":
Yuto Takano39639672021-08-05 19:47:48 +0100499 continue
Yuto Takano6f38ab32021-08-05 21:07:14 +0100500
Yuto Takanod93fa372021-08-06 23:05:55 +0100501 identifier = identifier_regex.search(line)
Yuto Takano39639672021-08-05 19:47:48 +0100502
Yuto Takanod70d4462021-08-09 12:45:51 +0100503 if not identifier:
504 continue
505
506 # Find the group that matched, and append it
507 for group in identifier.groups():
508 if not group:
509 continue
510
511 identifiers.append(Match(
512 header_file,
513 line,
514 (line_no, identifier.start(), identifier.end()),
515 group))
Yuto Takano39639672021-08-05 19:47:48 +0100516
517 return identifiers
518
519 def parse_symbols(self):
520 """
521 Compile the Mbed TLS libraries, and parse the TLS, Crypto, and x509
522 object files using nm to retrieve the list of referenced symbols.
Yuto Takano81528c02021-08-06 16:22:06 +0100523 Exceptions thrown here are rethrown because they would be critical
524 errors that void several tests, and thus needs to halt the program. This
525 is explicitly done for clarity.
Yuto Takano39639672021-08-05 19:47:48 +0100526
Yuto Takano81528c02021-08-06 16:22:06 +0100527 Returns a List of unique symbols defined and used in the libraries.
528 """
529 self.log.info("Compiling...")
Yuto Takano39639672021-08-05 19:47:48 +0100530 symbols = []
531
532 # Back up the config and atomically compile with the full configratuion.
Yuto Takanod70d4462021-08-09 12:45:51 +0100533 shutil.copy(
534 "include/mbedtls/mbedtls_config.h",
535 "include/mbedtls/mbedtls_config.h.bak"
536 )
Darryl Greend5802922018-05-08 15:30:59 +0100537 try:
Yuto Takano81528c02021-08-06 16:22:06 +0100538 # Use check=True in all subprocess calls so that failures are raised
539 # as exceptions and logged.
Yuto Takano39639672021-08-05 19:47:48 +0100540 subprocess.run(
Yuto Takano81528c02021-08-06 16:22:06 +0100541 ["python3", "scripts/config.py", "full"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100542 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100543 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100544 )
545 my_environment = os.environ.copy()
546 my_environment["CFLAGS"] = "-fno-asynchronous-unwind-tables"
Yuto Takano39639672021-08-05 19:47:48 +0100547 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100548 ["make", "clean", "lib"],
549 env=my_environment,
Yuto Takanobcc3d992021-08-06 23:14:58 +0100550 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100551 stdout=subprocess.PIPE,
Darryl Greend5802922018-05-08 15:30:59 +0100552 stderr=subprocess.STDOUT,
Yuto Takano39639672021-08-05 19:47:48 +0100553 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100554 )
Yuto Takano39639672021-08-05 19:47:48 +0100555
556 # Perform object file analysis using nm
Yuto Takanod70d4462021-08-09 12:45:51 +0100557 symbols = self.parse_symbols_from_nm([
558 "library/libmbedcrypto.a",
559 "library/libmbedtls.a",
560 "library/libmbedx509.a"
561 ])
Yuto Takano39639672021-08-05 19:47:48 +0100562
563 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100564 ["make", "clean"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100565 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100566 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100567 )
568 except subprocess.CalledProcessError as error:
Yuto Takano25eeb7b2021-08-06 21:27:59 +0100569 self.log.debug(error.output)
Darryl Greend5802922018-05-08 15:30:59 +0100570 self.set_return_code(2)
Yuto Takano81528c02021-08-06 16:22:06 +0100571 raise error
Yuto Takano39639672021-08-05 19:47:48 +0100572 finally:
Yuto Takano6fececf2021-08-07 17:28:23 +0100573 # Put back the original config regardless of there being errors.
574 # Works also for keyboard interrupts.
Yuto Takanod70d4462021-08-09 12:45:51 +0100575 shutil.move(
576 "include/mbedtls/mbedtls_config.h.bak",
577 "include/mbedtls/mbedtls_config.h"
578 )
Yuto Takano39639672021-08-05 19:47:48 +0100579
580 return symbols
581
582 def parse_symbols_from_nm(self, object_files):
583 """
584 Run nm to retrieve the list of referenced symbols in each object file.
585 Does not return the position data since it is of no use.
586
Yuto Takano81528c02021-08-06 16:22:06 +0100587 Args:
588 * object_files: a List of compiled object files to search through.
589
590 Returns a List of unique symbols defined and used in any of the object
591 files.
Yuto Takano39639672021-08-05 19:47:48 +0100592 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100593 nm_undefined_regex = re.compile(r"^\S+: +U |^$|^\S+:$")
594 nm_valid_regex = re.compile(r"^\S+( [0-9A-Fa-f]+)* . _*(?P<symbol>\w+)")
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100595 exclusions = ("FStar", "Hacl")
Yuto Takano39639672021-08-05 19:47:48 +0100596
597 symbols = []
598
Yuto Takano81528c02021-08-06 16:22:06 +0100599 # Gather all outputs of nm
Yuto Takano39639672021-08-05 19:47:48 +0100600 nm_output = ""
601 for lib in object_files:
602 nm_output += subprocess.run(
603 ["nm", "-og", lib],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100604 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100605 stdout=subprocess.PIPE,
606 stderr=subprocess.STDOUT,
607 check=True
608 ).stdout
Yuto Takano81528c02021-08-06 16:22:06 +0100609
Yuto Takano39639672021-08-05 19:47:48 +0100610 for line in nm_output.splitlines():
Yuto Takanod93fa372021-08-06 23:05:55 +0100611 if not nm_undefined_regex.match(line):
612 symbol = nm_valid_regex.match(line)
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100613 if (symbol and not symbol.group("symbol").startswith(exclusions)):
Yuto Takanoe77f6992021-08-05 20:22:59 +0100614 symbols.append(symbol.group("symbol"))
Yuto Takano39639672021-08-05 19:47:48 +0100615 else:
616 self.log.error(line)
Yuto Takano81528c02021-08-06 16:22:06 +0100617
Yuto Takano39639672021-08-05 19:47:48 +0100618 return symbols
619
Yuto Takano55614b52021-08-07 01:00:18 +0100620 def perform_checks(self, quiet=False):
Yuto Takano39639672021-08-05 19:47:48 +0100621 """
622 Perform each check in order, output its PASS/FAIL status. Maintain an
623 overall test status, and output that at the end.
Yuto Takano977e07f2021-08-09 11:56:15 +0100624 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100625
626 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100627 * quiet: whether to hide detailed problem explanation.
Yuto Takano39639672021-08-05 19:47:48 +0100628 """
Yuto Takano81528c02021-08-06 16:22:06 +0100629 self.log.info("=============")
Yuto Takano39639672021-08-05 19:47:48 +0100630 problems = 0
631
Yuto Takano55614b52021-08-07 01:00:18 +0100632 problems += self.check_symbols_declared_in_header(quiet)
Yuto Takano39639672021-08-05 19:47:48 +0100633
Yuto Takanod70d4462021-08-09 12:45:51 +0100634 pattern_checks = [
635 ("macros", MACRO_PATTERN),
636 ("enum_consts", CONSTANTS_PATTERN),
637 ("identifiers", IDENTIFIER_PATTERN)
638 ]
Yuto Takano39639672021-08-05 19:47:48 +0100639 for group, check_pattern in pattern_checks:
Yuto Takano55614b52021-08-07 01:00:18 +0100640 problems += self.check_match_pattern(quiet, group, check_pattern)
Yuto Takano39639672021-08-05 19:47:48 +0100641
Yuto Takano55614b52021-08-07 01:00:18 +0100642 problems += self.check_for_typos(quiet)
Yuto Takano39639672021-08-05 19:47:48 +0100643
644 self.log.info("=============")
645 if problems > 0:
646 self.log.info("FAIL: {0} problem(s) to fix".format(str(problems)))
Yuto Takano55614b52021-08-07 01:00:18 +0100647 if quiet:
648 self.log.info("Remove --quiet to see explanations.")
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100649 else:
650 self.log.info("Use --quiet for minimal output.")
Yuto Takano39639672021-08-05 19:47:48 +0100651 else:
652 self.log.info("PASS")
Darryl Greend5802922018-05-08 15:30:59 +0100653
Yuto Takano55614b52021-08-07 01:00:18 +0100654 def check_symbols_declared_in_header(self, quiet):
Yuto Takano39639672021-08-05 19:47:48 +0100655 """
656 Perform a check that all detected symbols in the library object files
657 are properly declared in headers.
Yuto Takano977e07f2021-08-09 11:56:15 +0100658 Assumes parse_names_in_source() was called before this.
Darryl Greend5802922018-05-08 15:30:59 +0100659
Yuto Takano81528c02021-08-06 16:22:06 +0100660 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100661 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100662
663 Returns the number of problems that need fixing.
Yuto Takano39639672021-08-05 19:47:48 +0100664 """
665 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100666
Yuto Takano39639672021-08-05 19:47:48 +0100667 for symbol in self.parse_result["symbols"]:
668 found_symbol_declared = False
669 for identifier_match in self.parse_result["identifiers"]:
670 if symbol == identifier_match.name:
671 found_symbol_declared = True
672 break
Yuto Takano81528c02021-08-06 16:22:06 +0100673
Yuto Takano39639672021-08-05 19:47:48 +0100674 if not found_symbol_declared:
Yuto Takanod70d4462021-08-09 12:45:51 +0100675 problems.append(SymbolNotInHeader(symbol))
Yuto Takano39639672021-08-05 19:47:48 +0100676
Yuto Takanod70d4462021-08-09 12:45:51 +0100677 self.output_check_result(quiet, "All symbols in header", problems)
Yuto Takano39639672021-08-05 19:47:48 +0100678 return len(problems)
679
Yuto Takano55614b52021-08-07 01:00:18 +0100680 def check_match_pattern(self, quiet, group_to_check, check_pattern):
Yuto Takano81528c02021-08-06 16:22:06 +0100681 """
682 Perform a check that all items of a group conform to a regex pattern.
Yuto Takano977e07f2021-08-09 11:56:15 +0100683 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100684
685 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100686 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100687 * group_to_check: string key to index into self.parse_result.
688 * check_pattern: the regex to check against.
689
690 Returns the number of problems that need fixing.
691 """
Yuto Takano39639672021-08-05 19:47:48 +0100692 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100693
Yuto Takano39639672021-08-05 19:47:48 +0100694 for item_match in self.parse_result[group_to_check]:
695 if not re.match(check_pattern, item_match.name):
696 problems.append(PatternMismatch(check_pattern, item_match))
Yuto Takano201f9e82021-08-06 16:36:54 +0100697 # Double underscore is a reserved identifier, never to be used
Yuto Takanoc763cc32021-08-05 20:06:34 +0100698 if re.match(r".*__.*", item_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100699 problems.append(PatternMismatch("double underscore", item_match))
Yuto Takano81528c02021-08-06 16:22:06 +0100700
701 self.output_check_result(
Yuto Takanod70d4462021-08-09 12:45:51 +0100702 quiet,
Yuto Takano81528c02021-08-06 16:22:06 +0100703 "Naming patterns of {}".format(group_to_check),
Yuto Takano55614b52021-08-07 01:00:18 +0100704 problems)
Yuto Takano39639672021-08-05 19:47:48 +0100705 return len(problems)
Darryl Greend5802922018-05-08 15:30:59 +0100706
Yuto Takano55614b52021-08-07 01:00:18 +0100707 def check_for_typos(self, quiet):
Yuto Takano81528c02021-08-06 16:22:06 +0100708 """
709 Perform a check that all words in the soure code beginning with MBED are
710 either defined as macros, or as enum constants.
Yuto Takano977e07f2021-08-09 11:56:15 +0100711 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100712
713 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100714 * quiet: whether to hide detailed problem explanation.
Yuto Takano81528c02021-08-06 16:22:06 +0100715
716 Returns the number of problems that need fixing.
717 """
Yuto Takano39639672021-08-05 19:47:48 +0100718 problems = []
Yuto Takano39639672021-08-05 19:47:48 +0100719
Yuto Takanod70d4462021-08-09 12:45:51 +0100720 # Set comprehension, equivalent to a list comprehension wrapped by set()
Yuto Takanod93fa372021-08-06 23:05:55 +0100721 all_caps_names = {
722 match.name
723 for match
724 in self.parse_result["macros"] + self.parse_result["enum_consts"]}
725 typo_exclusion = re.compile(r"XXX|__|_$|^MBEDTLS_.*CONFIG_FILE$")
Yuto Takano39639672021-08-05 19:47:48 +0100726
Yuto Takanod93fa372021-08-06 23:05:55 +0100727 for name_match in self.parse_result["mbed_words"]:
Yuto Takano81528c02021-08-06 16:22:06 +0100728 found = name_match.name in all_caps_names
729
730 # Since MBEDTLS_PSA_ACCEL_XXX defines are defined by the
731 # PSA driver, they will not exist as macros. However, they
732 # should still be checked for typos using the equivalent
733 # BUILTINs that exist.
734 if "MBEDTLS_PSA_ACCEL_" in name_match.name:
735 found = name_match.name.replace(
736 "MBEDTLS_PSA_ACCEL_",
737 "MBEDTLS_PSA_BUILTIN_") in all_caps_names
738
Yuto Takanod93fa372021-08-06 23:05:55 +0100739 if not found and not typo_exclusion.search(name_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100740 problems.append(Typo(name_match))
Yuto Takano39639672021-08-05 19:47:48 +0100741
Yuto Takanod70d4462021-08-09 12:45:51 +0100742 self.output_check_result(quiet, "Likely typos", problems)
Yuto Takano81528c02021-08-06 16:22:06 +0100743 return len(problems)
744
Yuto Takanod70d4462021-08-09 12:45:51 +0100745 def output_check_result(self, quiet, name, problems):
Yuto Takano81528c02021-08-06 16:22:06 +0100746 """
747 Write out the PASS/FAIL status of a performed check depending on whether
748 there were problems.
Yuto Takanod70d4462021-08-09 12:45:51 +0100749
750 Args:
751 * quiet: whether to hide detailed problem explanation.
752 * name: the name of the test
753 * problems: a List of encountered Problems
Yuto Takano81528c02021-08-06 16:22:06 +0100754 """
Yuto Takano39639672021-08-05 19:47:48 +0100755 if problems:
Darryl Greend5802922018-05-08 15:30:59 +0100756 self.set_return_code(1)
Yuto Takano55614b52021-08-07 01:00:18 +0100757 self.log.info("{}: FAIL\n".format(name))
758 for problem in problems:
Yuto Takanod70d4462021-08-09 12:45:51 +0100759 problem.quiet = quiet
Yuto Takano55614b52021-08-07 01:00:18 +0100760 self.log.warning(str(problem))
Darryl Greend5802922018-05-08 15:30:59 +0100761 else:
Yuto Takano81528c02021-08-06 16:22:06 +0100762 self.log.info("{}: PASS".format(name))
Darryl Greend5802922018-05-08 15:30:59 +0100763
Yuto Takano39639672021-08-05 19:47:48 +0100764def main():
765 """
Yuto Takano81528c02021-08-06 16:22:06 +0100766 Perform argument parsing, and create an instance of NameCheck to begin the
767 core operation.
Yuto Takano39639672021-08-05 19:47:48 +0100768 """
Yuto Takanof005c332021-08-09 13:56:36 +0100769 parser = argparse.ArgumentParser(
Yuto Takano39639672021-08-05 19:47:48 +0100770 formatter_class=argparse.RawDescriptionHelpFormatter,
771 description=(
772 "This script confirms that the naming of all symbols and identifiers "
773 "in Mbed TLS are consistent with the house style and are also "
774 "self-consistent.\n\n"
Yuto Takanof005c332021-08-09 13:56:36 +0100775 "Expected to be run from the MbedTLS root directory.")
776 )
777 parser.add_argument(
778 "-v", "--verbose",
779 action="store_true",
780 help="show parse results"
781 )
782 parser.add_argument(
783 "-q", "--quiet",
784 action="store_true",
785 help="hide unnecessary text, explanations, and highlighs"
786 )
Darryl Greend5802922018-05-08 15:30:59 +0100787
Yuto Takanof005c332021-08-09 13:56:36 +0100788 args = parser.parse_args()
Darryl Greend5802922018-05-08 15:30:59 +0100789
Darryl Greend5802922018-05-08 15:30:59 +0100790 try:
Yuto Takano977e07f2021-08-09 11:56:15 +0100791 name_check = NameCheck(verbose=args.verbose)
Yuto Takano39639672021-08-05 19:47:48 +0100792 name_check.parse_names_in_source()
Yuto Takano55614b52021-08-07 01:00:18 +0100793 name_check.perform_checks(quiet=args.quiet)
Yuto Takano81528c02021-08-06 16:22:06 +0100794 sys.exit(name_check.return_code)
Yuto Takanod93fa372021-08-06 23:05:55 +0100795 except Exception: # pylint: disable=broad-except
Darryl Greend5802922018-05-08 15:30:59 +0100796 traceback.print_exc()
797 sys.exit(2)
798
Darryl Greend5802922018-05-08 15:30:59 +0100799if __name__ == "__main__":
Yuto Takano39639672021-08-05 19:47:48 +0100800 main()