2019-10-08 03:37:13 +02:00
|
|
|
# @file Edk2ToolsBuild.py
|
|
|
|
# Invocable class that builds the basetool c files.
|
|
|
|
#
|
2024-12-03 23:52:41 +01:00
|
|
|
# Supports VS2019, VS2022, and GCC5
|
2019-10-08 03:37:13 +02:00
|
|
|
##
|
|
|
|
# Copyright (c) Microsoft Corporation
|
|
|
|
#
|
|
|
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
##
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import logging
|
|
|
|
import argparse
|
2020-04-02 19:04:52 +02:00
|
|
|
import multiprocessing
|
2024-07-24 22:13:02 +02:00
|
|
|
import shutil
|
2019-10-08 03:37:13 +02:00
|
|
|
from edk2toolext import edk2_logging
|
2024-07-24 22:13:02 +02:00
|
|
|
from edk2toolext.environment import self_describing_environment, shell_environment
|
2019-10-08 03:37:13 +02:00
|
|
|
from edk2toolext.base_abstract_invocable import BaseAbstractInvocable
|
2024-07-24 22:13:02 +02:00
|
|
|
from edk2toollib.utility_functions import RunCmd, GetHostInfo
|
2019-10-08 03:37:13 +02:00
|
|
|
from edk2toollib.windows.locate_tools import QueryVcVariables
|
|
|
|
|
|
|
|
|
|
|
|
class Edk2ToolsBuild(BaseAbstractInvocable):
|
|
|
|
|
|
|
|
def ParseCommandLineOptions(self):
|
|
|
|
''' parse arguments '''
|
|
|
|
ParserObj = argparse.ArgumentParser()
|
2024-12-03 23:52:41 +01:00
|
|
|
ParserObj.add_argument("-t", "--tool_chain_tag", dest="tct", default="VS2022",
|
2019-10-08 03:37:13 +02:00
|
|
|
help="Set the toolchain used to compile the build tools")
|
2024-07-24 22:13:02 +02:00
|
|
|
ParserObj.add_argument("-a", "--target_arch", dest="arch", default=None, choices=[None, 'IA32', 'X64', 'ARM', 'AARCH64'],
|
|
|
|
help="Specify the architecture of the built base tools. Not specifying this will fall back to the default "
|
|
|
|
"behavior, for Windows builds, IA32 target will be built, for Linux builds, target arch will be the same as host arch.")
|
2019-10-08 03:37:13 +02:00
|
|
|
args = ParserObj.parse_args()
|
|
|
|
self.tool_chain_tag = args.tct
|
2024-07-24 22:13:02 +02:00
|
|
|
self.target_arch = args.arch
|
2019-10-08 03:37:13 +02:00
|
|
|
|
|
|
|
def GetWorkspaceRoot(self):
|
|
|
|
''' Return the workspace root for initializing the SDE '''
|
|
|
|
|
|
|
|
# this is the bastools dir...not the traditional EDK2 workspace root
|
|
|
|
return os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
|
|
|
def GetActiveScopes(self):
|
|
|
|
''' return tuple containing scopes that should be active for this process '''
|
|
|
|
|
2023-04-26 00:48:19 +02:00
|
|
|
# Adding scope for cross compilers when building for ARM/AARCH64
|
|
|
|
scopes = ('global',)
|
|
|
|
if GetHostInfo().os == "Linux" and self.tool_chain_tag.lower().startswith("gcc"):
|
|
|
|
if self.target_arch is None:
|
|
|
|
return scopes
|
|
|
|
if "AARCH64" in self.target_arch:
|
|
|
|
scopes += ("gcc_aarch64_linux",)
|
|
|
|
if "ARM" in self.target_arch:
|
|
|
|
scopes += ("gcc_arm_linux",)
|
|
|
|
return scopes
|
2019-10-08 03:37:13 +02:00
|
|
|
|
|
|
|
def GetLoggingLevel(self, loggerType):
|
|
|
|
''' Get the logging level for a given type (return Logging.Level)
|
|
|
|
base == lowest logging level supported
|
|
|
|
con == Screen logging
|
|
|
|
txt == plain text file logging
|
|
|
|
md == markdown file logging
|
|
|
|
'''
|
|
|
|
if(loggerType == "con"):
|
|
|
|
return logging.ERROR
|
|
|
|
else:
|
|
|
|
return logging.DEBUG
|
|
|
|
|
|
|
|
def GetLoggingFolderRelativeToRoot(self):
|
|
|
|
''' Return a path to folder for log files '''
|
|
|
|
return "BaseToolsBuild"
|
|
|
|
|
|
|
|
def GetVerifyCheckRequired(self):
|
|
|
|
''' Will call self_describing_environment.VerifyEnvironment if this returns True '''
|
|
|
|
return True
|
|
|
|
|
|
|
|
def GetLoggingFileName(self, loggerType):
|
|
|
|
''' Get the logging file name for the type.
|
|
|
|
Return None if the logger shouldn't be created
|
|
|
|
|
|
|
|
base == lowest logging level supported
|
|
|
|
con == Screen logging
|
|
|
|
txt == plain text file logging
|
|
|
|
md == markdown file logging
|
|
|
|
'''
|
|
|
|
return "BASETOOLS_BUILD"
|
|
|
|
|
|
|
|
def WritePathEnvFile(self, OutputDir):
|
|
|
|
''' Write a PyTool path env file for future PyTool based edk2 builds'''
|
|
|
|
content = '''##
|
|
|
|
# Set shell variable EDK_TOOLS_BIN to this folder
|
|
|
|
#
|
|
|
|
# Autogenerated by Edk2ToolsBuild.py
|
|
|
|
#
|
|
|
|
# Copyright (c), Microsoft Corporation
|
|
|
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
##
|
|
|
|
{
|
|
|
|
"id": "You-Built-BaseTools",
|
|
|
|
"scope": "edk2-build",
|
|
|
|
"flags": ["set_shell_var", "set_path"],
|
|
|
|
"var_name": "EDK_TOOLS_BIN"
|
|
|
|
}
|
|
|
|
'''
|
|
|
|
with open(os.path.join(OutputDir, "basetoolsbin_path_env.yaml"), "w") as f:
|
|
|
|
f.write(content)
|
|
|
|
|
|
|
|
def Go(self):
|
|
|
|
logging.info("Running Python version: " + str(sys.version_info))
|
|
|
|
|
|
|
|
(build_env, shell_env) = self_describing_environment.BootstrapEnvironment(
|
|
|
|
self.GetWorkspaceRoot(), self.GetActiveScopes())
|
|
|
|
|
|
|
|
# # Bind our current execution environment into the shell vars.
|
|
|
|
ph = os.path.dirname(sys.executable)
|
|
|
|
if " " in ph:
|
|
|
|
ph = '"' + ph + '"'
|
|
|
|
shell_env.set_shell_var("PYTHON_HOME", ph)
|
|
|
|
# PYTHON_COMMAND is required to be set for using edk2 python builds.
|
|
|
|
pc = sys.executable
|
|
|
|
if " " in pc:
|
|
|
|
pc = '"' + pc + '"'
|
|
|
|
shell_env.set_shell_var("PYTHON_COMMAND", pc)
|
|
|
|
|
|
|
|
if self.tool_chain_tag.lower().startswith("vs"):
|
2024-07-24 22:13:02 +02:00
|
|
|
if self.target_arch is None:
|
|
|
|
# Put a default as IA32
|
|
|
|
self.target_arch = "IA32"
|
|
|
|
|
|
|
|
if self.target_arch == "IA32":
|
|
|
|
VcToolChainArch = "x86"
|
|
|
|
TargetInfoArch = "x86"
|
|
|
|
OutputDir = "Win32"
|
|
|
|
elif self.target_arch == "ARM":
|
|
|
|
VcToolChainArch = "x86_arm"
|
|
|
|
TargetInfoArch = "ARM"
|
|
|
|
OutputDir = "Win32"
|
|
|
|
elif self.target_arch == "X64":
|
|
|
|
VcToolChainArch = "amd64"
|
|
|
|
TargetInfoArch = "x86"
|
|
|
|
OutputDir = "Win64"
|
|
|
|
elif self.target_arch == "AARCH64":
|
|
|
|
VcToolChainArch = "amd64_arm64"
|
|
|
|
TargetInfoArch = "ARM"
|
|
|
|
OutputDir = "Win64"
|
|
|
|
else:
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
self.OutputDir = os.path.join(
|
|
|
|
shell_env.get_shell_var("EDK_TOOLS_PATH"), "Bin", OutputDir)
|
|
|
|
|
|
|
|
# compiled tools need to be added to path because antlr is referenced
|
|
|
|
HostInfo = GetHostInfo()
|
|
|
|
if TargetInfoArch == HostInfo.arch:
|
|
|
|
# not cross compiling
|
|
|
|
shell_env.insert_path(self.OutputDir)
|
|
|
|
else:
|
|
|
|
# cross compiling:
|
|
|
|
# as the VfrCompile tool is needed in the build process, we need
|
|
|
|
# to build one for the host system, then add the path to the
|
|
|
|
# tools to the PATH environment variable
|
|
|
|
shell_environment.CheckpointBuildVars()
|
|
|
|
if HostInfo.arch == "x86" and HostInfo.bit == "64":
|
|
|
|
host_arch = "X64"
|
|
|
|
host_toolchain_arch = "amd64"
|
|
|
|
TempOutputDir = os.path.join(shell_env.get_shell_var("EDK_TOOLS_PATH"), "Bin", "Win64")
|
|
|
|
elif HostInfo.arch == "x86" and HostInfo.bit == "32":
|
|
|
|
host_arch = "IA32"
|
|
|
|
host_toolchain_arch = "x86"
|
|
|
|
TempOutputDir = os.path.join(shell_env.get_shell_var("EDK_TOOLS_PATH"), "Bin", "Win32")
|
|
|
|
elif HostInfo.arch == "ARM" and HostInfo.bit == "64":
|
|
|
|
host_arch = "AARCH64"
|
|
|
|
host_toolchain_arch = "amd64_arm64"
|
|
|
|
TempOutputDir = os.path.join(shell_env.get_shell_var("EDK_TOOLS_PATH"), "Bin", "Win64")
|
|
|
|
elif HostInfo.arch == "ARM" and HostInfo.bit == "32":
|
|
|
|
host_arch = "ARM"
|
|
|
|
host_toolchain_arch = "x86_arm"
|
|
|
|
TempOutputDir = os.path.join(shell_env.get_shell_var("EDK_TOOLS_PATH"), "Bin", "Win32")
|
|
|
|
else:
|
|
|
|
raise Exception("Unsupported host system. %s %s" % (HostInfo.arch, HostInfo.bit))
|
|
|
|
|
|
|
|
interesting_keys = ["ExtensionSdkDir", "INCLUDE", "LIB"]
|
|
|
|
interesting_keys.extend(
|
|
|
|
["LIBPATH", "Path", "UniversalCRTSdkDir", "UCRTVersion", "WindowsLibPath", "WindowsSdkBinPath"])
|
|
|
|
interesting_keys.extend(
|
|
|
|
["WindowsSdkDir", "WindowsSdkVerBinPath", "WindowsSDKVersion", "VCToolsInstallDir"])
|
|
|
|
vc_vars = QueryVcVariables(
|
|
|
|
interesting_keys, host_toolchain_arch, vs_version=self.tool_chain_tag.lower())
|
|
|
|
for key in vc_vars.keys():
|
|
|
|
logging.debug(f"Var - {key} = {vc_vars[key]}")
|
|
|
|
if key.lower() == 'path':
|
|
|
|
shell_env.set_path(vc_vars[key])
|
|
|
|
else:
|
|
|
|
shell_env.set_shell_var(key, vc_vars[key])
|
|
|
|
|
|
|
|
# Note: This HOST_ARCH is in respect to the BUILT base tools, not the host arch where
|
|
|
|
# this script is BUILDING the base tools.
|
|
|
|
shell_env.set_shell_var('HOST_ARCH', host_arch)
|
|
|
|
shell_env.insert_path(TempOutputDir)
|
|
|
|
|
|
|
|
# All set, build the tools for the host system.
|
|
|
|
ret = RunCmd('nmake.exe', None,
|
|
|
|
workingdir=shell_env.get_shell_var("EDK_TOOLS_PATH"))
|
|
|
|
if ret != 0:
|
|
|
|
raise Exception("Failed to build base tools for host system.")
|
|
|
|
|
|
|
|
# Copy the output to a temp directory
|
|
|
|
TempFolder = os.path.join(shell_env.get_shell_var("EDK_TOOLS_PATH"), "BaseToolsBuild", "Temp")
|
|
|
|
if not os.path.exists(TempFolder):
|
|
|
|
os.makedirs(TempFolder)
|
|
|
|
for file in os.listdir(TempOutputDir):
|
|
|
|
shutil.copy(os.path.join(TempOutputDir, file), TempFolder)
|
|
|
|
|
|
|
|
# Clean up the build output
|
|
|
|
ret = RunCmd('nmake.exe', 'cleanall',
|
|
|
|
workingdir=shell_env.get_shell_var("EDK_TOOLS_PATH"))
|
|
|
|
|
|
|
|
# Remove the entire TempOutputDir
|
|
|
|
shutil.rmtree(TempOutputDir)
|
|
|
|
|
|
|
|
shell_environment.RevertBuildVars()
|
|
|
|
shell_env.insert_path(TempFolder)
|
2019-10-08 03:37:13 +02:00
|
|
|
|
|
|
|
# # Update environment with required VC vars.
|
|
|
|
interesting_keys = ["ExtensionSdkDir", "INCLUDE", "LIB"]
|
|
|
|
interesting_keys.extend(
|
|
|
|
["LIBPATH", "Path", "UniversalCRTSdkDir", "UCRTVersion", "WindowsLibPath", "WindowsSdkBinPath"])
|
|
|
|
interesting_keys.extend(
|
|
|
|
["WindowsSdkDir", "WindowsSdkVerBinPath", "WindowsSDKVersion", "VCToolsInstallDir"])
|
|
|
|
vc_vars = QueryVcVariables(
|
2024-07-24 22:13:02 +02:00
|
|
|
interesting_keys, VcToolChainArch, vs_version=self.tool_chain_tag.lower())
|
2019-10-08 03:37:13 +02:00
|
|
|
for key in vc_vars.keys():
|
|
|
|
logging.debug(f"Var - {key} = {vc_vars[key]}")
|
|
|
|
if key.lower() == 'path':
|
2022-09-21 22:44:59 +02:00
|
|
|
shell_env.set_path(vc_vars[key])
|
2019-10-08 03:37:13 +02:00
|
|
|
else:
|
|
|
|
shell_env.set_shell_var(key, vc_vars[key])
|
|
|
|
|
2024-07-24 22:13:02 +02:00
|
|
|
# Note: This HOST_ARCH is in respect to the BUILT base tools, not the host arch where
|
|
|
|
# this script is BUILDING the base tools.
|
|
|
|
shell_env.set_shell_var('HOST_ARCH', self.target_arch)
|
2019-10-08 03:37:13 +02:00
|
|
|
|
|
|
|
# Actually build the tools.
|
2023-06-21 17:44:15 +02:00
|
|
|
output_stream = edk2_logging.create_output_stream()
|
2019-10-08 03:37:13 +02:00
|
|
|
ret = RunCmd('nmake.exe', None,
|
|
|
|
workingdir=shell_env.get_shell_var("EDK_TOOLS_PATH"))
|
2023-06-21 17:44:15 +02:00
|
|
|
edk2_logging.remove_output_stream(output_stream)
|
|
|
|
problems = edk2_logging.scan_compiler_output(output_stream)
|
|
|
|
for level, problem in problems:
|
|
|
|
logging.log(level, problem)
|
2019-10-08 03:37:13 +02:00
|
|
|
if ret != 0:
|
|
|
|
raise Exception("Failed to build.")
|
|
|
|
|
|
|
|
self.WritePathEnvFile(self.OutputDir)
|
|
|
|
return ret
|
|
|
|
|
|
|
|
elif self.tool_chain_tag.lower().startswith("gcc"):
|
2023-04-26 00:48:19 +02:00
|
|
|
# Note: This HOST_ARCH is in respect to the BUILT base tools, not the host arch where
|
|
|
|
# this script is BUILDING the base tools.
|
|
|
|
HostInfo = GetHostInfo()
|
|
|
|
prefix = None
|
|
|
|
TargetInfoArch = None
|
|
|
|
if self.target_arch is not None:
|
|
|
|
shell_env.set_shell_var('HOST_ARCH', self.target_arch)
|
|
|
|
|
|
|
|
if "AARCH64" in self.target_arch:
|
|
|
|
prefix = shell_env.get_shell_var("GCC5_AARCH64_PREFIX")
|
|
|
|
if prefix == None:
|
|
|
|
# now check for install dir. If set then set the Prefix
|
|
|
|
install_path = shell_environment.GetEnvironment().get_shell_var("GCC5_AARCH64_INSTALL")
|
|
|
|
|
|
|
|
# make GCC5_AARCH64_PREFIX to align with tools_def.txt
|
|
|
|
prefix = os.path.join(install_path, "bin", "aarch64-none-linux-gnu-")
|
|
|
|
|
|
|
|
shell_environment.GetEnvironment().set_shell_var("GCC_PREFIX", prefix)
|
|
|
|
TargetInfoArch = "ARM"
|
|
|
|
|
|
|
|
elif "ARM" in self.target_arch:
|
|
|
|
prefix = shell_env.get_shell_var("GCC5_ARM_PREFIX")
|
|
|
|
if prefix == None:
|
|
|
|
# now check for install dir. If set then set the Prefix
|
|
|
|
install_path = shell_environment.GetEnvironment().get_shell_var("GCC5_ARM_INSTALL")
|
|
|
|
|
|
|
|
# make GCC5_ARM_PREFIX to align with tools_def.txt
|
|
|
|
prefix = os.path.join(install_path, "bin", "arm-none-linux-gnueabihf-")
|
|
|
|
|
|
|
|
shell_environment.GetEnvironment().set_shell_var("GCC_PREFIX", prefix)
|
|
|
|
TargetInfoArch = "ARM"
|
|
|
|
|
|
|
|
else:
|
|
|
|
TargetInfoArch = "x86"
|
|
|
|
else:
|
|
|
|
self.target_arch = HostInfo.arch
|
|
|
|
TargetInfoArch = HostInfo.arch
|
|
|
|
# Otherwise, the built binary arch will be consistent with the host system
|
|
|
|
|
|
|
|
# Added logic to support cross compilation scenarios
|
|
|
|
if TargetInfoArch != HostInfo.arch:
|
|
|
|
# this is defaulting to the version that comes with Ubuntu 20.04
|
|
|
|
ver = shell_environment.GetBuildVars().GetValue("LIBUUID_VERSION", "2.34")
|
|
|
|
work_dir = os.path.join(shell_env.get_shell_var("EDK_TOOLS_PATH"), self.GetLoggingFolderRelativeToRoot())
|
|
|
|
pack_name = f"util-linux-{ver}"
|
|
|
|
unzip_dir = os.path.join(work_dir, pack_name)
|
|
|
|
|
|
|
|
if os.path.isfile(os.path.join(work_dir, f"{pack_name}.tar.gz")):
|
|
|
|
os.remove(os.path.join(work_dir, f"{pack_name}.tar.gz"))
|
|
|
|
if os.path.isdir(unzip_dir):
|
|
|
|
shutil.rmtree(unzip_dir)
|
|
|
|
|
|
|
|
# cross compiling, need to rebuild libuuid for the target
|
|
|
|
ret = RunCmd("wget", f"https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v{ver}/{pack_name}.tar.gz", workingdir=work_dir)
|
|
|
|
if ret != 0:
|
|
|
|
raise Exception(f"Failed to download libuuid version {ver} - {ret}")
|
|
|
|
|
|
|
|
ret = RunCmd("tar", f"xvzf {pack_name}.tar.gz", workingdir=work_dir)
|
|
|
|
if ret != 0:
|
|
|
|
raise Exception(f"Failed to untar the downloaded file {ret}")
|
|
|
|
|
|
|
|
# configure the source to use the cross compiler
|
|
|
|
pack_name = f"util-linux-{ver}"
|
|
|
|
if "AARCH64" in self.target_arch:
|
|
|
|
ret = RunCmd("sh", f"./configure --host=aarch64-linux -disable-all-programs --enable-libuuid CC={prefix}gcc", workingdir=unzip_dir)
|
|
|
|
elif "ARM" in self.target_arch:
|
|
|
|
ret = RunCmd("sh", f"./configure --host=arm-linux -disable-all-programs --enable-libuuid CC={prefix}gcc", workingdir=unzip_dir)
|
|
|
|
if ret != 0:
|
|
|
|
raise Exception(f"Failed to configure the util-linux to build with our gcc {ret}")
|
|
|
|
|
|
|
|
ret = RunCmd("make", "", workingdir=unzip_dir)
|
|
|
|
if ret != 0:
|
|
|
|
raise Exception(f"Failed to build the libuuid with our gcc {ret}")
|
|
|
|
|
|
|
|
shell_environment.GetEnvironment().set_shell_var("CROSS_LIB_UUID", unzip_dir)
|
|
|
|
shell_environment.GetEnvironment().set_shell_var("CROSS_LIB_UUID_INC", os.path.join(unzip_dir, "libuuid", "src"))
|
|
|
|
|
|
|
|
ret = RunCmd("make", "clean", workingdir=shell_env.get_shell_var("EDK_TOOLS_PATH"))
|
|
|
|
if ret != 0:
|
|
|
|
raise Exception("Failed to build.")
|
|
|
|
|
2020-04-02 19:04:52 +02:00
|
|
|
cpu_count = self.GetCpuThreads()
|
2023-06-21 17:44:15 +02:00
|
|
|
|
|
|
|
output_stream = edk2_logging.create_output_stream()
|
2020-04-02 19:04:52 +02:00
|
|
|
ret = RunCmd("make", f"-C . -j {cpu_count}", workingdir=shell_env.get_shell_var("EDK_TOOLS_PATH"))
|
2023-06-21 17:44:15 +02:00
|
|
|
edk2_logging.remove_output_stream(output_stream)
|
|
|
|
problems = edk2_logging.scan_compiler_output(output_stream)
|
|
|
|
for level, problem in problems:
|
|
|
|
logging.log(level, problem)
|
2019-10-08 03:37:13 +02:00
|
|
|
if ret != 0:
|
|
|
|
raise Exception("Failed to build.")
|
|
|
|
|
|
|
|
self.OutputDir = os.path.join(
|
|
|
|
shell_env.get_shell_var("EDK_TOOLS_PATH"), "Source", "C", "bin")
|
|
|
|
|
|
|
|
self.WritePathEnvFile(self.OutputDir)
|
|
|
|
return ret
|
|
|
|
|
|
|
|
logging.critical("Tool Chain not supported")
|
|
|
|
return -1
|
|
|
|
|
2020-04-02 19:04:52 +02:00
|
|
|
def GetCpuThreads(self) -> int:
|
|
|
|
''' Function to return number of cpus. If error return 1'''
|
|
|
|
cpus = 1
|
|
|
|
try:
|
|
|
|
cpus = multiprocessing.cpu_count()
|
|
|
|
except:
|
|
|
|
# from the internet there are cases where cpu_count is not implemented.
|
|
|
|
# will handle error by just doing single proc build
|
|
|
|
pass
|
|
|
|
return cpus
|
|
|
|
|
|
|
|
|
2019-10-08 03:37:13 +02:00
|
|
|
|
|
|
|
def main():
|
|
|
|
Edk2ToolsBuild().Invoke()
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|