# @file HostUnitTestDscCompleteCheck.py | |
# | |
# This is a copy of DscCompleteCheck with different filtering logic. | |
# It should be discussed if this should be one plugin | |
# | |
# 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.dsc_parser import DscParser | |
from edk2toollib.uefi.edk2.parsers.inf_parser import InfParser, AllPhases | |
from edk2toolext.environment.var_dict import VarDict | |
class HostUnitTestDscCompleteCheck(ICiBuildPlugin): | |
""" | |
A CiBuildPlugin that scans the package Host Unit Test dsc file and confirms all Host application modules (inf files) are | |
listed in the components sections. | |
Configuration options: | |
"HostUnitTestDscCompleteCheck": { | |
"DscPath": "", # Path to Host based unit test DSC file | |
"IgnoreInf": [] # Ignore INF if found in filesystem but not dsc | |
} | |
""" | |
def GetTestName(self, packagename: str, environment: VarDict) -> tuple: | |
""" Provide the testcase name and classname for use in reporting | |
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) | |
testclassname: a descriptive string for the testcase can include whitespace | |
classname: should be patterned <packagename>.<plugin>.<optionally any unique condition> | |
""" | |
return ("Check the " + packagename + " Host Unit Test DSC for a being complete", packagename + ".HostUnitTestDscCompleteCheck") | |
## | |
# 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 | |
# - VarDict containing the shell environment Build Vars | |
# - 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 | |
# Parse the config for required DscPath element | |
if "DscPath" not in pkgconfig: | |
tc.SetSkipped() | |
tc.LogStdError( | |
"DscPath not found in config file. Nothing to check.") | |
return -1 | |
abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSystemFromEdk2RelativePath( | |
packagename) | |
abs_dsc_path = os.path.join(abs_pkg_path, pkgconfig["DscPath"].strip()) | |
wsr_dsc_path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath( | |
abs_dsc_path) | |
if abs_dsc_path is None or wsr_dsc_path == "" or not os.path.isfile(abs_dsc_path): | |
tc.SetSkipped() | |
tc.LogStdError("Package Host Unit Test Dsc not found") | |
return 0 | |
# Get INF Files | |
INFFiles = self.WalkDirectoryForExtension([".inf"], abs_pkg_path) | |
INFFiles = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath( | |
x) for x in INFFiles] # make edk2relative path so can compare with DSC | |
# remove ignores | |
if "IgnoreInf" in pkgconfig: | |
for a in pkgconfig["IgnoreInf"]: | |
a = a.replace(os.sep, "/") | |
try: | |
tc.LogStdOut("Ignoring INF {0}".format(a)) | |
INFFiles.remove(a) | |
except: | |
tc.LogStdError( | |
"HostUnitTestDscCompleteCheck.IgnoreInf -> {0} not found in filesystem. Invalid ignore file".format(a)) | |
logging.info( | |
"HostUnitTestDscCompleteCheck.IgnoreInf -> {0} not found in filesystem. Invalid ignore file".format(a)) | |
# DSC Parser | |
dp = DscParser() | |
dp.SetBaseAbsPath(Edk2pathObj.WorkspacePath) | |
dp.SetPackagePaths(Edk2pathObj.PackagePathList) | |
dp.SetInputVars(environment.GetAllBuildKeyValues()) | |
dp.ParseFile(wsr_dsc_path) | |
# Check if INF in component section | |
for INF in INFFiles: | |
if not any(INF.strip() in x for x in dp.ThreeMods) and \ | |
not any(INF.strip() in x for x in dp.SixMods) and \ | |
not any(INF.strip() in x for x in dp.OtherMods): | |
infp = InfParser().SetBaseAbsPath(Edk2pathObj.WorkspacePath) | |
infp.SetPackagePaths(Edk2pathObj.PackagePathList) | |
infp.ParseFile(INF) | |
if("MODULE_TYPE" not in infp.Dict): | |
tc.LogStdOut( | |
"Ignoring INF. Missing key for MODULE_TYPE {0}".format(INF)) | |
continue | |
if(infp.Dict["MODULE_TYPE"] == "HOST_APPLICATION"): | |
# should compile test a library that is declared type HOST_APPLICATION | |
pass | |
elif (len(infp.SupportedPhases) > 0 and | |
"HOST_APPLICATION" in infp.SupportedPhases and | |
infp.SupportedPhases != AllPhases): | |
# should compile test a library that supports HOST_APPLICATION but | |
# require it to be an explicit opt-in | |
pass | |
else: | |
tc.LogStdOut( | |
"Ignoring INF. MODULE_TYPE or suppored phases not HOST_APPLICATION {0}".format(INF)) | |
continue | |
logging.critical(INF + " not in " + wsr_dsc_path) | |
tc.LogStdError("{0} not in {1}".format(INF, wsr_dsc_path)) | |
overall_status = overall_status + 1 | |
# If XML object exists, add result | |
if overall_status != 0: | |
tc.SetFailed("HostUnitTestDscCompleteCheck {0} Failed. Errors {1}".format( | |
wsr_dsc_path, overall_status), "CHECK_FAILED") | |
else: | |
tc.SetSuccess() | |
return overall_status |