From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by mx.groups.io with SMTP id smtpd.web10.17019.1637699685674567895 for ; Tue, 23 Nov 2021 12:34:45 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@linux.microsoft.com header.s=default header.b=NBxTBi/M; spf=pass (domain: linux.microsoft.com, ip: 13.77.154.182, mailfrom: mikuback@linux.microsoft.com) Received: from [10.0.0.19] (c-73-27-179-174.hsd1.fl.comcast.net [73.27.179.174]) by linux.microsoft.com (Postfix) with ESMTPSA id 500A220CEAB3; Tue, 23 Nov 2021 12:34:44 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 500A220CEAB3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1637699685; bh=/aUUZthu67SU6WlUyS0DVdhLEXmKnaDLKI96fAjyqYU=; h=Date:Subject:To:Cc:References:From:In-Reply-To:From; b=NBxTBi/MTWmH3RPBwt3dcCMgTIv+C4+AXaT9UeMtqFAkt932X5cq/oui5Dk6VTOwO AqpNdg83pHmPB/iFLHmiwzqHNIkSFm2TWgRfooVQI8sZM5iAYyBzmHh0XHOPDsV2A5 snerRGwKY/OdaJ10hJFRBMMCgTYtKaQmPEFm7ijg= Message-ID: Date: Tue, 23 Nov 2021 15:34:43 -0500 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.3.1 Subject: Re: [PATCH v1 1/1] .pytool/Plugin/Uncrustify: Add Uncrustify plugin To: "Kinney, Michael D" , "devel@edk2.groups.io" Cc: Liming Gao , Sean Brogan , Bret Barkelew References: <20211123165352.2001-1-mikuback@linux.microsoft.com> From: "Michael Kubacki" In-Reply-To: Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Response below. On 11/23/2021 3:19 PM, Kinney, Michael D wrote: > Hi Michael, > > Comments below. > > Mike > >> -----Original Message----- >> From: Michael Kubacki >> Sent: Tuesday, November 23, 2021 12:07 PM >> To: Kinney, Michael D ; devel@edk2.groups.io >> Cc: Liming Gao ; Sean Brogan ; Bret Barkelew >> >> Subject: Re: [PATCH v1 1/1] .pytool/Plugin/Uncrustify: Add Uncrustify plugin >> >> I'll file a BZ and add it. >> >> I don't see a difference comparing my template file contents against the >> corresponding files in the branch you linked. Am I missing something? > > You are correct. Please ignore. > > I saw reference to Project Mu, but that is in the > .pytool/Plugin/Uncrustify/uncrustify_ext_dep.yaml file, which is correct > because that is where the fork of uncrustify tool with EDK II extensions > is hosted. > >> >> I saw the temp file directory change recently added to those plugin >> patches. My concern is that .pytool/ is tracked by git and Build/ is not. > > My suggestion is to use Build/.pytool/Plugin/ as a temp > directory that is in Build output directory that is ignored by .gitignore. > Okay, I missed this is relative to the Build directory. I can make that change. >> >> In the unlikely case the temp file removal fails (for whatever reason), >> the git workspace does not pick up the left over files. >> >> Given the plugin is storing temp files in a "UncrustifyPlugin" directory >> in each package's build directory, I don't anticipate conflicts (unless >> the user has intentionally tried to do so). I've also taken time to >> improve temp directory removal robustness in ways I haven't seen in >> other plugins including doing so for cases like keyboard interrupts and >> attempting multiple times for sporadic issues that might occur during >> removal (that we've very rarely observed across many CI instances). >> >> Technically, since .pytool/ is tracked, the user could also have local >> changes there that conflict with what the plugin produces as temporary >> output. >> >> So I didn't see a strong practical benefit to changing it from >> Build/{PackageName}/UncrustifyPlugin. Is there a benefit/issue I missed? > > I have seen a difference between CRTL-C and CRTL-BREAK. CRTL-C is caught > by Python exception processing so the temp dir can be removed. CRTL-BREAK > or other OS specific actions to kill a process bypasses the Python exception > handling and the temp dir will not be removed. > > I recommend moving all temp dirs/files into locations that are guaranteed > to never be seen as untracked files by git. > >> >> Thanks, >> Michael >> >> On 11/23/2021 2:20 PM, Kinney, Michael D wrote: >>> Hi Michael, >>> >>> Have you opened a BZ for this feature? Can you do that and add REF: to the commit message? >>> >>> Also, the template files for uncrustify need to be updated to match the contents >>> from the EDK II C Coding Standard. Pease see the file and function header templates >>> in this location: >>> >>> https://github.com/mdkinney/edk2/tree/Bug_3737_ApplyUncrustifyChanges_V4/.uncrustify >>> >>> I also see you use a temp directory in Build output: >>> >>>> + self._working_dir = os.path.join( >>>> + self._abs_workspace_path, "Build", self._package_name, "UncrustifyPlugin") >>> >>> In the EccCheck and LicenseCheck patches I sent earlier today, I decided to put all >>> temp directories associated with pytools Plugins in the path: >>> >>> Build/.pytool/Plugin/ >>> >>> This provide a unique path for temp files associated with Plugins that matches >>> the source path to the Plugin so plugins will never use the same temp dir path >>> and each plugin can safely clean up its own temp directory. >>> >>> Thanks, >>> >>> Mike >>> >>>> -----Original Message----- >>>> From: mikuback@linux.microsoft.com >>>> Sent: Tuesday, November 23, 2021 8:54 AM >>>> To: devel@edk2.groups.io >>>> Cc: Kinney, Michael D ; Liming Gao ; Sean Brogan >>>> ; Bret Barkelew >>>> Subject: [PATCH v1 1/1] .pytool/Plugin/Uncrustify: Add Uncrustify plugin >>>> >>>> From: Michael Kubacki >>>> >>>> Adds a new CI plugin for Uncrustify. This is used to check >>>> coding standard compliance of source code to the EDK II C Coding >>>> Standards Specification. >>>> >>>> An external dependency is added in the plugin directory to retrieve >>>> the Uncrustify executable. Currently, the executable is from an edk2 >>>> fork of the application. >>>> >>>> The default Uncrustify configuration files are added in the plugin >>>> directory. Additional details are in the Readme.md file added in >>>> the Uncrustify plugin directory. >>>> >>>> Note: A V2 is planned that will fail the Uncrustify plugin results >>>> if a file or function template header is found in a formatted file. >>>> This will be additional functionality added in the plugin and will >>>> not change the way Uncrustify is invoked. >>>> >>>> Cc: Michael D Kinney >>>> Cc: Liming Gao >>>> Cc: Sean Brogan >>>> Cc: Bret Barkelew >>>> Signed-off-by: Michael Kubacki >>>> --- >>>> .pytool/Plugin/Uncrustify/Readme.md | 116 ++++ >>>> .pytool/Plugin/Uncrustify/Uncrustify.py | 556 ++++++++++++++++++++ >>>> .pytool/Plugin/Uncrustify/default_file_header.txt | 9 + >>>> .pytool/Plugin/Uncrustify/default_function_header.txt | 15 + >>>> .pytool/Plugin/Uncrustify/uncrustify.cfg | 466 ++++++++++++++++ >>>> .pytool/Plugin/Uncrustify/uncrustify_ext_dep.yaml | 16 + >>>> .pytool/Plugin/Uncrustify/uncrustify_plug_in.yaml | 12 + >>>> .pytool/Readme.md | 4 + >>>> 8 files changed, 1194 insertions(+) >>>> >>>> diff --git a/.pytool/Plugin/Uncrustify/Readme.md b/.pytool/Plugin/Uncrustify/Readme.md >>>> new file mode 100644 >>>> index 000000000000..46586d81c68c >>>> --- /dev/null >>>> +++ b/.pytool/Plugin/Uncrustify/Readme.md >>>> @@ -0,0 +1,116 @@ >>>> +# Uncrustify Plugin >>>> + >>>> +This CiBuildPlugin scans all the files in a given package and checks for coding standard compliance issues. >>>> + >>>> +This plugin requires the directory containing the Uncrustify executable that should be used for this plugin to >>>> +be specified in an environment variable named `UNCRUSTIFY_CI_PATH`. This unique variable name is used to avoid >> confusion >>>> +with other paths to Uncrustify which might not be the expected build for use by this plugin. >>>> + >>>> +By default, an Uncrustify configuration file named "uncrustify.cfg" located in the same directory as the plugin is >>>> +used. The value can be overridden to a package-specific path with the `ConfigFilePath` configuration file option. >>>> + >>>> +* Uncrustify source code and documentation: https://github.com/uncrustify/uncrustify >>>> +* Project Mu Uncrustify fork source code and documentation: https://dev.azure.com/projectmu/Uncrustify >>>> + >>>> +## Files Checked in a Package >>>> + >>>> +By default, this plugin will discover all files in the package with the following default paths: >>>> + >>>> +```python >>>> +[ >>>> +# C source >>>> +"*.c", >>>> +"*.h" >>>> +] >>>> +``` >>>> + >>>> +From this list of files, any files ignored by Git or residing in a Git submodule will be removed. If Git is not >>>> +found, submodules are not found, or ignored files are not found no changes are made to the list of discovered files. >>>> + >>>> +To control the paths checked in a given package, review the configuration options described in this file. >>>> + >>>> +## Configuration >>>> + >>>> +The plugin can be configured with a few optional configuration options. >>>> + >>>> +``` yaml >>>> + "Uncrustify": { >>>> + "AuditOnly": False, # Don't fail the build if there are errors. Just log them. >>>> + "OutputFileDiffs": False # Output chunks of formatting diffs in the test case log. >>>> + # This can significantly slow down the plugin on very large packages. >>>> + "ConfigFilePath": "", # Custom path to an Uncrustify config file. Path is relative to the package. >>>> + "IgnoreStandardPaths": [], # Standard Plugin defined paths that should be ignored. >>>> + "AdditionalIncludePaths": [] # Additional paths to check formatting (wildcards supported). >>>> + } >>>> +``` >>>> + >>>> +### `AdditionalIncludePaths` >>>> + >>>> +A package configuration file can specify any additional paths to be included with this option. >>>> + >>>> +At this time, it is recommended all files run against the plugin be written in the C or C++ language. >>>> + >>>> +### `AuditOnly` >>>> + >>>> +`Boolean` - Default is `False`. >>>> + >>>> +If `True`, run the test in an "audit only mode" which will log all errors but instead of failing the build, it will >> set >>>> +the test as skipped. This allows visibility into the failures without breaking the build. >>>> + >>>> +### `ConfigFilePath` >>>> + >>>> +`String` - Default is `"uncrustify.cfg"` >>>> + >>>> +When specified in the config file, this is a package relative path to the Uncrustify configuration file. >>>> + >>>> +### `IgnoreStandardPaths` >>>> + >>>> +This plugin by default will check the below standard paths. A package configuration file can specify any of these >> paths >>>> +to be ignored. >>>> + >>>> +```python >>>> +[ >>>> +# C source >>>> +"*.c", >>>> +"*.h" >>>> +] >>>> +``` >>>> + >>>> +### `OutputFileDiffs` >>>> + >>>> +`Boolean` - Default is `False`. >>>> + >>>> +If `True`, output diffs of formatting changes into the test case log. This is helpful to exactly understand what >> changes >>>> +need to be made to the source code in order to fix a coding standard compliance issue. >>>> + >>>> +Note that calculating the file diffs on a very large set of of results (e.g. >100 files) can significantly slow down >>>> +plugin execution. >>>> + >>>> +### `SkipGitExclusions` >>>> + >>>> +`Boolean` - Default is `False`. >>>> + >>>> +By default, files in paths matched in a .gitignore file or a recognized git submodule are excluded. If this option >>>> +is `True`, the plugin will not attempt to recognize these files and exclude them. >>>> + >>>> +## High-Level Plugin Operation >>>> + >>>> +This plugin generates two main sets of temporary files: >>>> + >>>> + 1. A working directory in the directory `Build/{package_name}` >>>> + 2. For each source file with formatting errors, a sibling file with the `.uncrustify_plugin` extension >>>> + >>>> +The working directory contains temporary files unique to operation of the plugin. All of these files are removed on >>>> +exit of the plugin including successful or unsuccessful execution (such as a Python exception occurring). If for any >>>> +reason, any files in the package exist prior to running the plugin with the `.uncrustify_plugin` extension, the plugin >>>> +will inform the user to remove these files and exit before running Uncrustify. This is to ensure the accuracy of the >>>> +results reported from each execution instance of the plugin. >>>> + >>>> +The plugin determines the list of relevant files to check with Uncrustify and then invokes Uncrustify with that file >>>> +list. For any files not compliant to the configuration file provided, Uncrustify will generate a corresponding file >>>> +with the `.uncrustify_plugin` extension. The plugin discovers all of these files. If any such files are present, this >>>> +indicates a formatting issue was found and the test is marked failed (unless `AuditOnly` mode is enabled). >>>> + >>>> +The test case log will contain a report of which files failed to format properly, allowing the user to run Uncrustify >>>> +against the file locally to fix the issue. If the `OutputFileDiffs` configuration option is set to `True`, the plugin >>>> +will output diff chunks for all code formatting issues in the test case log. >>>> diff --git a/.pytool/Plugin/Uncrustify/Uncrustify.py b/.pytool/Plugin/Uncrustify/Uncrustify.py >>>> new file mode 100644 >>>> index 000000000000..b207b1df4cb1 >>>> --- /dev/null >>>> +++ b/.pytool/Plugin/Uncrustify/Uncrustify.py >>>> @@ -0,0 +1,556 @@ >>>> +# @file Uncrustify.py >>>> +# >>>> +# An edk2-pytool based plugin wrapper for Uncrustify >>>> +# >>>> +# Copyright (c) Microsoft Corporation. >>>> +# SPDX-License-Identifier: BSD-2-Clause-Patent >>>> +## >>>> +import difflib >>>> +import errno >>>> +import logging >>>> +import os >>>> +import pathlib >>>> +import shutil >>>> +import timeit >>>> +from edk2toolext.environment import version_aggregator >>>> +from edk2toolext.environment.plugin_manager import PluginManager >>>> +from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin >>>> +from edk2toolext.environment.plugintypes.uefi_helper_plugin import HelperFunctions >>>> +from edk2toolext.environment.var_dict import VarDict >>>> +from edk2toollib.log.junit_report_format import JunitReportTestCase >>>> +from edk2toollib.uefi.edk2.path_utilities import Edk2Path >>>> +from edk2toollib.utility_functions import RunCmd >>>> +from io import StringIO >>>> +from typing import Any, Dict, List, Tuple >>>> + >>>> +# >>>> +# Provide more user friendly messages for certain scenarios >>>> +# >>>> +class UncrustifyException(Exception): >>>> + def __init__(self, message, exit_code): >>>> + super().__init__(message) >>>> + self.exit_code = exit_code >>>> + >>>> + >>>> +class UncrustifyAppEnvVarNotFoundException(UncrustifyException): >>>> + def __init__(self, message): >>>> + super().__init__(message, -101) >>>> + >>>> + >>>> +class UncrustifyAppVersionErrorException(UncrustifyException): >>>> + def __init__(self, message): >>>> + super().__init__(message, -102) >>>> + >>>> + >>>> +class UncrustifyAppExecutionException(UncrustifyException): >>>> + def __init__(self, message): >>>> + super().__init__(message, -103) >>>> + >>>> + >>>> +class UncrustifyStalePluginFormattedFilesException(UncrustifyException): >>>> + def __init__(self, message): >>>> + super().__init__(message, -120) >>>> + >>>> + >>>> +class UncrustifyInputFileCreationErrorException(UncrustifyException): >>>> + def __init__(self, message): >>>> + super().__init__(message, -121) >>>> + >>>> +class UncrustifyInvalidIgnoreStandardPathsException(UncrustifyException): >>>> + def __init__(self, message): >>>> + super().__init__(message, -122) >>>> + >>>> +class UncrustifyGitIgnoreFileException(UncrustifyException): >>>> + def __init__(self, message): >>>> + super().__init__(message, -140) >>>> + >>>> + >>>> +class UncrustifyGitSubmoduleException(UncrustifyException): >>>> + def __init__(self, message): >>>> + super().__init__(message, -141) >>>> + >>>> + >>>> +class Uncrustify(ICiBuildPlugin): >>>> + """ >>>> + A CiBuildPlugin that uses Uncrustify to format the source files in the >>>> + package being tested for coding standard issues. >>>> + >>>> + By default, the plugin runs against standard C source file extensions but >>>> + its configuration can be modified through its configuration file. >>>> + >>>> + Configuration options: >>>> + "Uncrustify": { >>>> + "AdditionalIncludePaths": [], # Additional paths to check formatting (wildcards supported). >>>> + "AuditOnly": False, # Don't fail the build if there are errors. Just log them. >>>> + "ConfigFilePath": "", # Custom path to an Uncrustify config file. >>>> + "IgnoreStandardPaths": [], # Standard Plugin defined paths that should be ignored. >>>> + "OutputFileDiffs": False, # Output chunks of formatting diffs in the test case log. >>>> + # This can significantly slow down the plugin on very large packages. >>>> + "SkipGitExclusions": False # Don't exclude git ignored files and files in git submodules. >>>> + } >>>> + """ >>>> + >>>> + # >>>> + # By default, use an "uncrustify.cfg" config file in the plugin directory >>>> + # A package can override this path via "ConfigFilePath" >>>> + # >>>> + # Note: Values specified via "ConfigFilePath" are relative to the package >>>> + # >>>> + DEFAULT_CONFIG_FILE_PATH = os.path.join( >>>> + pathlib.Path(__file__).parent.resolve(), "uncrustify.cfg") >>>> + >>>> + # >>>> + # The extension used for formatted files produced by this plugin >>>> + # >>>> + FORMATTED_FILE_EXTENSION = ".uncrustify_plugin" >>>> + >>>> + # >>>> + # A package can add any additional paths with "AdditionalIncludePaths" >>>> + # A package can remove any of these paths with "IgnoreStandardPaths" >>>> + # >>>> + STANDARD_PLUGIN_DEFINED_PATHS = ("*.c", "*.h") >>>> + >>>> + # >>>> + # The Uncrustify application path should set in this environment variable >>>> + # >>>> + UNCRUSTIFY_PATH_ENV_KEY = "UNCRUSTIFY_CI_PATH" >>>> + >>>> + def GetTestName(self, packagename: str, environment: VarDict) -> Tuple: >>>> + """ Provide the testcase name and classname for use in reporting >>>> + >>>> + Args: >>>> + packagename: string containing name of package to build >>>> + environment: The VarDict for the test to run in >>>> + Returns: >>>> + A tuple containing the testcase name and the classname >>>> + (testcasename, classname) >>>> + testclassname: a descriptive string for the testcase can include whitespace >>>> + classname: should be patterned .. >>>> + """ >>>> + return ("Check file coding standard compliance in " + packagename, packagename + ".Uncrustify") >>>> + >>>> + def RunBuildPlugin(self, package_rel_path: str, edk2_path: Edk2Path, package_config: Dict[str, List[str]], >>>> environment_config: Any, plugin_manager: PluginManager, plugin_manager_helper: HelperFunctions, tc: >> JunitReportTestCase, >>>> output_stream=None) -> int: >>>> + """ >>>> + External function of plugin. This function is used to perform the task of the CiBuild Plugin. >>>> + >>>> + Args: >>>> + - package_rel_path: edk2 workspace relative path to the package >>>> + - edk2_path: Edk2Path object with workspace and packages paths >>>> + - package_config: Dictionary with the package configuration >>>> + - environment_config: Environment configuration >>>> + - plugin_manager: Plugin Manager Instance >>>> + - plugin_manager_helper: Plugin Manager Helper Instance >>>> + - tc: JUnit test case >>>> + - output_stream: The StringIO output stream from this plugin (logging) >>>> + >>>> + Returns >>>> + >0 : Number of errors found >>>> + 0 : Passed successfully >>>> + -1 : Skipped for missing prereq >>>> + """ >>>> + try: >>>> + # Initialize plugin and check pre-requisites. >>>> + self._initialize_environment_info( >>>> + package_rel_path, edk2_path, package_config, tc) >>>> + self._initialize_configuration() >>>> + self._check_for_preexisting_formatted_files() >>>> + >>>> + # Log important context information. >>>> + self._log_uncrustify_app_info() >>>> + >>>> + # Create meta input files & directories >>>> + self._create_temp_working_directory() >>>> + self._create_uncrustify_file_list_file() >>>> + >>>> + self._run_uncrustify() >>>> + >>>> + # Post-execution actions. >>>> + self._process_uncrustify_results() >>>> + >>>> + except UncrustifyException as e: >>>> + self._tc.LogStdError( >>>> + f"Uncrustify error {e.exit_code}. Details:\n\n{str(e)}") >>>> + logging.warning( >>>> + f"Uncrustify error {e.exit_code}. Details:\n\n{str(e)}") >>>> + return -1 >>>> + else: >>>> + if self._formatted_file_error_count > 0: >>>> + if self._audit_only_mode: >>>> + logging.info( >>>> + "Setting test as skipped since AuditOnly is enabled") >>>> + self._tc.SetSkipped() >>>> + return -1 >>>> + else: >>>> + self._tc.SetFailed( >>>> + f"Uncrustify checks failed due to {self._formatted_file_error_count} incorrectly formatted >>>> files.", "CHECK_FAILED") >>>> + else: >>>> + self._tc.SetSuccess() >>>> + return self._formatted_file_error_count >>>> + finally: >>>> + self._cleanup_temporary_formatted_files() >>>> + self._cleanup_temporary_directory() >>>> + >>>> + def _initialize_configuration(self) -> None: >>>> + """ >>>> + Initializes plugin configuration. >>>> + """ >>>> + self._initialize_app_info() >>>> + self._initialize_config_file_info() >>>> + self._initialize_file_to_format_info() >>>> + self._initialize_test_case_output_options() >>>> + >>>> + def _check_for_preexisting_formatted_files(self) -> None: >>>> + """ >>>> + Checks if any formatted files from prior execution are present. >>>> + >>>> + Existence of such files is an unexpected condition. This might result >>>> + from an error that occurred during a previous run or a premature exit from a debug scenario. In any case, the >>>> package should be clean before starting a new run. >>>> + """ >>>> + pre_existing_formatted_file_count = len( >>>> + [str(path.resolve()) for path in >>>> pathlib.Path(self._abs_package_path).rglob(f'*{Uncrustify.FORMATTED_FILE_EXTENSION}')]) >>>> + >>>> + if pre_existing_formatted_file_count > 0: >>>> + raise UncrustifyStalePluginFormattedFilesException( >>>> + f"{pre_existing_formatted_file_count} formatted files already exist. To prevent overwriting these >> files, >>>> please remove them before running this plugin.") >>>> + >>>> + def _cleanup_temporary_directory(self) -> None: >>>> + """ >>>> + Cleans up the temporary directory used for this execution instance. >>>> + >>>> + This removes the directory and all files created during this instance. >>>> + """ >>>> + if hasattr(self, '_working_dir'): >>>> + self._remove_tree(self._working_dir) >>>> + >>>> + def _cleanup_temporary_formatted_files(self) -> None: >>>> + """ >>>> + Cleans up the temporary formmatted files produced by Uncrustify. >>>> + >>>> + This will recursively remove all formatted files generated by Uncrustify >>>> + during this execution instance. >>>> + """ >>>> + if hasattr(self, '_abs_package_path'): >>>> + formatted_files = [str(path.resolve()) for path in pathlib.Path( >>>> + self._abs_package_path).rglob(f'*{Uncrustify.FORMATTED_FILE_EXTENSION}')] >>>> + >>>> + for formatted_file in formatted_files: >>>> + os.remove(formatted_file) >>>> + >>>> + def _create_temp_working_directory(self) -> None: >>>> + """ >>>> + Creates the temporary directory used for this execution instance. >>>> + """ >>>> + self._working_dir = os.path.join( >>>> + self._abs_workspace_path, "Build", self._package_name, "UncrustifyPlugin") >>>> + >>>> + try: >>>> + pathlib.Path(self._working_dir).mkdir(parents=True, exist_ok=True) >>>> + except OSError as e: >>>> + raise UncrustifyInputFileCreationErrorException( >>>> + f"Error creating plugin directory {self._working_dir}.\n\n{repr(e)}.") >>>> + >>>> + def _create_uncrustify_file_list_file(self) -> None: >>>> + """ >>>> + Creates the file with the list of source files for Uncrustify to process. >>>> + """ >>>> + self._app_input_file_path = os.path.join( >>>> + self._working_dir, "uncrustify_file_list.txt") >>>> + >>>> + with open(self._app_input_file_path, 'w', encoding='utf8') as f: >>>> + f.writelines(f"\n".join(self._abs_file_paths_to_format)) >>>> + >>>> + def _execute_uncrustify(self) -> None: >>>> + """ >>>> + Executes Uncrustify with the initialized configuration. >>>> + """ >>>> + output = StringIO() >>>> + self._app_exit_code = RunCmd( >>>> + self._app_path, >>>> + f"-c {self._app_config_file} -F {self._app_input_file_path} --if-changed --suffix >>>> {Uncrustify.FORMATTED_FILE_EXTENSION}", outstream=output) >>>> + self._app_output = output.getvalue().strip().splitlines() >>>> + >>>> + def _get_git_ignored_paths(self) -> List[str]: >>>> + """" >>>> + Returns a list of file absolute path strings to all files ignored in this git repository. >>>> + >>>> + If git is not found, an empty list will be returned. >>>> + """ >>>> + if not shutil.which("git"): >>>> + logging.warn( >>>> + "Git is not found on this system. Git submodule paths will not be considered.") >>>> + return [] >>>> + >>>> + outstream_buffer = StringIO() >>>> + exit_code = RunCmd("git", "ls-files --other", >>>> + workingdir=self._abs_workspace_path, outstream=outstream_buffer, >> logging_level=logging.NOTSET) >>>> + if (exit_code != 0): >>>> + raise UncrustifyGitIgnoreFileException( >>>> + f"An error occurred reading git ignore settings. This will prevent Uncrustify from running against the >>>> expected set of files.") >>>> + >>>> + # Note: This will potentially be a large list, but at least sorted >>>> + return outstream_buffer.getvalue().strip().splitlines() >>>> + >>>> + def _get_git_submodule_paths(self) -> List[str]: >>>> + """ >>>> + Returns a list of directory absolute path strings to the root of each submodule in the workspace repository. >>>> + >>>> + If git is not found, an empty list will be returned. >>>> + """ >>>> + if not shutil.which("git"): >>>> + logging.warn( >>>> + "Git is not found on this system. Git submodule paths will not be considered.") >>>> + return [] >>>> + >>>> + if os.path.isfile(os.path.join(self._abs_workspace_path, ".gitmodules")): >>>> + logging.info( >>>> + f".gitmodules file found. Excluding submodules in {self._package_name}.") >>>> + >>>> + outstream_buffer = StringIO() >>>> + exit_code = RunCmd("git", "config --file .gitmodules --get-regexp path", >> workingdir=self._abs_workspace_path, >>>> outstream=outstream_buffer, logging_level=logging.NOTSET) >>>> + if (exit_code != 0): >>>> + raise UncrustifyGitSubmoduleException( >>>> + f".gitmodule file detected but an error occurred reading the file. Cannot proceed with unknown >>>> submodule paths.") >>>> + >>>> + submodule_paths = [] >>>> + for line in outstream_buffer.getvalue().strip().splitlines(): >>>> + submodule_paths.append( >>>> + os.path.normpath(os.path.join(self._abs_workspace_path, line.split()[1]))) >>>> + >>>> + return submodule_paths >>>> + else: >>>> + return [] >>>> + >>>> + def _initialize_app_info(self) -> None: >>>> + """ >>>> + Initialize Uncrustify application information. >>>> + >>>> + This function will determine the application path and version. >>>> + """ >>>> + # Verify Uncrustify is specified in the environment. >>>> + if Uncrustify.UNCRUSTIFY_PATH_ENV_KEY not in os.environ: >>>> + raise UncrustifyAppEnvVarNotFoundException( >>>> + f"Uncrustify environment variable {Uncrustify.UNCRUSTIFY_PATH_ENV_KEY} is not present.") >>>> + >>>> + self._app_path = shutil.which('uncrustify', path=os.environ[Uncrustify.UNCRUSTIFY_PATH_ENV_KEY]) >>>> + >>>> + if self._app_path is None: >>>> + raise FileNotFoundError( >>>> + errno.ENOENT, os.strerror(errno.ENOENT), self._app_path) >>>> + >>>> + self._app_path = os.path.normcase(os.path.normpath(self._app_path)) >>>> + >>>> + if not os.path.isfile(self._app_path): >>>> + raise FileNotFoundError( >>>> + errno.ENOENT, os.strerror(errno.ENOENT), self._app_path) >>>> + >>>> + # Verify Uncrustify is present at the expected path. >>>> + return_buffer = StringIO() >>>> + ret = RunCmd(self._app_path, "--version", outstream=return_buffer) >>>> + if (ret != 0): >>>> + raise UncrustifyAppVersionErrorException( >>>> + f"Error occurred executing --version: {ret}.") >>>> + >>>> + # Log Uncrustify version information. >>>> + self._app_version = return_buffer.getvalue().strip() >>>> + self._tc.LogStdOut(f"Uncrustify version: {self._app_version}") >>>> + version_aggregator.GetVersionAggregator().ReportVersion( >>>> + "Uncrustify", self._app_version, version_aggregator.VersionTypes.INFO) >>>> + >>>> + def _initialize_config_file_info(self) -> None: >>>> + """ >>>> + Initialize Uncrustify configuration file info. >>>> + >>>> + The config file path is relative to the package root. >>>> + """ >>>> + self._app_config_file = Uncrustify.DEFAULT_CONFIG_FILE_PATH >>>> + if "ConfigFilePath" in self._package_config: >>>> + self._app_config_file = self._package_config["ConfigFilePath"].strip() >>>> + >>>> + self._app_config_file = os.path.normpath( >>>> + os.path.join(self._abs_package_path, self._app_config_file)) >>>> + >>>> + if not os.path.isfile(self._app_config_file): >>>> + raise FileNotFoundError( >>>> + errno.ENOENT, os.strerror(errno.ENOENT), self._app_config_file) >>>> + >>>> + def _initialize_environment_info(self, package_rel_path: str, edk2_path: Edk2Path, package_config: Dict[str, >>>> List[str]], tc: JunitReportTestCase) -> None: >>>> + """ >>>> + Initializes plugin environment information. >>>> + """ >>>> + self._abs_package_path = edk2_path.GetAbsolutePathOnThisSytemFromEdk2RelativePath( >>>> + package_rel_path) >>>> + self._abs_workspace_path = edk2_path.WorkspacePath >>>> + self._package_config = package_config >>>> + self._package_name = os.path.basename( >>>> + os.path.normpath(package_rel_path)) >>>> + self._rel_package_path = package_rel_path >>>> + self._tc = tc >>>> + >>>> + def _initialize_file_to_format_info(self) -> None: >>>> + """ >>>> + Forms the list of source files for Uncrustify to process. >>>> + """ >>>> + # Create a list of all the package relative file paths in the package to run against Uncrustify. >>>> + rel_file_paths_to_format = list( >>>> + Uncrustify.STANDARD_PLUGIN_DEFINED_PATHS) >>>> + >>>> + # Allow the ci.yaml to remove any of the pre-defined standard paths >>>> + if "IgnoreStandardPaths" in self._package_config: >>>> + for a in self._package_config["IgnoreStandardPaths"]: >>>> + if a.strip() in rel_file_paths_to_format: >>>> + self._tc.LogStdOut( >>>> + f"Ignoring standard path due to ci.yaml ignore: {a}") >>>> + rel_file_paths_to_format.remove(a.strip()) >>>> + else: >>>> + raise UncrustifyInvalidIgnoreStandardPathsException(f"Invalid IgnoreStandardPaths value: {a}") >>>> + >>>> + # Allow the ci.yaml to specify additional include paths for this package >>>> + if "AdditionalIncludePaths" in self._package_config: >>>> + rel_file_paths_to_format.extend( >>>> + self._package_config["AdditionalIncludePaths"]) >>>> + >>>> + self._abs_file_paths_to_format = [] >>>> + for path in rel_file_paths_to_format: >>>> + self._abs_file_paths_to_format.extend( >>>> + [str(path.resolve()) for path in pathlib.Path(self._abs_package_path).rglob(path)]) >>>> + >>>> + if not "SkipGitExclusions" in self._package_config or not self._package_config["SkipGitExclusions"]: >>>> + # Remove files ignored by git >>>> + logging.info( >>>> + f"{self._package_name} file count before git ignore file exclusion: >>>> {len(self._abs_file_paths_to_format)}") >>>> + >>>> + ignored_paths = self._get_git_ignored_paths() >>>> + self._abs_file_paths_to_format = list( >>>> + set(self._abs_file_paths_to_format).difference(ignored_paths)) >>>> + >>>> + logging.info( >>>> + f"{self._package_name} file count after git ignore file exclusion: >>>> {len(self._abs_file_paths_to_format)}") >>>> + >>>> + # Remove files in submodules >>>> + logging.info( >>>> + f"{self._package_name} file count before submodule exclusion: {len(self._abs_file_paths_to_format)}") >>>> + >>>> + submodule_paths = tuple(self._get_git_submodule_paths()) >>>> + for path in submodule_paths: >>>> + logging.info(f" submodule path: {path}") >>>> + >>>> + self._abs_file_paths_to_format = [ >>>> + f for f in self._abs_file_paths_to_format if not f.startswith(submodule_paths)] >>>> + >>>> + logging.info( >>>> + f"{self._package_name} file count after submodule exclusion: {len(self._abs_file_paths_to_format)}") >>>> + >>>> + # Sort the files for more consistent results >>>> + self._abs_file_paths_to_format.sort() >>>> + >>>> + def _initialize_test_case_output_options(self) -> None: >>>> + """ >>>> + Initializes options that influence test case output. >>>> + """ >>>> + self._audit_only_mode = False >>>> + self._output_file_diffs = False >>>> + >>>> + if "AuditOnly" in self._package_config and self._package_config["AuditOnly"]: >>>> + self._audit_only_mode = True >>>> + >>>> + if "OutputFileDiffs" in self._package_config and self._package_config["OutputFileDiffs"]: >>>> + self._output_file_diffs = True >>>> + >>>> + def _log_uncrustify_app_info(self) -> None: >>>> + """ >>>> + Logs Uncrustify application information. >>>> + """ >>>> + self._tc.LogStdOut(f"Found Uncrustify at {self._app_path}") >>>> + self._tc.LogStdOut(f"Uncrustify version: {self._app_version}") >>>> + self._tc.LogStdOut('\n') >>>> + logging.info(f"Found Uncrustify at {self._app_path}") >>>> + logging.info(f"Uncrustify version: {self._app_version}") >>>> + logging.info('\n') >>>> + >>>> + def _process_uncrustify_results(self) -> None: >>>> + """ >>>> + Process the results from Uncrustify. >>>> + >>>> + Determines whether formatting errors are present and logs failures. >>>> + """ >>>> + formatted_files = [str(path.resolve()) for path in pathlib.Path( >>>> + self._abs_package_path).rglob(f'*{Uncrustify.FORMATTED_FILE_EXTENSION}')] >>>> + >>>> + self._formatted_file_error_count = len(formatted_files) >>>> + >>>> + if self._formatted_file_error_count > 0: >>>> + self._tc.LogStdError("Files with formatting errors:\n") >>>> + >>>> + if self._output_file_diffs: >>>> + logging.info("Calculating file diffs. This might take a while...") >>>> + >>>> + for formatted_file in formatted_files: >>>> + pre_formatted_file = formatted_file[:- >>>> + len(Uncrustify.FORMATTED_FILE_EXTENSION)] >>>> + >>>> + if self._output_file_diffs: >>>> + with open(pre_formatted_file) as pf, open(formatted_file) as ff: >>>> + self._tc.LogStdError( >>>> + f"Formatting errors in {os.path.relpath(pre_formatted_file, self._abs_package_path)}:\n") >>>> + >>>> + for line in difflib.unified_diff(pf.readlines(), ff.readlines(), fromfile=pre_formatted_file, >>>> tofile=formatted_file, n=3): >>>> + self._tc.LogStdError(line) >>>> + >>>> + self._tc.LogStdError('\n') >>>> + else: >>>> + self._tc.LogStdError(pre_formatted_file) >>>> + >>>> + def _remove_tree(self, dir_path: str, ignore_errors: bool = False) -> None: >>>> + """ >>>> + Helper for removing a directory. Over time there have been >>>> + many private implementations of this due to reliability issues in the >>>> + shutil implementations. To consolidate on a single function this helper is added. >>>> + >>>> + On error try to change file attributes. Also add retry logic. >>>> + >>>> + This function is temporarily borrowed from edk2toollib.utility_functions >>>> + since the version used in edk2 is not recent enough to include the >>>> + function. >>>> + >>>> + This function should be replaced by "RemoveTree" when it is available. >>>> + >>>> + Args: >>>> + - dir_path: Path to directory to remove. >>>> + - ignore_errors: Whether to ignore errors during removal >>>> + """ >>>> + >>>> + def _remove_readonly(func, path, _): >>>> + """ >>>> + Private function to attempt to change permissions on file/folder being deleted. >>>> + """ >>>> + os.chmod(path, os.stat.S_IWRITE) >>>> + func(path) >>>> + >>>> + for _ in range(3): # retry up to 3 times >>>> + try: >>>> + print(dir_path) >>>> + shutil.rmtree(dir_path, ignore_errors=ignore_errors, onerror=_remove_readonly) >>>> + except OSError as err: >>>> + logging.warning(f"Failed to fully remove {dir_path}: {err}") >>>> + else: >>>> + break >>>> + else: >>>> + raise RuntimeError(f"Failed to remove {dir_path}") >>>> + >>>> + def _run_uncrustify(self) -> None: >>>> + """ >>>> + Runs Uncrustify for this instance of plugin execution. >>>> + """ >>>> + logging.info("Executing Uncrustify. This might take a while...") >>>> + start_time = timeit.default_timer() >>>> + self._execute_uncrustify() >>>> + end_time = timeit.default_timer() - start_time >>>> + >>>> + execution_summary = f"Uncrustify executed against {len(self._abs_file_paths_to_format)} files in >>>> {self._package_name} in {end_time:.2f} seconds.\n" >>>> + >>>> + self._tc.LogStdOut(execution_summary) >>>> + logging.info(execution_summary) >>>> + >>>> + if self._app_exit_code != 0 and self._app_exit_code != 1: >>>> + raise UncrustifyAppExecutionException( >>>> + f"Error {str(self._app_exit_code)} returned from Uncrustify:\n\n{str(self._app_output)}") >>>> diff --git a/.pytool/Plugin/Uncrustify/default_file_header.txt b/.pytool/Plugin/Uncrustify/default_file_header.txt >>>> new file mode 100644 >>>> index 000000000000..2955a734dfe1 >>>> --- /dev/null >>>> +++ b/.pytool/Plugin/Uncrustify/default_file_header.txt >>>> @@ -0,0 +1,9 @@ >>>> +/** @file >>>> + Brief description of the file's purpose. >>>> + >>>> + Detailed description of the file's contents and other useful >>>> + information for a person viewing the file for the first time. >>>> + >>>> + <> >>>> + SPDX-License-Identifier: BSD-2-Clause-Patent >>>> +**/ >>>> diff --git a/.pytool/Plugin/Uncrustify/default_function_header.txt >> b/.pytool/Plugin/Uncrustify/default_function_header.txt >>>> new file mode 100644 >>>> index 000000000000..66edc72e6731 >>>> --- /dev/null >>>> +++ b/.pytool/Plugin/Uncrustify/default_function_header.txt >>>> @@ -0,0 +1,15 @@ >>>> +/** >>>> + Brief description of this function's purpose. >>>> + >>>> + Follow it immediately with the detailed description. >>>> + >>>> + @param[in] Arg1 Description of Arg1. >>>> + @param[in] Arg2 Description of Arg2 This is complicated and requires >>>> + multiple lines to describe. >>>> + @param[out] Arg3 Description of Arg3. >>>> + @param[in, out] Arg4 Description of Arg4. >>>> + >>>> + @retval VAL_ONE Description of what VAL_ONE signifies. >>>> + @retval OTHER This is the only other return value. If there were other >>>> + return values, they would be listed. >>>> +**/ >>>> diff --git a/.pytool/Plugin/Uncrustify/uncrustify.cfg b/.pytool/Plugin/Uncrustify/uncrustify.cfg >>>> new file mode 100644 >>>> index 000000000000..d6e98dfa9c01 >>>> --- /dev/null >>>> +++ b/.pytool/Plugin/Uncrustify/uncrustify.cfg >>>> @@ -0,0 +1,466 @@ >>>> +## @file >>>> +# Uncrustify Configuration File for EDK II C Code >>>> +# >>>> +# Coding Standard: https://edk2-docs.gitbook.io/edk-ii-c-coding-standards-specification/ >>>> +# >>>> +# This configuration file is meant to be a "best attempt" to align with the >>>> +# definitions in the EDK II C Coding Standards Specification. >>>> +# >>>> +# Copyright (c) Microsoft Corporation. >>>> +# SPDX-License-Identifier: BSD-2-Clause-Patent >>>> +## >>>> + >>>> +# Force UTF-8 encoding (no UTF-16) >>>> +enable_digraphs = false >>>> +utf8_byte = false >>>> +utf8_force = true >>>> + >>>> +# Code width / line splitting >>>> +#code_width =120 # TODO: This causes non-deterministic behaviour in some cases when code >> wraps >>>> +ls_code_width =false >>>> +ls_for_split_full =true >>>> +ls_func_split_full =true >>>> +pos_comma =trail >>>> + >>>> +# 5.1.7 All files must end with CRLF >>>> +newlines = crlf >>>> + >>>> +# 5.1.2 Do not use tab characters >>>> + >>>> +cmt_convert_tab_to_spaces = true # Whether to convert all tabs to spaces in comments. If false, tabs in >>>> + # comments are left alone, unless used for indenting. >>>> +indent_columns = 2 # Number of spaces for indentation >>>> +indent_with_tabs = 0 # Do not use TAB characters >>>> +string_replace_tab_chars = true # Replace TAB with SPACE >>>> + # Note: This will break .robot files but is needed for edk2 style >>>> + >>>> +# 5.2.1.1 There shall be only one statement on a line (statement ends with ;) >>>> +nl_multi_line_cond = true # Add a newline between ')' and '{' if the ')' is on a different line than >>>> + # the if/for/etc. >>>> +nl_after_semicolon = true # Whether to add a newline after semicolons, except in 'for' statements. >>>> + >>>> +# 5.2.1.3 An open brace '{' goes on the same line as the closing parenthesis ')' of simple predicate expressions >>>> +mod_full_brace_do = add # Add or remove braces on a single-line 'do' statement. >>>> +mod_full_brace_for = add >>>> +mod_full_brace_function = add # Add or remove braces on a single-line function definition. >>>> +mod_full_brace_if = add # Add or remove braces on a single-line 'if' statement. Braces will not be >>>> + # removed if the braced statement contains an 'else'. >>>> +mod_full_brace_if_chain = false >>>> +mod_full_brace_while = add >>>> + >>>> +# 5.2.1.4 A close brace '}' always goes at the beginning of the last line of the body >>>> +eat_blanks_after_open_brace = true >>>> +eat_blanks_before_close_brace = true # Whether to remove blank lines before '}'. >>>> + >>>> +# 5.2.2.2 Always put space before and after binary operators. >>>> +sp_assign = add # Add or remove space around assignment operator '=', '+=', etc. >>>> +sp_assign_default = add >>>> +sp_bool = add # Add or remove space around boolean operators '&&' and '||'. >>>> +sp_compare = add # Add or remove space around compare operator '<', '>', '==', etc. >>>> + >>>> +# 5.2.2.3 Do not put space between unary operators and their object >>>> +sp_addr = remove # A or remove space after the '&' (address-of) unary operator. >>>> +sp_incdec = remove # Add or remove space between '++' and '--' the word to which it is being >>>> + # applied, as in '(--x)' or 'y++;'. >>>> +sp_inv = remove # Add or remove space after the '~' (invert) unary operator. >>>> +sp_not = remove # Add or remove space after the '!' (not) unary operator. >>>> +sp_sign = remove # Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. >>>> + >>>> +# 5.2.2.4 Subsequent lines of multi-line function calls should line up two spaces from the beginning of the function >>>> +# name >>>> +nl_func_call_args_multi_line = true # Whether to add a newline after each ',' in a function call if '(' and >> ')' >>>> + # are in different lines. >>>> +nl_func_call_args_multi_line_ignore_closures = false >>>> +nl_func_call_start_multi_line = true # Whether to add a newline after '(' in a function call if '(' and ')' are >>>> + # in different lines. >>>> + >>>> +# - Indent each argument 2 spaces from the start of the function name. If a >>>> +# function is called through a structure or union member, of type >>>> +# pointer-to-function, then indent each argument 2 spaces from the start of the >>>> +# member name. >>>> +indent_func_call_edk2_style = true # Use EDK2 indentation style for function calls (**CUSTOM SETTING**) >>>> +indent_paren_after_func_call = true # Whether to indent the open parenthesis of a function call, if the >>>> + # parenthesis is on its own line. >>>> + >>>> +# - Align the close parenthesis with the start of the last argument >>>> +indent_paren_close = 0 # How to indent a close parenthesis after a newline. >>>> + # (0: Body, 1: Openparenthesis, 2: Brace level) >>>> + >>>> + >>>> +# 5.2.2.5 Always put space after commas or semicolons that separate items >>>> +sp_after_comma = force # Add or remove space after ',', i.e. 'a,b' vs. 'a, b'. >>>> +sp_before_comma = remove # Add or remove space before ','. >>>> + >>>> +# 5.2.2.6 Always put space before an open parenthesis >>>> +sp_after_sparen = add # Add or remove space after ')' of control statements. >>>> +sp_attribute_paren = add # Add or remove space between '__attribute__' and '('. >>>> +sp_before_sparen = force # Add or remove space before '(' of control statements >>>> + # ('if', 'for', 'switch', 'while', etc.). >>>> +sp_defined_paren = force # Add or remove space between 'defined' and '(' in '#if defined (FOO)'. >>>> +sp_func_call_paren = force # Add or remove space between function name and '(' on function calls. >>>> +sp_func_call_paren_empty = force # Add or remove space between function name and '()' on function calls >>>> + # without parameters. If set to ignore (the default), sp_func_call_paren >> is >>>> + # used. >>>> +sp_func_def_paren = add # Add or remove space between alias name and '(' of a non-pointer function >>>> + # type typedef. >>>> +sp_func_proto_paren = add # Add or remove space between function name and '()' on function >> declaration >>>> +sp_sizeof_paren = force # Add or remove space between 'sizeof' and '('. >>>> +sp_type_func = add # Add or remove space between return type and function name. A minimum of >> 1 >>>> + # is forced except for pointer return types. >>>> + >>>> +# Not specified, but also good style to remove spaces inside parentheses (Optional) >>>> +sp_cparen_oparen = remove # Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') >> ('. >>>> +sp_inside_fparen = remove # Add or remove space inside function '(' and ')'. >>>> +sp_inside_fparens = remove # Add or remove space inside empty function '()'. >>>> +sp_inside_paren = remove # Add or remove space inside '(' and ')'. >>>> +sp_inside_paren_cast = remove # Add or remove spaces inside cast parentheses. '(int)x' >>>> +sp_inside_square = remove # Add or remove space inside a non-empty '[' and ']'. >>>> +sp_paren_paren = remove # Add or remove space between nested parentheses, i.e. '((' vs. ') )'. >>>> +sp_square_fparen = remove # Add or remove space between ']' and '(' when part of a function call. >>>> + >>>> +# 5.2.2.7 Put a space before an open brace if it is not on its own line >>>> +sp_do_brace_open = force # Add or remove space between 'do' and '{'. >>>> +sp_paren_brace = force # Add or remove space between ')' and '{'. >>>> +sp_sparen_brace = force # Add or remove space between ')' and '{' of of control statements. >>>> + >>>> +# 5.2.2.8 Do not put spaces around structure member and pointer operators >>>> +sp_after_byref = remove # Add or remove space after reference sign '&', if followed by a word. >>>> +sp_before_byref = add # Add or remove space before a reference sign '&'. >>>> +sp_deref = remove # Add or remove space after the '*' (dereference) unary operator. This >> does >>>> + # not affect the spacing after a '*' that is part of a type. >>>> +sp_member = remove # Add or remove space around the '.' or '->' operators. >>>> + >>>> +# 5.2.2.9 Do not put spaces before open brackets of array subscripts >>>> +sp_before_square = remove # Add or remove space before '[' (except '[]'). >>>> +sp_before_squares = remove # Add or remove space before '[]'. >>>> +sp_before_vardef_square = remove # Add or remove space before '[' for a variable definition. >>>> + >>>> +# 5.2.2.10 Use extra parentheses rather than depending on in-depth knowledge of the order of precedence of C >>>> +mod_full_paren_if_bool = true # Whether to fully parenthesize Boolean expressions in 'while' and 'if' >>>> + # statement, as in 'if (a && b > c)' => 'if (a && (b > c))'. >>>> + >>>> +# 5.2.2.11 Align a continuation line with the part of the line that it continues. >>>> +use_indent_continue_only_once = true >>>> + >>>> +# Additional '{}' bracing rules (Optional) >>>> +# NOTE - The style guide specifies two different styles for braces, >>>> +# so these are ignored for now to allow developers some flexibility. >>>> +nl_after_brace_close = true # Whether to add a newline after '}'. Does not apply if followed by a >>>> + # necessary ';'. >>>> +nl_brace_else = remove # Add or remove newline between '}' and 'else'. >>>> +nl_brace_while = remove # Add or remove newline between '}' and 'while' of 'do' statement. >>>> +nl_do_brace = remove # Add or remove newline between 'do' and '{'. >>>> +nl_else_brace = remove # Add or remove newline between 'else' and '{'. >>>> +nl_else_if = remove # Add or remove newline between 'else' and 'if'. >>>> +nl_elseif_brace = remove # Add or remove newline between 'else if' and '{'. >>>> +nl_enum_brace = remove # Add or remove newline between 'enum' and '{'. >>>> +nl_fcall_brace = remove # Add or remove newline between a function call's ')' and '{', >>>> + # as in 'list_for_each(item, &list) { }'. >>>> +nl_for_brace = remove # Add or remove newline between 'for' and '{'. >>>> +nl_if_brace = remove # Add or remove newline between 'if' and '{'. >>>> +nl_struct_brace = remove # Add or remove newline between 'struct and '{'. >>>> +nl_switch_brace = remove # Add or remove newline between 'switch' and '{'. >>>> +nl_union_brace = remove # Add or remove newline between 'union' and '{'. >>>> +nl_while_brace = remove # Add or remove newline between 'while' and '{'. >>>> + >>>> +# Additional whitespace rules (Optional) >>>> +sp_after_ptr_star = remove # Add or remove space after pointer star '*', if followed by a word. >>>> + # Useful when paired with align_var_def_star_style==2 >>>> +sp_after_ptr_star_func = remove # Add or remove space after a pointer star '*', if followed by a function >>>> + # prototype or function definition. >>>> +sp_after_semi = remove # Add or remove space after ';', except when followed by a comment. >>>> +sp_before_case_colon = remove # Add or remove space before case ':'. >>>> +sp_before_ptr_star = add # Add or remove space before pointer star '*'. >>>> +sp_before_ptr_star_func = add # Add or remove space before a pointer star '*', if followed by a function >>>> + # prototype or function definition. >>>> +sp_before_semi = remove # Add or remove space before ';' >>>> +sp_before_semi_for = remove # Add or remove space before ';' in non-empty 'for' statements. >>>> +sp_before_semi_for_empty = add # Add or remove space before a semicolon of an empty part of a for >> statement >>>> +sp_between_ptr_star = remove # Add or remove space between pointer stars '*'. (ie, 'VOID **') >>>> +sp_brace_close_while = force # Add or remove space between '}' and 'while'. >>>> + >>>> +sp_after_cast = remove >>>> +sp_after_type = add >>>> +sp_balance_nested_parens = false >>>> +sp_before_nl_cont = add >>>> +sp_before_square_asm_block = ignore >>>> +sp_before_unnamed_byref = add >>>> +sp_brace_brace = ignore >>>> +sp_brace_else = force >>>> +sp_brace_typedef = add >>>> +sp_case_label = force >>>> +sp_cmt_cpp_doxygen = true >>>> +sp_cond_colon = add >>>> +sp_cond_question = add >>>> +sp_cpp_cast_paren = force >>>> +sp_else_brace = force >>>> +sp_endif_cmt = force >>>> +sp_enum_assign = add >>>> +sp_inside_braces = force >>>> +sp_inside_braces_empty = force >>>> +sp_inside_braces_enum = force >>>> +sp_inside_braces_struct = force >>>> +sp_pp_concat = add >>>> +sp_pp_stringify = add >>>> +sp_return_paren = add >>>> +sp_special_semi = force >>>> +sp_while_paren_open = force >>>> + >>>> +# Additional Indentation Rules >>>> +indent_access_spec = 1 >>>> +indent_access_spec_body = false >>>> +indent_align_assign = true >>>> +indent_align_string = true >>>> +indent_bool_paren = true >>>> +indent_brace_parent = false >>>> +indent_braces = false >>>> +indent_braces_no_class = false >>>> +indent_braces_no_func = true >>>> +indent_braces_no_struct = false >>>> +indent_class = false >>>> +indent_class_colon = false >>>> +indent_cmt_with_tabs = false # Whether to indent comments that are not at a brace level with tabs >> on >>>> + # a tabstop. Requires indent_with_tabs=2. If false, will use spaces. >>>> +indent_col1_comment = true >>>> +indent_col1_multi_string_literal= true >>>> +indent_comma_paren = true >>>> +indent_else_if = true >>>> +indent_extern = false >>>> +indent_first_bool_expr = true >>>> + >>>> +indent_func_def_param_paren_pos_threshold = 0 >>>> +indent_func_param_double = false >>>> +indent_func_proto_param = true >>>> +indent_ignore_asm_block = true >>>> +indent_label = 1 >>>> +indent_member = 2 >>>> +indent_namespace = false >>>> +indent_param = 2 >>>> +indent_paren_nl = false >>>> +indent_paren_open_brace = false >>>> +indent_preserve_sql = false >>>> +indent_relative_single_line_comments = false >>>> +indent_sing_line_comments = 0 >>>> +indent_single_newlines = false >>>> +indent_square_nl = false >>>> +indent_switch_case = 2 >>>> +indent_template_param = true >>>> +indent_var_def_blk = 0 >>>> +indent_var_def_cont = false >>>> + >>>> +# Tidy-up rules (Optional) >>>> +mod_move_case_break = true # Whether to move a 'break' that appears after a fully braced 'case' >>>> + # before the close brace, as in 'case X: { ... } break;' => >>>> + # 'case X: { ... break; }'. >>>> +mod_pawn_semicolon = false >>>> +mod_remove_empty_return = false # Whether to remove a void 'return;' that appears as the last statement >>>> + # in a function. >>>> +mod_remove_extra_semicolon = true >>>> +mod_sort_import = false >>>> +mod_sort_include = false >>>> +mod_sort_using = false >>>> +nl_after_case = false # Whether to add a newline after a 'case' statement. >>>> +nl_end_of_file = force # Add or remove newline at the end of the file. >>>> +nl_end_of_file_min = 1 # The minimum number of newlines at the end of the file >>>> +nl_max = 2 # The maximum number of consecutive newlines (3 = 2 blank lines). >>>> +nl_start_of_file = remove # Add or remove newlines at the start of the file. >>>> + >>>> +# Code alignment rules (Optional) >>>> +align_asm_colon = false >>>> +align_assign_span = 1 # The span for aligning on '=' in assignments. >>>> +align_assign_thresh = 4 >>>> +align_edk2_style = true # Whether to apply edk2-specific alignment formatting >>>> +align_enum_equ_span = 1 # The span for aligning on '=' in enums. >>>> +align_func_params = true # Whether to align variable definitions in prototypes and functions. >>>> +align_func_params_gap = 2 >>>> +align_func_params_span = 2 # The span for aligning parameter definitions in function on parameter >> name. >>>> +align_func_params_thresh = 0 >>>> +align_func_proto_span = 0 >>>> +align_keep_tabs = false >>>> +align_left_shift = false >>>> +align_mix_var_proto = false >>>> +align_nl_cont = false >>>> +align_oc_decl_colon = false >>>> +align_on_operator = false >>>> +align_on_tabstop = false >>>> +align_pp_define_gap = 2 >>>> +align_pp_define_span = 1 >>>> +align_right_cmt_at_col = 0 # Align trailing comment at or beyond column N; 'pulls in' comments as >>>> + # a bonus side effect (0=ignore) >>>> +align_right_cmt_gap = 0 # If a trailing comment is more than this number of columns away from the >>>> + # text it follows, >>>> + # it will qualify for being aligned. This has to be > 0 to do anything. >>>> +align_right_cmt_mix = false # If aligning comments, mix with comments after '}' and #endif with less >>>> + # than 3 spaces before the comment >>>> +align_right_cmt_same_level = true # Whether to only align trailing comments that are at the same brace >> level. >>>> +align_right_cmt_span = 2 # The span for aligning comments that end lines. >>>> +align_same_func_call_params = false >>>> +align_single_line_brace = true >>>> +align_single_line_func = true >>>> +align_struct_init_span = 1 # The span for aligning struct initializer values. >>>> +align_typedef_amp_style = 1 >>>> +align_typedef_func = 1 # How to align typedef'd functions with other typedefs. >>>> + # (0: No align, 1: Align open paranthesis, 2: Align function type name) >>>> +align_typedef_gap = 2 >>>> +align_typedef_span = 1 # The span for aligning single-line typedefs. >>>> +align_typedef_star_style = 1 >>>> +align_var_def_amp_style = 1 >>>> +align_var_def_attribute = true >>>> +align_var_def_colon = true # Whether to align the colon in struct bit fields. >>>> +align_var_def_gap = 2 # The gap (minimum spacing for aligned items) for variable definitions. >>>> +align_var_def_inline = false >>>> +align_var_def_span = 1 # The span (lines needed to align) for aligning variable definitions. >>>> +align_var_def_star_style = 1 # How to consider (or treat) the '*' in the alignment of variable >>>> + # definitions. >>>> + # 0: Part of the type 'void * foo;' (default) >>>> + # 1: Part of the variable 'void *foo;' >>>> + # 2: Dangling 'void *foo;' >>>> + # (Note - should also set sp_after_ptr_star=remove) >>>> +align_var_struct_gap = 4 >>>> +align_var_struct_span = 8 # The span for aligning struct/union member definitions. >>>> +align_var_struct_thresh = 0 >>>> +align_with_tabs = false >>>> + >>>> +# Comment formatting >>>> +cmt_align_doxygen_javadoc_tags = true # Whether to align doxygen javadoc-style tags ('@param', '@return', etc.) >>>> + # TODO: Eats '[' in '[in]' >>>> +cmt_c_group = false >>>> +cmt_c_nl_end = true # Whether to add a newline before the closing '*/' of the combined c- >> comment. >>>> +cmt_c_nl_start = true >>>> +cmt_cpp_group = false >>>> +cmt_cpp_nl_end = true >>>> +cmt_cpp_nl_start = true >>>> +cmt_cpp_to_c = false >>>> +cmt_indent_multi = false # Whether to apply changes to multi-line comments, including cmt_width, >>>> + # keyword substitution and leading chars. >>>> +cmt_insert_before_preproc = false >>>> +#cmt_insert_file_header = default_file_header.txt >>>> +#cmt_insert_func_header = default_function_header.txt >>>> +cmt_multi_check_last = false >>>> +cmt_multi_first_len_minimum = 2 >>>> +cmt_reflow_mode = 1 # How to reflow comments. >>>> + # (0:No reflow, 1:No touching at all, 2: Full reflow) >>>> +cmt_sp_after_star_cont = 0 # The number of spaces to insert after the star on subsequent comment >> lines. >>>> +cmt_sp_before_star_cont = 0 # The number of spaces to insert at the start of subsequent comment lines. >>>> +cmt_star_cont = false # Whether to put a star on subsequent comment lines. >>>> +cmt_width = 120 # Try to wrap comments at N columns. >>>> +sp_cmt_cpp_start = add # Add or remove space after the opening of a C++ comment, as in >>>> + # '// A'. NOTE: Breaks indentation within comments. >>>> + >>>> +# Function definitions / declarations >>>> +indent_func_call_param = false # Whether to indent continued function call parameters one indent level, >>>> + # rather than aligning parameters under the open parenthesis. >>>> +indent_func_class_param = false # Whether to indent continued function call declaration one indent level, >>>> + # rather than aligning parameters under the open parenthesis. >>>> +indent_func_ctor_var_param = false # Whether to indent continued class variable constructors one indent >> level, >>>> + # rather than aligning parameters under the open parenthesis. >>>> +indent_func_def_param = true # Whether to indent continued function definition parameters one indent >>>> + # level, rather than aligning parameters under the open parenthesis. >>>> +nl_fdef_brace = add # Add or remove newline between function signature and '{'. >>>> +nl_func_call_end_multi_line = true # Whether to add a newline before ')' in a function call if '(' and ')' >> are >>>> + # in different lines. >>>> +nl_func_call_paren = remove # Add or remove newline between a function name and the opening '(' in the >>>> + # call. >>>> +nl_func_call_start_multi_line = true # Whether to add a newline after '(' in a function call if '(' and ')' are >>>> + # in different lines. >>>> +nl_func_decl_args = force # Add or remove newline after each ',' in a function declaration. >>>> +nl_func_decl_empty = add # Add or remove newline between '()' in a function declaration. >>>> +nl_func_def_args = force # Add or remove newline after each ',' in a function definition. >>>> +nl_func_def_empty = add # Add or remove newline between '()' in a function definition. >>>> +nl_func_def_paren = remove # Add or remove newline between a function name and the opening '(' >>>> + # in the definition. >>>> +nl_func_paren = remove # Add or remove newline between a function name and the opening '(' in >>>> + # the declaration. >>>> +nl_func_type_name = add # Add or remove newline between return type and function name in a >> function >>>> + # definition. >>>> +sp_fparen_brace = force # Add or remove space between ')' and '{' of function. >>>> +use_indent_func_call_param = true # indent_func_call_param will be used >>>> + >>>> +# Additional Newline Rules >>>> +nl_after_brace_open = true # Whether to add a newline after '{'. This also adds a newline >>>> + # before the matching '}'. >>>> +nl_after_brace_open_cmt = true # Whether to add a newline between the open brace and a >>>> + # trailing single-line comment. >>>> + # Requires nl_after_brace_open = true. >>>> +nl_after_do = add # Add or remove blank line after 'do/while' statement. >>>> +nl_after_for = add # Add or remove blank line after 'for' statement. >>>> +nl_after_func_body = 2 # The number of newlines after '}' of a multi-line function >> body >>>> +nl_after_func_body_one_liner = 2 >>>> +nl_after_func_proto = 2 >>>> +nl_after_func_proto_group = 2 >>>> +nl_after_if = add >>>> +nl_after_multiline_comment = false >>>> +nl_after_return = false >>>> +nl_after_struct = 2 >>>> +nl_after_switch = add >>>> +nl_after_vbrace_close = true >>>> +nl_after_vbrace_open = true >>>> +nl_after_vbrace_open_empty = true >>>> +nl_after_while = add >>>> +nl_assign_leave_one_liners = true >>>> +nl_before_block_comment = 2 >>>> +nl_before_case = false >>>> +nl_before_do = ignore >>>> +nl_before_for = ignore >>>> +nl_before_if = ignore >>>> +nl_before_switch = ignore >>>> +nl_before_while = ignore >>>> +nl_before_whole_file_ifdef = 2 >>>> +nl_brace_brace = force >>>> +nl_brace_struct_var = remove >>>> +nl_case_colon_brace = add >>>> +nl_class_leave_one_liners = false >>>> +nl_collapse_empty_body = false >>>> +nl_comment_func_def = 1 >>>> +nl_create_for_one_liner = false >>>> +nl_create_if_one_liner = false >>>> +nl_create_while_one_liner = false >>>> +nl_define_macro = false >>>> +nl_ds_struct_enum_close_brace = true >>>> +nl_ds_struct_enum_cmt = false >>>> +nl_enum_leave_one_liners = false >>>> +nl_func_call_args_multi_line = true >>>> +nl_func_call_args_multi_line_ignore_closures = false >>>> +nl_func_decl_end = add >>>> +nl_func_decl_start = add >>>> +nl_func_def_end = add >>>> +nl_func_def_start = add >>>> +nl_func_leave_one_liners = false >>>> +nl_func_proto_type_name = add >>>> +nl_func_var_def_blk = 1 >>>> +nl_getset_leave_one_liners = false >>>> +nl_if_leave_one_liners = false >>>> +nl_multi_line_define = false >>>> +nl_squeeze_ifdef = false >>>> +nl_var_def_blk_end = 0 >>>> +nl_var_def_blk_start = 0 >>>> + >>>> +# Preprocessor Rules >>>> +pp_define_at_level = true >>>> +pp_if_indent_code = false >>>> +pp_indent_func_def = false >>>> +pp_indent_extern = false >>>> +pp_ignore_define_body = true # Workaround: Turn off processing for #define body >>>> + # (current rules do not work for some defines) >>>> +pp_indent = add >>>> +pp_indent_at_level = true >>>> +pp_indent_count = 2 >>>> +pp_indent_if = 2 >>>> +pp_indent_region = 2 >>>> +pp_region_indent_code = false >>>> +pp_space = remove >>>> + >>>> +# >>>> +# The tokens below are assigned specific types so they are always recognized properly. >>>> +# >>>> + >>>> +# Explicitly define EDK II qualifiers >>>> +set QUALIFIER CONST >>>> +set QUALIFIER EFIAPI >>>> +set QUALIFIER IN >>>> +set QUALIFIER OPTIONAL >>>> +set QUALIFIER OUT >>>> + >>>> +# Explicitly define EDK II types >>>> +set TYPE EFI_STATUS >>>> +set TYPE VOID >>>> diff --git a/.pytool/Plugin/Uncrustify/uncrustify_ext_dep.yaml b/.pytool/Plugin/Uncrustify/uncrustify_ext_dep.yaml >>>> new file mode 100644 >>>> index 000000000000..d8c22403b4b1 >>>> --- /dev/null >>>> +++ b/.pytool/Plugin/Uncrustify/uncrustify_ext_dep.yaml >>>> @@ -0,0 +1,16 @@ >>>> +## @file >>>> +# Downloads the Uncrustify application from a Project Mu NuGet package. >>>> +# >>>> +# Copyright (c) Microsoft Corporation. >>>> +# SPDX-License-Identifier: BSD-2-Clause-Patent >>>> +## >>>> +{ >>>> + "id": "uncrustify-ci-1", >>>> + "scope": "cibuild", >>>> + "type": "nuget", >>>> + "name": "mu-uncrustify-release", >>>> + "source": "https://pkgs.dev.azure.com/projectmu/Uncrustify/_packaging/mu_uncrustify/nuget/v3/index.json", >>>> + "version": "73.0.3", >>>> + "flags": ["set_shell_var", "host_specific"], >>>> + "var_name": "UNCRUSTIFY_CI_PATH" >>>> +} >>>> diff --git a/.pytool/Plugin/Uncrustify/uncrustify_plug_in.yaml b/.pytool/Plugin/Uncrustify/uncrustify_plug_in.yaml >>>> new file mode 100644 >>>> index 000000000000..25d9a8d37a30 >>>> --- /dev/null >>>> +++ b/.pytool/Plugin/Uncrustify/uncrustify_plug_in.yaml >>>> @@ -0,0 +1,12 @@ >>>> +## @file >>>> +# CiBuildPlugin used to check coding standard compliance of EDK II style C source code >>>> +# >>>> +# Copyright (c) Microsoft Corporation. >>>> +# SPDX-License-Identifier: BSD-2-Clause-Patent >>>> +## >>>> +{ >>>> + "scope": "cibuild", >>>> + "name": "Uncrustify Coding Standard Test", >>>> + "module": "Uncrustify" >>>> +} >>>> + >>>> diff --git a/.pytool/Readme.md b/.pytool/Readme.md >>>> index f6505507966a..46d7248f7da3 100644 >>>> --- a/.pytool/Readme.md >>>> +++ b/.pytool/Readme.md >>>> @@ -264,6 +264,10 @@ BSD-2-Clause-Patent. >>>> Run the Ecc tool on the package. The Ecc tool is available in the BaseTools >>>> package. It checks that the code complies to the EDKII coding standard. >>>> >>>> +### Coding Standard Compliance - Uncrustify >>>> + >>>> +Runs the Uncrustify application to check for coding standard compliance issues. >>>> + >>>> ## PyTool Scopes >>>> >>>> Scopes are how the PyTool ext_dep, path_env, and plugins are activated. Meaning >>>> -- >>>> 2.28.0.windows.1 >>>