build_bios.py 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  1. #!/usr/bin/env python3
  2. #
  3. # @ build_bios.py
  4. # Builds BIOS using configuration files and dynamically
  5. # imported functions from board directory
  6. #
  7. # Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
  8. # Copyright (c) 2021, American Megatrends International LLC.<BR>
  9. # SPDX-License-Identifier: BSD-2-Clause-Patent
  10. #
  11. """
  12. This module builds BIOS using configuration files and dynamically
  13. imported functions from board directory
  14. """
  15. import os
  16. import re
  17. import sys
  18. import glob
  19. import signal
  20. import shutil
  21. import argparse
  22. import traceback
  23. import subprocess
  24. from importlib import import_module
  25. try:
  26. # python 2.7
  27. import ConfigParser as configparser
  28. except ImportError:
  29. # python 3
  30. import configparser
  31. def pre_build(build_config, build_type="DEBUG", silent=False, toolchain=None):
  32. """Sets the environment variables that shall be used for the build
  33. :param build_config: The build configuration as defined in the JSON
  34. configuration files
  35. :type build_config: Dictionary
  36. :param build_type: The build target, DEBUG, RELEASE, RELEASE_PDB,
  37. TEST_RELEASE
  38. :type build_type: String
  39. :param silent: Enables build in silent mode
  40. :type silent: Boolean
  41. :param toolchain: Specifies the tool chain tag to use for the build
  42. :type toolchain: String
  43. :returns: The updated environment variables
  44. :rtype: Dictionary
  45. """
  46. # get current environment variables
  47. config = os.environ.copy()
  48. # patch the build config
  49. build_config = patch_config(build_config)
  50. # update the current config with the build config
  51. config.update(build_config)
  52. # make the config and build python 2.7 compatible
  53. config = py_27_fix(config)
  54. # Set WORKSPACE environment.
  55. config["WORKSPACE"] = os.path.abspath(os.path.join("..", "..", "..", ""))
  56. print("Set WORKSPACE as: {}".format(config["WORKSPACE"]))
  57. # Check whether Git has been installed and been added to system path.
  58. try:
  59. subprocess.Popen(["git", "--help"], stdout=subprocess.PIPE)
  60. except OSError:
  61. print("The 'git' command is not recognized.")
  62. print("Please make sure that Git is installed\
  63. and has been added to system path.")
  64. sys.exit(1)
  65. # Create the Conf directory under WORKSPACE
  66. if not os.path.isdir(os.path.join(config["WORKSPACE"], "Conf")):
  67. try:
  68. # create directory
  69. os.makedirs(os.path.join(config["WORKSPACE"], "Conf"))
  70. # copy files to it
  71. config_template_path = os.path.join(config["WORKSPACE"],
  72. config["BASE_TOOLS_PATH"],
  73. "Conf")
  74. config_path = os.path.join(config["WORKSPACE"], "Conf")
  75. shutil.copyfile(config_template_path +
  76. os.sep + "target.template",
  77. config_path + os.sep + "target.txt")
  78. shutil.copyfile(config_template_path +
  79. os.sep + "tools_def.template",
  80. config_path + os.sep + "tools_def.txt")
  81. shutil.copyfile(config_template_path +
  82. os.sep + "build_rule.template",
  83. config_path + os.sep + "build_rule.txt")
  84. except OSError:
  85. print("Error while creating Conf")
  86. sys.exit(1)
  87. # Set other environments.
  88. # Basic Rule:
  89. # Platform override Silicon override Core
  90. # Source override Binary
  91. config["WORKSPACE_PLATFORM"] = os.path.join(config["WORKSPACE"],
  92. config["WORKSPACE_PLATFORM"])
  93. config["WORKSPACE_SILICON"] = os.path.join(config["WORKSPACE"],
  94. config["WORKSPACE_SILICON"])
  95. config["WORKSPACE_FEATURES"] = os.path.join(config["WORKSPACE"],
  96. config["WORKSPACE_FEATURES"])
  97. config["WORKSPACE_DRIVERS"] = os.path.join(config["WORKSPACE"],
  98. config["WORKSPACE_DRIVERS"])
  99. config["WORKSPACE_PLATFORM_BIN"] = \
  100. os.path.join(config["WORKSPACE"], config["WORKSPACE_PLATFORM_BIN"])
  101. config["WORKSPACE_SILICON_BIN"] = \
  102. os.path.join(config["WORKSPACE"], config["WORKSPACE_SILICON_BIN"])
  103. config["WORKSPACE_FSP_BIN"] = os.path.join(config["WORKSPACE"],
  104. config["WORKSPACE_FSP_BIN"])
  105. # add to package path
  106. config["PACKAGES_PATH"] = config["WORKSPACE_PLATFORM"]
  107. config["PACKAGES_PATH"] += os.pathsep + config["WORKSPACE_SILICON"]
  108. config["PACKAGES_PATH"] += os.pathsep + config["WORKSPACE_SILICON_BIN"]
  109. config["PACKAGES_PATH"] += os.pathsep + config["WORKSPACE_FEATURES"]
  110. # add all feature domains in WORKSPACE_FEATURES to package path
  111. for filename in os.listdir(config["WORKSPACE_FEATURES"]):
  112. filepath = os.path.join(config["WORKSPACE_FEATURES"], filename)
  113. # feature domains folder does not contain dec file
  114. if os.path.isdir(filepath) and \
  115. not glob.glob(os.path.join(filepath, "*.dec")):
  116. config["PACKAGES_PATH"] += os.pathsep + filepath
  117. config["PACKAGES_PATH"] += os.pathsep + config["WORKSPACE_DRIVERS"]
  118. config["PACKAGES_PATH"] += os.pathsep + \
  119. os.path.join(config["WORKSPACE"], config["WORKSPACE_FSP_BIN"])
  120. config["PACKAGES_PATH"] += os.pathsep + \
  121. os.path.join(config["WORKSPACE"], config["WORKSPACE_CORE"])
  122. config["PACKAGES_PATH"] += os.pathsep + os.path.join(config["WORKSPACE"])
  123. config["PACKAGES_PATH"] += os.pathsep + config["WORKSPACE_PLATFORM_BIN"]
  124. config["EDK_TOOLS_PATH"] = os.path.join(config["WORKSPACE"],
  125. config["EDK_TOOLS_PATH"])
  126. config["BASE_TOOLS_PATH"] = config["EDK_TOOLS_PATH"]
  127. config["EDK_TOOLS_BIN"] = os.path.join(config["WORKSPACE"],
  128. config["EDK_TOOLS_BIN"])
  129. #
  130. # Board may have different FSP binary between API and Dispatch modes.
  131. # In API mode if FSP_BIN_PKG_FOR_API_MODE is assigned, it should
  132. # override FSP_BIN_PKG.
  133. #
  134. if config.get("API_MODE_FSP_WRAPPER_BUILD", "FALSE") == "TRUE":
  135. if config.get("FSP_BIN_PKG_FOR_API_MODE") is not None:
  136. config['FSP_BIN_PKG'] = config['FSP_BIN_PKG_FOR_API_MODE']
  137. config["PLATFORM_FSP_BIN_PACKAGE"] = \
  138. os.path.join(config['WORKSPACE_FSP_BIN'], config['FSP_BIN_PKG'])
  139. config['PROJECT_DSC'] = os.path.join(config["WORKSPACE_PLATFORM"],
  140. config['PROJECT_DSC'])
  141. config['BOARD_PKG_PCD_DSC'] = os.path.join(config["WORKSPACE_PLATFORM"],
  142. config['BOARD_PKG_PCD_DSC'])
  143. config["CONF_PATH"] = os.path.join(config["WORKSPACE"], "Conf")
  144. # get the python path
  145. if os.environ.get("PYTHON_HOME") is None:
  146. if os.environ.get("PYTHONPATH") is not None:
  147. config["PYTHON_HOME"] = os.environ.get("PYTHONPATH")
  148. else:
  149. config["PYTHON_HOME"] = os.path.dirname(sys.executable)
  150. config["PYTHONPATH"] = config["PYTHON_HOME"]
  151. if config.get("PYTHON_HOME") is None or \
  152. not os.path.exists(config.get("PYTHON_HOME")):
  153. print("PYTHON_HOME environment variable is not found")
  154. sys.exit(1)
  155. # if python is installed, disable the binary base tools.
  156. # python is installed if this code is running :)
  157. if config.get("PYTHON_HOME") is not None:
  158. if config.get("EDK_TOOLS_BIN") is not None:
  159. del config["EDK_TOOLS_BIN"]
  160. # Run edk setup and update config
  161. if os.name == 'nt':
  162. edk2_setup_cmd = [os.path.join(config["EFI_SOURCE"], "edksetup"),
  163. "Rebuild"]
  164. if config.get("EDK_SETUP_OPTION") and \
  165. config["EDK_SETUP_OPTION"] != " ":
  166. edk2_setup_cmd.append(config["EDK_SETUP_OPTION"])
  167. _, _, result, return_code = execute_script(edk2_setup_cmd,
  168. config,
  169. collect_env=True,
  170. shell=True)
  171. if return_code == 0 and result is not None and isinstance(result,
  172. dict):
  173. config.update(result)
  174. # nmake BaseTools source
  175. # and enable BaseTools source build
  176. shell = True
  177. command = ["nmake", "-f", os.path.join(config["BASE_TOOLS_PATH"],
  178. "Makefile")]
  179. if os.name == "posix": # linux
  180. shell = False
  181. command = ["make", "-C", os.path.join(config["BASE_TOOLS_PATH"])]
  182. _, _, result, return_code = execute_script(command, config, shell=shell)
  183. if return_code != 0:
  184. build_failed(config)
  185. #
  186. # build platform silicon tools
  187. #
  188. # save the current workspace
  189. saved_work_directory = config["WORKSPACE"]
  190. # change the workspace to silicon tools directory
  191. config["WORKSPACE"] = os.path.join(config["WORKSPACE_SILICON"], "Tools")
  192. command = ["nmake"]
  193. if os.name == "posix": # linux
  194. command = ["make"]
  195. # add path to generated FitGen binary to
  196. # environment path variable
  197. config["PATH"] += os.pathsep + \
  198. os.path.join(config["BASE_TOOLS_PATH"],
  199. "Source", "C", "bin")
  200. # build the silicon tools
  201. _, _, result, return_code = execute_script(command, config, shell=shell)
  202. if return_code != 0:
  203. build_failed(config)
  204. # restore WORKSPACE environment variable
  205. config["WORKSPACE"] = saved_work_directory
  206. config["SILENT_MODE"] = 'TRUE' if silent else 'FALSE'
  207. print("==============================================")
  208. if os.path.isfile(os.path.join(config['WORKSPACE'], "Prep.log")):
  209. os.remove(os.path.join(config['WORKSPACE'], "Prep.log"))
  210. config["PROJECT"] = os.path.join(config["PLATFORM_BOARD_PACKAGE"],
  211. config["BOARD"])
  212. # Setup Build
  213. # @todo: Need better TOOL_CHAIN_TAG detection
  214. if toolchain is not None:
  215. config["TOOL_CHAIN_TAG"] = toolchain
  216. elif config.get("TOOL_CHAIN_TAG") is None:
  217. if os.name == 'nt':
  218. config["TOOL_CHAIN_TAG"] = "VS2015"
  219. else:
  220. config["TOOL_CHAIN_TAG"] = "GCC5"
  221. # echo Show CL revision
  222. config["PrepRELEASE"] = build_type
  223. if build_type == "DEBUG":
  224. config["TARGET"] = 'DEBUG'
  225. config["TARGET_SHORT"] = 'D'
  226. else:
  227. config["TARGET"] = 'RELEASE'
  228. config["TARGET_SHORT"] = 'R'
  229. # set BUILD_DIR_PATH path
  230. config["BUILD_DIR_PATH"] = os.path.join(config["WORKSPACE"],
  231. 'Build',
  232. config["PROJECT"],
  233. "{}_{}".format(
  234. config["TARGET"],
  235. config["TOOL_CHAIN_TAG"]))
  236. # set BUILD_DIR path
  237. config["BUILD_DIR"] = os.path.join('Build',
  238. config["PROJECT"],
  239. "{}_{}".format(
  240. config["TARGET"],
  241. config["TOOL_CHAIN_TAG"]))
  242. config["BUILD_X64"] = os.path.join(config["BUILD_DIR_PATH"], 'X64')
  243. config["BUILD_IA32"] = os.path.join(config["BUILD_DIR_PATH"], 'IA32')
  244. if not os.path.isdir(config["BUILD_DIR_PATH"]):
  245. try:
  246. os.makedirs(config["BUILD_DIR_PATH"])
  247. except OSError:
  248. print("Error while creating Build folder")
  249. sys.exit(1)
  250. # Set FSP_WRAPPER_BUILD
  251. if config['FSP_WRAPPER_BUILD'] == "TRUE":
  252. # Create dummy Fsp_Rebased_S_padded.fd to build the BiosInfo.inf
  253. # if it is wrapper build, due to the SECTION inclusion
  254. open(os.path.join(config["WORKSPACE_FSP_BIN"],
  255. config["FSP_BIN_PKG"],
  256. "Fsp_Rebased_S_padded.fd"), 'w').close()
  257. if not os.path.isdir(config["BUILD_X64"]):
  258. try:
  259. os.mkdir(config["BUILD_X64"])
  260. except OSError:
  261. print("Error while creating {}".format(config["BUILD_X64"]))
  262. sys.exit(1)
  263. # update config file with changes
  264. update_target_file(config)
  265. # Additional pre build scripts for this platform
  266. result = pre_build_ex(config)
  267. if result is not None and isinstance(result, dict):
  268. config.update(result)
  269. # print user settings
  270. print("BIOS_SIZE_OPTION = {}".format(config["BIOS_SIZE_OPTION"]))
  271. print("EFI_SOURCE = {}".format(config["EFI_SOURCE"]))
  272. print("TARGET = {}".format(config["TARGET"]))
  273. print("TARGET_ARCH = {}".format("IA32 X64"))
  274. print("TOOL_CHAIN_TAG = {}".format(config["TOOL_CHAIN_TAG"]))
  275. print("WORKSPACE = {}".format(config["WORKSPACE"]))
  276. print("WORKSPACE_CORE = {}".format(config["WORKSPACE_CORE"]))
  277. print("EXT_BUILD_FLAGS = {}".format(config["EXT_BUILD_FLAGS"]))
  278. return config
  279. def build(config):
  280. """Builds the BIOS image
  281. :param config: The environment variables to be used
  282. in the build process
  283. :type config: Dictionary
  284. :returns: nothing
  285. """
  286. if config["FSP_WRAPPER_BUILD"] == "TRUE":
  287. pattern = "Fsp_Rebased.*\\.fd$"
  288. file_dir = os.path.join(config['WORKSPACE_FSP_BIN'],
  289. config['FSP_BIN_PKG'])
  290. for item in os.listdir(file_dir):
  291. if re.search(pattern, item):
  292. os.remove(os.path.join(file_dir, item))
  293. command = [os.path.join(config['PYTHON_HOME'], "python"),
  294. os.path.join(config['WORKSPACE_PLATFORM'],
  295. config['PLATFORM_PACKAGE'],
  296. 'Tools', 'Fsp',
  297. 'RebaseFspBinBaseAddress.py'),
  298. os.path.join(config['WORKSPACE_PLATFORM'],
  299. config['FLASH_MAP_FDF']),
  300. os.path.join(config['WORKSPACE_FSP_BIN'],
  301. config['FSP_BIN_PKG']),
  302. "Fsp.fd",
  303. "0x0"]
  304. _, _, _, return_code = execute_script(command, config, shell=False)
  305. if return_code != 0:
  306. print("ERROR:RebaseFspBinBaseAddress failed")
  307. sys.exit(return_code)
  308. # create Fsp_Rebased.fd which is Fsp_Rebased_S.fd +
  309. # Fsp_Rebased_M + Fsp_Rebased_T
  310. with open(os.path.join(file_dir, "Fsp_Rebased_S.fd"), 'rb') as fsp_s, \
  311. open(os.path.join(file_dir,
  312. "Fsp_Rebased_M.fd"), 'rb') as fsp_m, \
  313. open(os.path.join(file_dir,
  314. "Fsp_Rebased_T.fd"), 'rb') as fsp_t:
  315. fsp_rebased = fsp_s.read() + fsp_m.read() + fsp_t.read()
  316. with open(os.path.join(file_dir,
  317. "Fsp_Rebased.fd"), 'wb') as new_fsp:
  318. new_fsp.write(fsp_rebased)
  319. if not os.path.isfile(os.path.join(file_dir, "Fsp_Rebased.fd")):
  320. print("!!! ERROR:failed to create fsp!!!")
  321. sys.exit(1)
  322. # Output the build variables the user has selected.
  323. print("==========================================")
  324. print(" User Selected build options:")
  325. print(" SILENT_MODE = ", config.get("SILENT_MODE"))
  326. print(" REBUILD_MODE = ", config.get("REBUILD_MODE"))
  327. print(" BUILD_ROM_ONLY = ", config.get("BUILD_ROM_ONLY"))
  328. print(" BINARY_CACHE_CMD_LINE = ", config.get("HASH"), config.get("BINARY_CACHE_CMD_LINE"))
  329. print("==========================================")
  330. command = ["build", "-n", config["NUMBER_OF_PROCESSORS"]]
  331. if config["REBUILD_MODE"] and config["REBUILD_MODE"] != "":
  332. command.append(config["REBUILD_MODE"])
  333. if config["EXT_BUILD_FLAGS"] and config["EXT_BUILD_FLAGS"] != "":
  334. ext_build_flags = config["EXT_BUILD_FLAGS"].split(" ")
  335. ext_build_flags = [x.strip() for x in ext_build_flags]
  336. ext_build_flags = [x for x in ext_build_flags if x != ""]
  337. command.extend(ext_build_flags)
  338. if config.get('BINARY_CACHE_CMD_LINE'):
  339. command.append(config['HASH'])
  340. command.append(config['BINARY_CACHE_CMD_LINE'])
  341. if config.get("SILENT_MODE", "FALSE") == "TRUE":
  342. command.append("--silent")
  343. command.append("--quiet")
  344. else:
  345. command.append("--log=" + config.get("BUILD_LOG", "Build.log"))
  346. command.append("--report-file=" +
  347. config.get("BUILD_REPORT", "BuildReport.log"))
  348. if config.get("VERBOSE", "FALSE") == "TRUE":
  349. command.append("--verbose")
  350. if config.get("MAX_SOCKET") is not None:
  351. command.append("-D")
  352. command.append("MAX_SOCKET=" + config["MAX_SOCKET"])
  353. if config.get("API_MODE_FSP_WRAPPER_BUILD", "FALSE") == "TRUE":
  354. #Override PCD to enable API mode FSP wrapper.
  355. command.append("--pcd")
  356. command.append("gIntelFsp2WrapperTokenSpaceGuid.PcdFspModeSelection=1")
  357. shell = True
  358. if os.name == "posix":
  359. shell = False
  360. _, _, _, exit_code = execute_script(command, config, shell=shell)
  361. if exit_code != 0:
  362. build_failed(config)
  363. # Additional build scripts for this platform
  364. result = build_ex(config)
  365. if result is not None and isinstance(result, dict):
  366. config.update(result)
  367. return config
  368. def post_build(config):
  369. """Post build process of BIOS image
  370. :param config: The environment variables to be used in the build process
  371. :type config: Dictionary
  372. :returns: nothing
  373. """
  374. print("Running post_build to complete the build process.")
  375. board_fd = config["BOARD"].upper()
  376. final_fd = os.path.join(config["BUILD_DIR_PATH"], "FV",
  377. "{}.fd".format(board_fd))
  378. if config["BIOS_INFO_GUID"]:
  379. # Generate the fit table
  380. print("Generating FIT ...")
  381. if os.path.isfile(final_fd):
  382. temp_fd = os.path.join(config["BUILD_DIR_PATH"], "FV",
  383. "{}_.fd".format(board_fd))
  384. shell = True
  385. command = ["FitGen", "-D",
  386. final_fd, temp_fd, "-NA",
  387. "-I", config["BIOS_INFO_GUID"]] #@todo: Need mechanism to add additional options to the FitGen command line
  388. if os.name == "posix": # linux
  389. shell = False
  390. _, _, result, return_code = execute_script(command, config, shell=shell)
  391. if return_code != 0:
  392. print("Error while generating fit")
  393. else:
  394. # copy output to final binary
  395. shutil.copyfile(temp_fd, final_fd)
  396. # remove temp file
  397. os.remove(temp_fd)
  398. else:
  399. print("{} does not exist".format(final_fd))
  400. # remove temp file
  401. # Additional build scripts for this platform
  402. result = post_build_ex(config)
  403. if result is not None and isinstance(result, dict):
  404. config.update(result)
  405. # cleanup
  406. pattern = "Fsp_Rebased.*\\.fd$"
  407. file_dir = os.path.join(config['WORKSPACE_FSP_BIN'],
  408. config['FSP_BIN_PKG'])
  409. for item in os.listdir(file_dir):
  410. if re.search(pattern, item):
  411. os.remove(os.path.join(file_dir, item))
  412. if config.get("DYNAMIC_BUILD_INIT_FILES") is not None:
  413. for item in config["DYNAMIC_BUILD_INIT_FILES"].split(","):
  414. try:
  415. os.remove(item) # remove __init__.py
  416. os.remove(item + "c") # remove __init__.pyc as well
  417. except OSError:
  418. pass
  419. print("Done")
  420. if os.path.isfile(final_fd):
  421. print("Fd file can be found at {}".format(final_fd))
  422. def build_failed(config):
  423. """Displays results when build fails
  424. :param config: The environment variables used in the build process
  425. :type config: Dictionary
  426. :returns: nothing
  427. """
  428. print(" The EDKII BIOS Build has failed!")
  429. # clean up
  430. if config.get("DYNAMIC_BUILD_INIT_FILES") is not None:
  431. for item in config["DYNAMIC_BUILD_INIT_FILES"].split(","):
  432. if os.path.isfile(item):
  433. try:
  434. os.remove(item) # remove __init__.py
  435. os.remove(item + "c") # remove __init__.pyc as well
  436. except OSError:
  437. pass
  438. sys.exit(1)
  439. def import_platform_lib(path, function):
  440. """Imports custom functions for the platforms being built
  441. :param path: the location of the custom build script to be executed
  442. :type path: String
  443. :param path: the function to be executed
  444. :type path: String
  445. :returns: nothing
  446. """
  447. if path.endswith(".py"):
  448. path = path[:-3]
  449. path = path.replace(os.sep, ".")
  450. module = import_module(path)
  451. lib = getattr(module, function)
  452. return lib
  453. def pre_build_ex(config):
  454. """ An extension of the pre_build process as defined platform
  455. specific pre_build setup script
  456. :param config: The environment variables used in the pre build process
  457. :type config: Dictionary
  458. :returns: config dictionary
  459. :rtype: Dictionary
  460. """
  461. if config.get("ADDITIONAL_SCRIPTS"):
  462. try:
  463. platform_function =\
  464. import_platform_lib(config["ADDITIONAL_SCRIPTS"],
  465. "pre_build_ex")
  466. functions = {"execute_script": execute_script}
  467. return platform_function(config, functions)
  468. except ImportError as error:
  469. print(config["ADDITIONAL_SCRIPTS"], str(error))
  470. build_failed(config)
  471. return None
  472. def build_ex(config):
  473. """ An extension of the build process as defined platform
  474. specific build setup script
  475. :param config: The environment variables used in the build process
  476. :type config: Dictionary
  477. :returns: config dictionary
  478. :rtype: Dictionary
  479. """
  480. if config.get("ADDITIONAL_SCRIPTS"):
  481. try:
  482. platform_function =\
  483. import_platform_lib(config["ADDITIONAL_SCRIPTS"],
  484. "build_ex")
  485. functions = {"execute_script": execute_script}
  486. return platform_function(config, functions)
  487. except ImportError as error:
  488. print("error", config["ADDITIONAL_SCRIPTS"], str(error))
  489. build_failed(config)
  490. return None
  491. def post_build_ex(config):
  492. """ An extension of the post build process as defined platform
  493. specific build setup script
  494. :param config: The environment variables used in the post build
  495. process
  496. :type config: Dictionary
  497. :returns: config dictionary
  498. :rtype: Dictionary
  499. """
  500. if config.get("ADDITIONAL_SCRIPTS"):
  501. try:
  502. platform_function =\
  503. import_platform_lib(config["ADDITIONAL_SCRIPTS"],
  504. "post_build_ex")
  505. functions = {"execute_script": execute_script}
  506. return platform_function(config, functions)
  507. except ImportError as error:
  508. print(config["ADDITIONAL_SCRIPTS"], str(error))
  509. build_failed(config)
  510. return None
  511. def clean_ex(config):
  512. """ An extension of the platform cleaning
  513. :param config: The environment variables used in the clean process
  514. :type config: Dictionary
  515. :returns: config dictionary
  516. :rtype: Dictionary
  517. """
  518. if config.get("ADDITIONAL_SCRIPTS"):
  519. try:
  520. platform_function =\
  521. import_platform_lib(config["ADDITIONAL_SCRIPTS"],
  522. "clean_ex")
  523. functions = {"execute_script": execute_script}
  524. return platform_function(config, functions)
  525. except ImportError as error:
  526. print(config["ADDITIONAL_SCRIPTS"], str(error))
  527. build_failed(config)
  528. return None
  529. def get_environment_variables(std_out_str, marker):
  530. """Gets the environment variables from a process
  531. :param std_out_str: The std_out pipe
  532. :type std_out_str: String
  533. :param marker: A begining and end mark of environment
  534. variables printed to std_out
  535. :type marker: String
  536. :returns: The environment variables read from the process' std_out pipe
  537. :rtype: Tuple
  538. """
  539. start_env_update = False
  540. environment_vars = {}
  541. out_put = ""
  542. for line in std_out_str.split("\n"):
  543. if start_env_update and len(line.split("=")) == 2:
  544. key, value = line.split("=")
  545. environment_vars[key] = value
  546. else:
  547. out_put += "\n" + line.replace(marker, "")
  548. if marker in line:
  549. if start_env_update:
  550. start_env_update = False
  551. else:
  552. start_env_update = True
  553. return (out_put, environment_vars)
  554. def execute_script(command, env_variables, collect_env=False,
  555. enable_std_pipe=False, shell=True):
  556. """launches a process that executes a script/shell command passed to it
  557. :param command: The command/script with its commandline
  558. arguments to be executed
  559. :type command: List:String
  560. :param env_variables: Environment variables passed to the process
  561. :type env_variables: String
  562. :param collect_env: Enables the collection of environment variables
  563. when process execution is done
  564. :type collect_env: Boolean
  565. :param enable_std_pipe: Enables process out to be piped to
  566. :type enable_std_pipe: String
  567. :returns: a tuple of std_out, stderr , environment variables,
  568. return code
  569. :rtype: Tuple: (std_out, stderr , enVar, return_code)
  570. """
  571. print("Calling " + " ".join(command))
  572. env_marker = '-----env-----'
  573. env = {}
  574. kwarg = {"env": env_variables,
  575. "universal_newlines": True,
  576. "shell": shell,
  577. "cwd": env_variables["WORKSPACE"]}
  578. if enable_std_pipe or collect_env:
  579. kwarg["stdout"] = subprocess.PIPE
  580. kwarg["stderr"] = subprocess.PIPE
  581. # collect environment variables
  582. if collect_env:
  583. # get the binary that prints environment variables based on os
  584. if os.name == 'nt':
  585. get_var_command = "set"
  586. else:
  587. get_var_command = "env"
  588. # modify the command to print the environment variables
  589. if isinstance(command, list):
  590. command += ["&&", "echo", env_marker, "&&",
  591. get_var_command, "&&", "echo", env_marker]
  592. else:
  593. command += " " + " ".join(["&&", "echo", env_marker,
  594. "&&", get_var_command,
  595. "&&", "echo", env_marker])
  596. # execute the command
  597. execute = subprocess.Popen(command, **kwarg)
  598. std_out, stderr = execute.communicate()
  599. code = execute.returncode
  600. # wait for process to be done
  601. execute.wait()
  602. # if collect environment variables
  603. if collect_env:
  604. # get the new environment variables
  605. std_out, env = get_environment_variables(std_out, env_marker)
  606. return (std_out, stderr, env, code)
  607. def patch_config(config):
  608. """ An extension of the platform cleaning
  609. :param config: The environment variables used in the build process
  610. :type config: Dictionary
  611. :returns: config dictionary
  612. :rtype: Dictionary
  613. """
  614. new_config = {}
  615. for key in config:
  616. new_config[str(key)] = str(config[key].replace("/", os.sep))
  617. return new_config
  618. def py_27_fix(config):
  619. """ Prepares build for python 2.7 => build
  620. :param config: The environment variables used in the build process
  621. :type config: Dictionary
  622. :returns: config dictionary
  623. :rtype: Dictionary
  624. """
  625. if not sys.version_info > (3, 0):
  626. path_list = []
  627. # create __init__.py in directories in this path
  628. if config.get("ADDITIONAL_SCRIPTS"):
  629. # get the directory
  630. path_to_directory =\
  631. os.path.dirname(config.get("ADDITIONAL_SCRIPTS"))
  632. path = ""
  633. for directories in path_to_directory.split(os.sep):
  634. path += directories + os.sep
  635. init_file = path + os.sep + "__init__.py"
  636. if not os.path.isfile(init_file):
  637. open(init_file, 'w').close()
  638. path_list.append(init_file)
  639. config["DYNAMIC_BUILD_INIT_FILES"] = ",".join(path_list)
  640. return config
  641. def clean(build_config, board=False):
  642. """Cleans the build workspace
  643. :param config: The environment variables used in the build process
  644. :type config: Dictionary
  645. :param board: This flag specifies specific board clean
  646. :type board: Bool
  647. :returns: nothing
  648. """
  649. # patch the config
  650. build_config = patch_config(build_config)
  651. # get current environment variables
  652. config = os.environ.copy()
  653. # update it with the build variables
  654. config.update(build_config)
  655. if config.get('WORKSPACE') is None or not config.get('WORKSPACE'):
  656. config["WORKSPACE"] =\
  657. os.path.abspath(os.path.join("..", "..", "..", ""))
  658. # build cleanall
  659. print("Cleaning directories...")
  660. if board:
  661. platform_pkg = config.get("PLATFORM_BOARD_PACKAGE", None)
  662. if platform_pkg is None or\
  663. not os.path.isdir(os.path.join(config['WORKSPACE'],
  664. "Build", platform_pkg)):
  665. print("Platform package not found")
  666. sys.exit(1)
  667. else:
  668. print("Removing " + os.path.join(config['WORKSPACE'],
  669. "Build", platform_pkg))
  670. shutil.rmtree(os.path.join(config['WORKSPACE'],
  671. "Build", platform_pkg))
  672. else:
  673. if os.path.isdir(os.path.join(config['WORKSPACE'], "Build")):
  674. print("Removing " + os.path.join(config['WORKSPACE'], "Build"))
  675. shutil.rmtree(os.path.join(config['WORKSPACE'], "Build"))
  676. if os.path.isdir(os.path.join(config['WORKSPACE'], "Conf")):
  677. print("Removing " + os.path.join(config['WORKSPACE'], "Conf"))
  678. shutil.rmtree(os.path.join(config['WORKSPACE'], "Conf"))
  679. print("Cleaning files...")
  680. if os.path.isfile(os.path.join(config['WORKSPACE'],
  681. config.get("BUILD_REPORT",
  682. "BuildReport.log"))):
  683. print("Removing ", os.path.join(config['WORKSPACE'],
  684. config.get("BUILD_REPORT",
  685. "BuildReport.log")))
  686. os.remove(os.path.join(config['WORKSPACE'],
  687. config.get("BUILD_REPORT", "BuildReport.log")))
  688. print(" All done...")
  689. sys.exit(0)
  690. def update_target_file(config):
  691. """Updates Conf's target file that will be used in the build
  692. :param config: The environment variables used in the build process
  693. :type config: Dictionary
  694. :returns: True if update was successful and False if update fails
  695. :rtype: Boolean
  696. """
  697. contents = None
  698. result = False
  699. with open(os.path.join(config["CONF_PATH"], "target.txt"), 'r') as target:
  700. contents = target.readlines()
  701. options_list = ['ACTIVE_PLATFORM', 'TARGET',
  702. 'TARGET_ARCH', 'TOOL_CHAIN_TAG', 'BUILD_RULE_CONF']
  703. modified = []
  704. # remove these options from the config file
  705. for line in contents:
  706. if line.replace(" ", "")[0] != '#' and\
  707. any(opt in line for opt in options_list):
  708. continue
  709. modified.append(line)
  710. # replace with config options provided
  711. string = "{} = {}\n".format("ACTIVE_PLATFORM",
  712. os.path.join(
  713. config['WORKSPACE_PLATFORM'],
  714. config['PLATFORM_BOARD_PACKAGE'],
  715. config['BOARD'],
  716. config['PROJECT_DSC']))
  717. modified.append(string)
  718. string = "{} = {}\n".format("TARGET", config['TARGET'])
  719. modified.append(string)
  720. string = "TARGET_ARCH = IA32 X64\n"
  721. modified.append(string)
  722. string = "{} = {}\n".format("TOOL_CHAIN_TAG", config['TOOL_CHAIN_TAG'])
  723. modified.append(string)
  724. string = "{} = {}\n".format("BUILD_RULE_CONF",
  725. os.path.join("Conf", "build_rule.txt"))
  726. modified.append(string)
  727. if modified is not None:
  728. with open(os.path.join(config["WORKSPACE"],
  729. "Conf", "target.txt"), 'w') as target:
  730. for line in modified:
  731. target.write(line)
  732. result = True
  733. return result
  734. def get_config():
  735. """Reads the default projects config file
  736. :returns: The config defined in the the Build.cfg file
  737. :rtype: Dictionary
  738. """
  739. config_file = configparser.RawConfigParser()
  740. config_file.optionxform = str
  741. config_file.read('build.cfg')
  742. config_dictionary = {}
  743. for section in config_file.sections():
  744. dictionary = dict(config_file.items(section))
  745. config_dictionary[section] = dictionary
  746. return config_dictionary
  747. def get_platform_config(platform_name, config_data):
  748. """ Reads the platform specific config file
  749. param platform_name: The name of the platform to be built
  750. :type platform_name: String
  751. param configData: The environment variables to be
  752. used in the build process
  753. :type configData: Dictionary
  754. :returns: The config defined in the the Build.cfg file
  755. :rtype: Dictionary
  756. """
  757. config = {}
  758. platform_data = config_data.get("PLATFORMS")
  759. path = platform_data.get(platform_name)
  760. config_file = configparser.RawConfigParser()
  761. config_file.optionxform = str
  762. config_file.read(path)
  763. for section in config_file.sections():
  764. config[section] = dict(config_file.items(section))
  765. return config
  766. def get_cmd_config_arguments(arguments):
  767. """Get commandline config arguments
  768. param arguments: The environment variables to be used in the build process
  769. :type arguments: argparse
  770. :returns: The config dictionary built from the commandline arguments
  771. :rtype: Dictionary
  772. """
  773. result = {}
  774. if arguments.capsule is True:
  775. result["CAPSULE_BUILD"] = "1"
  776. if arguments.performance is True:
  777. result["PERFORMANCE_BUILD"] = "TRUE"
  778. if arguments.fsp is True:
  779. result["FSP_WRAPPER_BUILD"] = "TRUE"
  780. if arguments.fspapi is True:
  781. result["API_MODE_FSP_WRAPPER_BUILD"] = "TRUE"
  782. if not arguments.UseHashCache:
  783. result['BINARY_CACHE_CMD_LINE'] = ''
  784. elif arguments.BinCacheDest:
  785. result['HASH'] = '--hash'
  786. result['BINARY_CACHE_CMD_LINE'] = '--binary-destination=%s' % arguments.BinCacheDest
  787. elif arguments.BinCacheSource:
  788. result['HASH'] = '--hash'
  789. result['BINARY_CACHE_CMD_LINE'] = '--binary-source=%s' % arguments.BinCacheSource
  790. else:
  791. result['BINARY_CACHE_CMD_LINE'] = ''
  792. return result
  793. def get_cmd_arguments(build_config):
  794. """ Get commandline inputs from user
  795. param config_data: The environment variables to be
  796. used in the build process
  797. :type config_data: Dictionary
  798. :returns: The commandline arguments input by the user
  799. :rtype: argparse object
  800. """
  801. class PrintPlatforms(argparse.Action):
  802. """ this is an argparse action that lists the available platforms
  803. """
  804. def __call__(self, parser, namespace, values, option_string=None):
  805. print("Platforms:")
  806. for key in build_config.get("PLATFORMS"):
  807. print(" " + key)
  808. setattr(namespace, self.dest, values)
  809. sys.exit(0)
  810. # get the build commands
  811. parser = argparse.ArgumentParser(description="Build Help")
  812. parser.add_argument('--platform', '-p', dest="platform",
  813. help='the platform to build',
  814. choices=build_config.get("PLATFORMS"),
  815. required=('-l' not in sys.argv and
  816. '--cleanall' not in sys.argv))
  817. parser.add_argument('--toolchain', '-t', dest="toolchain",
  818. help="using the Tool Chain Tagname to build \
  819. the platform,overriding \
  820. target.txt's TOOL_CHAIN_TAG definition")
  821. parser.add_argument("--DEBUG", '-d', help="debug flag",
  822. action='store_const', dest="target",
  823. const="DEBUG", default="DEBUG")
  824. parser.add_argument("--RELEASE", '-r', help="release flag",
  825. action='store_const',
  826. dest="target", const="RELEASE")
  827. parser.add_argument("--TEST_RELEASE", '-tr', help="test Release flag",
  828. action='store_const',
  829. dest="target", const="TEST_RELEASE")
  830. parser.add_argument("--RELEASE_PDB", '-rp', help="release flag",
  831. action='store_const', dest="target",
  832. const="RELEASE_PDB")
  833. parser.add_argument('--list', '-l', action=PrintPlatforms,
  834. help='lists available platforms', nargs=0)
  835. parser.add_argument('--cleanall', dest='clean_all',
  836. help='cleans all', action='store_true')
  837. parser.add_argument('--clean', dest='clean',
  838. help='cleans specific platform', action='store_true')
  839. parser.add_argument("--capsule", help="capsule build enabled",
  840. action='store_true', dest="capsule")
  841. parser.add_argument("--silent", help="silent build enabled",
  842. action='store_true', dest="silent")
  843. parser.add_argument("--performance", help="performance build enabled",
  844. action='store_true', dest="performance")
  845. parser.add_argument("--fsp", help="fsp wrapper build enabled",
  846. action='store_true', dest="fsp")
  847. parser.add_argument("--fspapi", help="API mode fsp wrapper build enabled",
  848. action='store_true', dest="fspapi")
  849. parser.add_argument("--hash", action="store_true", dest="UseHashCache", default=False,
  850. help="Enable hash-based caching during build process.")
  851. parser.add_argument("--binary-destination", help="Generate a cache of binary \
  852. files in the specified directory.",
  853. action='store', dest="BinCacheDest")
  854. parser.add_argument("--binary-source", help="Consume a cache of binary files \
  855. from the specified directory.",
  856. action='store', dest="BinCacheSource")
  857. return parser.parse_args()
  858. def keyboard_interruption(int_signal, int_frame):
  859. """ Catches a keyboard interruption handler
  860. param int_signal: The signal this handler is called with
  861. :type int_signal: Signal
  862. param int_frame: The signal this handler is called with
  863. :type int_frame: frame
  864. :rtype: nothing
  865. """
  866. print("Signal #: {} Frame: {}".format(int_signal, int_frame))
  867. print("Quiting...")
  868. sys.exit(0)
  869. def main():
  870. """ The main function of this module
  871. :rtype: nothing
  872. """
  873. # to quit the build
  874. signal.signal(signal.SIGINT, keyboard_interruption)
  875. # get general build configurations
  876. build_config = get_config()
  877. # get commandline parameters
  878. arguments = get_cmd_arguments(build_config)
  879. if arguments.clean_all:
  880. clean(build_config.get("DEFAULT_CONFIG"))
  881. # get platform specific config
  882. platform_config = get_platform_config(arguments.platform, build_config)
  883. # update general build config with platform specific config
  884. config = build_config.get("DEFAULT_CONFIG")
  885. config.update(platform_config.get("CONFIG"))
  886. # if user selected clean
  887. if arguments.clean:
  888. clean(config, board=True)
  889. # Override config with cmd arguments
  890. cmd_config_args = get_cmd_config_arguments(arguments)
  891. config.update(cmd_config_args)
  892. # get pre_build configurations
  893. config = pre_build(config,
  894. build_type=arguments.target,
  895. toolchain=arguments.toolchain,
  896. silent=arguments.silent)
  897. # build selected platform
  898. config = build(config)
  899. # post build
  900. post_build(config)
  901. if __name__ == "__main__":
  902. try:
  903. EXIT_CODE = 0
  904. main()
  905. except Exception as error:
  906. EXIT_CODE = 1
  907. traceback.print_exc()
  908. sys.exit(EXIT_CODE)