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