build_bios.py 36 KB

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