# @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": "" } """ 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 .. 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