mirror of
https://github.com/ThomasKing2014/android-firmware-qti-sdm660
synced 2025-11-04 06:43:47 +08:00
1597 lines
58 KiB
Python
Executable File
1597 lines
58 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
#============================================================================
|
|
#
|
|
# @file buildit.py
|
|
#
|
|
# GENERAL DESCRIPTION
|
|
# Unified script for building any/all targets available
|
|
#
|
|
# Copyright 2015-2016 by Qualcomm Technologies, Incorporated.
|
|
# All Rights Reserved.
|
|
# QUALCOMM Proprietary/GTDR
|
|
#
|
|
#----------------------------------------------------------------------------
|
|
#
|
|
# EDIT HISTORY FOR MODULE
|
|
#
|
|
# This section contains comments describing changes made to the module.
|
|
# Notice that changes are listed in reverse chronological order.
|
|
#
|
|
# when who what, where, why
|
|
# -------- --- -----------------------------------------------------------
|
|
# 11/29/16 aus Added support for 32 bit LLVM compilation
|
|
# 10/18/16 ai Print warning if directory name contains s/S due to EDK2 bug
|
|
# 09/12/16 vk Add warning in parsing as error
|
|
# 08/03/16 yps Changed perform merges output folder name to CLANG38
|
|
# 07/06/16 yps update LLVM384 for XBLLoader and Core
|
|
# 06/22/16 bh Fix target and single dsc related building
|
|
# 06/03/16 lg Added options to build with QcomBinPkg folder
|
|
# 05/04/16 et Changed options, updated alias call, general cleanup
|
|
# 05/02/16 et Replaced extra_buildargs with compiler_flags and build_flags
|
|
# 04/26/16 et Fix missing copy to unsigned bin
|
|
# 04/19/16 et Update for new sectools drop
|
|
# 04/06/16 et Moved build logs, .fv no elf support, build.cfg curr dir logic
|
|
# 03/24/16 et Added dsc comiler tool support, --config arg case insensitivity
|
|
# 03/15/16 et Added config file argument support, workspace check for dirty builds
|
|
# 02/29/16 et Fix issue with stripping JtagProgrammer
|
|
# 02/23/16 et Support for case-insensitive release,variant and some error handling
|
|
# 02/11/16 et Removed buildproducts_builds output, formatted some output
|
|
# 02/09/16 et Added single_dsc option, fixed calls with clean
|
|
# 02/05/16 et Fixed search for flavors without giving variant
|
|
# 01/26/16 et Added support for building packages without a variant
|
|
# 01/22/16 et Fixed fd merging
|
|
# 01/21/16 et Removed uefiplat call, changed fd filename
|
|
# 01/19/16 et Added check to add extra buildargs, updated LLVM version
|
|
# 01/12/16 et Added --extra_buildargs, cleaned up build products output
|
|
# 01/12/16 et Added buildproducts feature, better help menu
|
|
# 01/08/16 et Added check for python version requirement
|
|
# 01/06/16 et Updated target searching logic, added fdf and dsc parsing, merge logic
|
|
# 10/28/15 et Updated image version call, version argument, merge issue
|
|
# 10/16/15 et Fixed env vars, builds tools if missing, handles sets of images, windows support
|
|
# 08/26/15 et Initial release
|
|
|
|
import shutil
|
|
import stat
|
|
import subprocess
|
|
import glob
|
|
import re
|
|
from optparse import OptionParser
|
|
from collections import defaultdict
|
|
import platform
|
|
import os
|
|
import sys
|
|
import fnmatch
|
|
|
|
|
|
DEVDEBUG = False
|
|
GENERAL_ERROR_CODE = 1
|
|
ENVIRONMENT_SETUP_ERROR_CODE = 2
|
|
EDK_TOOL_ERROR_CODE = 3
|
|
FAILED_TO_BUILD_FLAVOR_ERROR_CODE = 4
|
|
BAD_RELEASE_TYPE_ERROR_CODE = 5
|
|
|
|
MINIMUM_PYTHON_VERSION = 34014192
|
|
DEFAULT_COMPILER_ARCH = "AARCH64"
|
|
|
|
|
|
WIN_AARCH64_TOOL = "C:\\Apps\\LLVM\\3.8.4\\bin"
|
|
WIN_AARCH64_TOOL_PATH = "C:\\Apps\\Linaro\\gcc-linaro-aarch64-none-elf-4.9-2014.07"
|
|
WIN_ARM_TOOL = "C:\\Apps\\LLVM\\3.8.4\\bin"
|
|
WIN_ARM_TOOL_PATH = "C:\\Apps\\gcc-linaro-arm-linux-gnueabihf-4.9-2014.05_win32"
|
|
CLANG38LINUX_BIN = "/pkg/qct/software/llvm/release/arm/3.8.4/bin/"
|
|
CLANG38WIN_BIN = "C:\\Apps\\LLVM\\3.8.4\\bin\\"
|
|
DEFAULT_ARM_LICENSE_FILE = "7117@license-wan-arm1"
|
|
DEFAULT_RELEASE_MODES = "DEBUG,RELEASE"
|
|
BUILDIT_BUILDINFO_TAG = "UserExtensions.Buildit.BuildInfo"
|
|
QCOM_DIR = "QcomPkg"
|
|
BOOT_IMAGES_DIR = "boot_images"
|
|
COPY_IMAGES_AS_IS = ["jtagprogrammer"]
|
|
BUILD_CFG_FILE = "build.cfg"
|
|
|
|
# Default regexes
|
|
DEFAULT_TARGET_REGEX = "^([a-zA-Z]{3,4})(\d([\d|x|X]{2,3}))Pkg$"
|
|
DEFAULT_SUB_TARGET_REGEX = '^\d{3,4}$'
|
|
DEFAULT_VARIANT_REGEX = '^[A-Z][A-Z]$'
|
|
|
|
INF_HEADER = "#/** @file FILENAME\n" \
|
|
"# \n" \
|
|
"# Contains Qualcomm pre-built EFIs.\n" \
|
|
"# \n" \
|
|
"# Copyright (c) 2015,2016 Qualcomm Technologies, Inc. All rights reserved.\n" \
|
|
"#\n" \
|
|
"#**/\n\n"
|
|
|
|
class build_flavor:
|
|
def __init__(self, target, sub_target="", variant="", single_dsc=""):
|
|
self.target = target
|
|
self.sub_target = sub_target
|
|
self.variant = variant
|
|
self.single_dsc = single_dsc
|
|
if sub_target:
|
|
self.dsc_list = generate_dsc_list(os.path.join(os.environ["WORKSPACE"], QCOM_DIR, self.target, self.sub_target),
|
|
self.variant,
|
|
single_dsc)
|
|
else:
|
|
self.dsc_list = generate_dsc_list(os.path.join(os.environ["WORKSPACE"], QCOM_DIR, self.target),
|
|
self.variant,
|
|
single_dsc)
|
|
|
|
def to_string(self):
|
|
desc = "Target: " + self.target
|
|
desc += " Subtarget: " + self.sub_target if self.sub_target else " Subtarget: ----"
|
|
desc += " Varaint: " + self.variant if self.variant else " Variant: --"
|
|
desc += " Single: " + self.single_dsc if self.single_dsc else " Single: --"
|
|
return desc
|
|
|
|
# merge_book - Acts as a wrapper around a dictionary of merge_entry's
|
|
class merge_book:
|
|
def __init__(self):
|
|
self.merges = dict()
|
|
def add_merge_item(self, merge_name, dll_path, sign_type, xbl_sec=""):
|
|
# create merge_entry if it doesn't exist
|
|
if merge_name not in self.merges:
|
|
new_merge = merge_entry(merge_name, sign_type, xbl_sec)
|
|
self.merges[merge_name] = new_merge
|
|
# add the dll to merge_entry
|
|
self.merges[merge_name].add_merge_item(dll_path, xbl_sec)
|
|
|
|
# Same as above, but with a base to merge
|
|
def add_merge_base(self, merge_name, base_dll_path, sign_type, xbl_sec=""):
|
|
if merge_name not in self.merges:
|
|
new_merge = merge_entry(merge_name, sign_type, xbl_sec)
|
|
self.merges[merge_name] = new_merge
|
|
self.merges[merge_name].set_merge_base_dll(base_dll_path, xbl_sec)
|
|
|
|
|
|
class merge_entry:
|
|
def __init__(self, output_name, sign_type, xbl_sec=""):
|
|
self.output_name = output_name
|
|
self.sign_type = sign_type
|
|
self.xbl_sec = xbl_sec
|
|
self.dll_list = []
|
|
self.merge_base_dll = ""
|
|
|
|
if output_name == "":
|
|
print "Error: # MERGE value is invalid"
|
|
sys.exit(GENERAL_ERROR_CODE)
|
|
|
|
def add_merge_item(self, dll_path, xbl_sec=""):
|
|
self.dll_list.append(dll_path)
|
|
if xbl_sec:
|
|
self.xbl_sec = xbl_sec
|
|
|
|
def set_merge_base_dll(self, base_dll_path, xbl_sec=""):
|
|
if self.merge_base_dll:
|
|
print "Error: Multiple MERGE_BASE tags found for " + self.output_name
|
|
sys.exit(GENERAL_ERROR_CODE)
|
|
self.merge_base_dll = base_dll_path
|
|
if xbl_sec:
|
|
self.xbl_sec = xbl_sec
|
|
|
|
def modify_merge_entry_xbl_sec(self, xbl_sec):
|
|
self.xbl_sec = xbl_sec
|
|
|
|
def modify_merge_entry_dll_list(self, dll_list):
|
|
self.dll_list = dll_list
|
|
|
|
def print_merge_entry(self):
|
|
print "[buildit.py] [MERGE DETAILS] output name: {0}".format(self.output_name)
|
|
print "[buildit.py] [MERGE DETAILS] sign_type: {0}".format(self.sign_type)
|
|
print "[buildit.py] [MERGE DETAILS] xbl sec: {0}".format(self.xbl_sec)
|
|
print "[buildit.py] [MERGE DETAILS] dll list: {0}".format(self.dll_list)
|
|
print "[buildit.py] [MERGE DETAILS] merge base dll: {0}".format(self.merge_base_dll)
|
|
|
|
def main():
|
|
|
|
if sys.hexversion < MINIMUM_PYTHON_VERSION:
|
|
print "\n[buildit.py] Error: Incorrect version of Python detected: " + sys.version
|
|
print "[buildit.py] Requires use of Python 2.7.3 or later."
|
|
sys.exit(GENERAL_ERROR_CODE)
|
|
|
|
if re.search("\.[sS]", os.getcwd()):
|
|
print "\n[buildit.py] Error: Directory name containing \".[sS]\" detected: " + os.getcwd()
|
|
print "[buildit.py] Please rename directory or use a different vce workspace name"
|
|
print "[buildit.py] that does not start with an 's' or 'S'."
|
|
sys.exit(GENERAL_ERROR_CODE)
|
|
|
|
desc = "python buildit.py [options]\n"\
|
|
"\nExample invocations:\n"\
|
|
" python buildit.py -t Msm8998Pkg\n"\
|
|
" python buildit.py -t Msm8998Pkg --variant LA\n"\
|
|
" python buildit.py -t Msm8998Pkg --variant LA,WP -r DEBUG --version 123\n"
|
|
parser = OptionParser(usage=desc)
|
|
|
|
parser.add_option('-r', "--release",
|
|
action="store", default=DEFAULT_RELEASE_MODES,
|
|
help="Release mode for building, default is 'DEBUG,RELEASE'."\
|
|
" Both modes will be built.")
|
|
|
|
parser.add_option('-t', '--target', default="",
|
|
help="Target(s) that will be built, separated by comma. " + \
|
|
"Example: -t Msm8998Pkg. " + \
|
|
"If not specified, all targets will be built",)
|
|
|
|
parser.add_option('-s', '--sub_target', default="",
|
|
help="Sub target(s) that will be built, separated by comma. " + \
|
|
"If not specified, all sub targets will be built per target")
|
|
|
|
parser.add_option('-v', '--variant', default="",
|
|
help="Variant(s) that will be built, separated by comma. " + \
|
|
"Example: -v la. " + \
|
|
"If not specified, all variants will be built per target,sub_target")
|
|
|
|
parser.add_option('-q', '--query',
|
|
action = "store_true", default=False,
|
|
help="Displays target, sub_target, and variant for all available flavors.")
|
|
|
|
parser.add_option('-V', "--version",
|
|
action="store", default="0",
|
|
help="Specify build version. Default is 0. Example: -v 123.")
|
|
|
|
parser.add_option('-a', "--alias",
|
|
action="store", default="",
|
|
help="Specify argument alias from config file QcomPkg/build.cfg. " + \
|
|
"Example: -a 98ladbg")
|
|
|
|
parser.add_option('-b', '--build_flags',
|
|
action="store", default="",
|
|
help="Flags to be passed to the build invocation. " + \
|
|
"Specify multiple arguments separated by a comma or enclosed in double quotes. " + \
|
|
'Example: -b -v,-n,1')
|
|
|
|
parser.add_option('-c', '--compiler_flags',
|
|
action="store", default="",
|
|
help="Extra flags to be passed to the build compiler directly. " + \
|
|
"Specify multiple arguments enclosed in double quotes. " + \
|
|
'Example: "-c -DPRE_SIL -DANOTHER_FLAG"')
|
|
|
|
parser.add_option('-d', '--single_dsc',
|
|
action="store", default="",
|
|
help="Specify a single dsc to be built." + \
|
|
"Note: Merging may fail if a full build is not done prior to using this option.")
|
|
|
|
parser.add_option('--create_intermediate_core',
|
|
action="store", default="",
|
|
help="Saves EFI's generated in this build into a new folder, QcomBinPkg/<variant>/XBLCore.")
|
|
|
|
parser.add_option('--use_intermediate_core',
|
|
action="store", default="",
|
|
help="Speficy a path to the QcomBinPkg folder used to build using pre-built Core EFI's." +
|
|
"Note: This will fail if a full build with flag --create_intermediate_core is not done prior to using this option.")
|
|
|
|
parser.add_option('--create_intermediate_loader',
|
|
action="store", default="",
|
|
help="Saves XBLLoader binaries generated in this build into a new folder, QcomBinPkg/Common/XBLLoader.")
|
|
|
|
parser.add_option('--use_intermediate_loader',
|
|
action="store", default="",
|
|
help="Speficy a path to the QcomBinPkg folder used to build using pre-built Loader dll's." +
|
|
"Note: This will fail if a full build with flag --create_intermediate_loader is not done prior to using this option.")
|
|
|
|
(options,args) = parser.parse_args()
|
|
|
|
# Handle env setup for Windows or Linux
|
|
try:
|
|
setup_environment()
|
|
except Exception as error:
|
|
print error
|
|
sys.exit(ENVIRONMENT_SETUP_ERROR_CODE)
|
|
|
|
|
|
if options.query == True:
|
|
query_all_flavors()
|
|
|
|
if options.alias:
|
|
try:
|
|
options = parse_alias_options(options)
|
|
except Exception as error:
|
|
print error
|
|
sys.exit(GENERAL_ERROR_CODE)
|
|
print options
|
|
|
|
# make sure release_mode is valid
|
|
options.release = options.release.upper()
|
|
for mode in options.release.split(','):
|
|
if mode not in DEFAULT_RELEASE_MODES:
|
|
print 'Error: Release mode "' + mode + '" is invalid. Please see --help for more information'
|
|
sys.exit(BAD_RELEASE_TYPE_ERROR_CODE)
|
|
|
|
|
|
# Creates BuildVersion.h if build version is provided
|
|
create_build_version(options.version)
|
|
|
|
# Check that tools exist, otherwise make them
|
|
try:
|
|
make_edk_tools()
|
|
except Exception as error:
|
|
print error
|
|
sys.exit(EDK_TOOL_ERROR_CODE)
|
|
|
|
# Delete old build products file
|
|
build_products_file = os.path.join(os.environ["WORKSPACE"],
|
|
"..", "BuildProducts.txt")
|
|
if os.path.isfile(build_products_file):
|
|
os.remove(build_products_file)
|
|
|
|
# Collect flavors for given target/sub_target/variant
|
|
options.variant = options.variant.upper()
|
|
all_flavors = []
|
|
for target in options.target.split(","):
|
|
# Find all flavors with variants
|
|
for sub_target in options.sub_target.split(","):
|
|
for variant in options.variant.split(","):
|
|
if variant:
|
|
all_flavors = all_flavors + find_all_build_flavors(target, sub_target, variant, options.single_dsc)
|
|
else:
|
|
all_flavors = all_flavors + find_all_build_flavors(target, sub_target, single_dsc=options.single_dsc)
|
|
|
|
# Also search for flavors without variant, based on target only
|
|
all_flavors = all_flavors + find_all_build_flavors(target=target, variant="", single_dsc="")
|
|
|
|
# Build each flavor in specified mode (DEBUG and/or RELEASE)
|
|
try:
|
|
for flavor in all_flavors:
|
|
for release_mode in options.release.split(','):
|
|
build_single_flavor(flavor, release_mode, options.compiler_flags, options.build_flags,
|
|
options.use_intermediate_core, options.use_intermediate_loader)
|
|
|
|
# If --create_intermediate_core was used, copy EFI files to QcomBinPkg/<variant>/XBLCore folder and create new INF's for those EFI's
|
|
if options.create_intermediate_core and release_mode == "RELEASE":
|
|
copy_intermediate_files(os.path.join(options.create_intermediate_core, options.variant, "XBLCore"),
|
|
flavor, release_mode, "create_intermediate_core")
|
|
|
|
# If --create_intermediate_loader was used, copy dll's/mbn's to QcomBinPkg/Common/XBLLoader folder
|
|
if options.create_intermediate_loader and release_mode == "RELEASE":
|
|
copy_intermediate_files(os.path.join(options.create_intermediate_loader, "Common", "XBLLoader"),
|
|
flavor, release_mode, "create_intermediate_loader")
|
|
if("Sdm660Pkg" == flavor.target):
|
|
print "[buildit.py] Generating XBLLoader Memory Usage as XBLL_Memoy_Usage.txt... ",
|
|
Mapit_script_root = os.path.join(os.environ["WORKSPACE"], "QcomPkg", "Tools")
|
|
Mapit_script = os.path.join(Mapit_script_root, 'Mapit.py')
|
|
XBL_loader_map_path = os.path.join(os.environ["WORKSPACE"],
|
|
"Build",
|
|
os.environ["TARGETMSM"] + "_Loader",
|
|
release_mode + "_" + arch_to_compiler("AARCH64"),
|
|
"AARCH64","QcomPkg","XBLLoader","XBLLoader","OUTPUT")
|
|
map_file = os.path.join(XBL_loader_map_path ,"XBLLoader.map")
|
|
op_file = os.path.join(XBL_loader_map_path ,"XBLL_Memoy_Usage.txt")
|
|
if os.path.exists(Mapit_script) is True:
|
|
print "Done [Memory Usage - Section wise Summary]"
|
|
cmds = "python" + ' ' + Mapit_script + " -t " + os.path.join(Mapit_script_root,"SDM660 ") + map_file + ' > ' + op_file
|
|
subprocess.call(cmds,shell=True)
|
|
|
|
except Exception as error:
|
|
print error
|
|
sys.exit(FAILED_TO_BUILD_FLAVOR_ERROR_CODE)
|
|
|
|
display_build_summary(all_flavors, options.release)
|
|
|
|
print "\n[buildit.py] Now exiting...\n"
|
|
return
|
|
|
|
##############################################################################
|
|
# parse_alias_options
|
|
##############################################################################
|
|
|
|
def parse_alias_options(options):
|
|
|
|
# Check for cfg file in current dir
|
|
cfg_file = os.path.join(os.getcwd(),BUILD_CFG_FILE)
|
|
if not os.path.isfile(cfg_file):
|
|
# Otherwise check in QcomPkg dir
|
|
cfg_file = os.path.join(os.environ["WORKSPACE"],QCOM_DIR,BUILD_CFG_FILE)
|
|
if not os.path.isfile(cfg_file):
|
|
raise NameError("ERROR: buildit::parse_alias_options: " + \
|
|
"could not find config file " + cfg_file)
|
|
|
|
print "[buildit.py] Using config file: " + cfg_file.split(BOOT_IMAGES_DIR + os.sep)[1]
|
|
|
|
with open (cfg_file, "r") as cf:
|
|
|
|
# skip to desired preset
|
|
found = False
|
|
line = cf.readline()
|
|
while line:
|
|
if line.lower().find('[' + options.alias.lower() + ']') == -1:
|
|
line = cf.readline()
|
|
else:
|
|
found = True
|
|
break
|
|
|
|
if not found:
|
|
raise NameError("ERROR: buildit::parse_alias_options: " + \
|
|
"Could not find preset " + options.alias)
|
|
|
|
print "[buildit.py] Using preset from config file: " + options.alias.upper()
|
|
# Update each argument if present
|
|
for line in cf.readlines():
|
|
# break if another preset is reached
|
|
if line.find("[") != -1:
|
|
break
|
|
|
|
# skip comment lines
|
|
if not line.strip() or line.strip()[0] == "#":
|
|
continue
|
|
|
|
# This regex grabs the arg and value pair from each line
|
|
# " (arg) = (val) "
|
|
search = re.search("\s*(\S+)\s*=\s*(.+)", line)
|
|
if search:
|
|
arg = search.group(1)
|
|
val = search.group(2)
|
|
|
|
# options.arg = val
|
|
setattr(options, arg, val)
|
|
|
|
return options
|
|
|
|
##############################################################################
|
|
# display_build_summary
|
|
##############################################################################
|
|
def display_build_summary(all_flavors, release_modes):
|
|
if all_flavors:
|
|
print "\n[buildit.py] Successfully built following flavors:\n" \
|
|
"###################################################"
|
|
for flavor in all_flavors:
|
|
for release_mode in release_modes.split(','):
|
|
print flavor.to_string() + " release: " + release_mode
|
|
else:
|
|
print "\n[buildit.py] Nothing built\n"
|
|
|
|
##############################################################################
|
|
# build_single_flavor
|
|
##############################################################################
|
|
def build_single_flavor(flavor, release_mode, compiler_flags, build_flags, use_intermediate_core, use_intermediate_loader):
|
|
print "\n********************************************************************************"
|
|
print "[buildit.py] Now building following flavor..."
|
|
print "[buildit.py] " + flavor.to_string(), " Release: " + release_mode
|
|
print "********************************************************************************\n"
|
|
|
|
os.environ["TARGETPKG"] = flavor.target
|
|
os.environ["TARGETMSM"] = flavor.target.split("Pkg")[0]
|
|
|
|
matches = re.search(DEFAULT_TARGET_REGEX, flavor.target)
|
|
if matches:
|
|
os.environ["TARGETID"] = matches.group(2)
|
|
else:
|
|
# Handles app packages (e.g. QcomToolsPkg)
|
|
os.environ["TARGETID"] = os.environ["TARGETMSM"]
|
|
|
|
os.environ['TARGETROOT'] = os.path.join(QCOM_DIR, os.environ["TARGETPKG"])
|
|
|
|
image_version_setup(os.environ["TARGETMSM"], flavor.variant)
|
|
|
|
#
|
|
# Compile all dsc's for this flavor
|
|
#
|
|
|
|
for item in flavor.dsc_list:
|
|
|
|
if platform.system() == "Windows":
|
|
if item[1] == "ARM":
|
|
os.environ["TOOLS_PATH"] = WIN_ARM_TOOL
|
|
os.environ["TOOLS_PATH_GCC"] = WIN_ARM_TOOL_PATH
|
|
elif item[1] == "AARCH64":
|
|
os.environ["TOOLS_PATH"] = WIN_AARCH64_TOOL
|
|
os.environ["TOOLS_PATH_GCC"] = WIN_AARCH64_TOOL_PATH
|
|
if "CLANG38_BIN" not in os.environ:
|
|
os.environ["CLANG38_BIN"] = CLANG38WIN_BIN
|
|
if "CLANG38_ARM_PREFIX" not in os.environ:
|
|
clang38_path = re.search(r"(.*)(bin\b)",os.getenv("CLANG38_BIN"))
|
|
if clang38_path:
|
|
os.environ["CLANG38_ARM_PREFIX"] = clang38_path.group(1) + "tools\\bin\\"
|
|
os.environ["MAKE_PATH"] = "nmake"
|
|
os.environ["MAKE_FLAGS"] = "/C"
|
|
|
|
if not os.path.isdir(os.environ["TOOLS_PATH"]):
|
|
raise NameError("ERROR: buildit::build_single_flavor: " + \
|
|
"Missing compiler tool chain." + \
|
|
"Expected location " + os.environ["TOOLS_PATH"])
|
|
if not os.path.isdir(os.environ["TOOLS_PATH_GCC"]):
|
|
raise NameError("ERROR: buildit::build_single_flavor: " + \
|
|
"Missing GCC tool chain." + \
|
|
"Expected location " + os.environ["TOOLS_PATH_GCC"])
|
|
else:
|
|
if "CLANG38_BIN" not in os.environ:
|
|
os.environ["CLANG38_BIN"] = CLANG38LINUX_BIN
|
|
if "CLANG38_ARM_PREFIX" not in os.environ:
|
|
clang38_path = re.search(r"(.*)(bin\b)",os.getenv("CLANG38_BIN"))
|
|
if clang38_path:
|
|
os.environ["CLANG38_ARM_PREFIX"] = clang38_path.group(1) + "tools/bin/"
|
|
os.environ["MAKE_PATH"] = "make"
|
|
os.environ["MAKE_FLAGS"] = ""
|
|
if not os.path.isdir(os.environ["CLANG38_BIN"]):
|
|
raise NameError("ERROR: buildit::build_single_flavor: " + \
|
|
"Missing LLVM tool chain." + \
|
|
"Expected location " + os.environ["CLANG38_BIN"])
|
|
if not os.path.isdir(os.environ["CLANG38_ARM_PREFIX"]):
|
|
raise NameError("ERROR: buildit::build_single_flavor: " + \
|
|
"Missing LLVM tool chain." + \
|
|
"Expected location " + os.environ["CLANG38_ARM_PREFIX"])
|
|
|
|
is_clang38_bin_exist = False
|
|
for path in os.environ["PATH"].split(os.pathsep):
|
|
if path == os.getenv("CLANG38_BIN"):
|
|
is_clang38_bin_exist = True
|
|
break
|
|
|
|
if not is_clang38_bin_exist:
|
|
os.environ["PATH"] = os.environ["CLANG38_BIN"]+ os.pathsep + os.environ["PATH"]
|
|
|
|
log_file = os.path.join(QCOM_DIR, flavor.target, flavor.sub_target, flavor.variant,
|
|
"build_" + os.path.basename(item[0]).split(".dsc")[0] + ".log")
|
|
|
|
# if --use_intermediate_core was used, pick up *.dsc files from QcomBinPkg folder
|
|
ind = flavor.dsc_list.index(item)
|
|
if use_intermediate_core and "Core" in item[0]:
|
|
dsc = os.path.join(use_intermediate_core, flavor.variant, os.path.basename(item[0]))
|
|
if "boot_images" in dsc:
|
|
flavor.dsc_list[ind] = [dsc.split("boot_images" + os.sep)[1], item[1]]
|
|
item = flavor.dsc_list[ind]
|
|
log_file = os.path.join(use_intermediate_core, flavor.sub_target, flavor.variant,
|
|
"build_" + os.path.basename(item[0]).split(".dsc")[0] + ".log")
|
|
|
|
build_cmd = ['-p', item[0], #dsc
|
|
'-j', log_file,
|
|
'-w',
|
|
'-a', item[1], #arch
|
|
'-t', arch_to_compiler(item[1], item[0]),
|
|
'-b', release_mode]
|
|
|
|
if release_mode == "RELEASE":
|
|
build_cmd += ['-D', 'PRODMODE=PRODMODE']
|
|
else:
|
|
build_cmd += ['-D', 'PRODMODE=DEBUGMODE']
|
|
|
|
# Set FEATURE_FLAGS environment variable, dsc will pass to compiler
|
|
if compiler_flags:
|
|
os.environ["FEATURE_FLAGS"] = compiler_flags
|
|
|
|
# Add build.exe specific OptionParser
|
|
if build_flags:
|
|
build_cmd += build_flags.strip('"').split(',')
|
|
# if --use_intermediate_loader was used and we are compiling loader, skip build step
|
|
if use_intermediate_loader and "Loader" in item[0]:
|
|
print "[buildit.py] SKIP BUILDING XBL LOADER"
|
|
else:
|
|
print "[buildit.py] Building with arguments...\n", build_cmd, '\n'
|
|
try:
|
|
subprocess.check_call(["build"] + build_cmd)
|
|
except Exception as error:
|
|
raise NameError("ERROR: buildit::build_single_flavor: " + \
|
|
"Building failed with error... " + str(error))
|
|
|
|
if "clean" in build_flags:
|
|
return
|
|
|
|
# Make a full flavor; Requried for finding all merge dependencies
|
|
full_flavor = build_flavor(flavor.target,
|
|
flavor.sub_target,
|
|
flavor.variant)
|
|
|
|
# Determine what to merge
|
|
merge_dict = generate_merge_items(full_flavor,release_mode, flavor.single_dsc)
|
|
|
|
# Send each merge entry i.e. image.elf off to get merged,
|
|
# and collect the items(elfs) that were created
|
|
built_items = []
|
|
for merge_name, merge_entry in merge_dict.merges.iteritems():
|
|
build_product = perform_merges(merge_entry,
|
|
flavor.target,
|
|
flavor.sub_target,
|
|
flavor.variant,
|
|
release_mode,
|
|
use_intermediate_loader)
|
|
# make sure something was actually built.
|
|
if build_product:
|
|
built_items.append(build_product)
|
|
|
|
# Write out after merging for nicer looking output
|
|
try:
|
|
append_build_products(built_items)
|
|
except Exception as error:
|
|
raise NameError("ERROR: buildit::append_build_products: " + \
|
|
"Failed to append to build products file.")
|
|
|
|
print "*******\n[buildit.py] Completed building ", flavor.to_string(), " Release: " + release_mode
|
|
|
|
##############################################################################
|
|
# append_build_products
|
|
##############################################################################
|
|
def append_build_products(built_items):
|
|
for item in built_items:
|
|
output_dir = item[0]
|
|
file_name = item[1]
|
|
bp_cmd = ['python', os.path.join(os.environ['WORKSPACE'],'QcomPkg','Tools',
|
|
'scripts','buildproducts_builder.py'),
|
|
'-t', os.path.join(os.environ["WORKSPACE"],'..'),
|
|
'-s', output_dir,
|
|
'-i', file_name]
|
|
subprocess.check_output(bp_cmd)
|
|
|
|
##############################################################################
|
|
# perform_merges
|
|
##############################################################################
|
|
def perform_merges(merge_entry, target, sub_target ,variant, release_mode, use_intermediate_loader):
|
|
|
|
# if --use_intermediate_loader was used, modify paths to dll's
|
|
if use_intermediate_loader is not "" and "xbl" in merge_entry.output_name:
|
|
# xbl_sec
|
|
xbl_sec = os.path.join(os.path.basename(use_intermediate_loader), "Common", "XBLLoader",
|
|
os.path.basename(merge_entry.xbl_sec))
|
|
merge_entry.modify_merge_entry_xbl_sec(xbl_sec)
|
|
|
|
# dll_list
|
|
temp_dll_list = []
|
|
for index in range(len(merge_entry.dll_list)):
|
|
dll = merge_entry.dll_list[index]
|
|
if "XBLRamDumpLib" in dll or "XBLLoader" in dll:
|
|
temp_dll_list.append(os.path.join(use_intermediate_loader, "Common", "XBLLoader",
|
|
os.path.basename(dll)))
|
|
merge_entry.modify_merge_entry_dll_list(temp_dll_list)
|
|
if "XBLLoader.dll" in merge_entry.merge_base_dll:
|
|
merge_entry.merge_base_dll = os.path.join(use_intermediate_loader, "Common", "XBLLoader",
|
|
os.path.basename(merge_entry.merge_base_dll))
|
|
|
|
output_name = merge_entry.output_name
|
|
sign_type = merge_entry.sign_type
|
|
xbl_sec = merge_entry.xbl_sec
|
|
dlls = merge_entry.dll_list
|
|
merge_base_dll = merge_entry.merge_base_dll
|
|
|
|
merge_output_dir = os.path.join(os.environ["WORKSPACE"],
|
|
"Build",
|
|
os.environ["TARGETMSM"] + "_Loader",
|
|
release_mode + "_" + arch_to_compiler("AARCH64"),
|
|
"AARCH64")
|
|
merged_output_elf = os.path.join(merge_output_dir, output_name)
|
|
|
|
# Make sure path where merging will happen exists
|
|
if not os.path.isdir(merge_output_dir):
|
|
os.makedirs(merge_output_dir)
|
|
|
|
# Check that all images to merge with are present
|
|
for dll in dlls + [merge_base_dll]:
|
|
if dll and not os.path.isfile(dll):
|
|
print "** WARNING ** Cannot merge " + output_name + ". Missing file: " + dll.split(os.environ["WORKSPACE"] + os.sep)[1]
|
|
return None
|
|
|
|
|
|
cxbl_script = os.path.join(os.environ["WORKSPACE"], QCOM_DIR, 'Tools', 'createxbl.py')
|
|
|
|
#
|
|
# createxbl single
|
|
#
|
|
single_dll =""
|
|
if len(dlls) == 1:
|
|
single_dll = dlls[0]
|
|
elif len(dlls) == 0 and merge_base_dll != "":
|
|
single_dll = merge_base_dll
|
|
|
|
if single_dll:
|
|
# Skip createxbl step to avoid stripping symbols
|
|
if output_name.split('.')[0].lower() in COPY_IMAGES_AS_IS:
|
|
merged_output_elf = dlls[0]
|
|
else:
|
|
# Single merge
|
|
cxbl_cmd = [cxbl_script,
|
|
'-f', single_dll,
|
|
'-o', merged_output_elf,
|
|
'-a', "64"]
|
|
|
|
# Add xbl_sec if included
|
|
if xbl_sec:
|
|
cxbl_cmd += ["-x", os.path.join(os.environ["WORKSPACE"],xbl_sec),
|
|
"-d", "64"]
|
|
|
|
# Run merge command
|
|
try:
|
|
subprocess.check_output(['python'] + cxbl_cmd)
|
|
except Exception as error:
|
|
raise NameError("ERROR: buildit::perform_merges: " + \
|
|
"Failed to merge with error: " + str(error))
|
|
|
|
#
|
|
# createxbl multiple merge
|
|
#
|
|
else:
|
|
|
|
if merge_base_dll:
|
|
dlls = [merge_base_dll] + dlls
|
|
|
|
first_dll = dlls[0]
|
|
num_dlls = len(dlls)
|
|
|
|
for x in xrange(1,num_dlls):
|
|
# grab the next dll
|
|
second_dll = dlls[x]
|
|
# Everything is always merged into 64-bit elfs
|
|
cxbl_cmd = [cxbl_script,
|
|
'-f', first_dll,
|
|
'-s', second_dll,
|
|
'-o', merged_output_elf,
|
|
'-a', "64",
|
|
'-b', "64"]
|
|
|
|
# if not final merge
|
|
if x < (num_dlls - 1):
|
|
cxbl_cmd.append('-n')
|
|
if x == 1 and xbl_sec:
|
|
# final merge: add xbl_sec if included
|
|
cxbl_cmd += ["-x", os.path.join(os.environ["WORKSPACE"],xbl_sec),
|
|
"-d", "64"]
|
|
|
|
# Run merge command
|
|
try:
|
|
cmd_output = subprocess.check_output(["python"] + cxbl_cmd)
|
|
except Exception as error:
|
|
raise NameError("ERROR: buildit::perform_merges: " + \
|
|
"Failed to merge with error: " + str(error))
|
|
first_dll = merged_output_elf
|
|
|
|
# handle non-signed images
|
|
if not sign_type:
|
|
# setup directories
|
|
output_dir_root = get_output_dir_root(sub_target, variant, release_mode)
|
|
sign_bin = os.path.join(output_dir_root, "sign")
|
|
unsigned_bin = os.path.join(output_dir_root, "unsigned")
|
|
|
|
# create dirs if not yet created
|
|
for path in [sign_bin, unsigned_bin]:
|
|
if not os.path.isdir(path):
|
|
os.makedirs(path)
|
|
|
|
print "[buildit.py] Copying over " + output_name + " image as-is...",
|
|
shutil.copy2(merged_output_elf, os.path.join(output_dir_root, output_name))
|
|
shutil.copy2(merged_output_elf, os.path.join(unsigned_bin, output_name))
|
|
print "Done"
|
|
return [output_dir_root, output_name]
|
|
|
|
# handle signed images
|
|
sign_image(merged_output_elf, output_name, sign_type, variant, release_mode)
|
|
|
|
output_dir_root = get_output_dir_root(sub_target, variant, release_mode)
|
|
return [output_dir_root, output_name]
|
|
|
|
##############################################################################
|
|
# sign_image
|
|
##############################################################################
|
|
def sign_image(input_image, output_image, sign_type, variant, release_mode):
|
|
|
|
# Setup Bin directories
|
|
output_dir_root = get_output_dir_root(os.environ["TARGETID"], variant, release_mode)
|
|
sign_bin = os.path.join(output_dir_root, "sign")
|
|
unsigned_bin = os.path.join(output_dir_root, "unsigned")
|
|
|
|
# Create Bin directories
|
|
for path in [sign_bin, unsigned_bin]:
|
|
if not os.path.isdir(path):
|
|
os.makedirs(path)
|
|
|
|
# copy input image to unsigned bin
|
|
shutil.copy2(input_image, os.path.join(unsigned_bin, output_image))
|
|
|
|
print "[buildit.py] Generating default signed " + output_image + " image...",
|
|
|
|
sign_config = get_sectool_config()
|
|
|
|
sec_sign_cmd = [os.path.join(os.environ["SECTOOLS_SCRIPTS_PATH"], "sectools_builder.py"),
|
|
"-t" , output_dir_root,
|
|
"-i" , input_image,
|
|
"-g" , sign_type,
|
|
"--config" , os.path.join(os.environ["SECTOOLS_CFG_PATH"], sign_config["config_xml"])]
|
|
# append optional arguments
|
|
for option,arg in sign_config.iteritems():
|
|
if option in ["msmid", "soc_hw_version", "soc_vers"]:
|
|
sec_sign_cmd += ['--' + option, arg]
|
|
|
|
# Run sectools sign command
|
|
try:
|
|
sec_cmd_output = subprocess.check_output(['python'] + sec_sign_cmd)
|
|
except Exception as error:
|
|
raise NameError("ERROR: buildit::sign_image: " + \
|
|
"Failed to sign image with error: " + str(error))
|
|
|
|
# copy signed image to Bin root
|
|
signed_output_elf = os.path.join(sign_bin, "default", sign_type, output_image)
|
|
shutil.copy2(signed_output_elf, os.path.join(output_dir_root, output_image))
|
|
|
|
print "Done"
|
|
|
|
##############################################################################
|
|
# get_sectool_config
|
|
##############################################################################
|
|
def get_sectool_config():
|
|
# Add TargetPkg folder to path
|
|
pkgdir = os.path.join(os.environ["WORKSPACE"], QCOM_DIR, os.environ["TARGETPKG"])
|
|
sys.path.append(pkgdir)
|
|
|
|
import sectools_config
|
|
|
|
# Remove TargetPkg folder from path
|
|
sys.path = sys.path[:-1]
|
|
|
|
return sectools_config.get_config()
|
|
|
|
##############################################################################
|
|
# get_output_dir_root
|
|
##############################################################################
|
|
def get_output_dir_root(sub_target, variant, release_mode):
|
|
if sub_target:
|
|
return os.path.join(os.environ["WORKSPACE"],
|
|
os.environ["TARGETROOT"],"Bin",
|
|
sub_target, variant, release_mode)
|
|
else:
|
|
return os.path.join(os.environ["WORKSPACE"],
|
|
os.environ["TARGETROOT"],"Bin",
|
|
os.environ["TARGETID"], variant, release_mode)
|
|
|
|
##############################################################################
|
|
# get_dll_from_dsc_inf
|
|
##############################################################################
|
|
def get_dll_from_dsc_inf(dsc,inf,release_mode):
|
|
|
|
dsc_path = dsc[0]
|
|
arch = dsc[1]
|
|
|
|
output_dir = ""
|
|
for section in ["Defines", "Defines." + arch]:
|
|
result = find_tag_in_descriptor_file(dsc_path, section, "OUTPUT_DIRECTORY")
|
|
if result:
|
|
output_dir = result
|
|
if not output_dir:
|
|
raise NameError("ERROR: buildit::get_dll_from_dsc_inf: " + \
|
|
"Missing OUTPUT_DIRECTORY tag in dsc: " + dsc[0])
|
|
|
|
#if use_intermediate_loader and "Loader" in dsc[0]:
|
|
# dll_path = os.path.join(use_intermediate_loader, "Common", "XBLLoader",
|
|
# find_tag_in_descriptor_file(inf, "Defines", "BASE_NAME") + ".dll")
|
|
#else:
|
|
dll_path = os.path.join(os.environ["WORKSPACE"],
|
|
output_dir,
|
|
release_mode + "_" + arch_to_compiler(arch, dsc[0]),
|
|
dsc[1],
|
|
inf.split(".inf")[0],
|
|
"DEBUG",
|
|
find_tag_in_descriptor_file(inf, "Defines", "BASE_NAME") + ".dll")
|
|
return dll_path
|
|
|
|
##############################################################################
|
|
# generate_merge_items
|
|
##############################################################################
|
|
def generate_merge_items(flavor, release_mode, single_dsc):
|
|
|
|
merge_dict = merge_book()
|
|
|
|
# Check for normal merges of images inside dsc
|
|
for dsc in flavor.dsc_list:
|
|
|
|
# check for fdf
|
|
fdf_file = find_tag_in_descriptor_file(dsc[0], "Defines", "FLASH_DEFINITION")
|
|
if fdf_file:
|
|
|
|
# do fdf merging if this is the single_dsc or no single_dsc is given
|
|
do_fdf_merging = (os.path.basename(dsc[0]) == single_dsc) or (single_dsc == "")
|
|
fdf_details = handle_fdf_file(flavor, dsc, fdf_file, release_mode, do_fdf_merging)
|
|
|
|
# Only add an item if we are merging and have a dll
|
|
if fdf_details and fdf_details["merge_name"] and fdf_details["dll"]:
|
|
merge_dict.add_merge_item(fdf_details["merge_name"],
|
|
fdf_details["dll"],
|
|
fdf_details["sign_type"],
|
|
fdf_details["xbl_sec"])
|
|
|
|
#Check for any merge entries inside DSC
|
|
with open(os.path.join(os.environ["WORKSPACE"],dsc[0]), 'r') as d:
|
|
# skip to [Components.common]
|
|
while not re.search('\[Components.common\]', d.readline()):
|
|
pass
|
|
|
|
# Search for infs now
|
|
inf = sign_type = xbl_sec_image = ""
|
|
merge_name = None
|
|
merge_base = None
|
|
bracket_count = 0
|
|
|
|
for line in d.readlines():
|
|
|
|
# 0 implies not inside inf block yet
|
|
if bracket_count == 0:
|
|
|
|
# skip commented lines if outside of an inf block
|
|
if line.lstrip().find("#") == 0:
|
|
continue
|
|
|
|
# look for open bracket, start of inf block
|
|
if line.find(".inf") != -1 and line.find("{") != -1:
|
|
bracket_count = 1
|
|
inf = re.search("(\S+.inf)", line).group(1)
|
|
merge_name = sign_type = xbl_sec_image = ""
|
|
|
|
elif bracket_count > 0:
|
|
#Inside INF
|
|
if line.find("{") != -1:
|
|
bracket_count = bracket_count + 1
|
|
elif line.find("}") != -1:
|
|
bracket_count = bracket_count - 1
|
|
|
|
# Upon closing brackets, validate and add merge item
|
|
if bracket_count == 0 and inf and merge_name:
|
|
merge_dict.add_merge_item(merge_name,
|
|
get_dll_from_dsc_inf(dsc,inf,release_mode),
|
|
sign_type,
|
|
xbl_sec_image)
|
|
|
|
# Upon closing brackets, validate and add merge_base
|
|
elif bracket_count == 0 and inf and merge_base:
|
|
merge_dict.add_merge_base(merge_base,
|
|
get_dll_from_dsc_inf(dsc,inf,release_mode),
|
|
sign_type,
|
|
xbl_sec_image)
|
|
|
|
if bracket_count < 0:
|
|
raise NameError("ERROR: buildit::generate_merge_items: " + \
|
|
"Malformed dsc file: " + dsc)
|
|
|
|
# check for #MERGE TAG
|
|
search = re.search("#\s*BUILD.MERGE", line)
|
|
if search:
|
|
merge_name = ""
|
|
|
|
# check for #MERGE (note: no merge name)
|
|
search = re.search("#\s*BUILD.MERGE\s(\S+)", line)
|
|
if search:
|
|
merge_name = search.group(1)
|
|
|
|
# check for #MERGE_BASE
|
|
search = re.search("#\s*BUILD.MERGE_BASE\s(\S+)", line)
|
|
if search:
|
|
merge_base = search.group(1)
|
|
|
|
# check for #SIGN TAG
|
|
search = re.search("#\s*BUILD.SIGN\s*(\S+)", line)
|
|
if (search is not None):
|
|
sign_type = search.group(1)
|
|
|
|
# check for #XBL_SEC TAG
|
|
search = re.search("#\s*BUILD.XBL_SEC\s*(\S+)", line)
|
|
if (search is not None):
|
|
xbl_sec_image = search.group(1)
|
|
|
|
return merge_dict
|
|
|
|
##############################################################################
|
|
# handle_fdf_file
|
|
##############################################################################
|
|
def handle_fdf_file(flavor,dsc, fdf, release_mode, do_fdf_merging):
|
|
merge_details = {}
|
|
|
|
# setup bin paths
|
|
output_bin_root = get_output_dir_root(flavor.sub_target, flavor.variant, release_mode)
|
|
sign_bin = os.path.join(output_bin_root, "sign")
|
|
unsigned_bin = os.path.join(output_bin_root, "unsigned")
|
|
|
|
# create bin folders if non-existent
|
|
for path in [sign_bin, unsigned_bin]:
|
|
if not os.path.isdir(path):
|
|
os.makedirs(path)
|
|
|
|
build_dir = find_tag_in_descriptor_file(dsc[0], "Defines", "OUTPUT_DIRECTORY")
|
|
if not build_dir:
|
|
build_dir = find_tag_in_descriptor_file(dsc[0], "Defines." + dsc[1], "OUTPUT_DIRECTORY")
|
|
if not build_dir:
|
|
raise NameError("ERROR: buildit::handle_fdf_file: " + \
|
|
"Unable to find OUTPUT_DIRECTORY tag in dsc: " + dsc[0])
|
|
|
|
ih_output_name = find_tag_in_descriptor_file(dsc[0], BUILDIT_BUILDINFO_TAG, "OUTPUT_NAME")
|
|
if not ih_output_name:
|
|
raise NameError("ERROR: buildit::handle_fdf_file: " + \
|
|
"Unable to find OUTPUT_NAME tag in dsc: " + dsc[0])
|
|
|
|
target_msm_efi = "FVMAIN_COMPACT.Fv"
|
|
target_msm_elf = "FVMAIN_COMPACT.elf"
|
|
|
|
efi_fd_dir = os.path.join(os.environ["WORKSPACE"],
|
|
build_dir,
|
|
release_mode + "_" + arch_to_compiler(dsc[1], dsc[0]),
|
|
"FV")
|
|
|
|
if ih_output_name.endswith(".fv"):
|
|
# copy to bin root
|
|
shutil.copy2(os.path.join(efi_fd_dir, target_msm_efi),
|
|
os.path.join(output_bin_root, ih_output_name))
|
|
# copy to unsigned bin
|
|
shutil.copy2(os.path.join(efi_fd_dir, target_msm_efi),
|
|
os.path.join(unsigned_bin, ih_output_name))
|
|
return merge_details
|
|
|
|
# Find out what is to be added to the merge list
|
|
merge_details["merge_name"] = find_tag_in_descriptor_file(dsc[0], BUILDIT_BUILDINFO_TAG, "MERGE")
|
|
merge_details["sign_type"] = find_tag_in_descriptor_file(dsc[0], BUILDIT_BUILDINFO_TAG, "SIGN")
|
|
merge_details["xbl_sec"] = find_tag_in_descriptor_file(dsc[0], BUILDIT_BUILDINFO_TAG, "XBL_SEC")
|
|
merge_details["dll"] = os.path.join(efi_fd_dir,
|
|
target_msm_elf)
|
|
# Make sure we actually have merge_name and dll
|
|
for arg in ["merge_name","dll"]:
|
|
if arg not in merge_details:
|
|
raise NameError("ERROR: buildit::handle_fdf_file: " + \
|
|
"Unable to find " + arg + " tag in fdf: " + fdf)
|
|
|
|
# conditionally skip merging
|
|
if not do_fdf_merging:
|
|
return merge_details
|
|
|
|
# returned value will look like "0x00000|blahblahblah"
|
|
# so split on the "|" and take first one(0)
|
|
target_addr = find_tag_in_descriptor_file(fdf, "", "BaseAddress").split("|")[0]
|
|
if not target_addr:
|
|
raise NameError("ERROR: buildit::handle_fdf_file: " + \
|
|
"Unable to find BaseAddress tag in dsc: " + dsc[0])
|
|
|
|
ih_cmd = [os.path.join(os.environ["WORKSPACE"],QCOM_DIR,"Tools", "image_header.py"),
|
|
os.path.join(efi_fd_dir, target_msm_efi),
|
|
os.path.join(efi_fd_dir, ih_output_name),
|
|
target_addr,
|
|
"elf"]
|
|
if dsc[1] == "AARCH64":
|
|
ih_cmd.append("64")
|
|
|
|
print "[buildit.py] Generating ELF header for image " + ih_output_name,
|
|
try:
|
|
cmd_output = subprocess.check_output(["python"] + ih_cmd)
|
|
except Exception as error:
|
|
raise NameError("ERROR: buildit::handle_fdf_file: " + \
|
|
"Failed to generate ELF header with error: " + str(error))
|
|
|
|
print "Done"
|
|
|
|
# Get the sing_type
|
|
sign_type = find_tag_in_descriptor_file(dsc[0], BUILDIT_BUILDINFO_TAG, "SIGN_FD")
|
|
if not sign_type:
|
|
raise NameError("ERROR: buildit::handle_fdf_file: " + \
|
|
"SIGN_FD tag not found in dsc: " + dsc[0])
|
|
|
|
# Perform signing
|
|
sign_image(os.path.join(efi_fd_dir,ih_output_name),
|
|
ih_output_name,
|
|
sign_type,
|
|
flavor.variant,
|
|
release_mode)
|
|
|
|
return merge_details
|
|
|
|
##############################################################################
|
|
# arch_to_compiler
|
|
##############################################################################
|
|
def arch_to_compiler(arch, dsc=""):
|
|
|
|
# Look for COMPILER_<platform> tag if dsc was provided
|
|
if dsc:
|
|
if platform.system().lower() == "windows":
|
|
compiler_flag = "COMPILER_WIN"
|
|
else:
|
|
compiler_flag = "COMPILER_LINUX"
|
|
compiler = find_tag_in_descriptor_file(dsc, BUILDIT_BUILDINFO_TAG,compiler_flag)
|
|
if compiler: return compiler
|
|
|
|
# Otherwise, determine
|
|
if arch == "AARCH64":
|
|
if platform.system() == "Windows":
|
|
return "CLANG38WIN"
|
|
else:
|
|
return "CLANG38LINUX"
|
|
elif arch == "ARM":
|
|
if platform.system() == "Windows":
|
|
return "RVCT501"
|
|
else:
|
|
return "RVCT501LINUX"
|
|
|
|
##############################################################################
|
|
# search_valid_dirs
|
|
##############################################################################
|
|
def search_valid_dirs(search_path, regex):
|
|
valid_dirs = []
|
|
for f in os.listdir(search_path):
|
|
if re.search(regex, f):
|
|
valid_dirs += [os.path.join(search_path, f)]
|
|
|
|
return valid_dirs
|
|
|
|
##############################################################################
|
|
# match_whole_word
|
|
##############################################################################
|
|
def match_whole_word(raw_regex):
|
|
return '^'+raw_regex+'$'
|
|
|
|
|
|
##############################################################################
|
|
# find_all_build_flavors
|
|
##############################################################################
|
|
def find_all_build_flavors(target=None, sub_target=None, variant=None, single_dsc=""):
|
|
flavors = []
|
|
|
|
# Set Target regex
|
|
if target:
|
|
target_regex = target
|
|
else:
|
|
target_regex = DEFAULT_TARGET_REGEX
|
|
|
|
# Search for all matching targets
|
|
boot_dir = os.environ["WORKSPACE"]
|
|
search_path = os.path.join(boot_dir, QCOM_DIR)
|
|
for target_match in search_valid_dirs(search_path, target_regex):
|
|
# Set sub_target regex
|
|
if sub_target:
|
|
sub_target_regex = match_whole_word(sub_target)
|
|
else:
|
|
sub_target_regex = DEFAULT_SUB_TARGET_REGEX
|
|
|
|
# Search for all sub targets
|
|
for sub_target_match in search_valid_dirs(target_match, sub_target_regex):
|
|
|
|
# Search for variant
|
|
if variant:
|
|
variant_regex = match_whole_word(variant)
|
|
else:
|
|
variant_regex = DEFAULT_VARIANT_REGEX
|
|
|
|
for variant_match in search_valid_dirs(sub_target_match, variant_regex):
|
|
variant_flavor = build_flavor(strip_basename(target_match),
|
|
strip_basename(sub_target_match),
|
|
strip_basename(variant_match),
|
|
single_dsc)
|
|
# Flavor is only valid if there is at least one dsc collected
|
|
if variant_flavor.dsc_list:
|
|
flavors.append(variant_flavor)
|
|
|
|
# Search for variant
|
|
if variant is None:
|
|
variant_regex = DEFAULT_VARIANT_REGEX
|
|
elif variant is "":
|
|
no_variant_flavor = build_flavor(strip_basename(target_match),
|
|
"",
|
|
"",
|
|
single_dsc)
|
|
|
|
if (no_variant_flavor.dsc_list):
|
|
flavors.append(no_variant_flavor)
|
|
|
|
variant_regex = match_whole_word(variant)
|
|
|
|
else:
|
|
variant_regex = match_whole_word(variant)
|
|
|
|
for variant_match in search_valid_dirs(target_match, variant_regex):
|
|
variant_flavor = build_flavor(strip_basename(target_match),
|
|
"",
|
|
strip_basename(variant_match),
|
|
single_dsc)
|
|
if variant_flavor.dsc_list:
|
|
flavors.append(variant_flavor)
|
|
|
|
return flavors
|
|
|
|
##############################################################################
|
|
# query_all_flavors
|
|
##############################################################################
|
|
def query_all_flavors():
|
|
all_flavors = find_all_build_flavors()
|
|
print "\n[buildit.py] All available flavors:\n"\
|
|
"###############################################"
|
|
for flavor in all_flavors:
|
|
print flavor.to_string()
|
|
sys.exit(0)
|
|
|
|
##############################################################################
|
|
# generate_dsc_list
|
|
##############################################################################
|
|
def generate_dsc_list(target_dir, variant_dir, single_dsc):
|
|
|
|
# Either search for the single dsc specified or all dsc's
|
|
dsc_regex = single_dsc if single_dsc else "*.dsc"
|
|
|
|
# collect dsc's from variant_dir + target_dir/VARIANT
|
|
dsc_sources = glob.glob(os.path.join(target_dir, variant_dir, dsc_regex))
|
|
|
|
# Only check Common folder if this flavor has a variant
|
|
# Otherwise, flat structure is assumed
|
|
if variant_dir:
|
|
dsc_sources = dsc_sources + glob.glob(os.path.join(target_dir, "Common", dsc_regex))
|
|
|
|
trimmed_sources = []
|
|
for dsc in dsc_sources:
|
|
# Remove BOOT_IMAGES_DIR from path
|
|
trim_dsc = dsc.split(BOOT_IMAGES_DIR + os.sep)[1]
|
|
dsc_arch = find_tag_in_descriptor_file(dsc, BUILDIT_BUILDINFO_TAG, "COMPILER_ARCH")
|
|
if not dsc_arch:
|
|
dsc_arch = DEFAULT_COMPILER_ARCH
|
|
|
|
trimmed_sources.append([trim_dsc, dsc_arch])
|
|
|
|
return trimmed_sources
|
|
|
|
##############################################################################
|
|
# find_tag_in_descriptor_file
|
|
##############################################################################
|
|
def find_tag_in_descriptor_file(dsc, section, tag):
|
|
|
|
with open(os.path.join(os.environ["WORKSPACE"], dsc), 'r') as f:
|
|
|
|
if section:
|
|
in_desired_section = False
|
|
else:
|
|
in_desired_section = True
|
|
|
|
for line in f.readlines():
|
|
# Check if entering new section, only if section is provided
|
|
if section:
|
|
section_search = re.search("\[(\S+)\]", line)
|
|
if section_search:
|
|
# Check if entering desired section
|
|
if section_search.group(1) == section:
|
|
in_desired_section = True
|
|
elif in_desired_section:
|
|
# Hit new section, and already hit desired
|
|
# No tag was found, return early
|
|
return ""
|
|
|
|
if in_desired_section or not section:
|
|
expr = tag + "\s*=\s*(\S+)"
|
|
tag_search = re.search(expr, line)
|
|
if tag_search:
|
|
return tag_search.group(1)
|
|
|
|
# Not found anywhere
|
|
return ""
|
|
|
|
##############################################################################
|
|
# strip_basename
|
|
##############################################################################
|
|
def strip_basename(string):
|
|
return string.split(os.path.dirname(string)+os.sep)[1]
|
|
|
|
##############################################################################
|
|
# setup_environment
|
|
##############################################################################
|
|
def setup_environment():
|
|
|
|
print "[buildit.py] Building on " + platform.system()
|
|
make_name = ""
|
|
if platform.system() is "Windows":
|
|
make_name = "nmake.exe"
|
|
EDK_ENV_FLAG = 'EDK_ENV_STARTS_HERE'
|
|
os.chdir(os.path.dirname(os.path.realpath(__file__)) + os.sep + "..")
|
|
edk_output = subprocess.check_output('edksetup.bat --nt32 && echo ' + EDK_ENV_FLAG + ' && set', shell=True)
|
|
parse_and_set_edk_env(edk_output, EDK_ENV_FLAG)
|
|
|
|
else:
|
|
make_name = "make"
|
|
os.environ["ARMLMD_LICENSE_FILE"] = DEFAULT_ARM_LICENSE_FILE
|
|
curr_dir = os.getcwd()
|
|
os.chdir(os.path.dirname(os.path.realpath(__file__)) + os.sep + "..")
|
|
edk_command = ['bash', '-c', 'source edksetup.sh BaseTools && env']
|
|
proc = subprocess.Popen( edk_command, stdout=subprocess.PIPE)
|
|
for line in proc.stdout:
|
|
(key, _, value) = line.partition("=")
|
|
os.environ[key] = value.strip()
|
|
proc.communicate()
|
|
os.chdir(curr_dir)
|
|
|
|
# Check if nmake.exe/make.exe is in PATH
|
|
make_found = False
|
|
for path in os.environ["PATH"].split(os.pathsep):
|
|
if os.path.exists(os.path.join(path, make_name)):
|
|
make_found = True
|
|
break
|
|
if not make_found:
|
|
raise NameError("ERROR: buildit::setup_environment: " + make_name +\
|
|
" not found in PATH")
|
|
|
|
if "WORKSPACE" not in os.environ:
|
|
raise NameError("ERROR: buildit::setup_environment: " + \
|
|
"Failed to setup environment variable WORKSPACE.")
|
|
|
|
# Check that boot_images folder exists, there are dependencies on this
|
|
if BOOT_IMAGES_DIR not in os.environ["WORKSPACE"]:
|
|
raise NameError("ERROR: buildit::setup_environment: " + \
|
|
"Build root folder 'boot_images' is missing. Please ensure this folder exist.")
|
|
|
|
# Enable sectools and set sectools_policy
|
|
os.environ["USE_SECTOOLS"] = "1"
|
|
os.environ["SECTOOLS_POLICY"] = "USES_SEC_POLICY_MULTIPLE_DEFAULT_SIGN"
|
|
os.environ["USES_PLATFORM_IMAGE_INTEGRITY_CHECK"] = "1"
|
|
os.environ[os.environ["SECTOOLS_POLICY"]] = "1"
|
|
os.environ["SECTOOLS_CFG_PATH"] = os.path.join(os.environ["WORKSPACE"], QCOM_DIR,"Tools","sectools", "config","integration")
|
|
os.environ["SECTOOLS_SCRIPTS_PATH"] = os.path.join(os.environ["WORKSPACE"], QCOM_DIR,"Tools","sectools")
|
|
|
|
|
|
if DEVDEBUG:
|
|
print "[buildit.py] WORKSPACE:" + os.environ["WORKSPACE"]
|
|
print "[buildit.py] Environment\n"
|
|
print os.environ
|
|
|
|
##############################################################################
|
|
# parse_and_set_edk_env
|
|
##############################################################################
|
|
def parse_and_set_edk_env(edk_output, EDK_ENV_FLAG):
|
|
|
|
# Go through every line
|
|
# Skip until 'ENDFLAG'
|
|
# Then add everything in env to current env
|
|
skipping = True
|
|
for line in edk_output.splitlines():
|
|
if not skipping:
|
|
envvar = line[0:line.index('=')]
|
|
envval = line[line.index('=')+1:]
|
|
os.environ[envvar] = envval
|
|
|
|
elif re.search(EDK_ENV_FLAG, line):
|
|
skipping = False
|
|
return
|
|
|
|
##############################################################################
|
|
# create_build_version
|
|
##############################################################################
|
|
def create_build_version(build_version):
|
|
build_version_file = os.path.join(os.environ["WORKSPACE"],
|
|
QCOM_DIR,
|
|
"Include",
|
|
"BuildVersion.h" )
|
|
|
|
if os.path.isfile(build_version_file) and build_version == "0":
|
|
print "[buildit.py] QcomPkg\Include\BuildVersion.h already exists."
|
|
print "[buildit.py] No build version specified. Will use existing version."
|
|
return
|
|
|
|
print "[buildit.py] Creating QcomPkg\Include\BuildVersion.h with version: " + build_version
|
|
try:
|
|
with open(build_version_file, 'w') as vf:
|
|
vf.write("/* DO NOT EDIT: This file is autogenerated */\n")
|
|
vf.write("#ifndef __BUILDVERSION_H__\n")
|
|
vf.write("#define __BUILDVERSION_H__\n\n")
|
|
vf.write("#ifndef UEFI_BUILD_VERSION\n")
|
|
vf.write(" #define UEFI_BUILD_VERSION \"" + build_version + "\"\n")
|
|
vf.write("#endif\n\n")
|
|
vf.write("#endif /* __BUILDVERSION_H__ */\n")
|
|
except:
|
|
raise NameError("ERROR: buildit::create_build_version: " + \
|
|
"Unable to build version file")
|
|
|
|
##############################################################################
|
|
# make_edk_tools
|
|
##############################################################################
|
|
def make_edk_tools():
|
|
if platform.system() == "Windows":
|
|
return
|
|
|
|
workspace_file = os.path.join(os.environ["WORKSPACE"], "BaseTools", "workspace.txt")
|
|
|
|
# Skip building if file exists and current workspace matches
|
|
if os.path.isfile(workspace_file) and \
|
|
os.environ["WORKSPACE"] == open(workspace_file).read().strip():
|
|
print "[buildit.py] Using prebuilt tools"
|
|
return
|
|
|
|
tools_path = os.path.join(os.environ["WORKSPACE"], "BaseTools")
|
|
|
|
# Clean build tools if it already exists
|
|
if not os.path.isdir(os.path.join(os.environ["WORKSPACE"],"BaseTools","Source","C", "bin")):
|
|
subprocess.call(["make", "-C", tools_path, "clean"])
|
|
|
|
print "[buildit.py] Building tools..."
|
|
subprocess.call(["make", "-C", tools_path])
|
|
# write workspace to file
|
|
with open(workspace_file, 'w') as wsf:
|
|
wsf.write(os.environ["WORKSPACE"])
|
|
print "Done"
|
|
|
|
##############################################################################
|
|
# image_version_setup
|
|
##############################################################################
|
|
def image_version_setup(target, variant):
|
|
image_version_dir = os.path.join(os.environ['WORKSPACE'], 'QcomPkg', 'Library', 'ImageVersionLib')
|
|
image_version_builder = os.path.join(os.environ['WORKSPACE'], 'QcomPkg', 'Tools', 'scripts', 'version_builder.py')
|
|
|
|
version_files = [os.path.join(image_version_dir, 'oem_version.c'),
|
|
os.path.join(image_version_dir, 'oem_uuid.c'),
|
|
os.path.join(image_version_dir, 'qc_version.c')]
|
|
|
|
for vf in version_files:
|
|
if os.path.isfile(vf): os.remove(vf)
|
|
|
|
version_build_cmd = ['python', image_version_builder,
|
|
'-t', image_version_dir,
|
|
'-b', target + variant]
|
|
curr_dir = os.getcwd()
|
|
os.chdir(os.environ["WORKSPACE"])
|
|
print "[buildit.py] Generating image version file..."
|
|
|
|
if platform.system() == "Windows":
|
|
print "\tPlease be patient. This step may take a while on Windows machines..."
|
|
|
|
try:
|
|
subprocess.check_call(version_build_cmd)
|
|
except Exception as error:
|
|
if error.returncode == 1:
|
|
raise NameError("ERROR: buildit::image_version_setup:" + \
|
|
"image version setup failed.")
|
|
elif error.returncode == 2:
|
|
raise NameError("ERROR: buildit::image_version_setup:" + \
|
|
"image version setup failed with following error: " + \
|
|
"manifest.xml not found!")
|
|
|
|
for vf in version_files:
|
|
if not os.path.isfile(vf):
|
|
print "\nError: Image Version File not generated"
|
|
raise NameError("ERROR buildit::image_version_setup:" + \
|
|
"image version setup failed to genearte file: " + vf)
|
|
print "Done"
|
|
|
|
##############################################################################
|
|
# copy_intermediate_files
|
|
##############################################################################
|
|
|
|
def copy_intermediate_files(output_dir_root, flavor, release_mode, flag):
|
|
print "*******\n[buildit] Copying binaries to QcomBinPkg:"
|
|
if not os.path.isdir(output_dir_root):
|
|
os.makedirs(output_dir_root)
|
|
|
|
if flag == "create_intermediate_core":
|
|
# get dsc filename
|
|
dsc_file = ""
|
|
arch = ""
|
|
for item in flavor.dsc_list:
|
|
if "Core" in item[0]:
|
|
(dsc_file,arch) = (item[0], item[1])
|
|
break
|
|
|
|
# Get target dir and source dir for EFI copy
|
|
target_dir = os.path.basename(dsc_file.split(".dsc")[0])
|
|
r = re.search(r'(Msm\d{4})(.*)(_.*)', target_dir)
|
|
if r is not None:
|
|
target_dir = r.group(1) + flavor.variant + r.group(3)
|
|
else:
|
|
print "Could not find target directory."
|
|
return
|
|
|
|
# construct efi source dir path
|
|
source_dir_efis = os.path.join(os.environ["WORKSPACE"], "Build", target_dir, release_mode +
|
|
"_" + arch_to_compiler(arch, dsc_file), arch, "*.efi")
|
|
|
|
# delete old efi's and inf's
|
|
filelist = glob.glob(os.path.join(output_dir_root, "*.efi")) + glob.glob(os.path.join(output_dir_root, "*.inf"))
|
|
for f in filelist:
|
|
os.remove(f)
|
|
|
|
# Get list of Qcom packages
|
|
dsc_fd = open(dsc_file)
|
|
dsc_contents = dsc_fd.read()
|
|
dsc_fd.close()
|
|
dsc_components = dsc_contents.split("[Components.common]")[1]
|
|
dsc_components_qcom = re.findall(r'([^#|]QcomPkg/[\w/]+\.inf)', dsc_components)
|
|
for index in range(len(dsc_components_qcom)):
|
|
inf_filepath_list = dsc_components_qcom[index].split("/")
|
|
inf_filepath = os.environ["WORKSPACE"]
|
|
for str in inf_filepath_list:
|
|
inf_filepath = os.path.join(inf_filepath, str.strip())
|
|
dsc_components_qcom[index] = find_basename_using_inf(inf_filepath)
|
|
|
|
for file in glob.glob(source_dir_efis):
|
|
# copy the efi if it is a Qcom package and generate a new INF for it
|
|
basename = os.path.splitext(os.path.basename(file))[0]
|
|
if basename in dsc_components_qcom:
|
|
output_dir_file = os.path.join(output_dir_root, os.path.basename(file))
|
|
print "[buildit] Copying {0} to {1}".format(file, output_dir_file)
|
|
inf_location = find_inf_using_basename(basename)
|
|
try:
|
|
if inf_location != "":
|
|
shutil.copy(file, output_dir_file)
|
|
except:
|
|
print "[buildit] Could not copy file {0}".format(file)
|
|
try:
|
|
if inf_location != "":
|
|
create_inf(basename, inf_location, output_dir_root, release_mode)
|
|
except:
|
|
print "[buildit] Could not create inf for {0}".format(file)
|
|
|
|
elif flag == "create_intermediate_loader":
|
|
# get dsc filename
|
|
dsc_file = ""
|
|
arch = ""
|
|
for item in flavor.dsc_list:
|
|
if "Loader" in item[0]:
|
|
(dsc_file,arch) = (item[0], item[1])
|
|
break
|
|
|
|
target_dir = os.path.basename(dsc_file.split(".dsc")[0])
|
|
r = re.search(r'(Msm\d{4})(.*)(_.*)', target_dir)
|
|
if r is not None:
|
|
target_dir = r.group(1) + "_Loader"
|
|
target_dir_pkg = r.group(1) + r.group(2)
|
|
else:
|
|
print "Could not find target directory."
|
|
return
|
|
|
|
source_dll_root = os.path.join(os.environ["WORKSPACE"], "Build", target_dir, release_mode +
|
|
"_" + arch_to_compiler(arch, dsc_file), arch)
|
|
|
|
source_mbn_root = os.path.join(os.environ["WORKSPACE"], "QcomPkg", target_dir_pkg, "Library", "XBL_SEC")
|
|
|
|
for p in [source_dll_root, source_mbn_root]:
|
|
for root, dirnames, filenames in os.walk(p):
|
|
for dll in fnmatch.filter(filenames, '*.dll') + fnmatch.filter(filenames, '*.mbn'):
|
|
match = os.path.join(root, dll)
|
|
basename = os.path.basename(match)
|
|
output_dir_file = os.path.join(output_dir_root, basename)
|
|
print "[buildit] Copying {0} to {1}".format(match, output_dir_file)
|
|
try:
|
|
shutil.copy(match, output_dir_file)
|
|
except:
|
|
print "[buildit] Could not copy file {0}".format(match)
|
|
|
|
##############################################################################
|
|
# create_inf
|
|
##############################################################################
|
|
|
|
def create_inf(filename, initial_inf, copy_location, release_mode):
|
|
filepath = os.path.join(copy_location, os.path.basename(initial_inf))
|
|
# first create file
|
|
newfile = open(filepath, 'w+')
|
|
|
|
# open and read existing inf
|
|
oldfile = open(initial_inf, 'r')
|
|
oldfile_contents = oldfile.read()
|
|
|
|
# header
|
|
newfile.write(INF_HEADER.replace("FILENAME", os.path.basename(initial_inf)))
|
|
|
|
# defines
|
|
newfile.write("[Defines]\n")
|
|
new_defines = (oldfile_contents.split("[Defines]")[1]).split("[")[0]
|
|
for line in new_defines.split("\n"):
|
|
if "ENTRY_POINT" not in line and "#" not in line:
|
|
newfile.write(line + "\n")
|
|
newfile.write("\n")
|
|
|
|
# binaries
|
|
newfile.write("[Binaries]\n")
|
|
newfile.write("\t{0}|{1}|{2}\n\n".format("PE32", os.path.basename(filename)+".efi", release_mode.upper()))
|
|
newfile.close()
|
|
|
|
##############################################################################
|
|
# find_inf_using_basename
|
|
##############################################################################
|
|
|
|
def find_inf_using_basename(basename):
|
|
root_folder = os.path.join(os.environ["WORKSPACE"], "QcomPkg")
|
|
for root, dirnames, filenames in os.walk(root_folder):
|
|
for inf in fnmatch.filter(filenames, '*.inf'):
|
|
match = os.path.join(root, inf)
|
|
inf_fd = open(match)
|
|
inf_contents = inf_fd.read()
|
|
r = re.search(r'(BASE_NAME\s+=\s+)(\w+)', inf_contents)
|
|
if r is not None:
|
|
temp_basename = r.group(2)
|
|
if temp_basename == basename:
|
|
inf_fd.close()
|
|
return match
|
|
inf_fd.close()
|
|
return ""
|
|
|
|
##############################################################################
|
|
# find_inf_using_basename
|
|
##############################################################################
|
|
|
|
def find_basename_using_inf(inf):
|
|
inf_fd = open(inf)
|
|
inf_contents = inf_fd.read()
|
|
r = re.search(r'(BASE_NAME\s+=\s+)(\w+)', inf_contents)
|
|
if r is not None:
|
|
return r.group(2)
|
|
return ""
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|