# @file Edk2ToolsBuild.py # Invocable class that builds the basetool c files. # # Supports VS2017, VS2019, and GCC5 ## # Copyright (c) Microsoft Corporation # # SPDX-License-Identifier: BSD-2-Clause-Patent ## import os import sys import logging import argparse import multiprocessing from edk2toolext import edk2_logging from edk2toolext.environment import self_describing_environment from edk2toolext.base_abstract_invocable import BaseAbstractInvocable from edk2toollib.utility_functions import RunCmd from edk2toollib.windows.locate_tools import QueryVcVariables class Edk2ToolsBuild(BaseAbstractInvocable): def ParseCommandLineOptions(self): ''' parse arguments ''' ParserObj = argparse.ArgumentParser() ParserObj.add_argument("-t", "--tool_chain_tag", dest="tct", default="VS2017", help="Set the toolchain used to compile the build tools") args = ParserObj.parse_args() self.tool_chain_tag = args.tct 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 ''' # for now don't use scopes return ('global',) 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"): # # 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( interesting_keys, 'x86', 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.insert_path(vc_vars[key]) else: shell_env.set_shell_var(key, vc_vars[key]) self.OutputDir = os.path.join( shell_env.get_shell_var("EDK_TOOLS_PATH"), "Bin", "Win32") # compiled tools need to be added to path because antlr is referenced shell_env.insert_path(self.OutputDir) # Actually build the tools. ret = RunCmd('nmake.exe', None, workingdir=shell_env.get_shell_var("EDK_TOOLS_PATH")) if ret != 0: raise Exception("Failed to build.") self.WritePathEnvFile(self.OutputDir) return ret elif self.tool_chain_tag.lower().startswith("gcc"): cpu_count = self.GetCpuThreads() ret = RunCmd("make", f"-C . -j {cpu_count}", workingdir=shell_env.get_shell_var("EDK_TOOLS_PATH")) 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 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 def main(): Edk2ToolsBuild().Invoke() if __name__ == "__main__": main()