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