#!/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.<BR>
|
# Copyright (c) 2021, American Megatrends International LLC.<BR>
|
# 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)
|