diff --git a/.azurepipelines/templates/UnitTest-build-steps.yml b/.azurepipelines/templates/UnitTest-build-steps.yml new file mode 100644 index 000000000000..9f0a81a34795 --- /dev/null +++ b/.azurepipelines/templates/UnitTest-build-steps.yml @@ -0,0 +1,25 @@ +## @file +# File templates/basetools-build-job.yml +# +# template file to build basetools +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## +parameters: + build_UnitTest_flag: '' + +# Set default +- bash: echo "##vso[task.setvariable variable=UT_Count]${{ 0 }}" +# Pr eval +- task: CmdLine@1 + inputs: + filename: stuart_pr_eval + arguments: -c OvmfPkg/PlatformCI/UnitTestBuild.py --output-count-format-string "##vso[task.setvariable variable=UT_Count;isOutpout=true]{pkgcount}" + condition: and(eq(variables['Build.Reason'], 'PullRequest'), in(parameters.build_UnitTest_flag, 'true')) +# Build +- task: CmdLine@1 + inputs: + filename: stuart_build + arguments: -c OvmfPkg/PlatformCI/UnitTestBuild.py + condition: and(gt(variables.UT_Count, 0), succeeded()) \ No newline at end of file diff --git a/OvmfPkg/PlatformCI/TargetTestPlatformBuild.py b/OvmfPkg/PlatformCI/TargetTestPlatformBuild.py index 8210dad0330f..663abeba9b00 100644 --- a/OvmfPkg/PlatformCI/TargetTestPlatformBuild.py +++ b/OvmfPkg/PlatformCI/TargetTestPlatformBuild.py @@ -4,10 +4,14 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: BSD-2-Clause-Patent ## +from asyncio.windows_events import NULL +import shutil import os import sys import logging +import argparse from edk2toollib.utility_functions import RunCmd +from edk2toolext.environment import shell_environment sys.path.append(os.path.dirname(os.path.abspath(__file__))) from PlatformBuildLib import SettingsManager @@ -19,6 +23,8 @@ class CommonPlatform(): ''' Common settings for this platform. Define static data here and use for the different parts of stuart + + PackagesSupported = ("OvmfPkg", "UefiCpuPkg", "MdeModulePkg") ''' PackagesSupported = ("OvmfPkg",) ArchSupported = ("IA32", "X64") @@ -31,62 +37,163 @@ class CommonPlatform(): def GetDscName(cls, ArchCsv: str) -> str: ''' return the DSC given the architectures requested. - TargetUnitTest: dsc file which contains target unit test module. + ArchCsv: csv string containing all architectures to build + + dsc = "OvmfPkg" + if "IA32" in ArchCsv.upper().split(","): + dsc += "Ia32" + if "X64" in ArchCsv.upper().split(","): + dsc += "X64" + dsc += ".dsc" + return dsc ''' return "TargetUnitTest/OvmfPkgTargetTest.dsc" -import PlatformBuildLib -PlatformBuildLib.CommonPlatform = CommonPlatform +class UnitTestSupport(): + ''' Common settings for this platform. Define static data here and use + for the different parts of stuart + + UnitTestModule = {'UefiCpuPkg/Library/CpuExceptionHandlerLib/UnitTest/DxeCpuExceptionHandlerLibUnitTest.inf':'UefiCpuPkg.dsc', + 'MdeModulePkg/Application/HelloWorld/HelloWorld.inf' :'MdeModulePkg.dsc' + } + PackagesToBuild = ["UefiCpuPkg", "MdeModulePkg"] + ''' + UnitTestModule = {} + PackagesToBuild = [] -class UnitTestPlatformBuilder(PlatformBuilder): - def CheckBootLog(self): - # - # Find all test results in boot log - # - return 0 + def UpdateUnitTestConfig(self, args): + self.env = shell_environment.GetBuildVars() + '''Update UnitTestModule, PackagesSupported and PackagesToBuild in UnitTestSupport class''' + UnitTestModuleList = ';'.join(args.ShellUnitTestList).split(";") + if not UnitTestModuleList: + return + PackagesSupported = list(CommonPlatform.PackagesSupported) + for Module in UnitTestModuleList: + PkgName = Module.split("Pkg")[0] + 'Pkg' + if PkgName not in UnitTestSupport.UnitTestModule.keys(): + UnitTestSupport.UnitTestModule[Module] = PkgName + '.dsc' + if PkgName not in PackagesSupported: + PackagesSupported.append(PkgName) + CommonPlatform.PackagesSupported = tuple(PackagesSupported) + print('PackagesSupported is {}'.format(CommonPlatform.PackagesSupported)) + print('UnitTestModule is {}'.format(UnitTestSupport.UnitTestModule)) + try: + PackagesToBuild = ','.join(args.PackagesForUT).split(',') + if PackagesToBuild: + UnitTestSupport.PackagesToBuild = PackagesToBuild + print('PackagesToBuild is {}'.format(UnitTestSupport.PackagesToBuild)) + except: + pass - def FlashRomImage(self): + def BuildUnitTest(self, packageList): VirtualDrive = os.path.join(self.env.GetValue("BUILD_OUTPUT_BASE"), "VirtualDrive") - os.makedirs(VirtualDrive, exist_ok=True) - OutputPath_FV = os.path.join(self.env.GetValue("BUILD_OUTPUT_BASE"), "FV") + BuiltDsc = [] + for package in packageList: + print(package) + for Module, Dsc in UnitTestSupport.UnitTestModule.items(): + if package in Dsc: + if Dsc not in BuiltDsc: + ModuleName = os.path.normpath(Module).split('.inf')[0].rsplit('\\')[-1] + BuiltDsc.append(Dsc) + DscPath = os.path.join(package, Dsc) + # Build specific dsc for UnitTest modules + print('Going to build this {0} for {1}'.format(DscPath, ModuleName)) + Arch = self.env.GetValue("TARGET_ARCH").split(" ") + # Set the Unit Test arch the same as the Shell in Ovmf. + if 'X64' in Arch: + UTArch = 'X64' + else: + UTArch = 'IA32' + self.env.AllowOverride("ACTIVE_PLATFORM") + self.env.SetValue("ACTIVE_PLATFORM", DscPath, "Test") + self.env.AllowOverride("TARGET_ARCH") + self.env.SetValue("TARGET_ARCH", UTArch, "Test") + PlatformBuilder.Build(self) + # Copy the UnitTest efi files to VirtualDrive folder + EfiPath = os.path.join(self.env.GetValue("BUILD_OUTPUT_BASE"), UTArch, Module.split('.inf')[0], self.env.GetValue("TARGET"), ModuleName + '.efi') + print('Copy {0}.efi from:{1}'.format(ModuleName, EfiPath)) + shutil.copy(EfiPath, VirtualDrive) - if (self.env.GetValue("QEMU_SKIP") and - self.env.GetValue("QEMU_SKIP").upper() == "TRUE"): - logging.info("skipping qemu boot test") - return 0 + def WriteEfiToStartup(self, Folder): + ''' Write all the .efi files' name in VirtualDrive into Startup.nsh''' + if (self.env.GetValue("MAKE_STARTUP_NSH").upper() == "TRUE"): + f = open(os.path.join(Folder, "startup.nsh"), "w") + for root,dirs,files in os.walk(Folder): + for file in files: + print(file) + print(os.path.splitext(file)[1]) + if os.path.splitext(file)[1] == '.efi': + f.write("{0} \n".format(file)) + f.write("reset -s\n") + f.close() + def CheckBootLog(self): # - # QEMU must be on the path + # Find all FAILURE MESSAGE in boot log # - cmd = "qemu-system-x86_64" - args = "-debugcon stdio" # write messages to stdio - args += " -global isa-debugcon.iobase=0x402" # debug messages out thru virtual io port - args += " -net none" # turn off network - args += f" -drive file=fat:rw:{VirtualDrive},format=raw,media=disk" # Mount disk with startup.nsh + #BuildLog = "BUILDLOG_{0}.txt".format(PlatformBuilder.GetName(self)) + #LogPath = os.path.join(self.ws, 'Build', BuildLog) + LogPath = 'E:/code/FutureCoreRpArraw/RomImages/fail.log' + print('Checking the boot log: {0}'.format(LogPath)) + file = open(LogPath, "r") + fileContent = file.readlines() + for Index in range(len(fileContent)): + if 'FAILURE MESSAGE:' in fileContent[Index]: + if fileContent[Index + 1].strip() != '': + FailureMessage = fileContent[Index + 1] + fileContent[Index + 2] + print(FailureMessage) + return FailureMessage + return 0 - if (self.env.GetValue("QEMU_HEADLESS").upper() == "TRUE"): - args += " -display none" # no graphics +import PlatformBuildLib +UnitTestUnitTestConfig = UnitTestSupport() +PlatformBuildLib.CommonPlatform = CommonPlatform - print(os.path.join(OutputPath_FV, "OVMF.fd")) - args += " -pflash " + os.path.join(OutputPath_FV, "OVMF.fd") # path to firmware +class UnitTestSettingsManager(SettingsManager): + def GetPlatformDscAndConfig(self) -> tuple: + return None + def AddCommandLineOptions(self, parserObj): + print('AddCommandLineOptions in PrSettingsManager') + parserObj.add_argument('ShellUnitTestList', type=str, + help='Optional - A package or folder you want to update (workspace relative).' + 'Can list multiple by doing -p , or -p -p ', + action="append", default=[]) + def RetrieveCommandLineOptions(self, args): + print('RetrieveCommandLineOptions in PrSettingsManager') + UnitTestUnitTestConfig.UpdateUnitTestConfig(args) - if (self.env.GetValue("MAKE_STARTUP_NSH").upper() == "TRUE"): - f = open(os.path.join(VirtualDrive, "startup.nsh"), "w") - f.write("BOOT SUCCESS !!! \n") - ## add commands here - f.write("reset -s\n") - f.close() +class UnitTestPlatformBuilder(PlatformBuilder): + def AddCommandLineOptions(self, parserObj): + ''' Add command line options to the argparser ''' + print('AddCommandLineOptions in PlatformBuilder') + PlatformBuilder.AddCommandLineOptions(self, parserObj) + parserObj.add_argument('-p', '--pkg', '--pkg-dir', dest='PackagesForUT', type=str, + help='Optional - A package or folder you want to update (workspace relative).' + 'Can list multiple by doing -p , or -p -p ', + action="append", default=[]) + parserObj.add_argument('ShellUnitTestList', type=str, + help='Optional - A package or folder you want to update (workspace relative).' + 'Can list multiple by doing -p , or -p -p ', + action="append", default=['None']) - ret = RunCmd(cmd, args) - print(args) - UnitTestResult = self.CheckBootLog() + def RetrieveCommandLineOptions(self, args): + ''' Retrieve command line options from the argparser ''' + print('RetrieveCommandLineOptions in PlatformBuilder') + PlatformBuilder.RetrieveCommandLineOptions(self, args) + UnitTestUnitTestConfig.UpdateUnitTestConfig(args) + + def PlatformPostBuild(self): + ''' Build specific Pkg in command line for UnitTest modules.''' + UnitTestUnitTestConfig.BuildUnitTest(UnitTestSupport.PackagesToBuild) + VirtualDrive = os.path.join(self.env.GetValue("BUILD_OUTPUT_BASE"), "VirtualDrive") + UnitTestUnitTestConfig.WriteEfiToStartup(VirtualDrive) + return 0 + + def FlashRomImage(self): + PlatformBuilder.FlashRomImage(self) + UnitTestResult = UnitTestUnitTestConfig.CheckBootLog() if (UnitTestResult): - logging.info("UnitTest modules failed.") + logging.info("UnitTest failed with this FAILURE MESSAGE:\n{}".format(UnitTestResult)) return UnitTestResult - logging.info("UnitTest modules succussfully boot.") - if ret == 0xc0000005: - #for some reason getting a c0000005 on successful return - return 0 - - return ret + return 0