2019-10-18 06:40:58 +02:00
|
|
|
# @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.GetAbsolutePathOnThisSytemFromEdk2RelativePath(packagename)
|
|
|
|
abs_dec_path = self.__GetPkgDec(abs_pkg_path)
|
|
|
|
wsr_dec_path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(abs_dec_path)
|
|
|
|
|
2019-12-05 12:19:42 +01:00
|
|
|
if abs_dec_path is None or wsr_dec_path == "" or not os.path.isfile(abs_dec_path):
|
2019-10-18 06:40:58 +02:00
|
|
|
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
|
2020-01-22 19:17:23 +01:00
|
|
|
if overall_status != 0:
|
2019-10-18 06:40:58 +02:00
|
|
|
tc.SetFailed("LibraryClassCheck {0} Failed. Errors {1}".format(wsr_dec_path, overall_status), "CHECK_FAILED")
|
|
|
|
else:
|
|
|
|
tc.SetSuccess()
|
|
|
|
return overall_status
|