diff --git a/scripts/code_style.py b/scripts/code_style.py deleted file mode 100755 index 26b691c2f4..0000000000 --- a/scripts/code_style.py +++ /dev/null @@ -1,279 +0,0 @@ -#!/usr/bin/env python3 -"""Check or fix the code style by running Uncrustify. - -This script must be run from the root of a Git work tree containing Mbed TLS. -""" -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later -import argparse -import os -import re -import subprocess -import sys -from typing import FrozenSet, List, Optional - -UNCRUSTIFY_SUPPORTED_VERSION = "0.75.1" -CONFIG_FILE = ".uncrustify.cfg" -UNCRUSTIFY_EXE = "uncrustify" -UNCRUSTIFY_ARGS = ["-c", CONFIG_FILE] -CHECK_GENERATED_FILES = "tests/scripts/check-generated-files.sh" - -def print_err(*args): - print("Error: ", *args, file=sys.stderr) - -# Print the file names that will be skipped and the help message -def print_skip(files_to_skip): - print() - print(*files_to_skip, sep=", SKIP\n", end=", SKIP\n") - print("Warning: The listed files will be skipped because\n" - "they are not known to git.") - print() - -# Match FILENAME(s) in "check SCRIPT (FILENAME...)" -CHECK_CALL_RE = re.compile(r"\n\s*check\s+[^\s#$&*?;|]+([^\n#$&*?;|]+)", - re.ASCII) -def list_generated_files() -> FrozenSet[str]: - """Return the names of generated files. - - We don't reformat generated files, since the result might be different - from the output of the generator. Ideally the result of the generator - would conform to the code style, but this would be difficult, especially - with respect to the placement of line breaks in long logical lines. - """ - # Parse check-generated-files.sh to get an up-to-date list of - # generated files. Read the file rather than calling it so that - # this script only depends on Git, Python and uncrustify, and not other - # tools such as sh or grep which might not be available on Windows. - # This introduces a limitation: check-generated-files.sh must have - # the expected format and must list the files explicitly, not through - # wildcards or command substitution. - content = open(CHECK_GENERATED_FILES, encoding="utf-8").read() - checks = re.findall(CHECK_CALL_RE, content) - return frozenset(word for s in checks for word in s.split()) - -# Check for comment string indicating an auto-generated file -AUTOGEN_RE = re.compile(r"Warning[ :-]+This file is (now )?auto[ -]?generated", - re.ASCII | re.IGNORECASE) -def is_file_autogenerated(filename): - content = open(filename, encoding="utf-8").read() - return AUTOGEN_RE.search(content) is not None - -def get_src_files(since: Optional[str]) -> List[str]: - """ - Use git to get a list of the source files. - - The optional argument since is a commit, indicating to only list files - that have changed since that commit. Without this argument, list all - files known to git. - - Only C files are included, and certain files (generated, or third party) - are excluded. - """ - file_patterns = ["*.[hc]", - "tests/suites/*.function", - "tf-psa-crypto/tests/suites/*.function", - "scripts/data_files/*.fmt"] - output = subprocess.check_output(["git", "ls-files"] + file_patterns, - universal_newlines=True) - src_files = output.split() - - # When this script is called from a git hook, some environment variables - # are set by default which force all git commands to use the main repository - # (i.e. prevent us from performing commands on the framework repo). - # Create an environment without these variables for running commands on the - # framework repo. - framework_env = os.environ.copy() - # Get a list of environment vars that git sets - git_env_vars = subprocess.check_output(["git", "rev-parse", "--local-env-vars"], - universal_newlines=True) - # Remove the vars from the environment - for var in git_env_vars.split(): - framework_env.pop(var, None) - - output = subprocess.check_output(["git", "-C", "framework", "ls-files"] - + file_patterns, - universal_newlines=True, - env=framework_env) - framework_src_files = output.split() - - if since: - # get all files changed in commits since the starting point in ... - # ... the main repository - cmd = ["git", "log", since + "..HEAD", "--ignore-submodules", - "--name-only", "--pretty=", "--"] + src_files - output = subprocess.check_output(cmd, universal_newlines=True) - committed_changed_files = output.split() - - # ... the framework submodule - framework_since = get_submodule_hash(since, "framework") - cmd = ["git", "-C", "framework", "log", framework_since + "..HEAD", - "--name-only", "--pretty=", "--"] + framework_src_files - output = subprocess.check_output(cmd, universal_newlines=True, - env=framework_env) - committed_changed_files += ["framework/" + s for s in output.split()] - - # and also get all files with uncommitted changes in ... - # ... the main repository - cmd = ["git", "diff", "--name-only", "--"] + src_files - output = subprocess.check_output(cmd, universal_newlines=True) - uncommitted_changed_files = output.split() - # ... the framework submodule - cmd = ["git", "-C", "framework", "diff", "--name-only", "--"] + \ - framework_src_files - output = subprocess.check_output(cmd, universal_newlines=True, - env=framework_env) - uncommitted_changed_files += ["framework/" + s for s in output.split()] - - src_files = committed_changed_files + uncommitted_changed_files - else: - src_files += ["framework/" + s for s in framework_src_files] - - generated_files = list_generated_files() - # Don't correct style for third-party files (and, for simplicity, - # companion files in the same subtree), or for automatically - # generated files (we're correcting the templates instead). - src_files = [filename for filename in src_files - if not (filename.startswith("tf-psa-crypto/drivers/everest/") or - filename.startswith("tf-psa-crypto/drivers/p256-m/") or - filename in generated_files or - is_file_autogenerated(filename))] - return src_files - -def get_submodule_hash(commit: str, submodule: str) -> str: - """Get the commit hash of a submodule at a given commit in the Git repository.""" - cmd = ["git", "ls-tree", commit, submodule] - output = subprocess.check_output(cmd, universal_newlines=True) - return output.split()[2] - -def get_uncrustify_version() -> str: - """ - Get the version string from Uncrustify - """ - result = subprocess.run([UNCRUSTIFY_EXE, "--version"], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - check=False) - if result.returncode != 0: - print_err("Could not get Uncrustify version:", str(result.stderr, "utf-8")) - return "" - else: - return str(result.stdout, "utf-8") - -def check_style_is_correct(src_file_list: List[str]) -> bool: - """ - Check the code style and output a diff for each file whose style is - incorrect. - """ - style_correct = True - for src_file in src_file_list: - uncrustify_cmd = [UNCRUSTIFY_EXE] + UNCRUSTIFY_ARGS + [src_file] - result = subprocess.run(uncrustify_cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, check=False) - if result.returncode != 0: - print_err("Uncrustify returned " + str(result.returncode) + - " correcting file " + src_file) - return False - - # Uncrustify makes changes to the code and places the result in a new - # file with the extension ".uncrustify". To get the changes (if any) - # simply diff the 2 files. - diff_cmd = ["diff", "-u", src_file, src_file + ".uncrustify"] - cp = subprocess.run(diff_cmd, check=False) - - if cp.returncode == 1: - print(src_file + " changed - code style is incorrect.") - style_correct = False - elif cp.returncode != 0: - raise subprocess.CalledProcessError(cp.returncode, cp.args, - cp.stdout, cp.stderr) - - # Tidy up artifact - os.remove(src_file + ".uncrustify") - - return style_correct - -def fix_style_single_pass(src_file_list: List[str]) -> bool: - """ - Run Uncrustify once over the source files. - """ - code_change_args = UNCRUSTIFY_ARGS + ["--no-backup"] - for src_file in src_file_list: - uncrustify_cmd = [UNCRUSTIFY_EXE] + code_change_args + [src_file] - result = subprocess.run(uncrustify_cmd, check=False) - if result.returncode != 0: - print_err("Uncrustify with file returned: " + - str(result.returncode) + " correcting file " + - src_file) - return False - return True - -def fix_style(src_file_list: List[str]) -> int: - """ - Fix the code style. This takes 2 passes of Uncrustify. - """ - if not fix_style_single_pass(src_file_list): - return 1 - if not fix_style_single_pass(src_file_list): - return 1 - - # Guard against future changes that cause the codebase to require - # more passes. - if not check_style_is_correct(src_file_list): - print_err("Code style still incorrect after second run of Uncrustify.") - return 1 - else: - return 0 - -def main() -> int: - """ - Main with command line arguments. - """ - uncrustify_version = get_uncrustify_version().strip() - if UNCRUSTIFY_SUPPORTED_VERSION not in uncrustify_version: - print("Warning: Using unsupported Uncrustify version '" + - uncrustify_version + "'") - print("Note: The only supported version is " + - UNCRUSTIFY_SUPPORTED_VERSION) - - parser = argparse.ArgumentParser() - parser.add_argument('-f', '--fix', action='store_true', - help=('modify source files to fix the code style ' - '(default: print diff, do not modify files)')) - parser.add_argument('-s', '--since', metavar='COMMIT', const='development', nargs='?', - help=('only check files modified since the specified commit' - ' (e.g. --since=HEAD~3 or --since=development). If no' - ' commit is specified, default to development.')) - # --subset is almost useless: it only matters if there are no files - # ('code_style.py' without arguments checks all files known to Git, - # 'code_style.py --subset' does nothing). In particular, - # 'code_style.py --fix --subset ...' is intended as a stable ("porcelain") - # way to restyle a possibly empty set of files. - parser.add_argument('--subset', action='store_true', - help='only check the specified files (default with non-option arguments)') - parser.add_argument('operands', nargs='*', metavar='FILE', - help='files to check (files MUST be known to git, if none: check all)') - - args = parser.parse_args() - - covered = frozenset(get_src_files(args.since)) - # We only check files that are known to git - if args.subset or args.operands: - src_files = [f for f in args.operands if f in covered] - skip_src_files = [f for f in args.operands if f not in covered] - if skip_src_files: - print_skip(skip_src_files) - else: - src_files = list(covered) - - if args.fix: - # Fix mode - return fix_style(src_files) - else: - # Check mode - if check_style_is_correct(src_files): - print("Checked {} files, style ok.".format(len(src_files))) - return 0 - else: - return 1 - -if __name__ == '__main__': - sys.exit(main())