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