summaryrefslogtreecommitdiffstats
path: root/EmulatorPkg/PlatformCI/PlatformBuild.py
blob: 6defbf6ef1480a05be380e92d286146e5aa60356 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# @file
# Script to Build EmulatorPkg UEFI firmware
#
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
import os
import logging
import io

from edk2toolext.environment import shell_environment
from edk2toolext.environment.uefi_build import UefiBuilder
from edk2toolext.invocables.edk2_platform_build import BuildSettingsManager
from edk2toolext.invocables.edk2_setup import SetupSettingsManager, RequiredSubmodule
from edk2toolext.invocables.edk2_update import UpdateSettingsManager
from edk2toolext.invocables.edk2_pr_eval import PrEvalSettingsManager
from edk2toollib.utility_functions import RunCmd
from edk2toollib.utility_functions import GetHostInfo

# ####################################################################################### #
#                                Common Configuration                                     #
# ####################################################################################### #


class CommonPlatform():
    ''' Common settings for this platform.  Define static data here and use
        for the different parts of stuart
    '''
    PackagesSupported = ("EmulatorPkg",)
    ArchSupported = ("X64", "IA32")
    TargetsSupported = ("DEBUG", "RELEASE", "NOOPT")
    Scopes = ('emulatorpkg', 'edk2-build')
    WorkspaceRoot = os.path.realpath(os.path.join(
        os.path.dirname(os.path.abspath(__file__)), "..", ".."))

    # ####################################################################################### #
    #                         Configuration for Update & Setup                                #
    # ####################################################################################### #


class SettingsManager(UpdateSettingsManager, SetupSettingsManager, PrEvalSettingsManager):

    def GetPackagesSupported(self):
        ''' return iterable of edk2 packages supported by this build.
        These should be edk2 workspace relative paths '''
        return CommonPlatform.PackagesSupported

    def GetArchitecturesSupported(self):
        ''' return iterable of edk2 architectures supported by this build '''
        return CommonPlatform.ArchSupported

    def GetTargetsSupported(self):
        ''' return iterable of edk2 target tags supported by this build '''
        return CommonPlatform.TargetsSupported

    def GetRequiredSubmodules(self):
        ''' return iterable containing RequiredSubmodule objects.
        If no RequiredSubmodules return an empty iterable
        '''
        rs = []
        # intentionally declare this one with recursive false to avoid overhead
        rs.append(RequiredSubmodule(
            "CryptoPkg/Library/OpensslLib/openssl", False))

        # To avoid maintenance of this file for every new submodule
        # lets just parse the .gitmodules and add each if not already in list.
        # The GetRequiredSubmodules is designed to allow a build to optimize
        # the desired submodules but it isn't necessary for this repository.
        result = io.StringIO()
        ret = RunCmd("git", "config --file .gitmodules --get-regexp path", workingdir=self.GetWorkspaceRoot(), outstream=result)
        # Cmd output is expected to look like:
        # submodule.CryptoPkg/Library/OpensslLib/openssl.path CryptoPkg/Library/OpensslLib/openssl
        # submodule.SoftFloat.path ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3
        if ret == 0:
            for line in result.getvalue().splitlines():
                _, _, path = line.partition(" ")
                if path is not None:
                    if path not in [x.path for x in rs]:
                        rs.append(RequiredSubmodule(path, True)) # add it with recursive since we don't know
        return rs

    def SetArchitectures(self, list_of_requested_architectures):
        ''' Confirm the requests architecture list is valid and configure SettingsManager
        to run only the requested architectures.

        Raise Exception if a list_of_requested_architectures is not supported
        '''
        unsupported = set(list_of_requested_architectures) - \
            set(self.GetArchitecturesSupported())
        if(len(unsupported) > 0):
            errorString = (
                "Unsupported Architecture Requested: " + " ".join(unsupported))
            logging.critical(errorString)
            raise Exception(errorString)
        self.ActualArchitectures = list_of_requested_architectures

    def GetWorkspaceRoot(self):
        ''' get WorkspacePath '''
        return CommonPlatform.WorkspaceRoot

    def GetActiveScopes(self):
        ''' return tuple containing scopes that should be active for this process '''
        return CommonPlatform.Scopes

    def FilterPackagesToTest(self, changedFilesList: list, potentialPackagesList: list) -> list:
        ''' Filter other cases that this package should be built
        based on changed files. This should cover things that can't
        be detected as dependencies. '''
        build_these_packages = []
        possible_packages = potentialPackagesList.copy()
        for f in changedFilesList:
            # BaseTools files that might change the build
            if "BaseTools" in f:
                if os.path.splitext(f) not in [".txt", ".md"]:
                    build_these_packages = possible_packages
                    break
            # if the azure pipeline platform template file changed
            if "platform-build-run-steps.yml" in f:
                build_these_packages = possible_packages
                break
        return build_these_packages

    def GetPlatformDscAndConfig(self) -> tuple:
        ''' If a platform desires to provide its DSC then Policy 4 will evaluate if
        any of the changes will be built in the dsc.

        The tuple should be (<workspace relative path to dsc file>, <input dictionary of dsc key value pairs>)
        '''
        return (os.path.join("EmulatorPkg", "EmulatorPkg.dsc"), {})

    # ####################################################################################### #
    #                         Actual Configuration for Platform Build                         #
    # ####################################################################################### #


class PlatformBuilder(UefiBuilder, BuildSettingsManager):
    def __init__(self):
        UefiBuilder.__init__(self)

    def AddCommandLineOptions(self, parserObj):
        ''' Add command line options to the argparser '''
        parserObj.add_argument('-a', "--arch", dest="build_arch", type=str, default="X64",
                               help="Optional - architecture to build.  IA32 will use IA32 for Pei & Dxe. "
                               "X64 will use X64 for both PEI and DXE.")

    def RetrieveCommandLineOptions(self, args):
        '''  Retrieve command line options from the argparser '''

        shell_environment.GetBuildVars().SetValue(
            "TARGET_ARCH", args.build_arch.upper(), "From CmdLine")
        shell_environment.GetBuildVars().SetValue(
            "ACTIVE_PLATFORM", "EmulatorPkg/EmulatorPkg.dsc", "From CmdLine")

    def GetWorkspaceRoot(self):
        ''' get WorkspacePath '''
        return CommonPlatform.WorkspaceRoot

    def GetPackagesPath(self):
        ''' Return a list of workspace relative paths that should be mapped as edk2 PackagesPath '''
        return ()

    def GetActiveScopes(self):
        ''' return tuple containing scopes that should be active for this process '''
        return CommonPlatform.Scopes

    def GetName(self):
        ''' Get the name of the repo, platform, or product being build '''
        ''' Used for naming the log file, among others '''

        # check the startup nsh flag and if set then rename the log file.
        # this helps in CI so we don't overwrite the build log since running
        # uses the stuart_build command.
        if(shell_environment.GetBuildVars().GetValue("MAKE_STARTUP_NSH", "FALSE") == "TRUE"):
            return "EmulatorPkg_With_Run"
        return "EmulatorPkg"

    def GetLoggingLevel(self, loggerType):
        ''' Get the logging level for a given type
        base == lowest logging level supported
        con  == Screen logging
        txt  == plain text file logging
        md   == markdown file logging
        '''
        return logging.DEBUG

    def SetPlatformEnv(self):
        logging.debug("PlatformBuilder SetPlatformEnv")
        self.env.SetValue("PRODUCT_NAME", "EmulatorPkg", "Platform Hardcoded")
        self.env.SetValue("TOOL_CHAIN_TAG", "VS2019", "Default Toolchain")

        # Add support for using the correct Platform Headers, tools, and Libs based on emulator architecture
        # requested to be built when building VS2019 or VS2017
        if self.env.GetValue("TOOL_CHAIN_TAG") == "VS2019" or self.env.GetValue("TOOL_CHAIN_TAG") == "VS2017":
            key = self.env.GetValue("TOOL_CHAIN_TAG") + "_HOST"
            if self.env.GetValue("TARGET_ARCH") == "IA32":
                shell_environment.ShellEnvironment().set_shell_var(key, "x86")
            elif self.env.GetValue("TARGET_ARCH") == "X64":
                shell_environment.ShellEnvironment().set_shell_var(key, "x64")

        # Add support for using the correct Platform Headers, tools, and Libs based on emulator architecture
        # requested to be built when building on linux.
        if GetHostInfo().os.upper() == "LINUX":
            self.ConfigureLinuxDLinkPath()

        if GetHostInfo().os.upper() == "WINDOWS":
            self.env.SetValue("BLD_*_WIN_HOST_BUILD", "TRUE",
                              "Trigger Windows host build")

        self.env.SetValue("MAKE_STARTUP_NSH", "FALSE", "Default to false")

        # I don't see what this does but it is in build.sh
        key = "BLD_*_BUILD_" + self.env.GetValue("TARGET_ARCH")
        self.env.SetValue(key, "TRUE", "match script in build.sh")
        return 0

    def PlatformPreBuild(self):
        return 0

    def PlatformPostBuild(self):
        return 0

    def FlashRomImage(self):
        ''' Use the FlashRom Function to run the emulator.  This gives an easy stuart command line to
        activate the emulator. '''

        OutputPath = os.path.join(self.env.GetValue(
            "BUILD_OUTPUT_BASE"), self.env.GetValue("TARGET_ARCH"))

        if (self.env.GetValue("MAKE_STARTUP_NSH") == "TRUE"):
            f = open(os.path.join(OutputPath, "startup.nsh"), "w")
            f.write("BOOT SUCCESS !!! \n")
            # add commands here
            f.write("reset\n")
            f.close()

        if GetHostInfo().os.upper() == "WINDOWS":
            cmd = "WinHost.exe"
        elif GetHostInfo().os.upper() == "LINUX":
            cmd = "./Host"
        else:
            logging.critical("Unsupported Host")
            return -1
        return RunCmd(cmd, "", workingdir=OutputPath)

    def ConfigureLinuxDLinkPath(self):
        '''
        logic copied from build.sh to setup the correct libraries
        '''
        if self.env.GetValue("TARGET_ARCH") == "IA32":
            LIB_NAMES = ["ld-linux.so.2", "libdl.so.2 crt1.o", "crti.o crtn.o"]
            LIB_SEARCH_PATHS = ["/usr/lib/i386-linux-gnu",
                                "/usr/lib32", "/lib32", "/usr/lib", "/lib"]
        elif self.env.GetValue("TARGET_ARCH") == "X64":
            LIB_NAMES = ["ld-linux-x86-64.so.2",
                         "libdl.so.2", "crt1.o", "crti.o", "crtn.o"]
            LIB_SEARCH_PATHS = ["/usr/lib/x86_64-linux-gnu",
                                "/usr/lib64", "/lib64", "/usr/lib", "/lib"]

        HOST_DLINK_PATHS = ""
        for lname in LIB_NAMES:
            logging.debug(f"Looking for {lname}")
            for dname in LIB_SEARCH_PATHS:
                logging.debug(f"In {dname}")
                if os.path.isfile(os.path.join(dname, lname)):
                    logging.debug(f"Found {lname} in {dname}")
                    HOST_DLINK_PATHS += os.path.join(
                        os.path.join(dname, lname)) + os.pathsep
                    break
        HOST_DLINK_PATHS = HOST_DLINK_PATHS.rstrip(os.pathsep)
        logging.critical(f"Setting HOST_DLINK_PATHS to {HOST_DLINK_PATHS}")
        shell_environment.ShellEnvironment().set_shell_var(
            "HOST_DLINK_PATHS", HOST_DLINK_PATHS)