Edk2ToolsBuild.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. # @file Edk2ToolsBuild.py
  2. # Invocable class that builds the basetool c files.
  3. #
  4. # Supports VS2017, VS2019, and GCC5
  5. ##
  6. # Copyright (c) Microsoft Corporation
  7. #
  8. # SPDX-License-Identifier: BSD-2-Clause-Patent
  9. ##
  10. import os
  11. import sys
  12. import logging
  13. import argparse
  14. import multiprocessing
  15. from edk2toolext import edk2_logging
  16. from edk2toolext.environment import self_describing_environment
  17. from edk2toolext.base_abstract_invocable import BaseAbstractInvocable
  18. from edk2toollib.utility_functions import RunCmd
  19. from edk2toollib.windows.locate_tools import QueryVcVariables
  20. class Edk2ToolsBuild(BaseAbstractInvocable):
  21. def ParseCommandLineOptions(self):
  22. ''' parse arguments '''
  23. ParserObj = argparse.ArgumentParser()
  24. ParserObj.add_argument("-t", "--tool_chain_tag", dest="tct", default="VS2017",
  25. help="Set the toolchain used to compile the build tools")
  26. args = ParserObj.parse_args()
  27. self.tool_chain_tag = args.tct
  28. def GetWorkspaceRoot(self):
  29. ''' Return the workspace root for initializing the SDE '''
  30. # this is the bastools dir...not the traditional EDK2 workspace root
  31. return os.path.dirname(os.path.abspath(__file__))
  32. def GetActiveScopes(self):
  33. ''' return tuple containing scopes that should be active for this process '''
  34. # for now don't use scopes
  35. return ('global',)
  36. def GetLoggingLevel(self, loggerType):
  37. ''' Get the logging level for a given type (return Logging.Level)
  38. base == lowest logging level supported
  39. con == Screen logging
  40. txt == plain text file logging
  41. md == markdown file logging
  42. '''
  43. if(loggerType == "con"):
  44. return logging.ERROR
  45. else:
  46. return logging.DEBUG
  47. def GetLoggingFolderRelativeToRoot(self):
  48. ''' Return a path to folder for log files '''
  49. return "BaseToolsBuild"
  50. def GetVerifyCheckRequired(self):
  51. ''' Will call self_describing_environment.VerifyEnvironment if this returns True '''
  52. return True
  53. def GetLoggingFileName(self, loggerType):
  54. ''' Get the logging file name for the type.
  55. Return None if the logger shouldn't be created
  56. base == lowest logging level supported
  57. con == Screen logging
  58. txt == plain text file logging
  59. md == markdown file logging
  60. '''
  61. return "BASETOOLS_BUILD"
  62. def WritePathEnvFile(self, OutputDir):
  63. ''' Write a PyTool path env file for future PyTool based edk2 builds'''
  64. content = '''##
  65. # Set shell variable EDK_TOOLS_BIN to this folder
  66. #
  67. # Autogenerated by Edk2ToolsBuild.py
  68. #
  69. # Copyright (c), Microsoft Corporation
  70. # SPDX-License-Identifier: BSD-2-Clause-Patent
  71. ##
  72. {
  73. "id": "You-Built-BaseTools",
  74. "scope": "edk2-build",
  75. "flags": ["set_shell_var", "set_path"],
  76. "var_name": "EDK_TOOLS_BIN"
  77. }
  78. '''
  79. with open(os.path.join(OutputDir, "basetoolsbin_path_env.yaml"), "w") as f:
  80. f.write(content)
  81. def Go(self):
  82. logging.info("Running Python version: " + str(sys.version_info))
  83. (build_env, shell_env) = self_describing_environment.BootstrapEnvironment(
  84. self.GetWorkspaceRoot(), self.GetActiveScopes())
  85. # # Bind our current execution environment into the shell vars.
  86. ph = os.path.dirname(sys.executable)
  87. if " " in ph:
  88. ph = '"' + ph + '"'
  89. shell_env.set_shell_var("PYTHON_HOME", ph)
  90. # PYTHON_COMMAND is required to be set for using edk2 python builds.
  91. pc = sys.executable
  92. if " " in pc:
  93. pc = '"' + pc + '"'
  94. shell_env.set_shell_var("PYTHON_COMMAND", pc)
  95. if self.tool_chain_tag.lower().startswith("vs"):
  96. # # Update environment with required VC vars.
  97. interesting_keys = ["ExtensionSdkDir", "INCLUDE", "LIB"]
  98. interesting_keys.extend(
  99. ["LIBPATH", "Path", "UniversalCRTSdkDir", "UCRTVersion", "WindowsLibPath", "WindowsSdkBinPath"])
  100. interesting_keys.extend(
  101. ["WindowsSdkDir", "WindowsSdkVerBinPath", "WindowsSDKVersion", "VCToolsInstallDir"])
  102. vc_vars = QueryVcVariables(
  103. interesting_keys, 'x86', vs_version=self.tool_chain_tag.lower())
  104. for key in vc_vars.keys():
  105. logging.debug(f"Var - {key} = {vc_vars[key]}")
  106. if key.lower() == 'path':
  107. shell_env.set_path(vc_vars[key])
  108. else:
  109. shell_env.set_shell_var(key, vc_vars[key])
  110. self.OutputDir = os.path.join(
  111. shell_env.get_shell_var("EDK_TOOLS_PATH"), "Bin", "Win32")
  112. # compiled tools need to be added to path because antlr is referenced
  113. shell_env.insert_path(self.OutputDir)
  114. # Actually build the tools.
  115. ret = RunCmd('nmake.exe', None,
  116. workingdir=shell_env.get_shell_var("EDK_TOOLS_PATH"))
  117. if ret != 0:
  118. raise Exception("Failed to build.")
  119. self.WritePathEnvFile(self.OutputDir)
  120. return ret
  121. elif self.tool_chain_tag.lower().startswith("gcc"):
  122. cpu_count = self.GetCpuThreads()
  123. ret = RunCmd("make", f"-C . -j {cpu_count}", workingdir=shell_env.get_shell_var("EDK_TOOLS_PATH"))
  124. if ret != 0:
  125. raise Exception("Failed to build.")
  126. self.OutputDir = os.path.join(
  127. shell_env.get_shell_var("EDK_TOOLS_PATH"), "Source", "C", "bin")
  128. self.WritePathEnvFile(self.OutputDir)
  129. return ret
  130. logging.critical("Tool Chain not supported")
  131. return -1
  132. def GetCpuThreads(self) -> int:
  133. ''' Function to return number of cpus. If error return 1'''
  134. cpus = 1
  135. try:
  136. cpus = multiprocessing.cpu_count()
  137. except:
  138. # from the internet there are cases where cpu_count is not implemented.
  139. # will handle error by just doing single proc build
  140. pass
  141. return cpus
  142. def main():
  143. Edk2ToolsBuild().Invoke()
  144. if __name__ == "__main__":
  145. main()