1# @file 2# Script to Build EmulatorPkg UEFI firmware 3# 4# Copyright (c) Microsoft Corporation. 5# SPDX-License-Identifier: BSD-2-Clause-Patent 6## 7import os 8import logging 9import io 10 11from edk2toolext.environment import shell_environment 12from edk2toolext.environment.uefi_build import UefiBuilder 13from edk2toolext.invocables.edk2_platform_build import BuildSettingsManager 14from edk2toolext.invocables.edk2_setup import SetupSettingsManager, RequiredSubmodule 15from edk2toolext.invocables.edk2_update import UpdateSettingsManager 16from edk2toolext.invocables.edk2_pr_eval import PrEvalSettingsManager 17from edk2toollib.utility_functions import RunCmd 18from edk2toollib.utility_functions import GetHostInfo 19 20# ####################################################################################### # 21# Common Configuration # 22# ####################################################################################### # 23 24 25class CommonPlatform(): 26 ''' Common settings for this platform. Define static data here and use 27 for the different parts of stuart 28 ''' 29 PackagesSupported = ("EmulatorPkg",) 30 ArchSupported = ("X64", "IA32") 31 TargetsSupported = ("DEBUG", "RELEASE", "NOOPT") 32 Scopes = ('emulatorpkg', 'edk2-build') 33 WorkspaceRoot = os.path.realpath(os.path.join( 34 os.path.dirname(os.path.abspath(__file__)), "..", "..")) 35 36 # ####################################################################################### # 37 # Configuration for Update & Setup # 38 # ####################################################################################### # 39 40 41class SettingsManager(UpdateSettingsManager, SetupSettingsManager, PrEvalSettingsManager): 42 43 def GetPackagesSupported(self): 44 ''' return iterable of edk2 packages supported by this build. 45 These should be edk2 workspace relative paths ''' 46 return CommonPlatform.PackagesSupported 47 48 def GetArchitecturesSupported(self): 49 ''' return iterable of edk2 architectures supported by this build ''' 50 return CommonPlatform.ArchSupported 51 52 def GetTargetsSupported(self): 53 ''' return iterable of edk2 target tags supported by this build ''' 54 return CommonPlatform.TargetsSupported 55 56 def GetRequiredSubmodules(self): 57 ''' return iterable containing RequiredSubmodule objects. 58 If no RequiredSubmodules return an empty iterable 59 ''' 60 rs = [] 61 # intentionally declare this one with recursive false to avoid overhead 62 rs.append(RequiredSubmodule( 63 "CryptoPkg/Library/OpensslLib/openssl", False)) 64 65 # To avoid maintenance of this file for every new submodule 66 # lets just parse the .gitmodules and add each if not already in list. 67 # The GetRequiredSubmodules is designed to allow a build to optimize 68 # the desired submodules but it isn't necessary for this repository. 69 result = io.StringIO() 70 ret = RunCmd("git", "config --file .gitmodules --get-regexp path", workingdir=self.GetWorkspaceRoot(), outstream=result) 71 # Cmd output is expected to look like: 72 # submodule.CryptoPkg/Library/OpensslLib/openssl.path CryptoPkg/Library/OpensslLib/openssl 73 # submodule.SoftFloat.path ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 74 if ret == 0: 75 for line in result.getvalue().splitlines(): 76 _, _, path = line.partition(" ") 77 if path is not None: 78 if path not in [x.path for x in rs]: 79 rs.append(RequiredSubmodule(path, True)) # add it with recursive since we don't know 80 return rs 81 82 def SetArchitectures(self, list_of_requested_architectures): 83 ''' Confirm the requests architecture list is valid and configure SettingsManager 84 to run only the requested architectures. 85 86 Raise Exception if a list_of_requested_architectures is not supported 87 ''' 88 unsupported = set(list_of_requested_architectures) - \ 89 set(self.GetArchitecturesSupported()) 90 if(len(unsupported) > 0): 91 errorString = ( 92 "Unsupported Architecture Requested: " + " ".join(unsupported)) 93 logging.critical(errorString) 94 raise Exception(errorString) 95 self.ActualArchitectures = list_of_requested_architectures 96 97 def GetWorkspaceRoot(self): 98 ''' get WorkspacePath ''' 99 return CommonPlatform.WorkspaceRoot 100 101 def GetActiveScopes(self): 102 ''' return tuple containing scopes that should be active for this process ''' 103 return CommonPlatform.Scopes 104 105 def FilterPackagesToTest(self, changedFilesList: list, potentialPackagesList: list) -> list: 106 ''' Filter other cases that this package should be built 107 based on changed files. This should cover things that can't 108 be detected as dependencies. ''' 109 build_these_packages = [] 110 possible_packages = potentialPackagesList.copy() 111 for f in changedFilesList: 112 # BaseTools files that might change the build 113 if "BaseTools" in f: 114 if os.path.splitext(f) not in [".txt", ".md"]: 115 build_these_packages = possible_packages 116 break 117 # if the azure pipeline platform template file changed 118 if "platform-build-run-steps.yml" in f: 119 build_these_packages = possible_packages 120 break 121 return build_these_packages 122 123 def GetPlatformDscAndConfig(self) -> tuple: 124 ''' If a platform desires to provide its DSC then Policy 4 will evaluate if 125 any of the changes will be built in the dsc. 126 127 The tuple should be (<workspace relative path to dsc file>, <input dictionary of dsc key value pairs>) 128 ''' 129 return (os.path.join("EmulatorPkg", "EmulatorPkg.dsc"), {}) 130 131 # ####################################################################################### # 132 # Actual Configuration for Platform Build # 133 # ####################################################################################### # 134 135 136class PlatformBuilder(UefiBuilder, BuildSettingsManager): 137 def __init__(self): 138 UefiBuilder.__init__(self) 139 140 def AddCommandLineOptions(self, parserObj): 141 ''' Add command line options to the argparser ''' 142 parserObj.add_argument('-a', "--arch", dest="build_arch", type=str, default="X64", 143 help="Optional - architecture to build. IA32 will use IA32 for Pei & Dxe. " 144 "X64 will use X64 for both PEI and DXE.") 145 146 def RetrieveCommandLineOptions(self, args): 147 ''' Retrieve command line options from the argparser ''' 148 149 shell_environment.GetBuildVars().SetValue( 150 "TARGET_ARCH", args.build_arch.upper(), "From CmdLine") 151 shell_environment.GetBuildVars().SetValue( 152 "ACTIVE_PLATFORM", "EmulatorPkg/EmulatorPkg.dsc", "From CmdLine") 153 154 def GetWorkspaceRoot(self): 155 ''' get WorkspacePath ''' 156 return CommonPlatform.WorkspaceRoot 157 158 def GetPackagesPath(self): 159 ''' Return a list of workspace relative paths that should be mapped as edk2 PackagesPath ''' 160 return () 161 162 def GetActiveScopes(self): 163 ''' return tuple containing scopes that should be active for this process ''' 164 return CommonPlatform.Scopes 165 166 def GetName(self): 167 ''' Get the name of the repo, platform, or product being build ''' 168 ''' Used for naming the log file, among others ''' 169 170 # check the startup nsh flag and if set then rename the log file. 171 # this helps in CI so we don't overwrite the build log since running 172 # uses the stuart_build command. 173 if(shell_environment.GetBuildVars().GetValue("MAKE_STARTUP_NSH", "FALSE") == "TRUE"): 174 return "EmulatorPkg_With_Run" 175 return "EmulatorPkg" 176 177 def GetLoggingLevel(self, loggerType): 178 ''' Get the logging level for a given type 179 base == lowest logging level supported 180 con == Screen logging 181 txt == plain text file logging 182 md == markdown file logging 183 ''' 184 return logging.DEBUG 185 186 def SetPlatformEnv(self): 187 logging.debug("PlatformBuilder SetPlatformEnv") 188 self.env.SetValue("PRODUCT_NAME", "EmulatorPkg", "Platform Hardcoded") 189 self.env.SetValue("TOOL_CHAIN_TAG", "VS2019", "Default Toolchain") 190 191 # Add support for using the correct Platform Headers, tools, and Libs based on emulator architecture 192 # requested to be built when building VS2019 or VS2017 193 if self.env.GetValue("TOOL_CHAIN_TAG") == "VS2019" or self.env.GetValue("TOOL_CHAIN_TAG") == "VS2017": 194 key = self.env.GetValue("TOOL_CHAIN_TAG") + "_HOST" 195 if self.env.GetValue("TARGET_ARCH") == "IA32": 196 shell_environment.ShellEnvironment().set_shell_var(key, "x86") 197 elif self.env.GetValue("TARGET_ARCH") == "X64": 198 shell_environment.ShellEnvironment().set_shell_var(key, "x64") 199 200 # Add support for using the correct Platform Headers, tools, and Libs based on emulator architecture 201 # requested to be built when building on linux. 202 if GetHostInfo().os.upper() == "LINUX": 203 self.ConfigureLinuxDLinkPath() 204 205 if GetHostInfo().os.upper() == "WINDOWS": 206 self.env.SetValue("BLD_*_WIN_HOST_BUILD", "TRUE", 207 "Trigger Windows host build") 208 209 self.env.SetValue("MAKE_STARTUP_NSH", "FALSE", "Default to false") 210 211 # I don't see what this does but it is in build.sh 212 key = "BLD_*_BUILD_" + self.env.GetValue("TARGET_ARCH") 213 self.env.SetValue(key, "TRUE", "match script in build.sh") 214 return 0 215 216 def PlatformPreBuild(self): 217 return 0 218 219 def PlatformPostBuild(self): 220 return 0 221 222 def FlashRomImage(self): 223 ''' Use the FlashRom Function to run the emulator. This gives an easy stuart command line to 224 activate the emulator. ''' 225 226 OutputPath = os.path.join(self.env.GetValue( 227 "BUILD_OUTPUT_BASE"), self.env.GetValue("TARGET_ARCH")) 228 229 if (self.env.GetValue("MAKE_STARTUP_NSH") == "TRUE"): 230 f = open(os.path.join(OutputPath, "startup.nsh"), "w") 231 f.write("BOOT SUCCESS !!! \n") 232 # add commands here 233 f.write("reset\n") 234 f.close() 235 236 if GetHostInfo().os.upper() == "WINDOWS": 237 cmd = "WinHost.exe" 238 elif GetHostInfo().os.upper() == "LINUX": 239 cmd = "./Host" 240 else: 241 logging.critical("Unsupported Host") 242 return -1 243 return RunCmd(cmd, "", workingdir=OutputPath) 244 245 def ConfigureLinuxDLinkPath(self): 246 ''' 247 logic copied from build.sh to setup the correct libraries 248 ''' 249 if self.env.GetValue("TARGET_ARCH") == "IA32": 250 LIB_NAMES = ["ld-linux.so.2", "libdl.so.2 crt1.o", "crti.o crtn.o"] 251 LIB_SEARCH_PATHS = ["/usr/lib/i386-linux-gnu", 252 "/usr/lib32", "/lib32", "/usr/lib", "/lib"] 253 elif self.env.GetValue("TARGET_ARCH") == "X64": 254 LIB_NAMES = ["ld-linux-x86-64.so.2", 255 "libdl.so.2", "crt1.o", "crti.o", "crtn.o"] 256 LIB_SEARCH_PATHS = ["/usr/lib/x86_64-linux-gnu", 257 "/usr/lib64", "/lib64", "/usr/lib", "/lib"] 258 259 HOST_DLINK_PATHS = "" 260 for lname in LIB_NAMES: 261 logging.debug(f"Looking for {lname}") 262 for dname in LIB_SEARCH_PATHS: 263 logging.debug(f"In {dname}") 264 if os.path.isfile(os.path.join(dname, lname)): 265 logging.debug(f"Found {lname} in {dname}") 266 HOST_DLINK_PATHS += os.path.join( 267 os.path.join(dname, lname)) + os.pathsep 268 break 269 HOST_DLINK_PATHS = HOST_DLINK_PATHS.rstrip(os.pathsep) 270 logging.critical(f"Setting HOST_DLINK_PATHS to {HOST_DLINK_PATHS}") 271 shell_environment.ShellEnvironment().set_shell_var( 272 "HOST_DLINK_PATHS", HOST_DLINK_PATHS) 273