Files
mbedtls/tf-psa-crypto/scripts/c_header_guards.py

174 lines
6.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Script to check/fix header guards in C header files.
Usage: scripts/c_header_guards.py [--fix]
The script must be called from the TF-PSA-Crypto root folder.
If called without any additional parameter the script checks current guards
and in case of any mismatching with respect to the expected format it print
the errors that it found and it returns an error code.
If called with '--fix' input parameter, instead, it checks and modifies headers'
guards if needed.
The expected format for the headers' guards is as follows:
1. "<rel_path>_<filename>_H" for public headers;
2. "TF_PSA_CRYPTO_<rel_path>_<filename>_H" for private ones;
where:
- 'TF_PSA_CRYPTO_' is an hardcoded prefix;
- 'rel_path' is the path used in C files to include such header file.
"""
## Copyright The Mbed TLS Contributors
## SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import sys
import os
import re
import argparse
from typing import List, Tuple, Match
import framework_scripts_path # pylint: disable=unused-import
from mbedtls_framework import build_tree
PREFIX = 'TF_PSA_CRYPTO_'
# Dictionary containing the folders where header files are to be fixed/checked:
# - key: path of header files to check;
# - value: path that must be trimmed from the key value in order to get the
# final guard.
FOLDERS_TO_CHECK = {
'drivers/builtin/include/mbedtls/': 'drivers/builtin/include/',
'drivers/builtin/include/mbedtls/private/': 'drivers/builtin/include/',
'drivers/builtin/src/': 'drivers/builtin/src/',
'drivers/everest/include/tf-psa-crypto/private/everest/':
'drivers/everest/include/tf-psa-crypto/private/everest/',
'drivers/p256-m/': 'drivers/p256-m/',
'drivers/p256-m/p256-m/': 'drivers/p256-m/',
'include/mbedtls/': 'include/',
'include/psa/': 'include/',
'include/tf-psa-crypto/': 'include/',
'core/': 'core/',
'dispatch/': 'dispatch/',
'extras/': 'extras/',
'platform/': 'platform/',
'utilities/': 'utilities/',
}
def generate_guard_name(file_path: str, trim_path: str) -> str:
"""Generate the new header guard."""
adjusted_name = file_path
# Trim prefix
if adjusted_name.startswith(trim_path):
adjusted_name = adjusted_name[len(trim_path):]
# Remove suffix
if adjusted_name.endswith('.h'):
adjusted_name = adjusted_name[:-2]
adjusted_name = re.sub(r'[^a-zA-Z0-9]', '_', adjusted_name).upper()
guard_elements = []
# Add prefix only to non public header files
if not file_path.startswith('include'):
guard_elements.append('TF_PSA_CRYPTO')
guard_elements.extend([adjusted_name, 'H'])
return '_'.join(guard_elements)
def find_line(lines: List[str], search_range, regex: str) -> Tuple[int, Match]:
"""Try to match the regex expression on the range of lines given in input.
The first successful match is returned; an exception is returned if no
match is found in all the given lines.
Input params:
lines: content of the file given as list of strings;
search_range: range() to use when going through the list of lines;
regex: the regex expression to be used for the match.
Returns: a tuple where
int: the first line where the match was successful;
Match: the Match object returned on the matching line.
"""
for i in search_range:
stripped = lines[i].strip()
match = re.match(regex, stripped)
if match is not None:
return i, match
raise ValueError(f'Error: {regex} line not found')
def process_header(file_path, trim_path, fix) -> None:
"""Check/fix header guard in a single file.
On failure an exception is thrown.
"""
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
correct_guard = generate_guard_name(file_path, trim_path)
ifndef_line, match = find_line(lines, range(0, len(lines)),
r'#ifndef\s+(\w+)\b')
expected_line = f'#ifndef {correct_guard}'
if expected_line not in lines[ifndef_line]:
if fix:
lines[ifndef_line] = expected_line
else:
raise ValueError(f'Invalid guard format:\n'
f' Expected:{expected_line}'
f' Found:{match.group(0)}')
define_line, match = find_line(lines, range(ifndef_line, len(lines)),
r'#define\s+(\w+)\b')
expected_line = f'#define {correct_guard}'
if expected_line not in lines[define_line]:
if fix:
lines[define_line] = expected_line
else:
raise ValueError(f'Invalid guard format:\n'
f' Expected:{expected_line}'
f' Found:{match.group(0)}')
define_line, match = find_line(lines, range(len(lines) - 1, -1, -1),
r'#endif\s*\/\*\s*([\w+.]+)\s*\*\/')
expected_line = f'#endif /* {correct_guard} */'
if expected_line not in lines[define_line]:
if fix:
lines[define_line] = expected_line
else:
raise ValueError(f'Invalid guard format:\n'
f' Expected:{expected_line}'
f' Found:{match.group(0)}')
# Write back to file
new_content = ''.join(lines)
with open(file_path, 'w', encoding='utf-8') as f:
f.write(new_content)
def main():
current_path = os.getcwd()
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('--fix', action='store_true',
help="Fix guards in header files. Default behavior is to check only.")
args = arg_parser.parse_args()
if not build_tree.looks_like_tf_psa_crypto_root(current_path):
print("Error: this script must be launched from TF-PSA-Crypto root")
sys.exit(1)
had_failure = False
for folder in FOLDERS_TO_CHECK:
files = [filename for filename in os.listdir(folder) if re.match(r'.*\.h$', filename)]
for file in files:
full_file_path = os.path.join(folder, file)
try:
process_header(full_file_path, FOLDERS_TO_CHECK[folder], args.fix)
except ValueError as e:
print(f"Error processing {full_file_path}: {e}")
had_failure = True
if had_failure:
sys.exit(1)
if __name__ == "__main__":
main()