]>
Commit | Line | Data |
---|---|---|
9da7846c SB |
1 | # @file LibraryClassCheck.py\r |
2 | #\r | |
3 | # Copyright (c) Microsoft Corporation.\r | |
4 | # SPDX-License-Identifier: BSD-2-Clause-Patent\r | |
5 | ##\r | |
6 | import logging\r | |
7 | import os\r | |
8 | from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin\r | |
9 | from edk2toollib.uefi.edk2.parsers.dec_parser import DecParser\r | |
10 | from edk2toollib.uefi.edk2.parsers.inf_parser import InfParser\r | |
11 | from edk2toolext.environment.var_dict import VarDict\r | |
12 | \r | |
13 | \r | |
14 | class LibraryClassCheck(ICiBuildPlugin):\r | |
15 | """\r | |
16 | A CiBuildPlugin that scans the code tree and library classes for undeclared\r | |
17 | files\r | |
18 | \r | |
19 | Configuration options:\r | |
20 | "LibraryClassCheck": {\r | |
21 | IgnoreHeaderFile: [], # Ignore a file found on disk\r | |
22 | IgnoreLibraryClass: [] # Ignore a declaration found in dec file\r | |
23 | }\r | |
24 | """\r | |
25 | \r | |
26 | def GetTestName(self, packagename: str, environment: VarDict) -> tuple:\r | |
27 | """ Provide the testcase name and classname for use in reporting\r | |
28 | testclassname: a descriptive string for the testcase can include whitespace\r | |
29 | classname: should be patterned <packagename>.<plugin>.<optionally any unique condition>\r | |
30 | \r | |
31 | Args:\r | |
32 | packagename: string containing name of package to build\r | |
33 | environment: The VarDict for the test to run in\r | |
34 | Returns:\r | |
35 | a tuple containing the testcase name and the classname\r | |
36 | (testcasename, classname)\r | |
37 | """\r | |
38 | return ("Check library class declarations in " + packagename, packagename + ".LibraryClassCheck")\r | |
39 | \r | |
40 | def __GetPkgDec(self, rootpath):\r | |
41 | try:\r | |
42 | allEntries = os.listdir(rootpath)\r | |
43 | for entry in allEntries:\r | |
44 | if entry.lower().endswith(".dec"):\r | |
45 | return(os.path.join(rootpath, entry))\r | |
46 | except Exception:\r | |
47 | logging.error("Unable to find DEC for package:{0}".format(rootpath))\r | |
48 | \r | |
49 | return None\r | |
50 | \r | |
51 | ##\r | |
52 | # External function of plugin. This function is used to perform the task of the MuBuild Plugin\r | |
53 | #\r | |
54 | # - package is the edk2 path to package. This means workspace/packagepath relative.\r | |
55 | # - edk2path object configured with workspace and packages path\r | |
56 | # - PkgConfig Object (dict) for the pkg\r | |
57 | # - EnvConfig Object\r | |
58 | # - Plugin Manager Instance\r | |
59 | # - Plugin Helper Obj Instance\r | |
60 | # - Junit Logger\r | |
61 | # - output_stream the StringIO output stream from this plugin via logging\r | |
62 | def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None):\r | |
63 | overall_status = 0\r | |
64 | LibraryClassIgnore = []\r | |
65 | \r | |
2aac8bb7 | 66 | abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSystemFromEdk2RelativePath(packagename)\r |
9da7846c SB |
67 | abs_dec_path = self.__GetPkgDec(abs_pkg_path)\r |
68 | wsr_dec_path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(abs_dec_path)\r | |
69 | \r | |
e8b9296c | 70 | if abs_dec_path is None or wsr_dec_path == "" or not os.path.isfile(abs_dec_path):\r |
9da7846c SB |
71 | tc.SetSkipped()\r |
72 | tc.LogStdError("No DEC file {0} in package {1}".format(abs_dec_path, abs_pkg_path))\r | |
73 | return -1\r | |
74 | \r | |
75 | # Get all include folders\r | |
76 | dec = DecParser()\r | |
77 | dec.SetBaseAbsPath(Edk2pathObj.WorkspacePath).SetPackagePaths(Edk2pathObj.PackagePathList)\r | |
78 | dec.ParseFile(wsr_dec_path)\r | |
79 | \r | |
80 | AllHeaderFiles = []\r | |
81 | \r | |
82 | for includepath in dec.IncludePaths:\r | |
83 | ## Get all header files in the library folder\r | |
84 | AbsLibraryIncludePath = os.path.join(abs_pkg_path, includepath, "Library")\r | |
85 | if(not os.path.isdir(AbsLibraryIncludePath)):\r | |
86 | continue\r | |
87 | \r | |
88 | hfiles = self.WalkDirectoryForExtension([".h"], AbsLibraryIncludePath)\r | |
89 | hfiles = [os.path.relpath(x,abs_pkg_path) for x in hfiles] # make package root relative path\r | |
90 | hfiles = [x.replace("\\", "/") for x in hfiles] # make package relative path\r | |
91 | \r | |
92 | AllHeaderFiles.extend(hfiles)\r | |
93 | \r | |
94 | if len(AllHeaderFiles) == 0:\r | |
95 | tc.SetSkipped()\r | |
96 | tc.LogStdError(f"No Library include folder in any Include path")\r | |
97 | return -1\r | |
98 | \r | |
99 | # Remove ignored paths\r | |
100 | if "IgnoreHeaderFile" in pkgconfig:\r | |
101 | for a in pkgconfig["IgnoreHeaderFile"]:\r | |
102 | try:\r | |
103 | tc.LogStdOut("Ignoring Library Header File {0}".format(a))\r | |
104 | AllHeaderFiles.remove(a)\r | |
105 | except:\r | |
106 | tc.LogStdError("LibraryClassCheck.IgnoreHeaderFile -> {0} not found. Invalid Header File".format(a))\r | |
107 | logging.info("LibraryClassCheck.IgnoreHeaderFile -> {0} not found. Invalid Header File".format(a))\r | |
108 | \r | |
109 | if "IgnoreLibraryClass" in pkgconfig:\r | |
110 | LibraryClassIgnore = pkgconfig["IgnoreLibraryClass"]\r | |
111 | \r | |
112 | \r | |
113 | ## Attempt to find library classes\r | |
114 | for lcd in dec.LibraryClasses:\r | |
115 | ## Check for correct file path separator\r | |
116 | if "\\" in lcd.path:\r | |
117 | tc.LogStdError("LibraryClassCheck.DecFilePathSeparator -> {0} invalid.".format(lcd.path))\r | |
118 | logging.error("LibraryClassCheck.DecFilePathSeparator -> {0} invalid.".format(lcd.path))\r | |
119 | overall_status += 1\r | |
120 | continue\r | |
121 | \r | |
122 | if lcd.name in LibraryClassIgnore:\r | |
123 | tc.LogStdOut("Ignoring Library Class Name {0}".format(lcd.name))\r | |
124 | LibraryClassIgnore.remove(lcd.name)\r | |
125 | continue\r | |
126 | \r | |
127 | logging.debug(f"Looking for Library Class {lcd.path}")\r | |
128 | try:\r | |
129 | AllHeaderFiles.remove(lcd.path)\r | |
130 | \r | |
131 | except ValueError:\r | |
132 | tc.LogStdError(f"Library {lcd.name} with path {lcd.path} not found in package filesystem")\r | |
133 | logging.error(f"Library {lcd.name} with path {lcd.path} not found in package filesystem")\r | |
134 | overall_status += 1\r | |
135 | \r | |
136 | ## any remaining AllHeaderFiles are not described in DEC\r | |
137 | for h in AllHeaderFiles:\r | |
138 | tc.LogStdError(f"Library Header File {h} not declared in package DEC {wsr_dec_path}")\r | |
139 | logging.error(f"Library Header File {h} not declared in package DEC {wsr_dec_path}")\r | |
140 | overall_status += 1\r | |
141 | \r | |
142 | ## Warn about any invalid library class names in the ignore list\r | |
143 | for r in LibraryClassIgnore:\r | |
144 | tc.LogStdError("LibraryClassCheck.IgnoreLibraryClass -> {0} not found. Library Class not found".format(r))\r | |
145 | logging.info("LibraryClassCheck.IgnoreLibraryClass -> {0} not found. Library Class not found".format(r))\r | |
146 | \r | |
147 | \r | |
148 | # If XML object exists, add result\r | |
61364ab9 | 149 | if overall_status != 0:\r |
9da7846c SB |
150 | tc.SetFailed("LibraryClassCheck {0} Failed. Errors {1}".format(wsr_dec_path, overall_status), "CHECK_FAILED")\r |
151 | else:\r | |
152 | tc.SetSuccess()\r | |
153 | return overall_status\r |