From: "Michael D Kinney" <michael.d.kinney@intel.com>
To: Michael Kubacki <mikuback@linux.microsoft.com>,
"devel@edk2.groups.io" <devel@edk2.groups.io>,
"Kinney, Michael D" <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>,
Sean Brogan <sean.brogan@microsoft.com>,
Bret Barkelew <Bret.Barkelew@microsoft.com>
Subject: Re: [PATCH v1 1/1] .pytool/Plugin/Uncrustify: Add Uncrustify plugin
Date: Tue, 23 Nov 2021 20:19:46 +0000 [thread overview]
Message-ID: <CO1PR11MB4929509F9EF433E691731DFDD2609@CO1PR11MB4929.namprd11.prod.outlook.com> (raw)
In-Reply-To: <fc2d3757-ab83-374a-7012-91fa2d14927f@linux.microsoft.com>
Hi Michael,
Comments below.
Mike
> -----Original Message-----
> From: Michael Kubacki <mikuback@linux.microsoft.com>
> Sent: Tuesday, November 23, 2021 12:07 PM
> To: Kinney, Michael D <michael.d.kinney@intel.com>; devel@edk2.groups.io
> Cc: Liming Gao <gaoliming@byosoft.com.cn>; Sean Brogan <sean.brogan@microsoft.com>; Bret Barkelew
> <Bret.Barkelew@microsoft.com>
> 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/<PluginName> as a temp
directory that is in Build output directory that is ignored by .gitignore.
>
> 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/<PluginName>
> >
> > 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 <mikuback@linux.microsoft.com>
> >> Sent: Tuesday, November 23, 2021 8:54 AM
> >> To: devel@edk2.groups.io
> >> Cc: Kinney, Michael D <michael.d.kinney@intel.com>; Liming Gao <gaoliming@byosoft.com.cn>; Sean Brogan
> >> <sean.brogan@microsoft.com>; Bret Barkelew <Bret.Barkelew@microsoft.com>
> >> Subject: [PATCH v1 1/1] .pytool/Plugin/Uncrustify: Add Uncrustify plugin
> >>
> >> From: Michael Kubacki <michael.kubacki@microsoft.com>
> >>
> >> 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 <michael.d.kinney@intel.com>
> >> Cc: Liming Gao <gaoliming@byosoft.com.cn>
> >> Cc: Sean Brogan <sean.brogan@microsoft.com>
> >> Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
> >> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com>
> >> ---
> >> .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 <packagename>.<plugin>.<optionally any unique condition>
> >> + """
> >> + 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.
> >> +
> >> + <<Copyright>>
> >> + 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
> >> + # '// <here> 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
> >
next prev parent reply other threads:[~2021-11-23 20:19 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-23 16:53 [PATCH v1 1/1] .pytool/Plugin/Uncrustify: Add Uncrustify plugin Michael Kubacki
2021-11-23 19:20 ` Michael D Kinney
2021-11-23 20:06 ` Michael Kubacki
2021-11-23 20:19 ` Michael D Kinney [this message]
2021-11-23 20:34 ` Michael Kubacki
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CO1PR11MB4929509F9EF433E691731DFDD2609@CO1PR11MB4929.namprd11.prod.outlook.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox