]> git.proxmox.com Git - mirror_edk2.git/blob - EmulatorPkg/PlatformCI/PlatformBuild.py
EmulatorPkg: Add Platform CI and configuration for Core CI
[mirror_edk2.git] / EmulatorPkg / PlatformCI / PlatformBuild.py
1 # @file
2 # Script to Build EmulatorPkg UEFI firmware
3 #
4 # Copyright (c) Microsoft Corporation.
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
6 ##
7 import os
8 import logging
9 import io
10
11 from edk2toolext.environment import shell_environment
12 from edk2toolext.environment.uefi_build import UefiBuilder
13 from edk2toolext.invocables.edk2_platform_build import BuildSettingsManager
14 from edk2toolext.invocables.edk2_setup import SetupSettingsManager, RequiredSubmodule
15 from edk2toolext.invocables.edk2_update import UpdateSettingsManager
16 from edk2toolext.invocables.edk2_pr_eval import PrEvalSettingsManager
17 from edk2toollib.utility_functions import RunCmd
18 from edk2toollib.utility_functions import GetHostInfo
19
20 # ####################################################################################### #
21 # Common Configuration #
22 # ####################################################################################### #
23
24
25 class 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
41 class 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
136 class 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)