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.web08.6200.1637799894683902834 for ; Wed, 24 Nov 2021 16:24:54 -0800 Authentication-Results: mx.groups.io; dkim=fail reason="body hash did not verify" header.i=@linux.microsoft.com header.s=default header.b=OgLU8UhU; 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 414C820D4DF8; Wed, 24 Nov 2021 16:24:53 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 414C820D4DF8 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1637799894; bh=J4OX83/dyida2bihtB2pxwoyU+ovRETgkkjmHa/uSfQ=; h=Date:Subject:To:Cc:References:From:In-Reply-To:From; b=OgLU8UhUIxbl+TebTqSCiThH/tNmfF9I1jb7vjzDUUfE8fF8bWgQF8U3wmzd+9J6k xs+ZDEb8WT1LgOYoyFw2q1JyHhwyJcUcUrxdhcLUxnx/7dNDjJLzfZVpatEj7oP2zI uqqOEYZAERhrLuKq8AkaVOqC8qoEOE8BWe9Ub06o= Message-ID: Date: Wed, 24 Nov 2021 19:24:52 -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: [edk2-devel] [PATCH v4 1/1] .pytool/Plugin/UncrustifyCheck: Add Uncrustify CI plugin To: devel@edk2.groups.io, michael.d.kinney@intel.com Cc: Liming Gao , Sean Brogan , Bret Barkelew References: <20211124214131.2705-1-mikuback@linux.microsoft.com> <87b4f4bb-8d19-abdc-85f9-6b7bffa1b12e@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: quoted-printable Hi Mike, How about I quickly send a v5 with the lines commented out and follow up=20 with a patch to uncomment them that we can review but merge when ready? Thanks, Michael On 11/24/2021 7:16 PM, Michael D Kinney wrote: > The 2nd option I listed won't work. That will fail UncrustifyCheck as we= ll because EDK II CI > will insert missing file/function headers and the files will not match. >=20 > We will need to comment out these 2 lines and submit an extra commit to r= e-enable. >=20 > Mike >=20 >> -----Original Message----- >> From: Kinney, Michael D >> Sent: Wednesday, November 24, 2021 4:08 PM >> To: devel@edk2.groups.io; mikuback@linux.microsoft.com; Kinney, Michael = D >> Cc: Liming Gao ; Sean Brogan ; Bret Barkelew >> >> Subject: RE: [edk2-devel] [PATCH v4 1/1] .pytool/Plugin/UncrustifyCheck:= Add Uncrustify CI plugin >> >> Hi Michael, >> >> Unfortunately, enabling the templates in the uncrustify.cfg files modifi= es 340 files with missing >> file/function headers that will now fail UncrustifyCheck. >> >> cmt_insert_file_header =3D default_file_header.txt >> cmt_insert_func_header =3D default_function_header.txt >> >> Perhaps we should leave this feature off to get the uncrustify format ch= anges committed and >> enable this feature immediately after so patch reviews after that will s= lowly fix these missing >> file/function headers. >> >> Or I can comment these two lines out when I generate the final version o= f the patch series so >> we don=E2=80=99t have to do an extra commit to re-enable. >> >> Mike >> >>> -----Original Message----- >>> From: Kinney, Michael D >>> Sent: Wednesday, November 24, 2021 3:06 PM >>> To: devel@edk2.groups.io; mikuback@linux.microsoft.com; Kinney, Michael= D >>> Cc: Liming Gao ; Sean Brogan ; Bret Barkelew >>> >>> Subject: RE: [edk2-devel] [PATCH v4 1/1] .pytool/Plugin/UncrustifyCheck= : Add Uncrustify CI plugin >>> >>> Thanks. That makes sense. >>> >>> Mike >>> >>>> -----Original Message----- >>>> From: devel@edk2.groups.io On Behalf Of Michael= Kubacki >>>> Sent: Wednesday, November 24, 2021 2:55 PM >>>> To: devel@edk2.groups.io; Kinney, Michael D >>>> Cc: Liming Gao ; Sean Brogan ; Bret Barkelew >>>> >>>> Subject: Re: [edk2-devel] [PATCH v4 1/1] .pytool/Plugin/UncrustifyChec= k: Add Uncrustify CI plugin >>>> >>>> Yes. Those were redundant and detected by the Python configparser modu= le >>>> when I used it to parse the config file. >>>> >>>> Thanks, >>>> Michael >>>> >>>> On 11/24/2021 5:45 PM, Michael D Kinney wrote: >>>>> Hi Michael, >>>>> >>>>> I see a couple settings deleted from the uncrustify.cfg. Was that in= purpose? Does it change the format? >>>>> >>>>> >>>>> -nl_func_call_start_multi_line =3D true # Whether to add a new= line after '(' in a function call if '(' and ')' >>> are >>>>> - # in different lines. >>>>> >>>>> >>>>> -nl_func_call_args_multi_line =3D true >>>>> -nl_func_call_args_multi_line_ignore_closures =3D false >>>>> >>>>> >>>>> Thanks, >>>>> >>>>> Mike >>>>> >>>>>> -----Original Message----- >>>>>> From: mikuback@linux.microsoft.com >>>>>> Sent: Wednesday, November 24, 2021 1:42 PM >>>>>> To: devel@edk2.groups.io >>>>>> Cc: Kinney, Michael D ; Liming Gao ; Sean Brogan >>>>>> ; Bret Barkelew >>>>>> Subject: [PATCH v4 1/1] .pytool/Plugin/UncrustifyCheck: Add Uncrusti= fy CI plugin >>>>>> >>>>>> From: Michael Kubacki >>>>>> >>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=3D3748 >>>>>> >>>>>> 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 host in this repository: >>>>>> >>>>>> https://dev.azure.com/projectmu/Uncrustify/ >>>>>> >>>>>> Note that the Uncrustify application outputs the commit ID that the >>>>>> application was built from when given the --version parameter. >>>>>> >>>>>> This ID can be mapped to the version specified in the Uncrustify CI >>>>>> plugin external dependency file (uncrustify_ext_dep.yaml) such as >>>>>> 73.0.3 by visiting the Uncrustify edk2 fork release pipeline page >>>>>> which associates the NuGet package version with the commit ID it was >>>>>> built from: >>>>>> >>>>>> https://dev.azure.com/projectmu/Uncrustify/_build >>>>>> >>>>>> 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. >>>>>> >>>>>> Cc: Michael D Kinney >>>>>> Cc: Liming Gao >>>>>> Cc: Sean Brogan >>>>>> Cc: Bret Barkelew >>>>>> Signed-off-by: Michael Kubacki >>>>>> --- >>>>>> >>>>>> Notes: >>>>>> V4 changes: >>>>>> >>>>>> 1. Updated commit subject to use the new plugin name. >>>>>> 2. Enabled file and function header templates to be placed in >>>>>> files missing a file or function header (comment block). >>>>>> 3. Added support to detect whether a file header or function >>>>>> header is missing in a file by searching files modified by >>>>>> Uncrustify for the template file text. >>>>>> 4. Updated the plugin name displayed in some log output messag= es. >>>>>> >>>>>> V3 changes: >>>>>> >>>>>> 1. Renamed the CI plugin to "UncrustifyCheck" from "Uncrustify= " >>>>>> to follow similar naming conventions used in other plugins >>>>>> that check files >>>>>> 2. Added a clarifying paragraph to the beginning of the Uncrus= tify >>>>>> CI plugin Readme.md file that the plugin is enabled by defa= ult >>>>>> and test results can be ignored by enabling "AuditOnly" mod= e. >>>>>> 3. Added instructions in the commit message on how to map the >>>>>> version reported by the Uncrustify application (--version) = to >>>>>> the version specified in the CI plugin external dependency >>>>>> YAML file. >>>>>> >>>>>> V2 changes: >>>>>> >>>>>> 1. Changed plugin temp directory to Build/.pytool/Plugin/Uncru= stify >>>>>> 2. Added TCBZ REF in patch commit message >>>>>> >>>>>> .pytool/Plugin/UncrustifyCheck/Readme.md | 120 = ++++ >>>>>> .pytool/Plugin/UncrustifyCheck/UncrustifyCheck.py | 617 = ++++++++++++++++++++ >>>>>> .pytool/Plugin/UncrustifyCheck/default_file_header.txt | 9 = + >>>>>> .pytool/Plugin/UncrustifyCheck/default_function_header.txt | 15 = + >>>>>> .pytool/Plugin/UncrustifyCheck/uncrustify.cfg | 462 = +++++++++++++++ >>>>>> .pytool/Plugin/UncrustifyCheck/uncrustify_ext_dep.yaml | 16 = + >>>>>> .pytool/Plugin/UncrustifyCheck/uncrustify_plug_in.yaml | 11 = + >>>>>> .pytool/Readme.md | 4 = + >>>>>> 8 files changed, 1254 insertions(+) >>>>>> >>>>>> diff --git a/.pytool/Plugin/UncrustifyCheck/Readme.md b/.pytool/Plug= in/UncrustifyCheck/Readme.md >>>>>> new file mode 100644 >>>>>> index 000000000000..bb263bcc87d7 >>>>>> --- /dev/null >>>>>> +++ b/.pytool/Plugin/UncrustifyCheck/Readme.md >>>>>> @@ -0,0 +1,120 @@ >>>>>> +# UncrustifyCheck Plugin >>>>>> + >>>>>> +This CiBuildPlugin scans all the files in a given package and check= s for coding standard compliance issues. >>>>>> + >>>>>> +This plugin is enabled by default. If a package would like to preve= nt the plugin from reporting errors, it can do >>>>>> +so by enabling [`AuditOnly`](#auditonly) mode. >>>>>> + >>>>>> +This plugin requires the directory containing the Uncrustify execut= able 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 buil= d 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 t= he `ConfigFilePath` configuration file option. >>>>>> + >>>>>> +* Uncrustify source code and documentation: https://github.com/uncr= ustify/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 configu= ration options described in this file. >>>>>> + >>>>>> +## Configuration >>>>>> + >>>>>> +The plugin can be configured with a few optional configuration opti= ons. >>>>>> + >>>>>> +``` yaml >>>>>> + "UncrustifyCheck": { >>>>>> + "AdditionalIncludePaths": [], # Additional paths to check for= matting (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 d= iffs in the test case log. >>>>>> + # This can significantly slow d= own the plugin on very large packages. >>>>>> + "SkipGitExclusions": False # Don't exclude git ignored fil= es and files in git submodules. >>>>>> + } >>>>>> +``` >>>>>> + >>>>>> +### `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 witho= ut 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 packa= ge 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 lo= g. This is helpful to exactly understand what >>>> changes >>>>>> +need to be made to the source code in order to fix a coding standar= d compliance issue. >>>>>> + >>>>>> +Note that calculating the file diffs on a very large set of of resu= lts (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 recogn= ized 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/.pytool/Plugin/Unc= rustify` >>>>>> + 2. For each source file with formatting errors, a sibling file wi= th 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 Uncr= ustify and then invokes Uncrustify with that >> file >>>>>> +list. For any files not compliant to the configuration file provide= d, Uncrustify will generate a corresponding >> file >>>>>> +with the `.uncrustify_plugin` extension. The plugin discovers all o= f these files. If any such files are present, >>> this >>>>>> +indicates a formatting issue was found and the test is marked faile= d (unless `AuditOnly` mode is enabled). >>>>>> + >>>>>> +The test case log will contain a report of which files failed to fo= rmat 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/UncrustifyCheck/UncrustifyCheck.py b/.py= tool/Plugin/UncrustifyCheck/UncrustifyCheck.py >>>>>> new file mode 100644 >>>>>> index 000000000000..59534469a2d3 >>>>>> --- /dev/null >>>>>> +++ b/.pytool/Plugin/UncrustifyCheck/UncrustifyCheck.py >>>>>> @@ -0,0 +1,617 @@ >>>>>> +# @file UncrustifyCheck.py >>>>>> +# >>>>>> +# An edk2-pytool based plugin wrapper for Uncrustify >>>>>> +# >>>>>> +# Copyright (c) Microsoft Corporation. >>>>>> +# SPDX-License-Identifier: BSD-2-Clause-Patent >>>>>> +## >>>>>> +import configparser >>>>>> +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 ICi= BuildPlugin >>>>>> +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 =3D 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(UncrustifyExcept= ion): >>>>>> + def __init__(self, message): >>>>>> + super().__init__(message, -120) >>>>>> + >>>>>> + >>>>>> +class UncrustifyInputFileCreationErrorException(UncrustifyException= ): >>>>>> + def __init__(self, message): >>>>>> + super().__init__(message, -121) >>>>>> + >>>>>> +class UncrustifyInvalidIgnoreStandardPathsException(UncrustifyExcep= tion): >>>>>> + 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 UncrustifyCheck(ICiBuildPlugin): >>>>>> + """ >>>>>> + A CiBuildPlugin that uses Uncrustify to check the source files = in the >>>>>> + package being tested for coding standard issues. >>>>>> + >>>>>> + By default, the plugin runs against standard C source file exte= nsions but >>>>>> + its configuration can be modified through its configuration fil= e. >>>>>> + >>>>>> + Configuration options: >>>>>> + "UncrustifyCheck": { >>>>>> + "AdditionalIncludePaths": [], # Additional paths to check f= ormatting (wildcards supported). >>>>>> + "AuditOnly": False, # Don't fail the build if the= re are errors. Just log them. >>>>>> + "ConfigFilePath": "", # Custom path to an Uncrustif= y config file. >>>>>> + "IgnoreStandardPaths": [], # Standard Plugin defined pat= hs 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 f= iles 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 t= he package >>>>>> + # >>>>>> + DEFAULT_CONFIG_FILE_PATH =3D os.path.join( >>>>>> + pathlib.Path(__file__).parent.resolve(), "uncrustify.cfg") >>>>>> + >>>>>> + # >>>>>> + # The extension used for formatted files produced by this plugi= n >>>>>> + # >>>>>> + FORMATTED_FILE_EXTENSION =3D ".uncrustify_plugin" >>>>>> + >>>>>> + # >>>>>> + # A package can add any additional paths with "AdditionalInclud= ePaths" >>>>>> + # A package can remove any of these paths with "IgnoreStandardP= aths" >>>>>> + # >>>>>> + STANDARD_PLUGIN_DEFINED_PATHS =3D ("*.c", "*.h") >>>>>> + >>>>>> + # >>>>>> + # The Uncrustify application path should set in this environmen= t variable >>>>>> + # >>>>>> + UNCRUSTIFY_PATH_ENV_KEY =3D "UNCRUSTIFY_CI_PATH" >>>>>> + >>>>>> + def GetTestName(self, packagename: str, environment: VarDict) -= > Tuple: >>>>>> + """ Provide the testcase name and classname for use in repo= rting >>>>>> + >>>>>> + Args: >>>>>> + packagename: string containing name of package to bui= ld >>>>>> + environment: The VarDict for the test to run in >>>>>> + Returns: >>>>>> + A tuple containing the testcase name and the classn= ame >>>>>> + (testcasename, classname) >>>>>> + testclassname: a descriptive string for the testcas= e can include whitespace >>>>>> + classname: should be patterned .. >>>>>> + """ >>>>>> + return ("Check file coding standard compliance in " + packa= gename, packagename + ".UncrustifyCheck") >>>>>> + >>>>>> + def RunBuildPlugin(self, package_rel_path: str, edk2_path: Edk2= Path, package_config: Dict[str, List[str]], >>>>>> environment_config: Any, plugin_manager: PluginManager, plugin_manag= er_helper: HelperFunctions, tc: >>>> JunitReportTestCase, >>>>>> output_stream=3DNone) -> int: >>>>>> + """ >>>>>> + External function of plugin. This function is used to perfo= rm the task of the CiBuild Plugin. >>>>>> + >>>>>> + Args: >>>>>> + - package_rel_path: edk2 workspace relative path to the p= ackage >>>>>> + - edk2_path: Edk2Path object with workspace and packages = paths >>>>>> + - package_config: Dictionary with the package configurati= on >>>>>> + - 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 plu= gin (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() >>>>>> + >>>>>> + # Get template file contents if specified >>>>>> + self._get_template_file_contents() >>>>>> + >>>>>> + # 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"{self._plugin_name} 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 pres= ent. >>>>>> + >>>>>> + Existence of such files is an unexpected condition. This mi= ght result >>>>>> + from an error that occurred during a previous run or a prem= ature exit from a debug scenario. In any case, >>> the >>>>>> package should be clean before starting a new run. >>>>>> + """ >>>>>> + pre_existing_formatted_file_count =3D len( >>>>>> + [str(path.resolve()) for path in >>>>>> pathlib.Path(self._abs_package_path).rglob(f'*{UncrustifyCheck.FORMA= TTED_FILE_EXTENSION}')]) >>>>>> + >>>>>> + if pre_existing_formatted_file_count > 0: >>>>>> + raise UncrustifyStalePluginFormattedFilesException( >>>>>> + f"{pre_existing_formatted_file_count} formatted fil= es 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 i= nstance. >>>>>> + >>>>>> + This removes the directory and all files created during thi= s 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 Uncrus= tify. >>>>>> + >>>>>> + This will recursively remove all formatted files generated = by Uncrustify >>>>>> + during this execution instance. >>>>>> + """ >>>>>> + if hasattr(self, '_abs_package_path'): >>>>>> + formatted_files =3D [str(path.resolve()) for path in pa= thlib.Path( >>>>>> + self._abs_package_path).rglob(f'*{UncrustifyCheck.F= ORMATTED_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 ins= tance. >>>>>> + """ >>>>>> + self._working_dir =3D os.path.join( >>>>>> + self._abs_workspace_path, "Build", ".pytool", "Plugin",= f"{self._plugin_name}") >>>>>> + >>>>>> + try: >>>>>> + pathlib.Path(self._working_dir).mkdir(parents=3DTrue, e= xist_ok=3DTrue) >>>>>> + except OSError as e: >>>>>> + raise UncrustifyInputFileCreationErrorException( >>>>>> + f"Error creating plugin directory {self._working_di= r}.\n\n{repr(e)}.") >>>>>> + >>>>>> + def _create_uncrustify_file_list_file(self) -> None: >>>>>> + """ >>>>>> + Creates the file with the list of source files for Uncrusti= fy to process. >>>>>> + """ >>>>>> + self._app_input_file_path =3D os.path.join( >>>>>> + self._working_dir, "uncrustify_file_list.txt") >>>>>> + >>>>>> + with open(self._app_input_file_path, 'w', encoding=3D'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 =3D StringIO() >>>>>> + self._app_exit_code =3D RunCmd( >>>>>> + self._app_path, >>>>>> + f"-c {self._app_config_file} -F {self._app_input_file_p= ath} --if-changed --suffix >>>>>> {UncrustifyCheck.FORMATTED_FILE_EXTENSION}", outstream=3Doutput) >>>>>> + self._app_output =3D output.getvalue().strip().splitlines() >>>>>> + >>>>>> + def _get_git_ignored_paths(self) -> List[str]: >>>>>> + """" >>>>>> + Returns a list of file absolute path strings to all files i= gnored 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 pat= hs will not be considered.") >>>>>> + return [] >>>>>> + >>>>>> + outstream_buffer =3D StringIO() >>>>>> + exit_code =3D RunCmd("git", "ls-files --other", >>>>>> + workingdir=3Dself._abs_workspace_path, o= utstream=3Doutstream_buffer, >>>> logging_level=3Dlogging.NOTSET) >>>>>> + if (exit_code !=3D 0): >>>>>> + raise UncrustifyGitIgnoreFileException( >>>>>> + f"An error occurred reading git ignore settings. Th= is 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 ro= ot 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 pat= hs 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 =3D StringIO() >>>>>> + exit_code =3D RunCmd("git", "config --file .gitmodules = --get-regexp path", >>>> workingdir=3Dself._abs_workspace_path, >>>>>> outstream=3Doutstream_buffer, logging_level=3Dlogging.NOTSET) >>>>>> + if (exit_code !=3D 0): >>>>>> + raise UncrustifyGitSubmoduleException( >>>>>> + f".gitmodule file detected but an error occurre= d reading the file. Cannot proceed with unknown >>>>>> submodule paths.") >>>>>> + >>>>>> + submodule_paths =3D [] >>>>>> + for line in outstream_buffer.getvalue().strip().splitli= nes(): >>>>>> + submodule_paths.append( >>>>>> + os.path.normpath(os.path.join(self._abs_workspa= ce_path, line.split()[1]))) >>>>>> + >>>>>> + return submodule_paths >>>>>> + else: >>>>>> + return [] >>>>>> + >>>>>> + def _get_template_file_contents(self) -> None: >>>>>> + """ >>>>>> + Gets the contents of Uncrustify template files if they are = specified >>>>>> + in the Uncrustify configuration file. >>>>>> + """ >>>>>> + >>>>>> + self._file_template_contents =3D None >>>>>> + self._func_template_contents =3D None >>>>>> + >>>>>> + # Allow no value to allow "set" statements in the config fi= le which do >>>>>> + # not specify value assignment >>>>>> + parser =3D configparser.ConfigParser(allow_no_value=3DTrue) >>>>>> + with open(self._app_config_file, 'r') as cf: >>>>>> + parser.read_string("[dummy_section]\n" + cf.read()) >>>>>> + >>>>>> + try: >>>>>> + file_template_name =3D parser["dummy_section"]["cmt_ins= ert_file_header"] >>>>>> + >>>>>> + file_template_path =3D pathlib.Path(file_template_name) >>>>>> + >>>>>> + if not file_template_path.is_file(): >>>>>> + file_template_path =3D pathlib.Path(os.path.join(se= lf._plugin_path, file_template_name)) >>>>>> + self._file_template_contents =3D file_template_path= .read_text() >>>>>> + except KeyError: >>>>>> + logging.warn("A file header template is not specified i= n the config file.") >>>>>> + except FileNotFoundError: >>>>>> + logging.warn("The specified file header template file w= as not found.") >>>>>> + try: >>>>>> + func_template_name =3D parser["dummy_section"]["cmt_ins= ert_func_header"] >>>>>> + >>>>>> + func_template_path =3D pathlib.Path(func_template_name) >>>>>> + >>>>>> + if not func_template_path.is_file(): >>>>>> + func_template_path =3D pathlib.Path(os.path.join(se= lf._plugin_path, func_template_name)) >>>>>> + self._func_template_contents =3D func_template_path= .read_text() >>>>>> + except KeyError: >>>>>> + logging.warn("A function header template is not specifi= ed in the config file.") >>>>>> + except FileNotFoundError: >>>>>> + logging.warn("The specified function header template fi= le was not found.") >>>>>> + >>>>>> + def _initialize_app_info(self) -> None: >>>>>> + """ >>>>>> + Initialize Uncrustify application information. >>>>>> + >>>>>> + This function will determine the application path and versi= on. >>>>>> + """ >>>>>> + # Verify Uncrustify is specified in the environment. >>>>>> + if UncrustifyCheck.UNCRUSTIFY_PATH_ENV_KEY not in os.enviro= n: >>>>>> + raise UncrustifyAppEnvVarNotFoundException( >>>>>> + f"Uncrustify environment variable {UncrustifyCheck.= UNCRUSTIFY_PATH_ENV_KEY} is not present.") >>>>>> + >>>>>> + self._app_path =3D shutil.which('uncrustify', path=3Dos.env= iron[UncrustifyCheck.UNCRUSTIFY_PATH_ENV_KEY]) >>>>>> + >>>>>> + if self._app_path is None: >>>>>> + raise FileNotFoundError( >>>>>> + errno.ENOENT, os.strerror(errno.ENOENT), self._app_= path) >>>>>> + >>>>>> + self._app_path =3D 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 =3D StringIO() >>>>>> + ret =3D RunCmd(self._app_path, "--version", outstream=3Dret= urn_buffer) >>>>>> + if (ret !=3D 0): >>>>>> + raise UncrustifyAppVersionErrorException( >>>>>> + f"Error occurred executing --version: {ret}.") >>>>>> + >>>>>> + # Log Uncrustify version information. >>>>>> + self._app_version =3D return_buffer.getvalue().strip() >>>>>> + self._tc.LogStdOut(f"Uncrustify version: {self._app_version= }") >>>>>> + version_aggregator.GetVersionAggregator().ReportVersion( >>>>>> + "Uncrustify", self._app_version, version_aggregator.Ver= sionTypes.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 =3D UncrustifyCheck.DEFAULT_CONFIG_FI= LE_PATH >>>>>> + if "ConfigFilePath" in self._package_config: >>>>>> + self._app_config_file =3D self._package_config["ConfigF= ilePath"].strip() >>>>>> + >>>>>> + self._app_config_file =3D os.path.normpath( >>>>>> + os.path.join(self._abs_package_path, self._app_conf= ig_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, e= dk2_path: Edk2Path, package_config: Dict[str, >>>>>> List[str]], tc: JunitReportTestCase) -> None: >>>>>> + """ >>>>>> + Initializes plugin environment information. >>>>>> + """ >>>>>> + self._abs_package_path =3D edk2_path.GetAbsolutePathOnThisS= ytemFromEdk2RelativePath( >>>>>> + package_rel_path) >>>>>> + self._abs_workspace_path =3D edk2_path.WorkspacePath >>>>>> + self._package_config =3D package_config >>>>>> + self._package_name =3D os.path.basename( >>>>>> + os.path.normpath(package_rel_path)) >>>>>> + self._plugin_name =3D self.__class__.__name__ >>>>>> + self._plugin_path =3D os.path.dirname(os.path.realpath(__fi= le__)) >>>>>> + self._rel_package_path =3D package_rel_path >>>>>> + self._tc =3D 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 t= he package to run against Uncrustify. >>>>>> + rel_file_paths_to_format =3D list( >>>>>> + UncrustifyCheck.STANDARD_PLUGIN_DEFINED_PATHS) >>>>>> + >>>>>> + # Allow the ci.yaml to remove any of the pre-defined standa= rd 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 ign= ore: {a}") >>>>>> + rel_file_paths_to_format.remove(a.strip()) >>>>>> + else: >>>>>> + raise UncrustifyInvalidIgnoreStandardPathsExcep= tion(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 =3D [] >>>>>> + 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 s= elf._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 =3D self._get_git_ignored_paths() >>>>>> + self._abs_file_paths_to_format =3D list( >>>>>> + set(self._abs_file_paths_to_format).difference(igno= red_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 =3D tuple(self._get_git_submodule_paths= ()) >>>>>> + for path in submodule_paths: >>>>>> + logging.info(f" submodule path: {path}") >>>>>> + >>>>>> + self._abs_file_paths_to_format =3D [ >>>>>> + 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 e= xclusion: >> {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 =3D False >>>>>> + self._output_file_diffs =3D False >>>>>> + >>>>>> + if "AuditOnly" in self._package_config and self._package_co= nfig["AuditOnly"]: >>>>>> + self._audit_only_mode =3D True >>>>>> + >>>>>> + if "OutputFileDiffs" in self._package_config and self._pack= age_config["OutputFileDiffs"]: >>>>>> + self._output_file_diffs =3D 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 f= ailures. >>>>>> + """ >>>>>> + formatted_files =3D [str(path.resolve()) for path in pathli= b.Path( >>>>>> + self._abs_package_path).rglob(f'*{UncrustifyCheck.FORMA= TTED_FILE_EXTENSION}')] >>>>>> + >>>>>> + self._formatted_file_error_count =3D 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 ta= ke a while...") >>>>>> + >>>>>> + for formatted_file in formatted_files: >>>>>> + pre_formatted_file =3D formatted_file[:- >>>>>> + len(UncrustifyCheck= .FORMATTED_FILE_EXTENSION)] >>>>>> + >>>>>> + if (self._output_file_diffs or >>>>>> + self._file_template_contents is not None or >>>>>> + self._func_template_contents is not None): >>>>>> + self._tc.LogStdError( >>>>>> + f"Formatting errors in {os.path.relpath(pre_for= matted_file, self._abs_package_path)}\n") >>>>>> + >>>>>> + with open(formatted_file) as ff: >>>>>> + formatted_file_text =3D ff.read() >>>>>> + >>>>>> + if (self._file_template_contents is not None an= d >>>>>> + self._file_template_contents in formatt= ed_file_text): >>>>>> + self._tc.LogStdError(f"File header is missi= ng in {os.path.relpath(pre_formatted_file, >>>>>> self._abs_package_path)}\n") >>>>>> + >>>>>> + if (self._func_template_contents is not None an= d >>>>>> + self._func_template_contents in formatt= ed_file_text): >>>>>> + self._tc.LogStdError(f"A function header is= missing in >> {os.path.relpath(pre_formatted_file, >>>>>> self._abs_package_path)}\n") >>>>>> + >>>>>> + if self._output_file_diffs: >>>>>> + with open(pre_formatted_file) as pf: >>>>>> + pre_formatted_file_text =3D pf.read() >>>>>> + >>>>>> + for line in difflib.unified_diff(pre_format= ted_file_text.split('\n'), >>>>>> formatted_file_text.split('\n'), fromfile=3Dpre_formatted_file, tofi= le=3Dformatted_file, n=3D3): >>>>>> + 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 =3D F= alse) -> None: >>>>>> + """ >>>>>> + Helper for removing a directory. Over time there have been >>>>>> + many private implementations of this due to reliability iss= ues in the >>>>>> + shutil implementations. To consolidate on a single function= this helper is added. >>>>>> + >>>>>> + On error try to change file attributes. Also add retry logi= c. >>>>>> + >>>>>> + This function is temporarily borrowed from edk2toollib.util= ity_functions >>>>>> + since the version used in edk2 is not recent enough to incl= ude 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 fi= le/folder being deleted. >>>>>> + """ >>>>>> + os.chmod(path, os.stat.S_IWRITE) >>>>>> + func(path) >>>>>> + >>>>>> + for _ in range(3): # retry up to 3 times >>>>>> + try: >>>>>> + shutil.rmtree(dir_path, ignore_errors=3Dignore_erro= rs, onerror=3D_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 =3D timeit.default_timer() >>>>>> + self._execute_uncrustify() >>>>>> + end_time =3D timeit.default_timer() - start_time >>>>>> + >>>>>> + execution_summary =3D f"Uncrustify executed against {len(se= lf._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 !=3D 0 and self._app_exit_code !=3D = 1: >>>>>> + raise UncrustifyAppExecutionException( >>>>>> + f"Error {str(self._app_exit_code)} returned from Un= crustify:\n\n{str(self._app_output)}") >>>>>> diff --git a/.pytool/Plugin/UncrustifyCheck/default_file_header.txt >>>>>> b/.pytool/Plugin/UncrustifyCheck/default_file_header.txt >>>>>> new file mode 100644 >>>>>> index 000000000000..2955a734dfe1 >>>>>> --- /dev/null >>>>>> +++ b/.pytool/Plugin/UncrustifyCheck/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/UncrustifyCheck/default_function_header.= txt >>>>>> b/.pytool/Plugin/UncrustifyCheck/default_function_header.txt >>>>>> new file mode 100644 >>>>>> index 000000000000..66edc72e6731 >>>>>> --- /dev/null >>>>>> +++ b/.pytool/Plugin/UncrustifyCheck/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 we= re other >>>>>> + return values, they would be listed. >>>>>> +**/ >>>>>> diff --git a/.pytool/Plugin/UncrustifyCheck/uncrustify.cfg b/.pytool= /Plugin/UncrustifyCheck/uncrustify.cfg >>>>>> new file mode 100644 >>>>>> index 000000000000..1a87295ec9f9 >>>>>> --- /dev/null >>>>>> +++ b/.pytool/Plugin/UncrustifyCheck/uncrustify.cfg >>>>>> @@ -0,0 +1,462 @@ >>>>>> +## @file >>>>>> +# Uncrustify Configuration File for EDK II C Code >>>>>> +# >>>>>> +# Coding Standard: https://edk2-docs.gitbook.io/edk-ii-c-coding-sta= ndards-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 =3D false >>>>>> +utf8_byte =3D false >>>>>> +utf8_force =3D true >>>>>> + >>>>>> +# Code width / line splitting >>>>>> +#code_width =3D120 # TODO: This causes non= -deterministic behaviour in some cases when code >>>> wraps >>>>>> +ls_code_width =3Dfalse >>>>>> +ls_for_split_full =3Dtrue >>>>>> +ls_func_split_full =3Dtrue >>>>>> +pos_comma =3Dtrail >>>>>> + >>>>>> +# 5.1.7 All files must end with CRLF >>>>>> +newlines =3D crlf >>>>>> + >>>>>> +# 5.1.2 Do not use tab characters >>>>>> + >>>>>> +cmt_convert_tab_to_spaces =3D true # Whether to convert = all tabs to spaces in comments. If false, tabs in >>>>>> + # comments are left alo= ne, unless used for indenting. >>>>>> +indent_columns =3D 2 # Number of spaces fo= r indentation >>>>>> +indent_with_tabs =3D 0 # Do not use TAB char= acters >>>>>> +string_replace_tab_chars =3D true # Replace TAB with SP= ACE >>>>>> + # 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 en= ds with ;) >>>>>> +nl_multi_line_cond =3D true # Add a newline betwe= en ')' and '{' if the ')' is on a different line >>> than >>>>>> + # the if/for/etc. >>>>>> +nl_after_semicolon =3D true # Whether to add a ne= wline after semicolons, except in 'for' >> statements. >>>>>> + >>>>>> +# 5.2.1.3 An open brace '{' goes on the same line as the closing pa= renthesis ')' of simple predicate expressions >>>>>> +mod_full_brace_do =3D add # Add or remove brace= s on a single-line 'do' statement. >>>>>> +mod_full_brace_for =3D add >>>>>> +mod_full_brace_function =3D add # Add or remove brace= s on a single-line function definition. >>>>>> +mod_full_brace_if =3D add # Add or remove brace= s on a single-line 'if' statement. Braces will >> not >>> be >>>>>> + # removed if the braced= statement contains an 'else'. >>>>>> +mod_full_brace_if_chain =3D false >>>>>> +mod_full_brace_while =3D add >>>>>> + >>>>>> +# 5.2.1.4 A close brace '}' always goes at the beginning of the las= t line of the body >>>>>> +eat_blanks_after_open_brace =3D true >>>>>> +eat_blanks_before_close_brace =3D true # Whether to remove b= lank lines before '}'. >>>>>> + >>>>>> +# 5.2.2.2 Always put space before and after binary operators. >>>>>> +sp_assign =3D add # Add or remove space= around assignment operator '=3D', '+=3D', etc. >>>>>> +sp_assign_default =3D add >>>>>> +sp_bool =3D add # Add or remove space= around boolean operators '&&' and '||'. >>>>>> +sp_compare =3D add # Add or remove space= around compare operator '<', '>', '=3D=3D', etc. >>>>>> + >>>>>> +# 5.2.2.3 Do not put space between unary operators and their object >>>>>> +sp_addr =3D remove # A or remove space a= fter the '&' (address-of) unary operator. >>>>>> +sp_incdec =3D remove # Add or remove space= between '++' and '--' the word to which it is >>> being >>>>>> + # applied, as in '(--x)= ' or 'y++;'. >>>>>> +sp_inv =3D remove # Add or remove space= after the '~' (invert) unary operator. >>>>>> +sp_not =3D remove # Add or remove space= after the '!' (not) unary operator. >>>>>> +sp_sign =3D remove # Add or remove space= after '+' or '-', as in 'x =3D -5' or 'y =3D +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 =3D true # Whether to add a ne= wline after each ',' in a function call if '(' >> and >>>> ')' >>>>>> + # are in different line= s. >>>>>> +nl_func_call_args_multi_line_ignore_closures =3D false >>>>>> + >>>>>> +# - Indent each argument 2 spaces from the start of the function na= me. If a >>>>>> +# function is called through a structure or union member, of type >>>>>> +# pointer-to-function, then indent each argument 2 spaces from th= e start of the >>>>>> +# member name. >>>>>> +indent_func_call_edk2_style =3D true # Use EDK2 indentatio= n style for function calls (**CUSTOM SETTING**) >>>>>> +indent_paren_after_func_call =3D true # Whether to indent t= he 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 =3D 0 # How to indent a clo= se parenthesis after a newline. >>>>>> + # (0: Body, 1: Openpare= nthesis, 2: Brace level) >>>>>> + >>>>>> + >>>>>> +# 5.2.2.5 Always put space after commas or semicolons that separate= items >>>>>> +sp_after_comma =3D force # Add or remove space= after ',', i.e. 'a,b' vs. 'a, b'. >>>>>> +sp_before_comma =3D remove # Add or remove space= before ','. >>>>>> + >>>>>> +# 5.2.2.6 Always put space before an open parenthesis >>>>>> +sp_after_sparen =3D add # Add or remove space= after ')' of control statements. >>>>>> +sp_attribute_paren =3D add # Add or remove space= between '__attribute__' and '('. >>>>>> +sp_before_sparen =3D force # Add or remove space= before '(' of control statements >>>>>> + # ('if', 'for', 'switch= ', 'while', etc.). >>>>>> +sp_defined_paren =3D force # Add or remove space= between 'defined' and '(' in '#if defined >> (FOO)'. >>>>>> +sp_func_call_paren =3D force # Add or remove space= between function name and '(' on function calls. >>>>>> +sp_func_call_paren_empty =3D force # Add or remove space= between function name and '()' on function calls >>>>>> + # without parameters. I= f set to ignore (the default), >> sp_func_call_paren >>>> is >>>>>> + # used. >>>>>> +sp_func_def_paren =3D add # Add or remove space= between alias name and '(' of a non-pointer >>> function >>>>>> + # type typedef. >>>>>> +sp_func_proto_paren =3D add # Add or remove space= between function name and '()' on function >>>> declaration >>>>>> +sp_sizeof_paren =3D force # Add or remove space= between 'sizeof' and '('. >>>>>> +sp_type_func =3D 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 parent= heses (Optional) >>>>>> +sp_cparen_oparen =3D remove # Add or remove space= between back-to-back parentheses, i.e. ')(' vs. >> ') >>>> ('. >>>>>> +sp_inside_fparen =3D remove # Add or remove space= inside function '(' and ')'. >>>>>> +sp_inside_fparens =3D remove # Add or remove space= inside empty function '()'. >>>>>> +sp_inside_paren =3D remove # Add or remove space= inside '(' and ')'. >>>>>> +sp_inside_paren_cast =3D remove # Add or remove space= s inside cast parentheses. '(int)x' >>>>>> +sp_inside_square =3D remove # Add or remove space= inside a non-empty '[' and ']'. >>>>>> +sp_paren_paren =3D remove # Add or remove space= between nested parentheses, i.e. '((' vs. ') )'. >>>>>> +sp_square_fparen =3D 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 =3D force # Add or remove space= between 'do' and '{'. >>>>>> +sp_paren_brace =3D force # Add or remove space= between ')' and '{'. >>>>>> +sp_sparen_brace =3D force # Add or remove space= between ')' and '{' of of control statements. >>>>>> + >>>>>> +# 5.2.2.8 Do not put spaces around structure member and pointer ope= rators >>>>>> +sp_after_byref =3D remove # Add or remove space= after reference sign '&', if followed by a word. >>>>>> +sp_before_byref =3D add # Add or remove space= before a reference sign '&'. >>>>>> +sp_deref =3D remove # Add or remove space= after the '*' (dereference) unary operator. This >>>> does >>>>>> + # not affect the spacin= g after a '*' that is part of a type. >>>>>> +sp_member =3D remove # Add or remove space= around the '.' or '->' operators. >>>>>> + >>>>>> +# 5.2.2.9 Do not put spaces before open brackets of array subscript= s >>>>>> +sp_before_square =3D remove # Add or remove space= before '[' (except '[]'). >>>>>> +sp_before_squares =3D remove # Add or remove space= before '[]'. >>>>>> +sp_before_vardef_square =3D 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 =3D true # Whether to fully pa= renthesize Boolean expressions in 'while' and >> 'if' >>>>>> + # statement, as in 'if = (a && b > c)' =3D> '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 =3D 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 =3D true # Whether to add a ne= wline after '}'. Does not apply if followed by a >>>>>> + # necessary ';'. >>>>>> +nl_brace_else =3D remove # Add or remove newli= ne between '}' and 'else'. >>>>>> +nl_brace_while =3D remove # Add or remove newli= ne between '}' and 'while' of 'do' statement. >>>>>> +nl_do_brace =3D remove # Add or remove newli= ne between 'do' and '{'. >>>>>> +nl_else_brace =3D remove # Add or remove newli= ne between 'else' and '{'. >>>>>> +nl_else_if =3D remove # Add or remove newli= ne between 'else' and 'if'. >>>>>> +nl_elseif_brace =3D remove # Add or remove newli= ne between 'else if' and '{'. >>>>>> +nl_enum_brace =3D remove # Add or remove newli= ne between 'enum' and '{'. >>>>>> +nl_fcall_brace =3D remove # Add or remove newli= ne between a function call's ')' and '{', >>>>>> + # as in 'list_for_each(= item, &list) { }'. >>>>>> +nl_for_brace =3D remove # Add or remove newli= ne between 'for' and '{'. >>>>>> +nl_if_brace =3D remove # Add or remove newli= ne between 'if' and '{'. >>>>>> +nl_struct_brace =3D remove # Add or remove newli= ne between 'struct and '{'. >>>>>> +nl_switch_brace =3D remove # Add or remove newli= ne between 'switch' and '{'. >>>>>> +nl_union_brace =3D remove # Add or remove newli= ne between 'union' and '{'. >>>>>> +nl_while_brace =3D remove # Add or remove newli= ne between 'while' and '{'. >>>>>> + >>>>>> +# Additional whitespace rules (Optional) >>>>>> +sp_after_ptr_star =3D remove # Add or remove space= after pointer star '*', if followed by a word. >>>>>> + # Useful when paired wi= th align_var_def_star_style=3D=3D2 >>>>>> +sp_after_ptr_star_func =3D remove # Add or remove space= after a pointer star '*', if followed by a >>> function >>>>>> + # prototype or function= definition. >>>>>> +sp_after_semi =3D remove # Add or remove space= after ';', except when followed by a comment. >>>>>> +sp_before_case_colon =3D remove # Add or remove space= before case ':'. >>>>>> +sp_before_ptr_star =3D add # Add or remove space= before pointer star '*'. >>>>>> +sp_before_ptr_star_func =3D add # Add or remove space= before a pointer star '*', if followed by a >>> function >>>>>> + # prototype or function= definition. >>>>>> +sp_before_semi =3D remove # Add or remove space= before ';' >>>>>> +sp_before_semi_for =3D remove # Add or remove space= before ';' in non-empty 'for' statements. >>>>>> +sp_before_semi_for_empty =3D add # Add or remove space= before a semicolon of an empty part of a for >>>> statement >>>>>> +sp_between_ptr_star =3D remove # Add or remove space= between pointer stars '*'. (ie, 'VOID **') >>>>>> +sp_brace_close_while =3D force # Add or remove space= between '}' and 'while'. >>>>>> + >>>>>> +sp_after_cast =3D remove >>>>>> +sp_after_type =3D add >>>>>> +sp_balance_nested_parens =3D false >>>>>> +sp_before_nl_cont =3D add >>>>>> +sp_before_square_asm_block =3D ignore >>>>>> +sp_before_unnamed_byref =3D add >>>>>> +sp_brace_brace =3D ignore >>>>>> +sp_brace_else =3D force >>>>>> +sp_brace_typedef =3D add >>>>>> +sp_case_label =3D force >>>>>> +sp_cmt_cpp_doxygen =3D true >>>>>> +sp_cond_colon =3D add >>>>>> +sp_cond_question =3D add >>>>>> +sp_cpp_cast_paren =3D force >>>>>> +sp_else_brace =3D force >>>>>> +sp_endif_cmt =3D force >>>>>> +sp_enum_assign =3D add >>>>>> +sp_inside_braces =3D force >>>>>> +sp_inside_braces_empty =3D force >>>>>> +sp_inside_braces_enum =3D force >>>>>> +sp_inside_braces_struct =3D force >>>>>> +sp_pp_concat =3D add >>>>>> +sp_pp_stringify =3D add >>>>>> +sp_return_paren =3D add >>>>>> +sp_special_semi =3D force >>>>>> +sp_while_paren_open =3D force >>>>>> + >>>>>> +# Additional Indentation Rules >>>>>> +indent_access_spec =3D 1 >>>>>> +indent_access_spec_body =3D false >>>>>> +indent_align_assign =3D true >>>>>> +indent_align_string =3D true >>>>>> +indent_bool_paren =3D true >>>>>> +indent_brace_parent =3D false >>>>>> +indent_braces =3D false >>>>>> +indent_braces_no_class =3D false >>>>>> +indent_braces_no_func =3D true >>>>>> +indent_braces_no_struct =3D false >>>>>> +indent_class =3D false >>>>>> +indent_class_colon =3D false >>>>>> +indent_cmt_with_tabs =3D false # Whether to inde= nt comments that are not at a brace level with >> tabs >>>> on >>>>>> + # a tabstop. Requir= es indent_with_tabs=3D2. If false, will use >> spaces. >>>>>> +indent_col1_comment =3D true >>>>>> +indent_col1_multi_string_literal=3D true >>>>>> +indent_comma_paren =3D true >>>>>> +indent_else_if =3D true >>>>>> +indent_extern =3D false >>>>>> +indent_first_bool_expr =3D true >>>>>> + >>>>>> +indent_func_def_param_paren_pos_threshold =3D 0 >>>>>> +indent_func_param_double =3D false >>>>>> +indent_func_proto_param =3D true >>>>>> +indent_ignore_asm_block =3D true >>>>>> +indent_label =3D 1 >>>>>> +indent_member =3D 2 >>>>>> +indent_namespace =3D false >>>>>> +indent_param =3D 2 >>>>>> +indent_paren_nl =3D false >>>>>> +indent_paren_open_brace =3D false >>>>>> +indent_preserve_sql =3D false >>>>>> +indent_relative_single_line_comments =3D false >>>>>> +indent_sing_line_comments =3D 0 >>>>>> +indent_single_newlines =3D false >>>>>> +indent_square_nl =3D false >>>>>> +indent_switch_case =3D 2 >>>>>> +indent_template_param =3D true >>>>>> +indent_var_def_blk =3D 0 >>>>>> +indent_var_def_cont =3D false >>>>>> + >>>>>> +# Tidy-up rules (Optional) >>>>>> +mod_move_case_break =3D true # Whether to move a '= break' that appears after a fully braced 'case' >>>>>> + # before the close brac= e, as in 'case X: { ... } break;' =3D> >>>>>> + # 'case X: { ... break;= }'. >>>>>> +mod_pawn_semicolon =3D false >>>>>> +mod_remove_empty_return =3D false # Whether to remove a= void 'return;' that appears as the last >> statement >>>>>> + # in a function. >>>>>> +mod_remove_extra_semicolon =3D true >>>>>> +mod_sort_import =3D false >>>>>> +mod_sort_include =3D false >>>>>> +mod_sort_using =3D false >>>>>> +nl_after_case =3D false # Whether to add a ne= wline after a 'case' statement. >>>>>> +nl_end_of_file =3D force # Add or remove newli= ne at the end of the file. >>>>>> +nl_end_of_file_min =3D 1 # The minimum number = of newlines at the end of the file >>>>>> +nl_max =3D 2 # The maximum number = of consecutive newlines (3 =3D 2 blank lines). >>>>>> +nl_start_of_file =3D remove # Add or remove newli= nes at the start of the file. >>>>>> + >>>>>> +# Code alignment rules (Optional) >>>>>> +align_asm_colon =3D false >>>>>> +align_assign_span =3D 1 # The span for aligni= ng on '=3D' in assignments. >>>>>> +align_assign_thresh =3D 4 >>>>>> +align_edk2_style =3D true # Whether to apply ed= k2-specific alignment formatting >>>>>> +align_enum_equ_span =3D 1 # The span for aligni= ng on '=3D' in enums. >>>>>> +align_func_params =3D true # Whether to align va= riable definitions in prototypes and functions. >>>>>> +align_func_params_gap =3D 2 >>>>>> +align_func_params_span =3D 2 # The span for aligni= ng parameter definitions in function on parameter >>>> name. >>>>>> +align_func_params_thresh =3D 0 >>>>>> +align_func_proto_span =3D 0 >>>>>> +align_keep_tabs =3D false >>>>>> +align_left_shift =3D false >>>>>> +align_mix_var_proto =3D false >>>>>> +align_nl_cont =3D false >>>>>> +align_oc_decl_colon =3D false >>>>>> +align_on_operator =3D false >>>>>> +align_on_tabstop =3D false >>>>>> +align_pp_define_gap =3D 2 >>>>>> +align_pp_define_span =3D 1 >>>>>> +align_right_cmt_at_col =3D 0 # Align trailing comm= ent at or beyond column N; 'pulls in' comments as >>>>>> + # a bonus side effect (= 0=3Dignore) >>>>>> +align_right_cmt_gap =3D 0 # If a trailing comme= nt is more than this number of columns away from >>> the >>>>>> + # text it follows, >>>>>> + # it will qualify for b= eing aligned. This has to be > 0 to do >> anything. >>>>>> +align_right_cmt_mix =3D false # If aligning comment= s, mix with comments after '}' and #endif with >> less >>>>>> + # than 3 spaces before = the comment >>>>>> +align_right_cmt_same_level =3D true # Whether to only ali= gn trailing comments that are at the same brace >>>> level. >>>>>> +align_right_cmt_span =3D 2 # The span for aligni= ng comments that end lines. >>>>>> +align_same_func_call_params =3D false >>>>>> +align_single_line_brace =3D true >>>>>> +align_single_line_func =3D true >>>>>> +align_struct_init_span =3D 1 # The span for aligni= ng struct initializer values. >>>>>> +align_typedef_amp_style =3D 1 >>>>>> +align_typedef_func =3D 1 # How to align typede= f'd functions with other typedefs. >>>>>> + # (0: No align, 1: Alig= n open paranthesis, 2: Align function type >> name) >>>>>> +align_typedef_gap =3D 2 >>>>>> +align_typedef_span =3D 1 # The span for aligni= ng single-line typedefs. >>>>>> +align_typedef_star_style =3D 1 >>>>>> +align_var_def_amp_style =3D 1 >>>>>> +align_var_def_attribute =3D true >>>>>> +align_var_def_colon =3D true # Whether to align th= e colon in struct bit fields. >>>>>> +align_var_def_gap =3D 2 # The gap (minimum sp= acing for aligned items) for variable >> definitions. >>>>>> +align_var_def_inline =3D false >>>>>> +align_var_def_span =3D 1 # The span (lines nee= ded to align) for aligning variable definitions. >>>>>> +align_var_def_star_style =3D 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 variab= le 'void *foo;' >>>>>> + # 2: Dangling = 'void *foo;' >>>>>> + # (Note - should also s= et sp_after_ptr_star=3Dremove) >>>>>> +align_var_struct_gap =3D 4 >>>>>> +align_var_struct_span =3D 8 # The span for aligni= ng struct/union member definitions. >>>>>> +align_var_struct_thresh =3D 0 >>>>>> +align_with_tabs =3D false >>>>>> + >>>>>> +# Comment formatting >>>>>> +cmt_align_doxygen_javadoc_tags =3D true # Whether to align do= xygen javadoc-style tags ('@param', '@return', >>> etc.) >>>>>> + # TODO: Eats '[' in '[i= n]' >>>>>> +cmt_c_group =3D false >>>>>> +cmt_c_nl_end =3D true # Whether to add a ne= wline before the closing '*/' of the combined c- >>>> comment. >>>>>> +cmt_c_nl_start =3D true >>>>>> +cmt_cpp_group =3D false >>>>>> +cmt_cpp_nl_end =3D true >>>>>> +cmt_cpp_nl_start =3D true >>>>>> +cmt_cpp_to_c =3D false >>>>>> +cmt_indent_multi =3D false # Whether to apply ch= anges to multi-line comments, including >> cmt_width, >>>>>> + # keyword substitution = and leading chars. >>>>>> +cmt_insert_before_preproc =3D false >>>>>> +cmt_insert_file_header =3D default_file_header.txt >>>>>> +cmt_insert_func_header =3D default_function_header.txt >>>>>> +cmt_multi_check_last =3D false >>>>>> +cmt_multi_first_len_minimum =3D 2 >>>>>> +cmt_reflow_mode =3D 1 # How to reflow comme= nts. >>>>>> + # (0:No reflow, 1:No to= uching at all, 2: Full reflow) >>>>>> +cmt_sp_after_star_cont =3D 0 # The number of space= s to insert after the star on subsequent comment >>>> lines. >>>>>> +cmt_sp_before_star_cont =3D 0 # The number of space= s to insert at the start of subsequent comment >>> lines. >>>>>> +cmt_star_cont =3D false # Whether to put a st= ar on subsequent comment lines. >>>>>> +cmt_width =3D 120 # Try to wrap comment= s at N columns. >>>>>> +sp_cmt_cpp_start =3D 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 =3D false # Whether to indent c= ontinued function call parameters one indent >> level, >>>>>> + # rather than aligning = parameters under the open parenthesis. >>>>>> +indent_func_class_param =3D false # Whether to indent c= ontinued function call declaration one indent >>> level, >>>>>> + # rather than aligning = parameters under the open parenthesis. >>>>>> +indent_func_ctor_var_param =3D false # Whether to indent c= ontinued class variable constructors one indent >>>> level, >>>>>> + # rather than aligning = parameters under the open parenthesis. >>>>>> +indent_func_def_param =3D true # Whether to indent c= ontinued function definition parameters one >> indent >>>>>> + # level, rather than al= igning parameters under the open parenthesis. >>>>>> +nl_fdef_brace =3D add # Add or remove newli= ne between function signature and '{'. >>>>>> +nl_func_call_end_multi_line =3D true # Whether to add a ne= wline before ')' in a function call if '(' and >> ')' >>>> are >>>>>> + # in different lines. >>>>>> +nl_func_call_paren =3D remove # Add or remove newli= ne between a function name and the opening '(' in >>> the >>>>>> + # call. >>>>>> +nl_func_call_start_multi_line =3D true # Whether to add a ne= wline after '(' in a function call if '(' and ')' >>> are >>>>>> + # in different lines. >>>>>> +nl_func_decl_args =3D force # Add or remove newli= ne after each ',' in a function declaration. >>>>>> +nl_func_decl_empty =3D add # Add or remove newli= ne between '()' in a function declaration. >>>>>> +nl_func_def_args =3D force # Add or remove newli= ne after each ',' in a function definition. >>>>>> +nl_func_def_empty =3D add # Add or remove newli= ne between '()' in a function definition. >>>>>> +nl_func_def_paren =3D remove # Add or remove newli= ne between a function name and the opening '(' >>>>>> + # in the definition. >>>>>> +nl_func_paren =3D remove # Add or remove newli= ne between a function name and the opening '(' in >>>>>> + # the declaration. >>>>>> +nl_func_type_name =3D add # Add or remove newli= ne between return type and function name in a >>>> function >>>>>> + # definition. >>>>>> +sp_fparen_brace =3D force # Add or remove space= between ')' and '{' of function. >>>>>> +use_indent_func_call_param =3D true # indent_func_call_pa= ram will be used >>>>>> + >>>>>> +# Additional Newline Rules >>>>>> +nl_after_brace_open =3D true # Whether= to add a newline after '{'. This also adds a >>> newline >>>>>> + # before th= e matching '}'. >>>>>> +nl_after_brace_open_cmt =3D true # Whether= to add a newline between the open brace and a >>>>>> + # trailing = single-line comment. >>>>>> + # Requires = nl_after_brace_open =3D true. >>>>>> +nl_after_do =3D add # Add or = remove blank line after 'do/while' statement. >>>>>> +nl_after_for =3D add # Add or = remove blank line after 'for' statement. >>>>>> +nl_after_func_body =3D 2 # The num= ber of newlines after '}' of a multi-line >> function >>>> body >>>>>> +nl_after_func_body_one_liner =3D 2 >>>>>> +nl_after_func_proto =3D 2 >>>>>> +nl_after_func_proto_group =3D 2 >>>>>> +nl_after_if =3D add >>>>>> +nl_after_multiline_comment =3D false >>>>>> +nl_after_return =3D false >>>>>> +nl_after_struct =3D 2 >>>>>> +nl_after_switch =3D add >>>>>> +nl_after_vbrace_close =3D true >>>>>> +nl_after_vbrace_open =3D true >>>>>> +nl_after_vbrace_open_empty =3D true >>>>>> +nl_after_while =3D add >>>>>> +nl_assign_leave_one_liners =3D true >>>>>> +nl_before_block_comment =3D 2 >>>>>> +nl_before_case =3D false >>>>>> +nl_before_do =3D ignore >>>>>> +nl_before_for =3D ignore >>>>>> +nl_before_if =3D ignore >>>>>> +nl_before_switch =3D ignore >>>>>> +nl_before_while =3D ignore >>>>>> +nl_before_whole_file_ifdef =3D 2 >>>>>> +nl_brace_brace =3D force >>>>>> +nl_brace_struct_var =3D remove >>>>>> +nl_case_colon_brace =3D add >>>>>> +nl_class_leave_one_liners =3D false >>>>>> +nl_collapse_empty_body =3D false >>>>>> +nl_comment_func_def =3D 1 >>>>>> +nl_create_for_one_liner =3D false >>>>>> +nl_create_if_one_liner =3D false >>>>>> +nl_create_while_one_liner =3D false >>>>>> +nl_define_macro =3D false >>>>>> +nl_ds_struct_enum_close_brace =3D true >>>>>> +nl_ds_struct_enum_cmt =3D false >>>>>> +nl_enum_leave_one_liners =3D false >>>>>> +nl_func_decl_end =3D add >>>>>> +nl_func_decl_start =3D add >>>>>> +nl_func_def_end =3D add >>>>>> +nl_func_def_start =3D add >>>>>> +nl_func_leave_one_liners =3D false >>>>>> +nl_func_proto_type_name =3D add >>>>>> +nl_func_var_def_blk =3D 1 >>>>>> +nl_getset_leave_one_liners =3D false >>>>>> +nl_if_leave_one_liners =3D false >>>>>> +nl_multi_line_define =3D false >>>>>> +nl_squeeze_ifdef =3D false >>>>>> +nl_var_def_blk_end =3D 0 >>>>>> +nl_var_def_blk_start =3D 0 >>>>>> + >>>>>> +# Preprocessor Rules >>>>>> +pp_define_at_level =3D true >>>>>> +pp_if_indent_code =3D false >>>>>> +pp_indent_func_def =3D false >>>>>> +pp_indent_extern =3D false >>>>>> +pp_ignore_define_body =3D true # Workaround: Turn = off processing for #define body >>>>>> + # (current rules do n= ot work for some defines) >>>>>> +pp_indent =3D add >>>>>> +pp_indent_at_level =3D true >>>>>> +pp_indent_count =3D 2 >>>>>> +pp_indent_if =3D 2 >>>>>> +pp_indent_region =3D 2 >>>>>> +pp_region_indent_code =3D false >>>>>> +pp_space =3D remove >>>>>> + >>>>>> +# >>>>>> +# The tokens below are assigned specific types so they are always r= ecognized 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/UncrustifyCheck/uncrustify_ext_dep.yaml >>>>>> b/.pytool/Plugin/UncrustifyCheck/uncrustify_ext_dep.yaml >>>>>> new file mode 100644 >>>>>> index 000000000000..d8c22403b4b1 >>>>>> --- /dev/null >>>>>> +++ b/.pytool/Plugin/UncrustifyCheck/uncrustify_ext_dep.yaml >>>>>> @@ -0,0 +1,16 @@ >>>>>> +## @file >>>>>> +# Downloads the Uncrustify application from a Project Mu NuGet pack= age. >>>>>> +# >>>>>> +# 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/_packa= ging/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/UncrustifyCheck/uncrustify_plug_in.yaml >>>>>> b/.pytool/Plugin/UncrustifyCheck/uncrustify_plug_in.yaml >>>>>> new file mode 100644 >>>>>> index 000000000000..06c76af02759 >>>>>> --- /dev/null >>>>>> +++ b/.pytool/Plugin/UncrustifyCheck/uncrustify_plug_in.yaml >>>>>> @@ -0,0 +1,11 @@ >>>>>> +## @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": "UncrustifyCheck" >>>>>> +} >>>>>> diff --git a/.pytool/Readme.md b/.pytool/Readme.md >>>>>> index f6505507966a..e0d07f317049 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 sta= ndard. >>>>>> >>>>>> +### Coding Standard Compliance - UncrustifyCheck >>>>>> + >>>>>> +Runs the Uncrustify application to check for coding standard compli= ance issues. >>>>>> + >>>>>> ## PyTool Scopes >>>>>> >>>>>> Scopes are how the PyTool ext_dep, path_env, and plugins are acti= vated. Meaning >>>>>> -- >>>>>> 2.28.0.windows.1 >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>> >>>> >>>> >>>> >=20 >=20 >=20 >=20 >=20 >=20