| # @file HostUnitTestCompilerPlugin.py | |
| ## | |
| # Copyright (c) Microsoft Corporation. | |
| # SPDX-License-Identifier: BSD-2-Clause-Patent | |
| ## | |
| import logging | |
| import os | |
| import re | |
| from edk2toollib.uefi.edk2.parsers.dsc_parser import DscParser | |
| from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin | |
| from edk2toolext.environment.uefi_build import UefiBuilder | |
| from edk2toolext import edk2_logging | |
| from edk2toolext.environment.var_dict import VarDict | |
| from edk2toollib.utility_functions import GetHostInfo | |
| class HostUnitTestCompilerPlugin(ICiBuildPlugin): | |
| """ | |
| A CiBuildPlugin that compiles the dsc for host based unit test apps. | |
| An IUefiBuildPlugin may be attached to this plugin that will run the | |
| unit tests and collect the results after successful compilation. | |
| Configuration options: | |
| "HostUnitTestCompilerPlugin": { | |
| "DscPath": "<path to dsc from root of pkg>" | |
| } | |
| """ | |
| 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) | |
| """ | |
| num,types = self.__GetHostUnitTestArch(environment) | |
| types = types.replace(" ", "_") | |
| return ("Compile and Run Host-Based UnitTests for " + packagename + " on arch " + types, | |
| packagename + ".HostUnitTestCompiler." + types) | |
| def RunsOnTargetList(self): | |
| return ["NOOPT"] | |
| # | |
| # Find the intersection of application types that can run on this host | |
| # and the TARGET_ARCH being build in this request. | |
| # | |
| # return tuple with (number of UEFI arch types, space separated string) | |
| def __GetHostUnitTestArch(self, environment): | |
| requested = environment.GetValue("TARGET_ARCH").split(' ') | |
| host = [] | |
| if GetHostInfo().arch == 'x86': | |
| #assume 64bit can handle 64 and 32 | |
| #assume 32bit can only handle 32 | |
| ## change once IA32 issues resolved host.append("IA32") | |
| if GetHostInfo().bit == '64': | |
| host.append("X64") | |
| elif GetHostInfo().arch == 'ARM': | |
| if GetHostInfo().bit == '64': | |
| host.append("AARCH64") | |
| elif GetHostInfo().bit == '32': | |
| host.append("ARM") | |
| willrun = set(requested) & set(host) | |
| return (len(willrun), " ".join(willrun)) | |
| ## | |
| # External function of plugin. This function is used to perform the task of the ICiBuildPlugin 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): | |
| self._env = environment | |
| environment.SetValue("CI_BUILD_TYPE", "host_unit_test", "Set in HostUnitTestCompilerPlugin") | |
| # Parse the config for required DscPath element | |
| if "DscPath" not in pkgconfig: | |
| tc.SetSkipped() | |
| tc.LogStdError("DscPath not found in config file. Nothing to compile for HostBasedUnitTests.") | |
| return -1 | |
| AP = Edk2pathObj.GetAbsolutePathOnThisSystemFromEdk2RelativePath(packagename) | |
| APDSC = os.path.join(AP, pkgconfig["DscPath"].strip()) | |
| AP_Path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(APDSC) | |
| if AP is None or AP_Path is None or not os.path.isfile(APDSC): | |
| tc.SetSkipped() | |
| tc.LogStdError("Package HostBasedUnitTest Dsc not found.") | |
| return -1 | |
| logging.info("Building {0}".format(AP_Path)) | |
| self._env.SetValue("ACTIVE_PLATFORM", AP_Path, "Set in Compiler Plugin") | |
| num, RUNNABLE_ARCHITECTURES = self.__GetHostUnitTestArch(environment) | |
| if(num == 0): | |
| tc.SetSkipped() | |
| tc.LogStdError("No host architecture compatibility") | |
| return -1 | |
| if not environment.SetValue("TARGET_ARCH", | |
| RUNNABLE_ARCHITECTURES, | |
| "Update Target Arch based on Host Support"): | |
| #use AllowOverride function since this is a controlled attempt to change | |
| environment.AllowOverride("TARGET_ARCH") | |
| if not environment.SetValue("TARGET_ARCH", | |
| RUNNABLE_ARCHITECTURES, | |
| "Update Target Arch based on Host Support"): | |
| raise RuntimeError("Can't Change TARGET_ARCH as required") | |
| # Parse DSC to check for SUPPORTED_ARCHITECTURES | |
| dp = DscParser() | |
| dp.SetBaseAbsPath(Edk2pathObj.WorkspacePath) | |
| dp.SetPackagePaths(Edk2pathObj.PackagePathList) | |
| dp.ParseFile(AP_Path) | |
| if "SUPPORTED_ARCHITECTURES" in dp.LocalVars: | |
| SUPPORTED_ARCHITECTURES = dp.LocalVars["SUPPORTED_ARCHITECTURES"].split('|') | |
| TARGET_ARCHITECTURES = environment.GetValue("TARGET_ARCH").split(' ') | |
| # Skip if there is no intersection between SUPPORTED_ARCHITECTURES and TARGET_ARCHITECTURES | |
| if len(set(SUPPORTED_ARCHITECTURES) & set(TARGET_ARCHITECTURES)) == 0: | |
| tc.SetSkipped() | |
| tc.LogStdError("No supported architecutres to build for host unit tests") | |
| return -1 | |
| uefiBuilder = UefiBuilder() | |
| # do all the steps | |
| # WorkSpace, PackagesPath, PInHelper, PInManager | |
| ret = uefiBuilder.Go(Edk2pathObj.WorkspacePath, os.pathsep.join(Edk2pathObj.PackagePathList), PLMHelper, PLM) | |
| if ret != 0: # failure: | |
| tc.SetFailed("Compile failed for {0}".format(packagename), "Compile_FAILED") | |
| tc.LogStdError("{0} Compile failed with error code {1} ".format(AP_Path, ret)) | |
| return 1 | |
| else: | |
| tc.SetSuccess() | |
| return 0 |