mirror of
				https://github.com/Mbed-TLS/mbedtls.git
				synced 2025-11-03 20:33:16 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			220 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/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
 | 
						|
#
 | 
						|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
						|
# not use this file except in compliance with the License.
 | 
						|
# You may obtain a copy of the License at
 | 
						|
#
 | 
						|
# http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
#
 | 
						|
# Unless required by applicable law or agreed to in writing, software
 | 
						|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
						|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
# See the License for the specific language governing permissions and
 | 
						|
# limitations under the License.
 | 
						|
import argparse
 | 
						|
import os
 | 
						|
import re
 | 
						|
import subprocess
 | 
						|
import sys
 | 
						|
from typing import FrozenSet, List
 | 
						|
 | 
						|
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())
 | 
						|
 | 
						|
def get_src_files() -> List[str]:
 | 
						|
    """
 | 
						|
    Use git ls-files to get a list of the source files
 | 
						|
    """
 | 
						|
    git_ls_files_cmd = ["git", "ls-files",
 | 
						|
                        "*.[hc]",
 | 
						|
                        "tests/suites/*.function",
 | 
						|
                        "scripts/data_files/*.fmt"]
 | 
						|
 | 
						|
    result = subprocess.run(git_ls_files_cmd, stdout=subprocess.PIPE,
 | 
						|
                            check=False)
 | 
						|
 | 
						|
    if result.returncode != 0:
 | 
						|
        print_err("git ls-files returned: " + str(result.returncode))
 | 
						|
        return []
 | 
						|
    else:
 | 
						|
        generated_files = list_generated_files()
 | 
						|
        src_files = str(result.stdout, "utf-8").split()
 | 
						|
        # 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("3rdparty/") or
 | 
						|
                             filename in generated_files)]
 | 
						|
        return src_files
 | 
						|
 | 
						|
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)'))
 | 
						|
    # --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())
 | 
						|
    # 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())
 |