]> git.proxmox.com Git - mirror_edk2.git/commitdiff
.pytool/Plugin: Add CI plugins
authorSean Brogan <sean.brogan@microsoft.com>
Fri, 18 Oct 2019 04:40:58 +0000 (21:40 -0700)
committerMichael D Kinney <michael.d.kinney@intel.com>
Mon, 11 Nov 2019 21:02:03 +0000 (13:02 -0800)
https://bugzilla.tianocore.org/show_bug.cgi?id=2315

Add .pytool directory to the edk2 repository with the
following plugins.  These plugins are in a top level
directory because that can be used with all packages
and platforms.

* CharEncodingCheck
* CompilerPlugin
* DependencyCheck
* DscCompleteCheck
* GuidCheck
* LibraryClassCheck
* SpellCheck

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Liming Gao <liming.gao@intel.com>
22 files changed:
.pytool/Plugin/CharEncodingCheck/CharEncodingCheck.py [new file with mode: 0644]
.pytool/Plugin/CharEncodingCheck/CharEncodingCheck_plug_in.yaml [new file with mode: 0644]
.pytool/Plugin/CharEncodingCheck/Readme.md [new file with mode: 0644]
.pytool/Plugin/CompilerPlugin/CompilerPlugin.py [new file with mode: 0644]
.pytool/Plugin/CompilerPlugin/Compiler_plug_in.yaml [new file with mode: 0644]
.pytool/Plugin/CompilerPlugin/Readme.md [new file with mode: 0644]
.pytool/Plugin/DependencyCheck/DependencyCheck.py [new file with mode: 0644]
.pytool/Plugin/DependencyCheck/DependencyCheck_plug_in.yaml [new file with mode: 0644]
.pytool/Plugin/DependencyCheck/Readme.md [new file with mode: 0644]
.pytool/Plugin/DscCompleteCheck/DscCompleteCheck.py [new file with mode: 0644]
.pytool/Plugin/DscCompleteCheck/DscCompleteCheck_plug_in.yaml [new file with mode: 0644]
.pytool/Plugin/DscCompleteCheck/Readme.md [new file with mode: 0644]
.pytool/Plugin/GuidCheck/GuidCheck.py [new file with mode: 0644]
.pytool/Plugin/GuidCheck/GuidCheck_plug_in.yaml [new file with mode: 0644]
.pytool/Plugin/GuidCheck/Readme.md [new file with mode: 0644]
.pytool/Plugin/LibraryClassCheck/LibraryClassCheck.py [new file with mode: 0644]
.pytool/Plugin/LibraryClassCheck/LibraryClassCheck_plug_in.yaml [new file with mode: 0644]
.pytool/Plugin/LibraryClassCheck/Readme.md [new file with mode: 0644]
.pytool/Plugin/SpellCheck/Readme.md [new file with mode: 0644]
.pytool/Plugin/SpellCheck/SpellCheck.py [new file with mode: 0644]
.pytool/Plugin/SpellCheck/SpellCheck_plug_in.yaml [new file with mode: 0644]
.pytool/Plugin/SpellCheck/cspell.base.yaml [new file with mode: 0644]

diff --git a/.pytool/Plugin/CharEncodingCheck/CharEncodingCheck.py b/.pytool/Plugin/CharEncodingCheck/CharEncodingCheck.py
new file mode 100644 (file)
index 0000000..02f25ab
--- /dev/null
@@ -0,0 +1,118 @@
+# @file CharEncodingCheck.py\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+\r
+\r
+import os\r
+import logging\r
+from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin\r
+from edk2toolext.environment.var_dict import VarDict\r
+\r
+##\r
+# map\r
+##\r
+EcodingMap = {\r
+    ".md": 'utf-8',\r
+    ".dsc": 'utf-8',\r
+    ".dec": 'utf-8',\r
+    ".c": 'utf-8',\r
+    ".h": 'utf-8',\r
+    ".asm": 'utf-8',\r
+    ".masm": 'utf-8',\r
+    ".nasm": 'utf-8',\r
+    ".s": 'utf-8',\r
+    ".inf": 'utf-8',\r
+    ".asl": 'utf-8',\r
+    ".uni": 'utf-8',\r
+    ".py": 'utf-8'\r
+}\r
+\r
+\r
+class CharEncodingCheck(ICiBuildPlugin):\r
+    """\r
+    A CiBuildPlugin that scans each file in the code tree and confirms the encoding is correct.\r
+\r
+    Configuration options:\r
+    "CharEncodingCheck": {\r
+        "IgnoreFiles": []\r
+    }\r
+    """\r
+\r
+    def GetTestName(self, packagename: str, environment: VarDict) -> tuple:\r
+        """ Provide the testcase name and classname for use in reporting\r
+            testclassname: a descriptive string for the testcase can include whitespace\r
+            classname: should be patterned <packagename>.<plugin>.<optionally any unique condition>\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
+        """\r
+        return ("Check for valid file encoding for " + packagename, packagename + ".CharEncodingCheck")\r
+\r
+    ##\r
+    # External function of plugin.  This function is used to perform the task of the ci_build_plugin Plugin\r
+    #\r
+    #   - package is the edk2 path to package.  This means workspace/packagepath relative.\r
+    #   - edk2path object configured with workspace and packages path\r
+    #   - PkgConfig Object (dict) for the pkg\r
+    #   - EnvConfig Object\r
+    #   - Plugin Manager Instance\r
+    #   - Plugin Helper Obj Instance\r
+    #   - Junit Logger\r
+    #   - output_stream the StringIO output stream from this plugin via logging\r
+    def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None):\r
+        overall_status = 0\r
+        files_tested = 0\r
+\r
+        abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(packagename)\r
+\r
+        if abs_pkg_path is None:\r
+            tc.SetSkipped()\r
+            tc.LogStdError("No Package folder {0}".format(abs_pkg_path))\r
+            return 0\r
+\r
+        for (ext, enc) in EcodingMap.items():\r
+            files = self.WalkDirectoryForExtension([ext], abs_pkg_path)\r
+            files = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(x) for x in files]  # make edk2relative path so can process ignores\r
+\r
+            if "IgnoreFiles" in pkgconfig:\r
+                for a in pkgconfig["IgnoreFiles"]:\r
+                    a = a.replace(os.sep, "/")\r
+                    try:\r
+                        tc.LogStdOut("Ignoring File {0}".format(a))\r
+                        files.remove(a)\r
+                    except:\r
+                        tc.LogStdError("CharEncodingCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))\r
+                        logging.info("CharEncodingCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))\r
+\r
+            files = [Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(x) for x in files]\r
+            for a in files:\r
+                files_tested += 1\r
+                if(self.TestEncodingOk(a, enc)):\r
+                    logging.debug("File {0} Passed Encoding Check {1}".format(a, enc))\r
+                else:\r
+                    tc.LogStdError("Encoding Failure in {0}.  Not {1}".format(a, enc))\r
+                    overall_status += 1\r
+\r
+        tc.LogStdOut("Tested Encoding on {0} files".format(files_tested))\r
+        if overall_status is not 0:\r
+            tc.SetFailed("CharEncoding {0} Failed.  Errors {1}".format(packagename, overall_status), "CHAR_ENCODING_CHECK_FAILED")\r
+        else:\r
+            tc.SetSuccess()\r
+        return overall_status\r
+\r
+    def TestEncodingOk(self, apath, encodingValue):\r
+        try:\r
+            with open(apath, "rb") as fobj:\r
+                fobj.read().decode(encodingValue)\r
+        except Exception as exp:\r
+            logging.error("Encoding failure: file: {0} type: {1}".format(apath, encodingValue))\r
+            logging.debug("EXCEPTION: while processing {1} - {0}".format(exp, apath))\r
+            return False\r
+\r
+        return True\r
diff --git a/.pytool/Plugin/CharEncodingCheck/CharEncodingCheck_plug_in.yaml b/.pytool/Plugin/CharEncodingCheck/CharEncodingCheck_plug_in.yaml
new file mode 100644 (file)
index 0000000..f3ce0b3
--- /dev/null
@@ -0,0 +1,11 @@
+## @file\r
+# CiBuildPlugin used to check char encoding\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+{\r
+  "scope": "cibuild",\r
+  "name": "Char Encoding Check Test",\r
+  "module": "CharEncodingCheck"\r
+}\r
diff --git a/.pytool/Plugin/CharEncodingCheck/Readme.md b/.pytool/Plugin/CharEncodingCheck/Readme.md
new file mode 100644 (file)
index 0000000..47486e5
--- /dev/null
@@ -0,0 +1,18 @@
+# Character Encoding Check Plugin\r
+\r
+This CiBuildPlugin scans all the files in a package to make sure each file is\r
+correctly encoded and all characters can be read.  Improper encoding causes\r
+tools to fail in some situations especially in different locals.\r
+\r
+## Configuration\r
+\r
+The plugin can be configured to ignore certain files.\r
+\r
+``` yaml\r
+"CharEncodingCheck": {\r
+    "IgnoreFiles": []\r
+}\r
+```\r
+### IgnoreFiles\r
+\r
+OPTIONAL List of file to ignore.\r
diff --git a/.pytool/Plugin/CompilerPlugin/CompilerPlugin.py b/.pytool/Plugin/CompilerPlugin/CompilerPlugin.py
new file mode 100644 (file)
index 0000000..3b6f7c7
--- /dev/null
@@ -0,0 +1,102 @@
+# @file HostUnitTestCompiler_plugin.py\r
+##\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+\r
+import logging\r
+import os\r
+import re\r
+from edk2toollib.uefi.edk2.parsers.dsc_parser import DscParser\r
+from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin\r
+from edk2toolext.environment.uefi_build import UefiBuilder\r
+from edk2toolext import edk2_logging\r
+from edk2toolext.environment.var_dict import VarDict\r
+\r
+\r
+class CompilerPlugin(ICiBuildPlugin):\r
+    """\r
+    A CiBuildPlugin that compiles the package dsc\r
+    from the package being tested.\r
+\r
+    Configuration options:\r
+    "CompilerPlugin": {\r
+        "DscPath": "<path to dsc from root of pkg>"\r
+    }\r
+    """\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
+        """\r
+        target = environment.GetValue("TARGET")\r
+        return ("Compile " + packagename + " " + target, packagename + ".Compiler." + target)\r
+\r
+    def RunsOnTargetList(self):\r
+        return ["DEBUG", "RELEASE"]\r
+\r
+    ##\r
+    # External function of plugin.  This function is used to perform the task of the MuBuild Plugin\r
+    #\r
+    #   - package is the edk2 path to package.  This means workspace/packagepath relative.\r
+    #   - edk2path object configured with workspace and packages path\r
+    #   - PkgConfig Object (dict) for the pkg\r
+    #   - EnvConfig Object\r
+    #   - Plugin Manager Instance\r
+    #   - Plugin Helper Obj Instance\r
+    #   - Junit Logger\r
+    #   - output_stream the StringIO output stream from this plugin via logging\r
+    def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None):\r
+        self._env = environment\r
+\r
+        # Parse the config for required DscPath element\r
+        if "DscPath" not in pkgconfig:\r
+            tc.SetSkipped()\r
+            tc.LogStdError("DscPath not found in config file.  Nothing to compile.")\r
+            return -1\r
+\r
+        AP = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(packagename)\r
+\r
+        APDSC = os.path.join(AP, pkgconfig["DscPath"].strip())\r
+        AP_Path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(APDSC)\r
+        if AP is None or AP_Path is None or not os.path.isfile(APDSC):\r
+            tc.SetSkipped()\r
+            tc.LogStdError("Package Dsc not found.")\r
+            return -1\r
+\r
+        logging.info("Building {0}".format(AP_Path))\r
+        self._env.SetValue("ACTIVE_PLATFORM", AP_Path, "Set in Compiler Plugin")\r
+\r
+        # Parse DSC to check for SUPPORTED_ARCHITECTURES\r
+        dp = DscParser()\r
+        dp.SetBaseAbsPath(Edk2pathObj.WorkspacePath)\r
+        dp.SetPackagePaths(Edk2pathObj.PackagePathList)\r
+        dp.ParseFile(AP_Path)\r
+        if "SUPPORTED_ARCHITECTURES" in dp.LocalVars:\r
+            SUPPORTED_ARCHITECTURES = dp.LocalVars["SUPPORTED_ARCHITECTURES"].split('|')\r
+            TARGET_ARCHITECTURES = environment.GetValue("TARGET_ARCH").split(' ')\r
+\r
+            # Skip if there is no intersection between SUPPORTED_ARCHITECTURES and TARGET_ARCHITECTURES\r
+            if len(set(SUPPORTED_ARCHITECTURES) & set(TARGET_ARCHITECTURES)) == 0:\r
+                tc.SetSkipped()\r
+                tc.LogStdError("No supported architecutres to build")\r
+                return -1\r
+\r
+        uefiBuilder = UefiBuilder()\r
+        # do all the steps\r
+        # WorkSpace, PackagesPath, PInHelper, PInManager\r
+        ret = uefiBuilder.Go(Edk2pathObj.WorkspacePath, os.pathsep.join(Edk2pathObj.PackagePathList), PLMHelper, PLM)\r
+        if ret != 0:  # failure:\r
+            tc.SetFailed("Compile failed for {0}".format(packagename), "Compile_FAILED")\r
+            tc.LogStdError("{0} Compile failed with error code {1} ".format(AP_Path, ret))\r
+            return 1\r
+\r
+        else:\r
+            tc.SetSuccess()\r
+            return 0\r
diff --git a/.pytool/Plugin/CompilerPlugin/Compiler_plug_in.yaml b/.pytool/Plugin/CompilerPlugin/Compiler_plug_in.yaml
new file mode 100644 (file)
index 0000000..4d7ca32
--- /dev/null
@@ -0,0 +1,11 @@
+## @file\r
+# CiBuildPlugin used to compile each package\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+{\r
+  "scope": "cibuild",\r
+  "name": "Compiler Plugin",\r
+  "module": "CompilerPlugin"\r
+}\r
diff --git a/.pytool/Plugin/CompilerPlugin/Readme.md b/.pytool/Plugin/CompilerPlugin/Readme.md
new file mode 100644 (file)
index 0000000..44e6a2c
--- /dev/null
@@ -0,0 +1,17 @@
+# Compiler Plugin\r
+\r
+This CiBuildPlugin compiles the package DSC from the package being tested.\r
+\r
+## Configuration\r
+\r
+The package relative path of the DSC file to build.\r
+\r
+``` yaml\r
+"CompilerPlugin": {\r
+    "DscPath": "<path to dsc from root of pkg>"\r
+}\r
+```\r
+\r
+### DscPath\r
+\r
+Package relative path to the DSC file to build.\r
diff --git a/.pytool/Plugin/DependencyCheck/DependencyCheck.py b/.pytool/Plugin/DependencyCheck/DependencyCheck.py
new file mode 100644 (file)
index 0000000..2c3d8ba
--- /dev/null
@@ -0,0 +1,120 @@
+# @file dependency_check.py\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+\r
+import logging\r
+import os\r
+from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin\r
+from edk2toollib.uefi.edk2.parsers.inf_parser import InfParser\r
+from edk2toolext.environment.var_dict import VarDict\r
+\r
+\r
+class DependencyCheck(ICiBuildPlugin):\r
+    """\r
+    A CiBuildPlugin that finds all modules (inf files) in a package and reviews the packages used\r
+    to confirm they are acceptable.  This is to help enforce layering and identify improper\r
+    dependencies between packages.\r
+\r
+    Configuration options:\r
+    "DependencyCheck": {\r
+        "AcceptableDependencies": [], # Package dec files that are allowed in all INFs.  Example: MdePkg/MdePkg.dec\r
+        "AcceptableDependencies-<MODULE_TYPE>": [], # OPTIONAL Package dependencies for INFs that are HOST_APPLICATION\r
+        "AcceptableDependencies-HOST_APPLICATION": [], # EXAMPLE Package dependencies for INFs that are HOST_APPLICATION\r
+        "IgnoreInf": []  # Ignore INF if found in filesystem\r
+    }\r
+    """\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 ("Test Package Dependencies for modules in " + packagename, packagename + ".DependencyCheck")\r
+\r
+    ##\r
+    # External function of plugin.  This function is used to perform the task of the MuBuild Plugin\r
+    #\r
+    #   - package is the edk2 path to package.  This means workspace/packagepath relative.\r
+    #   - edk2path object configured with workspace and packages path\r
+    #   - PkgConfig Object (dict) for the pkg\r
+    #   - EnvConfig Object\r
+    #   - Plugin Manager Instance\r
+    #   - Plugin Helper Obj Instance\r
+    #   - Junit Logger\r
+    #   - output_stream the StringIO output stream from this plugin via logging\r
+    def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None):\r
+        overall_status = 0\r
+\r
+        # Get current platform\r
+        abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(packagename)\r
+\r
+        # Get INF Files\r
+        INFFiles = self.WalkDirectoryForExtension([".inf"], abs_pkg_path)\r
+        INFFiles = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(x) for x in INFFiles]  # make edk2relative path so can compare with Ignore List\r
+\r
+        # Remove ignored INFs\r
+        if "IgnoreInf" in pkgconfig:\r
+            for a in pkgconfig["IgnoreInf"]:\r
+                a = a.replace(os.sep, "/")  ## convert path sep in case ignore list is bad.  Can't change case\r
+                try:\r
+                    INFFiles.remove(a)\r
+                    tc.LogStdOut("IgnoreInf {0}".format(a))\r
+                except:\r
+                    logging.info("DependencyConfig.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))\r
+                    tc.LogStdError("DependencyConfig.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))\r
+\r
+\r
+        # Get the AccpetableDependencies list\r
+        if "AcceptableDependencies" not in pkgconfig:\r
+            logging.info("DependencyCheck Skipped.  No Acceptable Dependencies defined.")\r
+            tc.LogStdOut("DependencyCheck Skipped.  No Acceptable Dependencies defined.")\r
+            tc.SetSkipped()\r
+            return -1\r
+\r
+        # Log dependencies\r
+        for k in pkgconfig.keys():\r
+            if k.startswith("AcceptableDependencies"):\r
+                pkgstring = "\n".join(pkgconfig[k])\r
+                if ("-" in k):\r
+                    _, _, mod_type = k.partition("-")\r
+                    tc.LogStdOut(f"Additional dependencies for MODULE_TYPE {mod_type}:\n {pkgstring}")\r
+                else:\r
+                    tc.LogStdOut(f"Acceptable Dependencies:\n {pkgstring}")\r
+\r
+        # For each INF file\r
+        for file in INFFiles:\r
+            ip = InfParser()\r
+            logging.debug("Parsing " + file)\r
+            ip.SetBaseAbsPath(Edk2pathObj.WorkspacePath).SetPackagePaths(Edk2pathObj.PackagePathList).ParseFile(file)\r
+\r
+            if("MODULE_TYPE" not in ip.Dict):\r
+                tc.LogStdOut("Ignoring INF. Missing key for MODULE_TYPE {0}".format(file))\r
+                continue\r
+\r
+            mod_type = ip.Dict["MODULE_TYPE"].upper()\r
+            for p in ip.PackagesUsed:\r
+                if p not in pkgconfig["AcceptableDependencies"]:\r
+                    # If not in the main acceptable dependencies list then check module specific\r
+                    mod_specific_key = "AcceptableDependencies-" + mod_type\r
+                    if mod_specific_key in pkgconfig and p in pkgconfig[mod_specific_key]:\r
+                        continue\r
+\r
+                    logging.error("Dependency Check: Invalid Dependency INF: {0} depends on pkg {1}".format(file, p))\r
+                    tc.LogStdError("Dependency Check: Invalid Dependency INF: {0} depends on pkg {1}".format(file, p))\r
+                    overall_status += 1\r
+\r
+        # If XML object exists, add results\r
+        if overall_status is not 0:\r
+            tc.SetFailed("Failed with {0} errors".format(overall_status), "DEPENDENCYCHECK_FAILED")\r
+        else:\r
+            tc.SetSuccess()\r
+        return overall_status\r
diff --git a/.pytool/Plugin/DependencyCheck/DependencyCheck_plug_in.yaml b/.pytool/Plugin/DependencyCheck/DependencyCheck_plug_in.yaml
new file mode 100644 (file)
index 0000000..c983469
--- /dev/null
@@ -0,0 +1,13 @@
+## @file\r
+# CiBuildPlugin used to check all infs within a package\r
+# to confirm the packagesdependency are on the configured list of acceptable\r
+# dependencies.\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+{\r
+  "scope": "cibuild",\r
+  "name": "Dependency Check Test",\r
+  "module": "DependencyCheck"\r
+}\r
diff --git a/.pytool/Plugin/DependencyCheck/Readme.md b/.pytool/Plugin/DependencyCheck/Readme.md
new file mode 100644 (file)
index 0000000..62612f6
--- /dev/null
@@ -0,0 +1,31 @@
+# Depdendency Check Plugin\r
+\r
+A CiBuildPlugin that finds all modules (inf files) in a package and reviews the\r
+packages used to confirm they are acceptable.  This is to help enforce layering\r
+and identify improper dependencies between packages.\r
+\r
+## Configuration\r
+\r
+The plugin must be configured with the acceptabe package dependencies for the\r
+package.\r
+\r
+``` yaml\r
+"DependencyCheck": {\r
+    "AcceptableDependencies": [],\r
+    "AcceptableDependencies-<MODULE_TYPE>": [],\r
+    "IgnoreInf": []\r
+}\r
+```\r
+\r
+### AcceptableDependencies\r
+\r
+Package dec files that are allowed in all INFs.  Example: MdePkg/MdePkg.dec\r
+\r
+### AcceptableDependencies-<MODULE_TYPE>\r
+\r
+OPTIONAL Package dependencies for INFs that have module type <MODULE_TYPE>.\r
+Example: AcceptableDependencies-HOST_APPLICATION.\r
+\r
+### IgnoreInf\r
+\r
+OPTIONAL list of INFs to ignore for this dependency check.\r
diff --git a/.pytool/Plugin/DscCompleteCheck/DscCompleteCheck.py b/.pytool/Plugin/DscCompleteCheck/DscCompleteCheck.py
new file mode 100644 (file)
index 0000000..e2485f5
--- /dev/null
@@ -0,0 +1,118 @@
+# @file DscCompleteCheck.py\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+import logging\r
+import os\r
+from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin\r
+from edk2toollib.uefi.edk2.parsers.dsc_parser import DscParser\r
+from edk2toollib.uefi.edk2.parsers.inf_parser import InfParser\r
+from edk2toolext.environment.var_dict import VarDict\r
+\r
+\r
+class DscCompleteCheck(ICiBuildPlugin):\r
+    """\r
+    A CiBuildPlugin that scans the package dsc file and confirms all modules (inf files) are\r
+    listed in the components sections.\r
+\r
+    Configuration options:\r
+    "DscCompleteCheck": {\r
+        "DscPath": "<path to dsc from root of pkg>"\r
+        "IgnoreInf": []  # Ignore INF if found in filesystem by not dsc\r
+    }\r
+    """\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 the " + packagename + " DSC for a being complete", packagename + ".DscCompleteCheck")\r
+\r
+    ##\r
+    # External function of plugin.  This function is used to perform the task of the MuBuild Plugin\r
+    #\r
+    #   - package is the edk2 path to package.  This means workspace/packagepath relative.\r
+    #   - edk2path object configured with workspace and packages path\r
+    #   - PkgConfig Object (dict) for the pkg\r
+    #   - VarDict containing the shell environment Build Vars\r
+    #   - Plugin Manager Instance\r
+    #   - Plugin Helper Obj Instance\r
+    #   - Junit Logger\r
+    #   - output_stream the StringIO output stream from this plugin via logging\r
+    def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None):\r
+        overall_status = 0\r
+\r
+        # Parse the config for required DscPath element\r
+        if "DscPath" not in pkgconfig:\r
+            tc.SetSkipped()\r
+            tc.LogStdError("DscPath not found in config file.  Nothing to check.")\r
+            return -1\r
+\r
+        abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(packagename)\r
+        abs_dsc_path = os.path.join(abs_pkg_path, pkgconfig["DscPath"].strip())\r
+        wsr_dsc_path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(abs_dsc_path)\r
+\r
+        if abs_dsc_path is None or wsr_dsc_path is "" or not os.path.isfile(abs_dsc_path):\r
+            tc.SetSkipped()\r
+            tc.LogStdError("Package Dsc not found")\r
+            return 0\r
+\r
+        # Get INF Files\r
+        INFFiles = self.WalkDirectoryForExtension([".inf"], abs_pkg_path)\r
+        INFFiles = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(x) for x in INFFiles]  # make edk2relative path so can compare with DSC\r
+\r
+        # remove ignores\r
+\r
+        if "IgnoreInf" in pkgconfig:\r
+            for a in pkgconfig["IgnoreInf"]:\r
+                a = a.replace(os.sep, "/")\r
+                try:\r
+                    tc.LogStdOut("Ignoring INF {0}".format(a))\r
+                    INFFiles.remove(a)\r
+                except:\r
+                    tc.LogStdError("DscCompleteCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))\r
+                    logging.info("DscCompleteCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))\r
+\r
+        # DSC Parser\r
+        dp = DscParser()\r
+        dp.SetBaseAbsPath(Edk2pathObj.WorkspacePath)\r
+        dp.SetPackagePaths(Edk2pathObj.PackagePathList)\r
+        dp.SetInputVars(environment.GetAllBuildKeyValues())\r
+        dp.ParseFile(wsr_dsc_path)\r
+\r
+        # Check if INF in component section\r
+        for INF in INFFiles:\r
+            if not any(INF.strip() in x for x in dp.ThreeMods) and \\r
+               not any(INF.strip() in x for x in dp.SixMods) and \\r
+               not any(INF.strip() in x for x in dp.OtherMods):\r
+\r
+                infp = InfParser().SetBaseAbsPath(Edk2pathObj.WorkspacePath)\r
+                infp.SetPackagePaths(Edk2pathObj.PackagePathList)\r
+                infp.ParseFile(INF)\r
+                if("MODULE_TYPE" not in infp.Dict):\r
+                    tc.LogStdOut("Ignoring INF. Missing key for MODULE_TYPE {0}".format(INF))\r
+                    continue\r
+\r
+                if(infp.Dict["MODULE_TYPE"] == "HOST_APPLICATION"):\r
+                    tc.LogStdOut("Ignoring INF.  Module type is HOST_APPLICATION {0}".format(INF))\r
+                    continue\r
+\r
+                logging.critical(INF + " not in " + wsr_dsc_path)\r
+                tc.LogStdError("{0} not in {1}".format(INF, wsr_dsc_path))\r
+                overall_status = overall_status + 1\r
+\r
+        # If XML object exists, add result\r
+        if overall_status is not 0:\r
+            tc.SetFailed("DscCompleteCheck {0} Failed.  Errors {1}".format(wsr_dsc_path, overall_status), "CHECK_FAILED")\r
+        else:\r
+            tc.SetSuccess()\r
+        return overall_status\r
diff --git a/.pytool/Plugin/DscCompleteCheck/DscCompleteCheck_plug_in.yaml b/.pytool/Plugin/DscCompleteCheck/DscCompleteCheck_plug_in.yaml
new file mode 100644 (file)
index 0000000..215b8b7
--- /dev/null
@@ -0,0 +1,12 @@
+## @file\r
+# CiBuildPlugin used to confirm all INFs are listed in\r
+# the components section of package dsc\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+{\r
+  "scope": "cibuild",\r
+  "name": "Dsc Complete Check Test",\r
+  "module": "DscCompleteCheck"\r
+}\r
diff --git a/.pytool/Plugin/DscCompleteCheck/Readme.md b/.pytool/Plugin/DscCompleteCheck/Readme.md
new file mode 100644 (file)
index 0000000..eefbb98
--- /dev/null
@@ -0,0 +1,27 @@
+# Dsc Complete Check Plugin\r
+\r
+This CiBuildPlugin scans all INF files from a package and confirms they are\r
+listed in the package level DSC file. The test considers it an error if any INF\r
+does not appear in the `Components` section of the package-level DSC (indicating\r
+that it would not be built if the package were built). This is critical because\r
+much of the CI infrastructure assumes that all modules will be listed in the DSC\r
+and compiled.\r
+\r
+## Configuration\r
+\r
+The plugin has a few configuration options to support the UEFI codebase.\r
+\r
+``` yaml\r
+"DscCompleteCheck": {\r
+        "DscPath": "",   # Path to dsc from root of package\r
+        "IgnoreInf": []  # Ignore INF if found in filesystem by not dsc\r
+    }\r
+```\r
+\r
+### DscPath\r
+\r
+Path to DSC to consider platform dsc\r
+\r
+### IgnoreInf\r
+\r
+Ignore error if Inf file is not listed in DSC file\r
diff --git a/.pytool/Plugin/GuidCheck/GuidCheck.py b/.pytool/Plugin/GuidCheck/GuidCheck.py
new file mode 100644 (file)
index 0000000..f0b10be
--- /dev/null
@@ -0,0 +1,251 @@
+# @file GuidCheck.py\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+import logging\r
+from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin\r
+from edk2toollib.uefi.edk2.guid_list import GuidList\r
+from edk2toolext.environment.var_dict import VarDict\r
+\r
+\r
+class GuidCheck(ICiBuildPlugin):\r
+    """\r
+    A CiBuildPlugin that scans the code tree and looks for duplicate guids\r
+    from the package being tested.\r
+\r
+    Configuration options:\r
+    "GuidCheck": {\r
+        "IgnoreGuidName": [], # provide in format guidname=guidvalue or just guidname\r
+        "IgnoreGuidValue": [],\r
+        "IgnoreFoldersAndFiles": [],\r
+        "IgnoreDuplicates": [] # Provide in format guidname=guidname=guidname...\r
+    }\r
+    """\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 ("Confirm GUIDs are unique in " + packagename, packagename + ".GuidCheck")\r
+\r
+    def _FindConflictingGuidValues(self, guidlist: list) -> list:\r
+        """ Find all duplicate guids by guid value and report them as errors\r
+        """\r
+        # Sort the list by guid\r
+        guidsorted = sorted(\r
+            guidlist, key=lambda x: x.guid.upper(), reverse=True)\r
+\r
+        previous = None  # Store previous entry for comparison\r
+        error = None\r
+        errors = []\r
+        for index in range(len(guidsorted)):\r
+            i = guidsorted[index]\r
+            if(previous is not None):\r
+                if i.guid == previous.guid:  # Error\r
+                    if(error is None):\r
+                        # Catch errors with more than 1 conflict\r
+                        error = ErrorEntry("guid")\r
+                        error.entries.append(previous)\r
+                        errors.append(error)\r
+                    error.entries.append(i)\r
+                else:\r
+                    # no match.  clear error\r
+                    error = None\r
+            previous = i\r
+        return errors\r
+\r
+    def _FindConflictingGuidNames(self, guidlist: list) -> list:\r
+        """ Find all duplicate guids by name and if they are not all\r
+        from inf files report them as errors.  It is ok to have\r
+        BASE_NAME duplication.\r
+\r
+        Is this useful?  It would catch two same named guids in dec file\r
+        that resolve to different values.\r
+        """\r
+        # Sort the list by guid\r
+        namesorted = sorted(guidlist, key=lambda x: x.name.upper())\r
+\r
+        previous = None  # Store previous entry for comparison\r
+        error = None\r
+        errors = []\r
+        for index in range(len(namesorted)):\r
+            i = namesorted[index]\r
+            if(previous is not None):\r
+                # If name matches\r
+                if i.name == previous.name:\r
+                    if(error is None):\r
+                        # Catch errors with more than 1 conflict\r
+                        error = ErrorEntry("name")\r
+                        error.entries.append(previous)\r
+                        errors.append(error)\r
+                    error.entries.append(i)\r
+                else:\r
+                    # no match.  clear error\r
+                    error = None\r
+            previous = i\r
+\r
+            # Loop thru and remove any errors where all files are infs as it is ok if\r
+            # they have the same inf base name.\r
+            for e in errors[:]:\r
+                if len( [en for en in e.entries if not en.absfilepath.lower().endswith(".inf")]) == 0:\r
+                    errors.remove(e)\r
+\r
+        return errors\r
+\r
+    ##\r
+    # External function of plugin.  This function is used to perform the task of the MuBuild Plugin\r
+    #\r
+    #   - package is the edk2 path to package.  This means workspace/packagepath relative.\r
+    #   - edk2path object configured with workspace and packages path\r
+    #   - PkgConfig Object (dict) for the pkg\r
+    #   - EnvConfig Object\r
+    #   - Plugin Manager Instance\r
+    #   - Plugin Helper Obj Instance\r
+    #   - Junit Logger\r
+    #   - output_stream the StringIO output stream from this plugin via logging\r
+\r
+    def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None):\r
+        Errors = []\r
+\r
+        abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(\r
+            packagename)\r
+\r
+        if abs_pkg_path is None:\r
+            tc.SetSkipped()\r
+            tc.LogStdError("No package {0}".format(packagename))\r
+            return -1\r
+\r
+        All_Ignores = ["/Build", "/Conf"]\r
+        # Parse the config for other ignores\r
+        if "IgnoreFoldersAndFiles" in pkgconfig:\r
+            All_Ignores.extend(pkgconfig["IgnoreFoldersAndFiles"])\r
+\r
+        # Parse the workspace for all GUIDs\r
+        gs = GuidList.guidlist_from_filesystem(\r
+            Edk2pathObj.WorkspacePath, ignore_lines=All_Ignores)\r
+\r
+        # Remove ignored guidvalue\r
+        if "IgnoreGuidValue" in pkgconfig:\r
+            for a in pkgconfig["IgnoreGuidValue"]:\r
+                try:\r
+                    tc.LogStdOut("Ignoring Guid {0}".format(a.upper()))\r
+                    for b in gs[:]:\r
+                        if b.guid == a.upper():\r
+                            gs.remove(b)\r
+                except:\r
+                    tc.LogStdError("GuidCheck.IgnoreGuid -> {0} not found.  Invalid ignore guid".format(a.upper()))\r
+                    logging.info("GuidCheck.IgnoreGuid -> {0} not found.  Invalid ignore guid".format(a.upper()))\r
+\r
+        # Remove ignored guidname\r
+        if "IgnoreGuidName" in pkgconfig:\r
+            for a in pkgconfig["IgnoreGuidName"]:\r
+                entry = a.split("=")\r
+                if(len(entry) > 2):\r
+                    tc.LogStdError("GuidCheck.IgnoreGuidName -> {0} Invalid Format.".format(a))\r
+                    logging.info("GuidCheck.IgnoreGuidName -> {0} Invalid Format.".format(a))\r
+                    continue\r
+                try:\r
+                    tc.LogStdOut("Ignoring Guid {0}".format(a))\r
+                    for b in gs[:]:\r
+                        if b.name == entry[0]:\r
+                            if(len(entry) == 1):\r
+                                gs.remove(b)\r
+                            elif(len(entry) == 2 and b.guid.upper() == entry[1].upper()):\r
+                                gs.remove(b)\r
+                            else:\r
+                                c.LogStdError("GuidCheck.IgnoreGuidName -> {0} incomplete match.  Invalid ignore guid".format(a))\r
+\r
+                except:\r
+                    tc.LogStdError("GuidCheck.IgnoreGuidName -> {0} not found.  Invalid ignore name".format(a))\r
+                    logging.info("GuidCheck.IgnoreGuidName -> {0} not found.  Invalid ignore name".format(a))\r
+\r
+        # Find conflicting Guid Values\r
+        Errors.extend(self._FindConflictingGuidValues(gs))\r
+\r
+        # Check if there are expected duplicates and remove it from the error list\r
+        if "IgnoreDuplicates" in pkgconfig:\r
+            for a in pkgconfig["IgnoreDuplicates"]:\r
+                names = a.split("=")\r
+                if len(names) < 2:\r
+                    tc.LogStdError("GuidCheck.IgnoreDuplicates -> {0} invalid format".format(a))\r
+                    logging.info("GuidCheck.IgnoreDuplicates -> {0} invalid format".format(a))\r
+                    continue\r
+\r
+                for b in Errors[:]:\r
+                    if b.type != "guid":\r
+                        continue\r
+                    ## Make a list of the names that are not in the names list.  If there\r
+                    ## are any in the list then this error should not be ignored.\r
+                    t = [x for x in b.entries if x.name not in names]\r
+                    if(len(t) == len(b.entries)):\r
+                        ## did not apply to any entry\r
+                        continue\r
+                    elif(len(t) == 0):\r
+                        ## full match - ignore duplicate\r
+                        tc.LogStdOut("GuidCheck.IgnoreDuplicates -> {0}".format(a))\r
+                        Errors.remove(b)\r
+                    elif(len(t) < len(b.entries)):\r
+                        ## partial match\r
+                        tc.LogStdOut("GuidCheck.IgnoreDuplicates -> {0} incomplete match".format(a))\r
+                        logging.info("GuidCheck.IgnoreDuplicates -> {0} incomplete match".format(a))\r
+                    else:\r
+                        tc.LogStdOut("GuidCheck.IgnoreDuplicates -> {0} unknown error.".format(a))\r
+                        logging.info("GuidCheck.IgnoreDuplicates -> {0} unknown error".format(a))\r
+\r
+\r
+\r
+        # Find conflicting Guid Names\r
+        Errors.extend(self._FindConflictingGuidNames(gs))\r
+\r
+        # Log errors for anything within the package under test\r
+        for er in Errors[:]:\r
+            InMyPackage = False\r
+            for a in er.entries:\r
+                if abs_pkg_path in a.absfilepath:\r
+                    InMyPackage = True\r
+                    break\r
+            if(not InMyPackage):\r
+                Errors.remove(er)\r
+            else:\r
+                logging.error(str(er))\r
+                tc.LogStdError(str(er))\r
+\r
+        # add result to test case\r
+        overall_status = len(Errors)\r
+        if overall_status is not 0:\r
+            tc.SetFailed("GuidCheck {0} Failed.  Errors {1}".format(\r
+                packagename, overall_status), "CHECK_FAILED")\r
+        else:\r
+            tc.SetSuccess()\r
+        return overall_status\r
+\r
+\r
+class ErrorEntry():\r
+    """ Custom/private class for reporting errors in the GuidList\r
+    """\r
+\r
+    def __init__(self, errortype):\r
+        self.type = errortype  # 'guid' or 'name' depending on error type\r
+        self.entries = []  # GuidListEntry that are in error condition\r
+\r
+    def __str__(self):\r
+        a = f"Error Duplicate {self.type}: "\r
+        if(self.type == "guid"):\r
+            a += f" {self.entries[0].guid}"\r
+        elif(self.type == "name"):\r
+            a += f" {self.entries[0].name}"\r
+\r
+        a += f" ({len(self.entries)})\n"\r
+\r
+        for e in self.entries:\r
+            a += "\t" + str(e) + "\n"\r
+        return a\r
diff --git a/.pytool/Plugin/GuidCheck/GuidCheck_plug_in.yaml b/.pytool/Plugin/GuidCheck/GuidCheck_plug_in.yaml
new file mode 100644 (file)
index 0000000..b16f6c1
--- /dev/null
@@ -0,0 +1,11 @@
+## @file\r
+# CiBuildPlugin used to check guid uniqueness\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+{\r
+  "scope": "cibuild",\r
+  "name": "Guid Check Test",\r
+  "module": "GuidCheck"\r
+}\r
diff --git a/.pytool/Plugin/GuidCheck/Readme.md b/.pytool/Plugin/GuidCheck/Readme.md
new file mode 100644 (file)
index 0000000..4e2c353
--- /dev/null
@@ -0,0 +1,80 @@
+# Guid Check Plugin\r
+\r
+This CiBuildPlugin scans all the files in a code tree to find all the GUID\r
+definitions.  After collection it will then look for duplication in the package\r
+under test.  Uniqueness of all GUIDs are critical within the UEFI environment.\r
+Duplication can cause numerous issues including locating the wrong data\r
+structure, calling the wrong function, or decoding the wrong data members.\r
+\r
+Currently Scanned:\r
+\r
+* INF files are scanned for there Module guid\r
+* DEC files are scanned for all of their Protocols, PPIs, and Guids as well as\r
+  the one package GUID.\r
+\r
+Any GUID value being equal to two names or even just defined in two files is\r
+considered an error unless in the ignore list.\r
+\r
+Any GUID name that is found more than once is an error unless all occurrences\r
+are Module GUIDs.  Since the Module GUID is assigned to the Module name it is\r
+common to have numerous versions of the same module named the same.\r
+\r
+## Configuration\r
+\r
+The plugin has numerous configuration options to support the UEFI codebase.\r
+\r
+``` yaml\r
+"GuidCheck": {\r
+        "IgnoreGuidName": [],\r
+        "IgnoreGuidValue": [],\r
+        "IgnoreFoldersAndFiles": [],\r
+        "IgnoreDuplicates": []\r
+    }\r
+```\r
+\r
+### IgnoreGuidName\r
+\r
+This list allows strings in two formats.\r
+\r
+* _GuidName_\r
+  * This will remove any entry with this GuidName from the list of GUIDs\r
+    therefore ignoring any error associated with this name.\r
+* _GuidName=GuidValue_\r
+  * This will also ignore the GUID by name but only if the value equals the\r
+    GuidValue.\r
+  * GuidValue should be in registry format.\r
+  * This is the suggested format to use as it will limit the ignore to only the\r
+    defined case.\r
+\r
+### IgnoreGuidValue\r
+\r
+This list allows strings in guid registry format _GuidValue_.\r
+\r
+* This will remove any entry with this GuidValue from the list of GUIDs\r
+  therefore ignoring any error associated with this value.\r
+* GuidValue must be in registry format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r
+\r
+### IgnoreFoldersAndFiles\r
+\r
+This supports .gitignore file and folder matching strings including wildcards\r
+\r
+* Any folder or file ignored will not be parsed and therefore any GUID defined\r
+  will be ignored.\r
+* The plugin will always ignores the following ["/Build", "/Conf"]\r
+\r
+### IgnoreDuplicates\r
+\r
+This supports strings in the format of _GuidName_=_GuidName_=_GuidName_\r
+\r
+* For the error with the GuidNames to be ignored the list must match completely\r
+  with what is found during the code scan.\r
+  * For example if there are two GUIDs that are by design equal within the code\r
+    tree then it should be _GuidName_=_GuidName_\r
+  * If instead there are three GUIDs then it must be\r
+    _GuidName_=_GuidName_=_GuidName_\r
+* This is the best ignore list to use because it is the most strict and will\r
+  catch new problems when new conflicts are introduced.\r
+* There are numerous places in the UEFI specification in which two GUID names\r
+  are assigned the same value.  These names should be set in this ignore list so\r
+  that they don't cause an error but any additional duplication would still be\r
+  caught.\r
diff --git a/.pytool/Plugin/LibraryClassCheck/LibraryClassCheck.py b/.pytool/Plugin/LibraryClassCheck/LibraryClassCheck.py
new file mode 100644 (file)
index 0000000..5dafcbc
--- /dev/null
@@ -0,0 +1,153 @@
+# @file LibraryClassCheck.py\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+import logging\r
+import os\r
+from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin\r
+from edk2toollib.uefi.edk2.parsers.dec_parser import DecParser\r
+from edk2toollib.uefi.edk2.parsers.inf_parser import InfParser\r
+from edk2toolext.environment.var_dict import VarDict\r
+\r
+\r
+class LibraryClassCheck(ICiBuildPlugin):\r
+    """\r
+    A CiBuildPlugin that scans the code tree and library classes for undeclared\r
+    files\r
+\r
+    Configuration options:\r
+    "LibraryClassCheck": {\r
+        IgnoreHeaderFile: [],  # Ignore a file found on disk\r
+        IgnoreLibraryClass: [] # Ignore a declaration found in dec file\r
+    }\r
+    """\r
+\r
+    def GetTestName(self, packagename: str, environment: VarDict) -> tuple:\r
+        """ Provide the testcase name and classname for use in reporting\r
+            testclassname: a descriptive string for the testcase can include whitespace\r
+            classname: should be patterned <packagename>.<plugin>.<optionally any unique condition>\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
+        """\r
+        return ("Check library class declarations in " + packagename, packagename + ".LibraryClassCheck")\r
+\r
+    def __GetPkgDec(self, rootpath):\r
+        try:\r
+            allEntries = os.listdir(rootpath)\r
+            for entry in allEntries:\r
+                if entry.lower().endswith(".dec"):\r
+                    return(os.path.join(rootpath, entry))\r
+        except Exception:\r
+            logging.error("Unable to find DEC for package:{0}".format(rootpath))\r
+\r
+        return None\r
+\r
+    ##\r
+    # External function of plugin.  This function is used to perform the task of the MuBuild Plugin\r
+    #\r
+    #   - package is the edk2 path to package.  This means workspace/packagepath relative.\r
+    #   - edk2path object configured with workspace and packages path\r
+    #   - PkgConfig Object (dict) for the pkg\r
+    #   - EnvConfig Object\r
+    #   - Plugin Manager Instance\r
+    #   - Plugin Helper Obj Instance\r
+    #   - Junit Logger\r
+    #   - output_stream the StringIO output stream from this plugin via logging\r
+    def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None):\r
+        overall_status = 0\r
+        LibraryClassIgnore = []\r
+\r
+        abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(packagename)\r
+        abs_dec_path = self.__GetPkgDec(abs_pkg_path)\r
+        wsr_dec_path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(abs_dec_path)\r
+\r
+        if abs_dec_path is None or wsr_dec_path is "" or not os.path.isfile(abs_dec_path):\r
+            tc.SetSkipped()\r
+            tc.LogStdError("No DEC file {0} in package {1}".format(abs_dec_path, abs_pkg_path))\r
+            return -1\r
+\r
+        # Get all include folders\r
+        dec = DecParser()\r
+        dec.SetBaseAbsPath(Edk2pathObj.WorkspacePath).SetPackagePaths(Edk2pathObj.PackagePathList)\r
+        dec.ParseFile(wsr_dec_path)\r
+\r
+        AllHeaderFiles = []\r
+\r
+        for includepath in dec.IncludePaths:\r
+            ## Get all header files in the library folder\r
+            AbsLibraryIncludePath = os.path.join(abs_pkg_path, includepath, "Library")\r
+            if(not os.path.isdir(AbsLibraryIncludePath)):\r
+                continue\r
+\r
+            hfiles = self.WalkDirectoryForExtension([".h"], AbsLibraryIncludePath)\r
+            hfiles = [os.path.relpath(x,abs_pkg_path) for x in hfiles]  # make package root relative path\r
+            hfiles = [x.replace("\\", "/") for x in hfiles]  # make package relative path\r
+\r
+            AllHeaderFiles.extend(hfiles)\r
+\r
+        if len(AllHeaderFiles) == 0:\r
+            tc.SetSkipped()\r
+            tc.LogStdError(f"No Library include folder in any Include path")\r
+            return -1\r
+\r
+        # Remove ignored paths\r
+        if "IgnoreHeaderFile" in pkgconfig:\r
+            for a in pkgconfig["IgnoreHeaderFile"]:\r
+                try:\r
+                    tc.LogStdOut("Ignoring Library Header File {0}".format(a))\r
+                    AllHeaderFiles.remove(a)\r
+                except:\r
+                    tc.LogStdError("LibraryClassCheck.IgnoreHeaderFile -> {0} not found.  Invalid Header File".format(a))\r
+                    logging.info("LibraryClassCheck.IgnoreHeaderFile -> {0} not found.  Invalid Header File".format(a))\r
+\r
+        if "IgnoreLibraryClass" in pkgconfig:\r
+            LibraryClassIgnore = pkgconfig["IgnoreLibraryClass"]\r
+\r
+\r
+        ## Attempt to find library classes\r
+        for lcd in dec.LibraryClasses:\r
+            ## Check for correct file path separator\r
+            if "\\" in lcd.path:\r
+                tc.LogStdError("LibraryClassCheck.DecFilePathSeparator -> {0} invalid.".format(lcd.path))\r
+                logging.error("LibraryClassCheck.DecFilePathSeparator -> {0} invalid.".format(lcd.path))\r
+                overall_status += 1\r
+                continue\r
+\r
+            if lcd.name in LibraryClassIgnore:\r
+                tc.LogStdOut("Ignoring Library Class Name {0}".format(lcd.name))\r
+                LibraryClassIgnore.remove(lcd.name)\r
+                continue\r
+\r
+            logging.debug(f"Looking for Library Class {lcd.path}")\r
+            try:\r
+                AllHeaderFiles.remove(lcd.path)\r
+\r
+            except ValueError:\r
+                tc.LogStdError(f"Library {lcd.name} with path {lcd.path} not found in package filesystem")\r
+                logging.error(f"Library {lcd.name} with path {lcd.path} not found in package filesystem")\r
+                overall_status += 1\r
+\r
+        ## any remaining AllHeaderFiles are not described in DEC\r
+        for h in AllHeaderFiles:\r
+            tc.LogStdError(f"Library Header File {h} not declared in package DEC {wsr_dec_path}")\r
+            logging.error(f"Library Header File {h} not declared in package DEC {wsr_dec_path}")\r
+            overall_status += 1\r
+\r
+        ## Warn about any invalid library class names in the ignore list\r
+        for r in LibraryClassIgnore:\r
+            tc.LogStdError("LibraryClassCheck.IgnoreLibraryClass -> {0} not found.  Library Class not found".format(r))\r
+            logging.info("LibraryClassCheck.IgnoreLibraryClass -> {0} not found.  Library Class not found".format(r))\r
+\r
+\r
+        # If XML object exists, add result\r
+        if overall_status is not 0:\r
+            tc.SetFailed("LibraryClassCheck {0} Failed.  Errors {1}".format(wsr_dec_path, overall_status), "CHECK_FAILED")\r
+        else:\r
+            tc.SetSuccess()\r
+        return overall_status\r
diff --git a/.pytool/Plugin/LibraryClassCheck/LibraryClassCheck_plug_in.yaml b/.pytool/Plugin/LibraryClassCheck/LibraryClassCheck_plug_in.yaml
new file mode 100644 (file)
index 0000000..f90eec3
--- /dev/null
@@ -0,0 +1,11 @@
+## @file\r
+# CiBuildPlugin used to check that all library classes are declared correctly in dec file\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+{\r
+  "scope": "cibuild",\r
+  "name": "Library Class Check Test",\r
+  "module": "LibraryClassCheck"\r
+}\r
diff --git a/.pytool/Plugin/LibraryClassCheck/Readme.md b/.pytool/Plugin/LibraryClassCheck/Readme.md
new file mode 100644 (file)
index 0000000..7fa2b20
--- /dev/null
@@ -0,0 +1,25 @@
+# Library Class Check Plugin\r
+\r
+This CiBuildPlugin scans at all library header files found in the `Library`\r
+folders in all of the package's declared include directories and ensures that\r
+all files have a matching LibraryClass declaration in the DEC file for the\r
+package. Any missing declarations will cause a failure.\r
+\r
+## Configuration\r
+\r
+The plugin has a few configuration options to support the UEFI codebase.\r
+\r
+``` yaml\r
+"LibraryClassCheck": {\r
+    IgnoreHeaderFile: [],  # Ignore a file found on disk\r
+    IgnoreLibraryClass: [] # Ignore a declaration found in dec file\r
+}\r
+```\r
+\r
+### IgnoreHeaderFile\r
+\r
+Ignore a file found on disk\r
+\r
+### IgnoreLibraryClass\r
+\r
+Ignore a declaration found in dec file\r
diff --git a/.pytool/Plugin/SpellCheck/Readme.md b/.pytool/Plugin/SpellCheck/Readme.md
new file mode 100644 (file)
index 0000000..394bb8e
--- /dev/null
@@ -0,0 +1,127 @@
+# Spell Check Plugin\r
+\r
+This CiBuildPlugin scans all the files in a given package and checks for\r
+spelling errors.\r
+\r
+This plugin requires NodeJs and cspell.  If the plugin doesn't find its required\r
+tools then it will mark the test as skipped.\r
+\r
+* NodeJS: https://nodejs.org/en/\r
+* cspell: https://www.npmjs.com/package/cspell\r
+  * Src and doc available: https://github.com/streetsidesoftware/cspell\r
+\r
+## Configuration\r
+\r
+The plugin has a few configuration options to support the UEFI codebase.\r
+\r
+``` yaml\r
+  "SpellCheck": {\r
+      "AuditOnly": False,          # If True, log all errors and then mark as skipped\r
+      "IgnoreFiles": [],           # use gitignore syntax to ignore errors in matching files\r
+      "ExtendWords": [],           # words to extend to the dictionary for this package\r
+      "IgnoreStandardPaths": [],   # Standard Plugin defined paths that should be ignore\r
+      "AdditionalIncludePaths": [] # Additional paths to spell check (wildcards supported)\r
+  }\r
+```\r
+\r
+### AuditOnly\r
+\r
+Boolean - Default is False.\r
+If True run the test in an Audit only mode which will log all errors but instead\r
+of failing the build it will set the test as skipped.  This allows visibility\r
+into the failures without breaking the build.\r
+\r
+### IgnoreFiles\r
+\r
+This supports .gitignore file and folder matching strings including wildcards\r
+\r
+* All files will be parsed regardless but then any spelling errors found within\r
+  ignored files will not be reported as an error.\r
+* Errors in ignored files will still be output to the test results as\r
+  informational comments.\r
+\r
+### ExtendWords\r
+\r
+This list allows words to be added to the dictionary for the spell checker when\r
+this package is tested.  These follow the rules of the cspell config words field.\r
+\r
+### IgnoreStandardPaths\r
+\r
+This plugin by default will check the below standard paths.  If the package\r
+would like to ignore any of them list that here.\r
+\r
+```python\r
+[\r
+# C source\r
+"*.c",\r
+"*.h",\r
+\r
+# Assembly files\r
+"*.nasm",\r
+"*.asm",\r
+"*.masm",\r
+"*.s",\r
+\r
+# ACPI source language\r
+"*.asl",\r
+\r
+# Edk2 build files\r
+"*.dsc", "*.dec", "*.fdf", "*.inf",\r
+\r
+# Documentation files\r
+"*.md", "*.txt"\r
+]\r
+```\r
+\r
+### AdditionalIncludePaths\r
+\r
+If the package would to add additional path patterns to be included in\r
+spellchecking they can be defined here.\r
+\r
+## Other configuration\r
+\r
+In the cspell.base.json there are numerous other settings configured.  There is\r
+no support to override these on a per package basis but future features could\r
+make this available.  One interesting configuration option is `minWordLength`.\r
+Currently it is set to _5_ which means all 2,3, and 4 letter words will be\r
+ignored.  This helps minimize the number of technical acronyms, register names,\r
+and other UEFI specific values that must be ignored.\r
+\r
+## False positives\r
+\r
+The cspell dictionary is not perfect and there are cases where technical words\r
+or acronyms are not found in the dictionary.  There are three ways to resolve\r
+false positives and the choice for which method should be based on how broadly\r
+the word should be accepted.\r
+\r
+### CSpell Base Config file\r
+\r
+If the change should apply to all UEFI code and documentation then it should be\r
+added to the base config file `words` section.  The base config file is adjacent\r
+to this file and titled `cspell.base.json`.  This is a list of accepted words\r
+for all spell checking operations on all packages.\r
+\r
+### Package Config\r
+\r
+In the package `*.ci.yaml` file there is a `SpellCheck` config section.  This\r
+section allows files to be ignored as well as words that should be considered\r
+valid for all files within this package.  Add the desired words to the\r
+"ExtendedWords" member.\r
+\r
+### In-line File\r
+\r
+CSpell supports numerous methods to annotate your files to ignore words,\r
+sections, etc.  This can be found in CSpell documentation.  Suggestion here is\r
+to use a c-style comment at the top of the file to add words that should be\r
+ignored just for this file.  Obviously this has the highest maintenance cost so\r
+it should only be used for file unique words.\r
+\r
+``` c\r
+// spell-checker:ignore unenroll, word2, word3\r
+```\r
+\r
+or\r
+\r
+```ini\r
+# spell-checker:ignore unenroll, word2, word3\r
+```\r
diff --git a/.pytool/Plugin/SpellCheck/SpellCheck.py b/.pytool/Plugin/SpellCheck/SpellCheck.py
new file mode 100644 (file)
index 0000000..4336544
--- /dev/null
@@ -0,0 +1,216 @@
+# @file SpellCheck.py\r
+#\r
+# An edk2-pytool based plugin wrapper for cspell\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+import logging\r
+import json\r
+import yaml\r
+from io import StringIO\r
+import os\r
+from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin\r
+from edk2toollib.utility_functions import RunCmd\r
+from edk2toolext.environment.var_dict import VarDict\r
+from edk2toollib.gitignore_parser import parse_gitignore_lines\r
+from edk2toolext.environment import version_aggregator\r
+\r
+\r
+class SpellCheck(ICiBuildPlugin):\r
+    """\r
+    A CiBuildPlugin that uses the cspell node module to scan the files\r
+    from the package being tested for spelling errors.  The plugin contains\r
+    the base cspell.json file then thru the configuration options other settings\r
+    can be changed or extended.\r
+\r
+    Configuration options:\r
+    "SpellCheck": {\r
+        "AuditOnly": False,          # Don't fail the build if there are errors.  Just log them\r
+        "IgnoreFiles": [],           # use gitignore syntax to ignore errors in matching files\r
+        "ExtendWords": [],           # words to extend to the dictionary for this package\r
+        "IgnoreStandardPaths": [],   # Standard Plugin defined paths that should be ignore\r
+        "AdditionalIncludePaths": [] # Additional paths to spell check (wildcards supported)\r
+    }\r
+    """\r
+\r
+    #\r
+    # A package can remove any of these using IgnoreStandardPaths\r
+    #\r
+    STANDARD_PLUGIN_DEFINED_PATHS = ["*.c", "*.h",\r
+                                     "*.nasm", "*.asm", "*.masm", "*.s",\r
+                                     "*.asl",\r
+                                     "*.dsc", "*.dec", "*.fdf", "*.inf",\r
+                                     "*.md", "*.txt"\r
+                                     ]\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 ("Spell check files in " + packagename, packagename + ".SpellCheck")\r
+\r
+    ##\r
+    # External function of plugin.  This function is used to perform the task of the CiBuild Plugin\r
+    #\r
+    #   - package is the edk2 path to package.  This means workspace/packagepath relative.\r
+    #   - edk2path object configured with workspace and packages path\r
+    #   - PkgConfig Object (dict) for the pkg\r
+    #   - EnvConfig Object\r
+    #   - Plugin Manager Instance\r
+    #   - Plugin Helper Obj Instance\r
+    #   - Junit Logger\r
+    #   - output_stream the StringIO output stream from this plugin via logging\r
+\r
+    def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None):\r
+        Errors = []\r
+\r
+        abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(\r
+            packagename)\r
+\r
+        if abs_pkg_path is None:\r
+            tc.SetSkipped()\r
+            tc.LogStdError("No package {0}".format(packagename))\r
+            return -1\r
+\r
+        # check for node\r
+        return_buffer = StringIO()\r
+        ret = RunCmd("node", "--version", outstream=return_buffer)\r
+        if (ret != 0):\r
+            tc.SetSkipped()\r
+            tc.LogStdError("NodeJs not installed. Test can't run")\r
+            logging.warning("NodeJs not installed. Test can't run")\r
+            return -1\r
+        node_version = return_buffer.getvalue().strip()  # format vXX.XX.XX\r
+        tc.LogStdOut(f"Node version: {node_version}")\r
+        version_aggregator.GetVersionAggregator().ReportVersion(\r
+            "NodeJs", node_version, version_aggregator.VersionTypes.INFO)\r
+\r
+        # Check for cspell\r
+        return_buffer = StringIO()\r
+        ret = RunCmd("cspell", "--version", outstream=return_buffer)\r
+        if (ret != 0):\r
+            tc.SetSkipped()\r
+            tc.LogStdError("cspell not installed.  Test can't run")\r
+            logging.warning("cspell not installed.  Test can't run")\r
+            return -1\r
+        cspell_version = return_buffer.getvalue().strip()  # format XX.XX.XX\r
+        tc.LogStdOut(f"CSpell version: {cspell_version}")\r
+        version_aggregator.GetVersionAggregator().ReportVersion(\r
+            "CSpell", cspell_version, version_aggregator.VersionTypes.INFO)\r
+\r
+        package_relative_paths_to_spell_check = SpellCheck.STANDARD_PLUGIN_DEFINED_PATHS\r
+\r
+        #\r
+        # Allow the ci.yaml to remove any of the above standard paths\r
+        #\r
+        if("IgnoreStandardPaths" in pkgconfig):\r
+            for a in pkgconfig["IgnoreStandardPaths"]:\r
+                if(a in package_relative_paths_to_spell_check):\r
+                    tc.LogStdOut(\r
+                        f"ignoring standard path due to ci.yaml ignore: {a}")\r
+                    package_relative_paths_to_spell_check.remove(a)\r
+                else:\r
+                    tc.LogStdOut(f"Invalid IgnoreStandardPaths value: {a}")\r
+\r
+        #\r
+        # check for any additional include paths defined by package config\r
+        #\r
+        if("AdditionalIncludePaths" in pkgconfig):\r
+            package_relative_paths_to_spell_check.extend(\r
+                pkgconfig["AdditionalIncludePaths"])\r
+\r
+        #\r
+        # Make the path string for cspell to check\r
+        #\r
+        relpath = os.path.relpath(abs_pkg_path)\r
+        cpsell_paths = " ".join(\r
+            [f"{relpath}/**/{x}" for x in package_relative_paths_to_spell_check])\r
+\r
+        # Make the config file\r
+        config_file_path = os.path.join(\r
+            Edk2pathObj.WorkspacePath, "Build", packagename, "cspell_actual_config.json")\r
+        mydir = os.path.dirname(os.path.abspath(__file__))\r
+        # load as yaml so it can have comments\r
+        base = os.path.join(mydir, "cspell.base.yaml")\r
+        with open(base, "r") as i:\r
+            config = yaml.safe_load(i)\r
+\r
+        if("ExtendWords" in pkgconfig):\r
+            config["words"].extend(pkgconfig["ExtendWords"])\r
+        with open(config_file_path, "w") as o:\r
+            json.dump(config, o)  # output as json so compat with cspell\r
+\r
+        All_Ignores = []\r
+        # Parse the config for other ignores\r
+        if "IgnoreFiles" in pkgconfig:\r
+            All_Ignores.extend(pkgconfig["IgnoreFiles"])\r
+\r
+        # spell check all the files\r
+        ignore = parse_gitignore_lines(All_Ignores, os.path.join(\r
+            abs_pkg_path, "nofile.txt"), abs_pkg_path)\r
+\r
+        # result is a list of strings like this\r
+        #  C:\src\sp-edk2\edk2\FmpDevicePkg\FmpDevicePkg.dec:53:9 - Unknown word (Capule)\r
+        EasyFix = []\r
+        results = self._check_spelling(cpsell_paths, config_file_path)\r
+        for r in results:\r
+            path, _, word = r.partition(" - Unknown word ")\r
+            if len(word) == 0:\r
+                # didn't find pattern\r
+                continue\r
+\r
+            pathinfo = path.rsplit(":", 2)  # remove the line no info\r
+            if(ignore(pathinfo[0])):  # check against ignore list\r
+                tc.LogStdOut(f"ignoring error due to ci.yaml ignore: {r}")\r
+                continue\r
+\r
+            # real error\r
+            EasyFix.append(word.strip().strip("()"))\r
+            Errors.append(r)\r
+\r
+        # Log all errors tc StdError\r
+        for l in Errors:\r
+            tc.LogStdError(l.strip())\r
+\r
+        # Helper - Log the syntax needed to add these words to dictionary\r
+        if len(EasyFix) > 0:\r
+            EasyFix = sorted(set(a.lower() for a in EasyFix))\r
+            tc.LogStdOut("\n Easy fix:")\r
+            OneString = "If these are not errors add this to your ci.yaml file.\n"\r
+            OneString += '"SpellCheck": {\n  "ExtendWords": ['\r
+            for a in EasyFix:\r
+                tc.LogStdOut(f'\n"{a}",')\r
+                OneString += f'\n    "{a}",'\r
+            logging.info(OneString.rstrip(",") + '\n  ]\n}')\r
+\r
+        # add result to test case\r
+        overall_status = len(Errors)\r
+        if overall_status != 0:\r
+            if "AuditOnly" in pkgconfig and pkgconfig["AuditOnly"]:\r
+                # set as skipped if AuditOnly\r
+                tc.SetSkipped()\r
+                return -1\r
+            else:\r
+                tc.SetFailed("SpellCheck {0} Failed.  Errors {1}".format(\r
+                    packagename, overall_status), "CHECK_FAILED")\r
+        else:\r
+            tc.SetSuccess()\r
+        return overall_status\r
+\r
+    def _check_spelling(self, abs_file_to_check: str, abs_config_file_to_use: str) -> []:\r
+        output = StringIO()\r
+        ret = RunCmd(\r
+            "cspell", f"--config {abs_config_file_to_use} {abs_file_to_check}", outstream=output)\r
+        if ret == 0:\r
+            return []\r
+        else:\r
+            return output.getvalue().strip().splitlines()\r
diff --git a/.pytool/Plugin/SpellCheck/SpellCheck_plug_in.yaml b/.pytool/Plugin/SpellCheck/SpellCheck_plug_in.yaml
new file mode 100644 (file)
index 0000000..d438199
--- /dev/null
@@ -0,0 +1,11 @@
+## @file\r
+# CiBuildPlugin used to check spelling\r
+#\r
+# Copyright (c) Microsoft Corporation.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+{\r
+  "scope": "cibuild",\r
+  "name": "Spell Check Test",\r
+  "module": "SpellCheck"\r
+}\r
diff --git a/.pytool/Plugin/SpellCheck/cspell.base.yaml b/.pytool/Plugin/SpellCheck/cspell.base.yaml
new file mode 100644 (file)
index 0000000..53000fc
--- /dev/null
@@ -0,0 +1,165 @@
+## @file\r
+# CSpell configuration\r
+#\r
+# Copyright (c) Microsoft Corporation\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+{\r
+    "version": "0.1",\r
+    "language": "en",\r
+    "dictionaries": [\r
+        "companies ",\r
+        "softwareTerms",\r
+        "python",\r
+        "cpp"\r
+    ],\r
+    "ignorePaths": [\r
+        "*.pdb",\r
+        "**/*_extdep/**",\r
+        "*.pdf",\r
+        "*.exe",\r
+        "*.jpg"\r
+    ],\r
+    "minWordLength": 5,\r
+    "allowCompoundWords": false,\r
+    "ignoreWords": [\r
+        "muchange"\r
+    ],\r
+    "words": [\r
+        "MTRRs",\r
+        "Microarchitecture",\r
+        "Goldmont",\r
+        "cpuid",\r
+        "mwait",\r
+        "cstate",\r
+        "smram",\r
+        "scrtm",\r
+        "smbus",\r
+        "selftest",\r
+        "socket",\r
+        "MMRAM",\r
+        "qword",\r
+        "ENDBR",\r
+        "SMBASE",\r
+        "FXSAVE",\r
+        "FXRSTOR",\r
+        "RDRAND",\r
+        "IOAPIC",\r
+        "ATAPI",\r
+        "movsb",\r
+        "iretw",\r
+        "XENSTORE",\r
+        "cdrom",\r
+        "oprom",\r
+        "oproms",\r
+        "varstore",\r
+        "EKU",\r
+        "ascii",\r
+        "nmake",\r
+        "NVDIMM",\r
+        "nasmb",\r
+        "Mtftp",\r
+        "Hypercall",\r
+        "hypercalls",\r
+        "IOMMU",\r
+        "QEMU",\r
+        "qemus",\r
+        "OVMF",\r
+        "tiano",\r
+        "tianocore",\r
+        "edkii",\r
+        "coreboot",\r
+        "uefipayload",\r
+        "bootloader",\r
+        "bootloaders",\r
+        "mdepkg",\r
+        "skuid",\r
+        "dxefv",\r
+        "toolchain",\r
+        "libraryclass",\r
+        "preboot",\r
+        "pythonpath",\r
+        "cygpath",\r
+        "nuget",\r
+        "basetools",\r
+        "prepi",\r
+        "OPTEE",\r
+        "stringid",\r
+        "peims",\r
+        "memmap",\r
+        "guids",\r
+        "uuids",\r
+        "smbios",\r
+        "certdb",\r
+        "certdbv",\r
+        "EfiSigList",\r
+        "depex",\r
+        "IHANDLE",\r
+        "Virtio",\r
+        "Mbytes",\r
+        "Citrix",\r
+        "initrd",\r
+        "semihost",\r
+        "Semihosting",\r
+        "Trustzone",\r
+        "Fastboot",\r
+        "framebuffer",\r
+        "genfw",\r
+        "TTYTERM",\r
+        "miniport",\r
+        "LFENCE",\r
+        "PCANSI",\r
+        "submodule",\r
+        "submodules",\r
+        "brotli",\r
+        "PCCTS",\r
+        "softfloat",\r
+        "whitepaper",\r
+        "ACPICA",\r
+        "plugfest",\r
+        "bringup",\r
+        "formset", #VFR\r
+        "ideqvallist",\r
+        "numberof",\r
+        "oneof",\r
+        "endformset",\r
+        "endnumeric",\r
+        "endoneof",\r
+        "disableif",\r
+        "guidid",\r
+        "classguid",\r
+        "efivarstore",\r
+        "formsetguid",\r
+        "formid",\r
+        "suppressif",\r
+        "grayoutif",\r
+        "ideqval",\r
+        "endform",\r
+        "endcheckbox",\r
+        "questionid",\r
+        "questionref",\r
+        "enddate",\r
+        "endstring",\r
+        "guidop",\r
+        "endguidop",\r
+        "langdef",\r
+        "dynamicex",\r
+        "tokenspace",\r
+        "tokenguid",\r
+        "pcd's", #seems like cspell bug\r
+        "peim's",\r
+        "autogen",\r
+        "Disasm",\r
+        "Torito",\r
+        "SRIOV",\r
+        "MRIOV",\r
+        "UARTs",\r
+        "Consplitter", # common module in UEFI\r
+        "FIFOs",\r
+        "ACPINVS",\r
+        "Endof",  # due to of not being uppercase\r
+        "bootability",\r
+        "Sdhci",\r
+        "inmodule",\r
+    ]\r
+}\r