#!/usr/bin/env python3
#
# @ build_bios.py
# Builds BIOS using configuration files and dynamically
# imported functions from board directory
#
# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.
# Copyright (c) 2021, American Megatrends International LLC.
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
"""
This module builds BIOS using configuration files and dynamically
imported functions from board directory
"""
import os
import re
import sys
import glob
import signal
import shutil
import argparse
import traceback
import subprocess
from importlib import import_module
try:
# python 2.7
import ConfigParser as configparser
except ImportError:
# python 3
import configparser
def pre_build(build_config, build_type="DEBUG", silent=False, toolchain=None):
"""Sets the environment variables that shall be used for the build
:param build_config: The build configuration as defined in the JSON
configuration files
:type build_config: Dictionary
:param build_type: The build target, DEBUG, RELEASE, RELEASE_PDB,
TEST_RELEASE
:type build_type: String
:param silent: Enables build in silent mode
:type silent: Boolean
:param toolchain: Specifies the tool chain tag to use for the build
:type toolchain: String
:returns: The updated environment variables
:rtype: Dictionary
"""
# get current environment variables
config = os.environ.copy()
# patch the build config
build_config = patch_config(build_config)
# update the current config with the build config
config.update(build_config)
# make the config and build python 2.7 compatible
config = py_27_fix(config)
# Set WORKSPACE environment.
config["WORKSPACE"] = os.path.abspath(os.path.join("..", "..", "..", ""))
print("Set WORKSPACE as: {}".format(config["WORKSPACE"]))
# Check whether Git has been installed and been added to system path.
try:
subprocess.Popen(["git", "--help"], stdout=subprocess.PIPE)
except OSError:
print("The 'git' command is not recognized.")
print("Please make sure that Git is installed\
and has been added to system path.")
sys.exit(1)
# Create the Conf directory under WORKSPACE
if not os.path.isdir(os.path.join(config["WORKSPACE"], "Conf")):
try:
# create directory
os.makedirs(os.path.join(config["WORKSPACE"], "Conf"))
# copy files to it
config_template_path = os.path.join(config["WORKSPACE"],
config["BASE_TOOLS_PATH"],
"Conf")
config_path = os.path.join(config["WORKSPACE"], "Conf")
shutil.copyfile(config_template_path +
os.sep + "target.template",
config_path + os.sep + "target.txt")
shutil.copyfile(config_template_path +
os.sep + "tools_def.template",
config_path + os.sep + "tools_def.txt")
shutil.copyfile(config_template_path +
os.sep + "build_rule.template",
config_path + os.sep + "build_rule.txt")
except OSError:
print("Error while creating Conf")
sys.exit(1)
# Set other environments.
# Basic Rule:
# Platform override Silicon override Core
# Source override Binary
config["WORKSPACE_PLATFORM"] = os.path.join(config["WORKSPACE"],
config["WORKSPACE_PLATFORM"])
config["WORKSPACE_SILICON"] = os.path.join(config["WORKSPACE"],
config["WORKSPACE_SILICON"])
config["WORKSPACE_FEATURES"] = os.path.join(config["WORKSPACE"],
config["WORKSPACE_FEATURES"])
config["WORKSPACE_DRIVERS"] = os.path.join(config["WORKSPACE"],
config["WORKSPACE_DRIVERS"])
config["WORKSPACE_PLATFORM_BIN"] = \
os.path.join(config["WORKSPACE"], config["WORKSPACE_PLATFORM_BIN"])
config["WORKSPACE_SILICON_BIN"] = \
os.path.join(config["WORKSPACE"], config["WORKSPACE_SILICON_BIN"])
config["WORKSPACE_FSP_BIN"] = os.path.join(config["WORKSPACE"],
config["WORKSPACE_FSP_BIN"])
# add to package path
config["PACKAGES_PATH"] = config["WORKSPACE_PLATFORM"]
config["PACKAGES_PATH"] += os.pathsep + config["WORKSPACE_SILICON"]
config["PACKAGES_PATH"] += os.pathsep + config["WORKSPACE_SILICON_BIN"]
config["PACKAGES_PATH"] += os.pathsep + config["WORKSPACE_FEATURES"]
# add all feature domains in WORKSPACE_FEATURES to package path
for filename in os.listdir(config["WORKSPACE_FEATURES"]):
filepath = os.path.join(config["WORKSPACE_FEATURES"], filename)
# feature domains folder does not contain dec file
if os.path.isdir(filepath) and \
not glob.glob(os.path.join(filepath, "*.dec")):
config["PACKAGES_PATH"] += os.pathsep + filepath
config["PACKAGES_PATH"] += os.pathsep + config["WORKSPACE_DRIVERS"]
config["PACKAGES_PATH"] += os.pathsep + \
os.path.join(config["WORKSPACE"], config["WORKSPACE_FSP_BIN"])
config["PACKAGES_PATH"] += os.pathsep + \
os.path.join(config["WORKSPACE"], config["WORKSPACE_CORE"])
config["PACKAGES_PATH"] += os.pathsep + os.path.join(config["WORKSPACE"])
config["PACKAGES_PATH"] += os.pathsep + config["WORKSPACE_PLATFORM_BIN"]
config["EDK_TOOLS_PATH"] = os.path.join(config["WORKSPACE"],
config["EDK_TOOLS_PATH"])
config["BASE_TOOLS_PATH"] = config["EDK_TOOLS_PATH"]
config["EDK_TOOLS_BIN"] = os.path.join(config["WORKSPACE"],
config["EDK_TOOLS_BIN"])
#
# Board may have different FSP binary between API and Dispatch modes.
# In API mode if FSP_BIN_PKG_FOR_API_MODE is assigned, it should
# override FSP_BIN_PKG.
#
if config.get("API_MODE_FSP_WRAPPER_BUILD", "FALSE") == "TRUE":
if config.get("FSP_BIN_PKG_FOR_API_MODE") is not None:
config['FSP_BIN_PKG'] = config['FSP_BIN_PKG_FOR_API_MODE']
config["PLATFORM_FSP_BIN_PACKAGE"] = \
os.path.join(config['WORKSPACE_FSP_BIN'], config['FSP_BIN_PKG'])
config['PROJECT_DSC'] = os.path.join(config["WORKSPACE_PLATFORM"],
config['PROJECT_DSC'])
config['BOARD_PKG_PCD_DSC'] = os.path.join(config["WORKSPACE_PLATFORM"],
config['BOARD_PKG_PCD_DSC'])
config["CONF_PATH"] = os.path.join(config["WORKSPACE"], "Conf")
# get the python path
if os.environ.get("PYTHON_HOME") is None:
if os.environ.get("PYTHONPATH") is not None:
config["PYTHON_HOME"] = os.environ.get("PYTHONPATH")
else:
config["PYTHON_HOME"] = os.path.dirname(sys.executable)
config["PYTHONPATH"] = config["PYTHON_HOME"]
if config.get("PYTHON_HOME") is None or \
not os.path.exists(config.get("PYTHON_HOME")):
print("PYTHON_HOME environment variable is not found")
sys.exit(1)
# if python is installed, disable the binary base tools.
# python is installed if this code is running :)
if config.get("PYTHON_HOME") is not None:
if config.get("EDK_TOOLS_BIN") is not None:
del config["EDK_TOOLS_BIN"]
# Run edk setup and update config
if os.name == 'nt':
edk2_setup_cmd = [os.path.join(config["EFI_SOURCE"], "edksetup"),
"Rebuild"]
if config.get("EDK_SETUP_OPTION") and \
config["EDK_SETUP_OPTION"] != " ":
edk2_setup_cmd.append(config["EDK_SETUP_OPTION"])
_, _, result, return_code = execute_script(edk2_setup_cmd,
config,
collect_env=True,
shell=True)
if return_code == 0 and result is not None and isinstance(result,
dict):
config.update(result)
# nmake BaseTools source
# and enable BaseTools source build
shell = True
command = ["nmake", "-f", os.path.join(config["BASE_TOOLS_PATH"],
"Makefile")]
if os.name == "posix": # linux
shell = False
command = ["make", "-C", os.path.join(config["BASE_TOOLS_PATH"])]
_, _, result, return_code = execute_script(command, config, shell=shell)
if return_code != 0:
build_failed(config)
#
# build platform silicon tools
#
# save the current workspace
saved_work_directory = config["WORKSPACE"]
# change the workspace to silicon tools directory
config["WORKSPACE"] = os.path.join(config["WORKSPACE_SILICON"], "Tools")
command = ["nmake"]
if os.name == "posix": # linux
command = ["make"]
# add path to generated FitGen binary to
# environment path variable
config["PATH"] += os.pathsep + \
os.path.join(config["BASE_TOOLS_PATH"],
"Source", "C", "bin")
# build the silicon tools
_, _, result, return_code = execute_script(command, config, shell=shell)
if return_code != 0:
build_failed(config)
# restore WORKSPACE environment variable
config["WORKSPACE"] = saved_work_directory
config["SILENT_MODE"] = 'TRUE' if silent else 'FALSE'
print("==============================================")
if os.path.isfile(os.path.join(config['WORKSPACE'], "Prep.log")):
os.remove(os.path.join(config['WORKSPACE'], "Prep.log"))
config["PROJECT"] = os.path.join(config["PLATFORM_BOARD_PACKAGE"],
config["BOARD"])
# Setup Build
# @todo: Need better TOOL_CHAIN_TAG detection
if toolchain is not None:
config["TOOL_CHAIN_TAG"] = toolchain
elif config.get("TOOL_CHAIN_TAG") is None:
if os.name == 'nt':
config["TOOL_CHAIN_TAG"] = "VS2015"
else:
config["TOOL_CHAIN_TAG"] = "GCC5"
# echo Show CL revision
config["PrepRELEASE"] = build_type
if build_type == "DEBUG":
config["TARGET"] = 'DEBUG'
config["TARGET_SHORT"] = 'D'
else:
config["TARGET"] = 'RELEASE'
config["TARGET_SHORT"] = 'R'
# set BUILD_DIR_PATH path
config["BUILD_DIR_PATH"] = os.path.join(config["WORKSPACE"],
'Build',
config["PROJECT"],
"{}_{}".format(
config["TARGET"],
config["TOOL_CHAIN_TAG"]))
# set BUILD_DIR path
config["BUILD_DIR"] = os.path.join('Build',
config["PROJECT"],
"{}_{}".format(
config["TARGET"],
config["TOOL_CHAIN_TAG"]))
config["BUILD_X64"] = os.path.join(config["BUILD_DIR_PATH"], 'X64')
config["BUILD_IA32"] = os.path.join(config["BUILD_DIR_PATH"], 'IA32')
if not os.path.isdir(config["BUILD_DIR_PATH"]):
try:
os.makedirs(config["BUILD_DIR_PATH"])
except OSError:
print("Error while creating Build folder")
sys.exit(1)
# Set FSP_WRAPPER_BUILD
if config['FSP_WRAPPER_BUILD'] == "TRUE":
# Create dummy Fsp_Rebased_S_padded.fd to build the BiosInfo.inf
# if it is wrapper build, due to the SECTION inclusion
open(os.path.join(config["WORKSPACE_FSP_BIN"],
config["FSP_BIN_PKG"],
"Fsp_Rebased_S_padded.fd"), 'w').close()
if not os.path.isdir(config["BUILD_X64"]):
try:
os.mkdir(config["BUILD_X64"])
except OSError:
print("Error while creating {}".format(config["BUILD_X64"]))
sys.exit(1)
# update config file with changes
update_target_file(config)
# Additional pre build scripts for this platform
result = pre_build_ex(config)
if result is not None and isinstance(result, dict):
config.update(result)
# print user settings
print("BIOS_SIZE_OPTION = {}".format(config["BIOS_SIZE_OPTION"]))
print("EFI_SOURCE = {}".format(config["EFI_SOURCE"]))
print("TARGET = {}".format(config["TARGET"]))
print("TARGET_ARCH = {}".format("IA32 X64"))
print("TOOL_CHAIN_TAG = {}".format(config["TOOL_CHAIN_TAG"]))
print("WORKSPACE = {}".format(config["WORKSPACE"]))
print("WORKSPACE_CORE = {}".format(config["WORKSPACE_CORE"]))
print("EXT_BUILD_FLAGS = {}".format(config["EXT_BUILD_FLAGS"]))
return config
def build(config):
"""Builds the BIOS image
:param config: The environment variables to be used
in the build process
:type config: Dictionary
:returns: nothing
"""
if config["FSP_WRAPPER_BUILD"] == "TRUE":
pattern = "Fsp_Rebased.*\\.fd$"
file_dir = os.path.join(config['WORKSPACE_FSP_BIN'],
config['FSP_BIN_PKG'])
for item in os.listdir(file_dir):
if re.search(pattern, item):
os.remove(os.path.join(file_dir, item))
command = [os.path.join(config['PYTHON_HOME'], "python"),
os.path.join(config['WORKSPACE_PLATFORM'],
config['PLATFORM_PACKAGE'],
'Tools', 'Fsp',
'RebaseFspBinBaseAddress.py'),
os.path.join(config['WORKSPACE_PLATFORM'],
config['FLASH_MAP_FDF']),
os.path.join(config['WORKSPACE_FSP_BIN'],
config['FSP_BIN_PKG']),
"Fsp.fd",
"0x0"]
_, _, _, return_code = execute_script(command, config, shell=False)
if return_code != 0:
print("ERROR:RebaseFspBinBaseAddress failed")
sys.exit(return_code)
# create Fsp_Rebased.fd which is Fsp_Rebased_S.fd +
# Fsp_Rebased_M + Fsp_Rebased_T
with open(os.path.join(file_dir, "Fsp_Rebased_S.fd"), 'rb') as fsp_s, \
open(os.path.join(file_dir,
"Fsp_Rebased_M.fd"), 'rb') as fsp_m, \
open(os.path.join(file_dir,
"Fsp_Rebased_T.fd"), 'rb') as fsp_t:
fsp_rebased = fsp_s.read() + fsp_m.read() + fsp_t.read()
with open(os.path.join(file_dir,
"Fsp_Rebased.fd"), 'wb') as new_fsp:
new_fsp.write(fsp_rebased)
if not os.path.isfile(os.path.join(file_dir, "Fsp_Rebased.fd")):
print("!!! ERROR:failed to create fsp!!!")
sys.exit(1)
# Output the build variables the user has selected.
print("==========================================")
print(" User Selected build options:")
print(" SILENT_MODE = ", config.get("SILENT_MODE"))
print(" REBUILD_MODE = ", config.get("REBUILD_MODE"))
print(" BUILD_ROM_ONLY = ", config.get("BUILD_ROM_ONLY"))
print(" BINARY_CACHE_CMD_LINE = ", config.get("HASH"), config.get("BINARY_CACHE_CMD_LINE"))
print("==========================================")
command = ["build", "-n", config["NUMBER_OF_PROCESSORS"]]
if config["REBUILD_MODE"] and config["REBUILD_MODE"] != "":
command.append(config["REBUILD_MODE"])
if config["EXT_BUILD_FLAGS"] and config["EXT_BUILD_FLAGS"] != "":
ext_build_flags = config["EXT_BUILD_FLAGS"].split(" ")
ext_build_flags = [x.strip() for x in ext_build_flags]
ext_build_flags = [x for x in ext_build_flags if x != ""]
command.extend(ext_build_flags)
if config.get('BINARY_CACHE_CMD_LINE'):
command.append(config['HASH'])
command.append(config['BINARY_CACHE_CMD_LINE'])
if config.get("SILENT_MODE", "FALSE") == "TRUE":
command.append("--silent")
command.append("--quiet")
else:
command.append("--log=" + config.get("BUILD_LOG", "Build.log"))
command.append("--report-file=" +
config.get("BUILD_REPORT", "BuildReport.log"))
if config.get("VERBOSE", "FALSE") == "TRUE":
command.append("--verbose")
if config.get("MAX_SOCKET") is not None:
command.append("-D")
command.append("MAX_SOCKET=" + config["MAX_SOCKET"])
if config.get("API_MODE_FSP_WRAPPER_BUILD", "FALSE") == "TRUE":
#Override PCD to enable API mode FSP wrapper.
command.append("--pcd")
command.append("gIntelFsp2WrapperTokenSpaceGuid.PcdFspModeSelection=1")
shell = True
if os.name == "posix":
shell = False
_, _, _, exit_code = execute_script(command, config, shell=shell)
if exit_code != 0:
build_failed(config)
# Additional build scripts for this platform
result = build_ex(config)
if result is not None and isinstance(result, dict):
config.update(result)
return config
def post_build(config):
"""Post build process of BIOS image
:param config: The environment variables to be used in the build process
:type config: Dictionary
:returns: nothing
"""
print("Running post_build to complete the build process.")
board_fd = config["BOARD"].upper()
final_fd = os.path.join(config["BUILD_DIR_PATH"], "FV",
"{}.fd".format(board_fd))
if config["BIOS_INFO_GUID"]:
# Generate the fit table
print("Generating FIT ...")
if os.path.isfile(final_fd):
temp_fd = os.path.join(config["BUILD_DIR_PATH"], "FV",
"{}_.fd".format(board_fd))
shell = True
command = ["FitGen", "-D",
final_fd, temp_fd, "-NA",
"-I", config["BIOS_INFO_GUID"]] #@todo: Need mechanism to add additional options to the FitGen command line
if os.name == "posix": # linux
shell = False
_, _, result, return_code = execute_script(command, config, shell=shell)
if return_code != 0:
print("Error while generating fit")
else:
# copy output to final binary
shutil.copyfile(temp_fd, final_fd)
# remove temp file
os.remove(temp_fd)
else:
print("{} does not exist".format(final_fd))
# remove temp file
# Additional build scripts for this platform
result = post_build_ex(config)
if result is not None and isinstance(result, dict):
config.update(result)
# cleanup
pattern = "Fsp_Rebased.*\\.fd$"
file_dir = os.path.join(config['WORKSPACE_FSP_BIN'],
config['FSP_BIN_PKG'])
for item in os.listdir(file_dir):
if re.search(pattern, item):
os.remove(os.path.join(file_dir, item))
if config.get("DYNAMIC_BUILD_INIT_FILES") is not None:
for item in config["DYNAMIC_BUILD_INIT_FILES"].split(","):
try:
os.remove(item) # remove __init__.py
os.remove(item + "c") # remove __init__.pyc as well
except OSError:
pass
print("Done")
if os.path.isfile(final_fd):
print("Fd file can be found at {}".format(final_fd))
def build_failed(config):
"""Displays results when build fails
:param config: The environment variables used in the build process
:type config: Dictionary
:returns: nothing
"""
print(" The EDKII BIOS Build has failed!")
# clean up
if config.get("DYNAMIC_BUILD_INIT_FILES") is not None:
for item in config["DYNAMIC_BUILD_INIT_FILES"].split(","):
if os.path.isfile(item):
try:
os.remove(item) # remove __init__.py
os.remove(item + "c") # remove __init__.pyc as well
except OSError:
pass
sys.exit(1)
def import_platform_lib(path, function):
"""Imports custom functions for the platforms being built
:param path: the location of the custom build script to be executed
:type path: String
:param path: the function to be executed
:type path: String
:returns: nothing
"""
if path.endswith(".py"):
path = path[:-3]
path = path.replace(os.sep, ".")
module = import_module(path)
lib = getattr(module, function)
return lib
def pre_build_ex(config):
""" An extension of the pre_build process as defined platform
specific pre_build setup script
:param config: The environment variables used in the pre build process
:type config: Dictionary
:returns: config dictionary
:rtype: Dictionary
"""
if config.get("ADDITIONAL_SCRIPTS"):
try:
platform_function =\
import_platform_lib(config["ADDITIONAL_SCRIPTS"],
"pre_build_ex")
functions = {"execute_script": execute_script}
return platform_function(config, functions)
except ImportError as error:
print(config["ADDITIONAL_SCRIPTS"], str(error))
build_failed(config)
return None
def build_ex(config):
""" An extension of the build process as defined platform
specific build setup script
:param config: The environment variables used in the build process
:type config: Dictionary
:returns: config dictionary
:rtype: Dictionary
"""
if config.get("ADDITIONAL_SCRIPTS"):
try:
platform_function =\
import_platform_lib(config["ADDITIONAL_SCRIPTS"],
"build_ex")
functions = {"execute_script": execute_script}
return platform_function(config, functions)
except ImportError as error:
print("error", config["ADDITIONAL_SCRIPTS"], str(error))
build_failed(config)
return None
def post_build_ex(config):
""" An extension of the post build process as defined platform
specific build setup script
:param config: The environment variables used in the post build
process
:type config: Dictionary
:returns: config dictionary
:rtype: Dictionary
"""
if config.get("ADDITIONAL_SCRIPTS"):
try:
platform_function =\
import_platform_lib(config["ADDITIONAL_SCRIPTS"],
"post_build_ex")
functions = {"execute_script": execute_script}
return platform_function(config, functions)
except ImportError as error:
print(config["ADDITIONAL_SCRIPTS"], str(error))
build_failed(config)
return None
def clean_ex(config):
""" An extension of the platform cleaning
:param config: The environment variables used in the clean process
:type config: Dictionary
:returns: config dictionary
:rtype: Dictionary
"""
if config.get("ADDITIONAL_SCRIPTS"):
try:
platform_function =\
import_platform_lib(config["ADDITIONAL_SCRIPTS"],
"clean_ex")
functions = {"execute_script": execute_script}
return platform_function(config, functions)
except ImportError as error:
print(config["ADDITIONAL_SCRIPTS"], str(error))
build_failed(config)
return None
def get_environment_variables(std_out_str, marker):
"""Gets the environment variables from a process
:param std_out_str: The std_out pipe
:type std_out_str: String
:param marker: A begining and end mark of environment
variables printed to std_out
:type marker: String
:returns: The environment variables read from the process' std_out pipe
:rtype: Tuple
"""
start_env_update = False
environment_vars = {}
out_put = ""
for line in std_out_str.split("\n"):
if start_env_update and len(line.split("=")) == 2:
key, value = line.split("=")
environment_vars[key] = value
else:
out_put += "\n" + line.replace(marker, "")
if marker in line:
if start_env_update:
start_env_update = False
else:
start_env_update = True
return (out_put, environment_vars)
def execute_script(command, env_variables, collect_env=False,
enable_std_pipe=False, shell=True):
"""launches a process that executes a script/shell command passed to it
:param command: The command/script with its commandline
arguments to be executed
:type command: List:String
:param env_variables: Environment variables passed to the process
:type env_variables: String
:param collect_env: Enables the collection of environment variables
when process execution is done
:type collect_env: Boolean
:param enable_std_pipe: Enables process out to be piped to
:type enable_std_pipe: String
:returns: a tuple of std_out, stderr , environment variables,
return code
:rtype: Tuple: (std_out, stderr , enVar, return_code)
"""
print("Calling " + " ".join(command))
env_marker = '-----env-----'
env = {}
kwarg = {"env": env_variables,
"universal_newlines": True,
"shell": shell,
"cwd": env_variables["WORKSPACE"]}
if enable_std_pipe or collect_env:
kwarg["stdout"] = subprocess.PIPE
kwarg["stderr"] = subprocess.PIPE
# collect environment variables
if collect_env:
# get the binary that prints environment variables based on os
if os.name == 'nt':
get_var_command = "set"
else:
get_var_command = "env"
# modify the command to print the environment variables
if isinstance(command, list):
command += ["&&", "echo", env_marker, "&&",
get_var_command, "&&", "echo", env_marker]
else:
command += " " + " ".join(["&&", "echo", env_marker,
"&&", get_var_command,
"&&", "echo", env_marker])
# execute the command
execute = subprocess.Popen(command, **kwarg)
std_out, stderr = execute.communicate()
code = execute.returncode
# wait for process to be done
execute.wait()
# if collect environment variables
if collect_env:
# get the new environment variables
std_out, env = get_environment_variables(std_out, env_marker)
return (std_out, stderr, env, code)
def patch_config(config):
""" An extension of the platform cleaning
:param config: The environment variables used in the build process
:type config: Dictionary
:returns: config dictionary
:rtype: Dictionary
"""
new_config = {}
for key in config:
new_config[str(key)] = str(config[key].replace("/", os.sep))
return new_config
def py_27_fix(config):
""" Prepares build for python 2.7 => build
:param config: The environment variables used in the build process
:type config: Dictionary
:returns: config dictionary
:rtype: Dictionary
"""
if not sys.version_info > (3, 0):
path_list = []
# create __init__.py in directories in this path
if config.get("ADDITIONAL_SCRIPTS"):
# get the directory
path_to_directory =\
os.path.dirname(config.get("ADDITIONAL_SCRIPTS"))
path = ""
for directories in path_to_directory.split(os.sep):
path += directories + os.sep
init_file = path + os.sep + "__init__.py"
if not os.path.isfile(init_file):
open(init_file, 'w').close()
path_list.append(init_file)
config["DYNAMIC_BUILD_INIT_FILES"] = ",".join(path_list)
return config
def clean(build_config, board=False):
"""Cleans the build workspace
:param config: The environment variables used in the build process
:type config: Dictionary
:param board: This flag specifies specific board clean
:type board: Bool
:returns: nothing
"""
# patch the config
build_config = patch_config(build_config)
# get current environment variables
config = os.environ.copy()
# update it with the build variables
config.update(build_config)
if config.get('WORKSPACE') is None or not config.get('WORKSPACE'):
config["WORKSPACE"] =\
os.path.abspath(os.path.join("..", "..", "..", ""))
# build cleanall
print("Cleaning directories...")
if board:
platform_pkg = config.get("PLATFORM_BOARD_PACKAGE", None)
if platform_pkg is None or\
not os.path.isdir(os.path.join(config['WORKSPACE'],
"Build", platform_pkg)):
print("Platform package not found")
sys.exit(1)
else:
print("Removing " + os.path.join(config['WORKSPACE'],
"Build", platform_pkg))
shutil.rmtree(os.path.join(config['WORKSPACE'],
"Build", platform_pkg))
else:
if os.path.isdir(os.path.join(config['WORKSPACE'], "Build")):
print("Removing " + os.path.join(config['WORKSPACE'], "Build"))
shutil.rmtree(os.path.join(config['WORKSPACE'], "Build"))
if os.path.isdir(os.path.join(config['WORKSPACE'], "Conf")):
print("Removing " + os.path.join(config['WORKSPACE'], "Conf"))
shutil.rmtree(os.path.join(config['WORKSPACE'], "Conf"))
print("Cleaning files...")
if os.path.isfile(os.path.join(config['WORKSPACE'],
config.get("BUILD_REPORT",
"BuildReport.log"))):
print("Removing ", os.path.join(config['WORKSPACE'],
config.get("BUILD_REPORT",
"BuildReport.log")))
os.remove(os.path.join(config['WORKSPACE'],
config.get("BUILD_REPORT", "BuildReport.log")))
print(" All done...")
sys.exit(0)
def update_target_file(config):
"""Updates Conf's target file that will be used in the build
:param config: The environment variables used in the build process
:type config: Dictionary
:returns: True if update was successful and False if update fails
:rtype: Boolean
"""
contents = None
result = False
with open(os.path.join(config["CONF_PATH"], "target.txt"), 'r') as target:
contents = target.readlines()
options_list = ['ACTIVE_PLATFORM', 'TARGET',
'TARGET_ARCH', 'TOOL_CHAIN_TAG', 'BUILD_RULE_CONF']
modified = []
# remove these options from the config file
for line in contents:
if line.replace(" ", "")[0] != '#' and\
any(opt in line for opt in options_list):
continue
modified.append(line)
# replace with config options provided
string = "{} = {}\n".format("ACTIVE_PLATFORM",
os.path.join(
config['WORKSPACE_PLATFORM'],
config['PLATFORM_BOARD_PACKAGE'],
config['BOARD'],
config['PROJECT_DSC']))
modified.append(string)
string = "{} = {}\n".format("TARGET", config['TARGET'])
modified.append(string)
string = "TARGET_ARCH = IA32 X64\n"
modified.append(string)
string = "{} = {}\n".format("TOOL_CHAIN_TAG", config['TOOL_CHAIN_TAG'])
modified.append(string)
string = "{} = {}\n".format("BUILD_RULE_CONF",
os.path.join("Conf", "build_rule.txt"))
modified.append(string)
if modified is not None:
with open(os.path.join(config["WORKSPACE"],
"Conf", "target.txt"), 'w') as target:
for line in modified:
target.write(line)
result = True
return result
def get_config():
"""Reads the default projects config file
:returns: The config defined in the the Build.cfg file
:rtype: Dictionary
"""
config_file = configparser.RawConfigParser()
config_file.optionxform = str
config_file.read('build.cfg')
config_dictionary = {}
for section in config_file.sections():
dictionary = dict(config_file.items(section))
config_dictionary[section] = dictionary
return config_dictionary
def get_platform_config(platform_name, config_data):
""" Reads the platform specific config file
param platform_name: The name of the platform to be built
:type platform_name: String
param configData: The environment variables to be
used in the build process
:type configData: Dictionary
:returns: The config defined in the the Build.cfg file
:rtype: Dictionary
"""
config = {}
platform_data = config_data.get("PLATFORMS")
path = platform_data.get(platform_name)
config_file = configparser.RawConfigParser()
config_file.optionxform = str
config_file.read(path)
for section in config_file.sections():
config[section] = dict(config_file.items(section))
return config
def get_cmd_config_arguments(arguments):
"""Get commandline config arguments
param arguments: The environment variables to be used in the build process
:type arguments: argparse
:returns: The config dictionary built from the commandline arguments
:rtype: Dictionary
"""
result = {}
if arguments.capsule is True:
result["CAPSULE_BUILD"] = "1"
if arguments.performance is True:
result["PERFORMANCE_BUILD"] = "TRUE"
if arguments.fsp is True:
result["FSP_WRAPPER_BUILD"] = "TRUE"
if arguments.fspapi is True:
result["API_MODE_FSP_WRAPPER_BUILD"] = "TRUE"
if not arguments.UseHashCache:
result['BINARY_CACHE_CMD_LINE'] = ''
elif arguments.BinCacheDest:
result['HASH'] = '--hash'
result['BINARY_CACHE_CMD_LINE'] = '--binary-destination=%s' % arguments.BinCacheDest
elif arguments.BinCacheSource:
result['HASH'] = '--hash'
result['BINARY_CACHE_CMD_LINE'] = '--binary-source=%s' % arguments.BinCacheSource
else:
result['BINARY_CACHE_CMD_LINE'] = ''
return result
def get_cmd_arguments(build_config):
""" Get commandline inputs from user
param config_data: The environment variables to be
used in the build process
:type config_data: Dictionary
:returns: The commandline arguments input by the user
:rtype: argparse object
"""
class PrintPlatforms(argparse.Action):
""" this is an argparse action that lists the available platforms
"""
def __call__(self, parser, namespace, values, option_string=None):
print("Platforms:")
for key in build_config.get("PLATFORMS"):
print(" " + key)
setattr(namespace, self.dest, values)
sys.exit(0)
# get the build commands
parser = argparse.ArgumentParser(description="Build Help")
parser.add_argument('--platform', '-p', dest="platform",
help='the platform to build',
choices=build_config.get("PLATFORMS"),
required=('-l' not in sys.argv and
'--cleanall' not in sys.argv))
parser.add_argument('--toolchain', '-t', dest="toolchain",
help="using the Tool Chain Tagname to build \
the platform,overriding \
target.txt's TOOL_CHAIN_TAG definition")
parser.add_argument("--DEBUG", '-d', help="debug flag",
action='store_const', dest="target",
const="DEBUG", default="DEBUG")
parser.add_argument("--RELEASE", '-r', help="release flag",
action='store_const',
dest="target", const="RELEASE")
parser.add_argument("--TEST_RELEASE", '-tr', help="test Release flag",
action='store_const',
dest="target", const="TEST_RELEASE")
parser.add_argument("--RELEASE_PDB", '-rp', help="release flag",
action='store_const', dest="target",
const="RELEASE_PDB")
parser.add_argument('--list', '-l', action=PrintPlatforms,
help='lists available platforms', nargs=0)
parser.add_argument('--cleanall', dest='clean_all',
help='cleans all', action='store_true')
parser.add_argument('--clean', dest='clean',
help='cleans specific platform', action='store_true')
parser.add_argument("--capsule", help="capsule build enabled",
action='store_true', dest="capsule")
parser.add_argument("--silent", help="silent build enabled",
action='store_true', dest="silent")
parser.add_argument("--performance", help="performance build enabled",
action='store_true', dest="performance")
parser.add_argument("--fsp", help="fsp wrapper build enabled",
action='store_true', dest="fsp")
parser.add_argument("--fspapi", help="API mode fsp wrapper build enabled",
action='store_true', dest="fspapi")
parser.add_argument("--hash", action="store_true", dest="UseHashCache", default=False,
help="Enable hash-based caching during build process.")
parser.add_argument("--binary-destination", help="Generate a cache of binary \
files in the specified directory.",
action='store', dest="BinCacheDest")
parser.add_argument("--binary-source", help="Consume a cache of binary files \
from the specified directory.",
action='store', dest="BinCacheSource")
return parser.parse_args()
def keyboard_interruption(int_signal, int_frame):
""" Catches a keyboard interruption handler
param int_signal: The signal this handler is called with
:type int_signal: Signal
param int_frame: The signal this handler is called with
:type int_frame: frame
:rtype: nothing
"""
print("Signal #: {} Frame: {}".format(int_signal, int_frame))
print("Quiting...")
sys.exit(0)
def main():
""" The main function of this module
:rtype: nothing
"""
# to quit the build
signal.signal(signal.SIGINT, keyboard_interruption)
# get general build configurations
build_config = get_config()
# get commandline parameters
arguments = get_cmd_arguments(build_config)
if arguments.clean_all:
clean(build_config.get("DEFAULT_CONFIG"))
# get platform specific config
platform_config = get_platform_config(arguments.platform, build_config)
# update general build config with platform specific config
config = build_config.get("DEFAULT_CONFIG")
config.update(platform_config.get("CONFIG"))
# if user selected clean
if arguments.clean:
clean(config, board=True)
# Override config with cmd arguments
cmd_config_args = get_cmd_config_arguments(arguments)
config.update(cmd_config_args)
# get pre_build configurations
config = pre_build(config,
build_type=arguments.target,
toolchain=arguments.toolchain,
silent=arguments.silent)
# build selected platform
config = build(config)
# post build
post_build(config)
if __name__ == "__main__":
try:
EXIT_CODE = 0
main()
except Exception as error:
EXIT_CODE = 1
traceback.print_exc()
sys.exit(EXIT_CODE)