Merge pull request #10065 from theotherjimmy/fix-postbuild-restrictions

Correct post-build-hook detection logic
pull/10081/head
Cruz Monrreal 2019-03-16 22:58:58 -05:00 committed by GitHub
commit 241e6d2b46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 112 additions and 58 deletions

View File

@ -22,9 +22,8 @@ import struct
import shutil
import inspect
import sys
from collections import namedtuple
from copy import copy
from inspect import getmro
from collections import namedtuple, Mapping
from future.utils import raise_from
from tools.resources import FileType
from tools.targets.LPC import patch
@ -45,16 +44,21 @@ CORE_LABELS = {
"Cortex-M4F": ["M4", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M4", "CORTEX"],
"Cortex-M7": ["M7", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M7", "CORTEX"],
"Cortex-M7F": ["M7", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M7", "CORTEX"],
"Cortex-M7FD": ["M7", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M7", "CORTEX"],
"Cortex-M7FD": ["M7", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M7",
"CORTEX"],
"Cortex-A9": ["A9", "CORTEX_A", "LIKE_CORTEX_A9", "CORTEX"],
"Cortex-M23": ["M23", "CORTEX_M", "LIKE_CORTEX_M23", "CORTEX"],
"Cortex-M23-NS": ["M23", "M23_NS", "CORTEX_M", "LIKE_CORTEX_M23", "CORTEX"],
"Cortex-M23-NS": ["M23", "M23_NS", "CORTEX_M", "LIKE_CORTEX_M23",
"CORTEX"],
"Cortex-M33": ["M33", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"],
"Cortex-M33-NS": ["M33", "M33_NS", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"],
"Cortex-M33-NS": ["M33", "M33_NS", "CORTEX_M", "LIKE_CORTEX_M33",
"CORTEX"],
"Cortex-M33F": ["M33", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"],
"Cortex-M33F-NS": ["M33", "M33_NS", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"],
"Cortex-M33F-NS": ["M33", "M33_NS", "CORTEX_M", "LIKE_CORTEX_M33",
"CORTEX"],
"Cortex-M33FE": ["M33", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"],
"Cortex-M33FE-NS": ["M33", "M33_NS", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"]
"Cortex-M33FE-NS": ["M33", "M33_NS", "CORTEX_M", "LIKE_CORTEX_M33",
"CORTEX"]
}
CORE_ARCH = {
@ -78,16 +82,20 @@ CORE_ARCH = {
"Cortex-M33FE-NS": 8,
}
################################################################################
###############################################################################
# Generic Target class that reads and interprets the data in targets.json
class HookError(Exception):
""" A simple class that represents all the exceptions associated with
hooking
"""
pass
CACHES = {}
def cached(func):
"""A simple decorator used for automatically caching data returned by a
function
@ -102,9 +110,15 @@ def cached(func):
# Cumulative attributes can have values appended to them, so they
# need to be computed differently than regular attributes
CUMULATIVE_ATTRIBUTES = ['extra_labels', 'macros', 'device_has', 'features', 'components']
default_build_tools_metadata = {u'version':0, u'public':False}
CUMULATIVE_ATTRIBUTES = [
'extra_labels', 'macros', 'device_has', 'features', 'components'
]
default_build_tools_metadata = {u'version': 0, u'public': False}
def get_resolution_order(json_data, target_name, order, level=0):
""" Return the order in which target descriptions are searched for
@ -128,8 +142,10 @@ def get_resolution_order(json_data, target_name, order, level=0):
def target(name, json_data):
"""Construct a target object"""
if name.startswith("_"):
raise Exception("Invalid target name '%s' specified, target name should not start with '_'" % name)
raise Exception(
"Invalid target name '%s' specified,"
" target name should not start with '_'" % name
)
try:
resolution_order = get_resolution_order(json_data, name, [])
except KeyError as exc:
@ -137,13 +153,18 @@ def target(name, json_data):
"target {} has an incomplete target definition".format(name)
), exc)
resolution_order_names = [tgt for tgt, _ in resolution_order]
return Target(name=name,
json_data={key: value for key, value in json_data.items()
if key in resolution_order_names},
resolution_order=resolution_order,
resolution_order_names=resolution_order_names,
build_tools_metadata=json_data.get("__build_tools_metadata__", default_build_tools_metadata))
return Target(
name=name,
json_data={key: value for key, value in json_data.items()
if key in resolution_order_names},
resolution_order=resolution_order,
resolution_order_names=resolution_order_names,
build_tools_metadata=json_data.get(
"__build_tools_metadata__",
default_build_tools_metadata
)
)
def generate_py_target(new_targets, name):
"""Add one or more new target(s) represented as a Python dictionary
@ -158,15 +179,21 @@ def generate_py_target(new_targets, name):
total_data = {}
total_data.update(new_targets)
total_data.update(base_targets)
return target(name, total_data)
class Target(namedtuple("Target", "name json_data resolution_order resolution_order_names build_tools_metadata")):
class Target(namedtuple(
"Target",
"name json_data resolution_order "
"resolution_order_names build_tools_metadata"
)):
"""An object to represent a Target (MCU/Board)"""
# Default location of the 'targets.json' file
__targets_json_location_default = os.path.join(
os.path.dirname(os.path.abspath(__file__)), '..', '..', 'targets', 'targets.json')
os.path.dirname(os.path.abspath(__file__)),
'..', '..', 'targets', 'targets.json'
)
# Current/new location of the 'targets.json' file
__targets_json_location = None
@ -188,8 +215,10 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
for extra_target in Target.__extra_target_json_files:
for k, v in json_file_to_dict(extra_target).items():
if k in targets:
print('WARNING: Custom target "%s" cannot replace existing '
'target.' % k)
print(
'WARNING: Custom target "%s" cannot replace existing '
'target.' % k
)
else:
targets[k] = v
targets[k]["_from_file"] = extra_target
@ -206,8 +235,10 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
@staticmethod
def set_targets_json_location(location=None):
"""Set the location of the targets.json file"""
Target.__targets_json_location = (location or
Target.__targets_json_location_default)
Target.__targets_json_location = (
location or
Target.__targets_json_location_default
)
Target.__extra_target_json_files = []
# Invalidate caches, since the location of the JSON file changed
CACHES.clear()
@ -229,8 +260,10 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
if isinstance(val, dict):
out[key] = Target.__add_paths_to_progen(val)
elif key == "template":
out[key] = [os.path.join(os.path.dirname(__file__), 'export', v)
for v in val]
out[key] = [
os.path.join(os.path.dirname(__file__), 'export', v)
for v in val
]
else:
out[key] = val
return out
@ -301,14 +334,13 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
return self.__getattr_cumulative(attrname)
else:
tdata = self.json_data
starting_value = None
for tgt in self.resolution_order:
data = tdata[tgt[0]]
try:
return data[attrname]
except KeyError:
pass
else: # Attribute not found
else: # Attribute not found
raise AttributeError(
"Attribute '%s' not found in target '%s'"
% (attrname, self.name))
@ -328,7 +360,6 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
""" Return the target instance starting from the target name """
return target(target_name, Target.get_json_target_data())
@property
def program_cycle_s(self):
"""Special override for program_cycle_s as it's default value depends
@ -356,7 +387,7 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
def is_PSA_non_secure_target(self):
return 'NSPE_Target' in self.labels
def get_post_build_hook(self, toolchain):
def get_post_build_hook(self, toolchain_labels):
"""Initialize the post-build hooks for a toolchain. For now, this
function only allows "post binary" hooks (hooks that are executed
after the binary image is extracted from the executable file)
@ -364,13 +395,15 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
Positional Arguments:
hook - the hook object to add post-binary-hooks to
toolchain - the toolchain object for inspection
Return Value:
A callable if any post-build hook is applicable or None
"""
# If there's no hook, simply return
try:
hook_data = self.post_binary_hook
except AttributeError:
return
return None
# A hook was found. The hook's name is in the format
# "classname.functionname"
temp = hook_data["function"].split(".")
@ -383,8 +416,7 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
# "class_name" must refer to a class in this file, so check if the
# class exists
mdata = self.get_module_data()
if class_name not in mdata or \
not inspect.isclass(mdata[class_name]):
if not inspect.isclass(mdata.get(class_name, None)):
raise HookError(
("Class '%s' required by '%s' in target '%s'"
% (class_name, hook_data["function"], self.name)) +
@ -392,26 +424,25 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
# "function_name" must refer to a static function inside class
# "class_name"
cls = mdata[class_name]
if (not hasattr(cls, function_name)) or \
(not inspect.isfunction(getattr(cls, function_name))):
if not inspect.isfunction(getattr(cls, function_name, None)):
raise HookError(
("Static function '%s' " % function_name) +
("required by '%s' " % hook_data["function"]) +
("in target '%s' " % self.name) +
("not found in class '%s'" % class_name))
("not found in class '%s'" % class_name))
# Check if the hook specification also has toolchain restrictions
toolchain_restrictions = set(hook_data.get("toolchains", []))
toolchain_labels = set(c.__name__ for c in getmro(toolchain.__class__))
if toolchain_restrictions and \
not toolchain_labels.intersection(toolchain_restrictions):
return
not set(toolchain_labels).intersection(toolchain_restrictions):
return None
return getattr(cls, function_name)
################################################################################
###############################################################################
# Target specific code goes in this section
# This code can be invoked from the target description using the
# "post_binary_hook" key
class LPCTargetCode(object):
"""General LPC Target patching code"""
@staticmethod
@ -420,6 +451,7 @@ class LPCTargetCode(object):
t_self.notify.debug("LPC Patch: %s" % os.path.split(binf)[1])
patch(binf)
class LPC4088Code(object):
"""Code specific to the LPC4088"""
@staticmethod
@ -451,18 +483,22 @@ class LPC4088Code(object):
# file to 'binf'
shutil.rmtree(binf, True)
os.rename(binf + '.temp', binf)
t_self.notify.debug("Generated custom binary file (internal flash + SPIFI)")
t_self.notify.debug(
"Generated custom binary file (internal flash + SPIFI)"
)
LPCTargetCode.lpc_patch(t_self, resources, elf, binf)
class TEENSY3_1Code(object):
"""Hooks for the TEENSY3.1"""
@staticmethod
def binary_hook(t_self, resources, elf, binf):
"""Hook that is run after elf is generated"""
# This function is referenced by old versions of targets.json and should
# be kept for backwards compatibility.
# This function is referenced by old versions of targets.json and
# should be kept for backwards compatibility.
pass
class MTSCode(object):
"""Generic MTS code"""
@staticmethod
@ -507,6 +543,7 @@ class MTSCode(object):
"""A hook for the MTB MTS Dragonfly"""
MTSCode._combine_bins_helper("MTB_MTS_DRAGONFLY", binf)
class MCU_NRF51Code(object):
"""NRF51 Hooks"""
@staticmethod
@ -514,8 +551,8 @@ class MCU_NRF51Code(object):
"""Hook that merges the soft device with the bin file"""
# Scan to find the actual paths of soft device
sdf = None
for softdevice_and_offset_entry\
in t_self.target.EXPECTED_SOFTDEVICES_WITH_OFFSETS:
sd_with_offsets = t_self.target.EXPECTED_SOFTDEVICES_WITH_OFFSETS
for softdevice_and_offset_entry in sd_with_offsets:
for hexf in resources.get_file_paths(FileType.HEX):
if hexf.find(softdevice_and_offset_entry['name']) != -1:
t_self.notify.debug("SoftDevice file found %s."
@ -537,8 +574,10 @@ class MCU_NRF51Code(object):
if t_self.target.MERGE_BOOTLOADER is True:
for hexf in resources.get_file_paths(FileType.HEX):
if hexf.find(t_self.target.OVERRIDE_BOOTLOADER_FILENAME) != -1:
t_self.notify.debug("Bootloader file found %s."
% t_self.target.OVERRIDE_BOOTLOADER_FILENAME)
t_self.notify.debug(
"Bootloader file found %s."
% t_self.target.OVERRIDE_BOOTLOADER_FILENAME
)
blf = hexf
break
elif hexf.find(softdevice_and_offset_entry['boot']) != -1:
@ -572,6 +611,7 @@ class MCU_NRF51Code(object):
with open(binf.replace(".bin", ".hex"), "w") as fileout:
binh.write_hex_file(fileout, write_start_addr=False)
class NCS36510TargetCode:
@staticmethod
def ncs36510_addfib(t_self, resources, elf, binf):
@ -579,6 +619,7 @@ class NCS36510TargetCode:
print("binf ", binf)
add_fib_at_start(binf[:-4])
class RTL8195ACode:
"""RTL8195A Hooks"""
@staticmethod
@ -586,6 +627,7 @@ class RTL8195ACode:
from tools.targets.REALTEK_RTL8195AM import rtl8195a_elf2bin
rtl8195a_elf2bin(t_self, elf, binf)
class PSOC6Code:
@staticmethod
def complete(t_self, resources, elf, binf):
@ -599,19 +641,27 @@ class PSOC6Code:
else:
psoc6_complete(t_self, elf, binf)
class LPC55S69Code:
"""LPC55S69 Hooks"""
@staticmethod
def binary_hook(t_self, resources, elf, binf):
from tools.targets.LPC55S69 import lpc55s69_complete
configured_secure_image_filename = t_self.target.secure_image_filename
secure_bin = find_secure_image(t_self.notify, resources, binf, configured_secure_image_filename, FileType.BIN)
secure_bin = find_secure_image(
t_self.notify,
resources,
binf,
configured_secure_image_filename,
FileType.BIN
)
lpc55s69_complete(t_self, binf, secure_bin)
################################################################################
# Instantiate all public targets
# End Target specific section
###############################################################################
def update_target_data():
"""Instantiate all public targets"""
TARGETS[:] = [Target.get_target(tgt) for tgt, obj
in Target.get_json_target_data().items()
if obj.get("public", True)]
@ -620,6 +670,7 @@ def update_target_data():
TARGET_MAP.update(dict([(tgt.name, tgt) for tgt in TARGETS]))
TARGET_NAMES[:] = TARGET_MAP.keys()
TARGETS = []
TARGET_MAP = dict()
TARGET_NAMES = []
@ -629,6 +680,7 @@ update_target_data()
# Some targets with different name have the same exporters
EXPORT_MAP = {}
# Detection APIs
def get_target_detect_codes():
""" Returns dictionary mapping detect_code -> platform_name
@ -639,6 +691,7 @@ def get_target_detect_codes():
result[detect_code] = tgt.name
return result
def set_targets_json_location(location=None):
"""Sets the location of the JSON file that contains the targets"""
# First instruct Target about the new location
@ -648,4 +701,3 @@ def set_targets_json_location(location=None):
# instead. This ensures compatibility with code that does
# "from tools.targets import TARGET_NAMES"
update_target_data()

View File

@ -136,8 +136,6 @@ class mbedToolchain:
self.target = target
self.name = self.__class__.__name__
# compile/assemble/link/binary hooks
self._post_build_hook = target.get_post_build_hook(self.name)
# Toolchain flags
self.flags = deepcopy(build_profile or self.profile_template)
@ -754,9 +752,13 @@ class mbedToolchain:
else:
updatable = None
if self._post_build_hook:
# compile/assemble/link/binary hooks
post_build_hook = self.target.get_post_build_hook(
self._get_toolchain_labels()
)
if post_build_hook:
self.progress("post-build", name)
self._post_build_hook(self, r, elf, full_path)
post_build_hook(self, r, elf, full_path)
# Initialize memap and process map file. This doesn't generate output.
self.mem_stats(mapfile)