1# @file DscCompleteCheck.py 2# 3# Copyright (c) Microsoft Corporation. 4# SPDX-License-Identifier: BSD-2-Clause-Patent 5## 6import logging 7import os 8from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin 9from edk2toollib.uefi.edk2.parsers.dsc_parser import DscParser 10from edk2toollib.uefi.edk2.parsers.inf_parser import InfParser 11from edk2toolext.environment.var_dict import VarDict 12 13 14class DscCompleteCheck(ICiBuildPlugin): 15 """ 16 A CiBuildPlugin that scans the package dsc file and confirms all modules (inf files) are 17 listed in the components sections. 18 19 Configuration options: 20 "DscCompleteCheck": { 21 "DscPath": "<path to dsc from root of pkg>" 22 "IgnoreInf": [] # Ignore INF if found in filesystem by not dsc 23 } 24 """ 25 26 def GetTestName(self, packagename: str, environment: VarDict) -> tuple: 27 """ Provide the testcase name and classname for use in reporting 28 29 Args: 30 packagename: string containing name of package to build 31 environment: The VarDict for the test to run in 32 Returns: 33 a tuple containing the testcase name and the classname 34 (testcasename, classname) 35 testclassname: a descriptive string for the testcase can include whitespace 36 classname: should be patterned <packagename>.<plugin>.<optionally any unique condition> 37 """ 38 return ("Check the " + packagename + " DSC for a being complete", packagename + ".DscCompleteCheck") 39 40 ## 41 # External function of plugin. This function is used to perform the task of the MuBuild Plugin 42 # 43 # - package is the edk2 path to package. This means workspace/packagepath relative. 44 # - edk2path object configured with workspace and packages path 45 # - PkgConfig Object (dict) for the pkg 46 # - VarDict containing the shell environment Build Vars 47 # - Plugin Manager Instance 48 # - Plugin Helper Obj Instance 49 # - Junit Logger 50 # - output_stream the StringIO output stream from this plugin via logging 51 def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None): 52 overall_status = 0 53 54 # Parse the config for required DscPath element 55 if "DscPath" not in pkgconfig: 56 tc.SetSkipped() 57 tc.LogStdError( 58 "DscPath not found in config file. Nothing to check.") 59 return -1 60 61 abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath( 62 packagename) 63 abs_dsc_path = os.path.join(abs_pkg_path, pkgconfig["DscPath"].strip()) 64 wsr_dsc_path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath( 65 abs_dsc_path) 66 67 if abs_dsc_path is None or wsr_dsc_path == "" or not os.path.isfile(abs_dsc_path): 68 tc.SetSkipped() 69 tc.LogStdError("Package Dsc not found") 70 return 0 71 72 # Get INF Files 73 INFFiles = self.WalkDirectoryForExtension([".inf"], abs_pkg_path) 74 INFFiles = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath( 75 x) for x in INFFiles] # make edk2relative path so can compare with DSC 76 77 # remove ignores 78 79 if "IgnoreInf" in pkgconfig: 80 for a in pkgconfig["IgnoreInf"]: 81 a = a.replace(os.sep, "/") 82 try: 83 tc.LogStdOut("Ignoring INF {0}".format(a)) 84 INFFiles.remove(a) 85 except: 86 tc.LogStdError( 87 "DscCompleteCheck.IgnoreInf -> {0} not found in filesystem. Invalid ignore file".format(a)) 88 logging.info( 89 "DscCompleteCheck.IgnoreInf -> {0} not found in filesystem. Invalid ignore file".format(a)) 90 91 # DSC Parser 92 dp = DscParser() 93 dp.SetBaseAbsPath(Edk2pathObj.WorkspacePath) 94 dp.SetPackagePaths(Edk2pathObj.PackagePathList) 95 dp.SetInputVars(environment.GetAllBuildKeyValues()) 96 dp.ParseFile(wsr_dsc_path) 97 98 # Check if INF in component section 99 for INF in INFFiles: 100 if not any(INF.strip() in x for x in dp.ThreeMods) and \ 101 not any(INF.strip() in x for x in dp.SixMods) and \ 102 not any(INF.strip() in x for x in dp.OtherMods): 103 104 infp = InfParser().SetBaseAbsPath(Edk2pathObj.WorkspacePath) 105 infp.SetPackagePaths(Edk2pathObj.PackagePathList) 106 infp.ParseFile(INF) 107 if("MODULE_TYPE" not in infp.Dict): 108 tc.LogStdOut( 109 "Ignoring INF. Missing key for MODULE_TYPE {0}".format(INF)) 110 continue 111 112 if(infp.Dict["MODULE_TYPE"] == "HOST_APPLICATION"): 113 tc.LogStdOut( 114 "Ignoring INF. Module type is HOST_APPLICATION {0}".format(INF)) 115 continue 116 117 if len(infp.SupportedPhases) == 1 and \ 118 "HOST_APPLICATION" in infp.SupportedPhases: 119 tc.LogStdOut( 120 "Ignoring Library INF due to only supporting type HOST_APPLICATION {0}".format(INF)) 121 continue 122 123 logging.critical(INF + " not in " + wsr_dsc_path) 124 tc.LogStdError("{0} not in {1}".format(INF, wsr_dsc_path)) 125 overall_status = overall_status + 1 126 127 # If XML object exists, add result 128 if overall_status != 0: 129 tc.SetFailed("DscCompleteCheck {0} Failed. Errors {1}".format( 130 wsr_dsc_path, overall_status), "CHECK_FAILED") 131 else: 132 tc.SetSuccess() 133 return overall_status 134