mirror of https://github.com/ARMmbed/mbed-os.git
Format targets.py
parent
9d09f8e049
commit
bb43f8d37a
434
tools/targets.py
434
tools/targets.py
|
|
@ -15,6 +15,16 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import binascii
|
||||||
|
import struct
|
||||||
|
import shutil
|
||||||
|
import inspect
|
||||||
|
import sys
|
||||||
|
from tools.patch import patch
|
||||||
|
from tools.paths import TOOLS_BOOTLOADERS
|
||||||
|
from tools.utils import json_file_to_dict
|
||||||
|
|
||||||
CORE_LABELS = {
|
CORE_LABELS = {
|
||||||
"ARM7TDMI-S": ["ARM7", "LIKE_CORTEX_ARM7"],
|
"ARM7TDMI-S": ["ARM7", "LIKE_CORTEX_ARM7"],
|
||||||
"Cortex-M0" : ["M0", "CORTEX_M", "LIKE_CORTEX_M0"],
|
"Cortex-M0" : ["M0", "CORTEX_M", "LIKE_CORTEX_M0"],
|
||||||
|
|
@ -29,243 +39,313 @@ CORE_LABELS = {
|
||||||
"Cortex-A9" : ["A9", "CORTEX_A", "LIKE_CORTEX_A9"]
|
"Cortex-A9" : ["A9", "CORTEX_A", "LIKE_CORTEX_A9"]
|
||||||
}
|
}
|
||||||
|
|
||||||
import os
|
################################################################################
|
||||||
import binascii
|
|
||||||
import struct
|
|
||||||
import shutil
|
|
||||||
from tools.patch import patch
|
|
||||||
from paths import TOOLS_BOOTLOADERS
|
|
||||||
import json
|
|
||||||
import inspect
|
|
||||||
import sys
|
|
||||||
from tools.utils import json_file_to_dict
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
# Generic Target class that reads and interprets the data in targets.json
|
# Generic Target class that reads and interprets the data in targets.json
|
||||||
|
|
||||||
# A simple class that represents all the exceptions associated with hooking
|
|
||||||
class HookError(Exception):
|
class HookError(Exception):
|
||||||
|
""" A simple class that represents all the exceptions associated with
|
||||||
|
hooking
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# A simple decorator used for automatically caching data returned by a function
|
CACHES = {}
|
||||||
caches = {}
|
|
||||||
def cached(func):
|
def cached(func):
|
||||||
|
"""A simple decorator used for automatically caching data returned by a
|
||||||
|
function
|
||||||
|
"""
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
if not caches.has_key((func.__name__, args)):
|
"""The wrapped function itself"""
|
||||||
caches[(func.__name__, args)] = func(*args, **kwargs)
|
if not CACHES.has_key((func.__name__, args)):
|
||||||
return caches[(func.__name__, args)]
|
CACHES[(func.__name__, args)] = func(*args, **kwargs)
|
||||||
|
return CACHES[(func.__name__, args)]
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
class Target:
|
class Target(object):
|
||||||
|
"""An object to represent a Target (MCU/Board)"""
|
||||||
# Cumulative attributes can have values appended to them, so they
|
# Cumulative attributes can have values appended to them, so they
|
||||||
# need to be computed differently than regular attributes
|
# need to be computed differently than regular attributes
|
||||||
cumulative_attributes = ['extra_labels', 'macros', 'device_has', 'features']
|
cumulative_attributes = ['extra_labels', 'macros', 'device_has', 'features']
|
||||||
|
|
||||||
# List of targets that were added dynamically using "add_py_targets" (see below)
|
# List of targets that were added dynamically using "add_py_targets" (see
|
||||||
|
# below)
|
||||||
__py_targets = set()
|
__py_targets = set()
|
||||||
|
|
||||||
# Default location of the 'targets.json' file
|
# Default location of the 'targets.json' file
|
||||||
__targets_json_location_default = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'hal', 'targets.json')
|
__targets_json_location_default = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)), '..', 'hal', 'targets.json')
|
||||||
|
|
||||||
# Current/new location of the 'targets.json' file
|
# Current/new location of the 'targets.json' file
|
||||||
__targets_json_location = None
|
__targets_json_location = None
|
||||||
|
|
||||||
# Load the description of JSON target data
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@cached
|
@cached
|
||||||
def get_json_target_data():
|
def get_json_target_data():
|
||||||
return json_file_to_dict(Target.__targets_json_location or Target.__targets_json_location_default)
|
"""Load the description of JSON target data"""
|
||||||
|
return json_file_to_dict(Target.__targets_json_location or
|
||||||
|
Target.__targets_json_location_default)
|
||||||
|
|
||||||
# Set the location of the targets.json file
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_targets_json_location(location=None):
|
def set_targets_json_location(location=None):
|
||||||
Target.__targets_json_location = location or Target.__targets_json_location_default
|
"""Set the location of the targets.json file"""
|
||||||
|
Target.__targets_json_location = (location or
|
||||||
|
Target.__targets_json_location_default)
|
||||||
# Invalidate caches, since the location of the JSON file changed
|
# Invalidate caches, since the location of the JSON file changed
|
||||||
caches.clear()
|
CACHES.clear()
|
||||||
|
|
||||||
# Get the members of this module using Python's "inspect" module
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@cached
|
@cached
|
||||||
def get_module_data():
|
def get_module_data():
|
||||||
return dict([(m[0], m[1]) for m in inspect.getmembers(sys.modules[__name__])])
|
"""Get the members of this module using Python's "inspect" module"""
|
||||||
|
return dict([(m[0], m[1]) for m in
|
||||||
|
inspect.getmembers(sys.modules[__name__])])
|
||||||
|
|
||||||
# Return the order in which target descriptions are searched for attributes
|
def __get_resolution_order(self, target_name, order, level=0):
|
||||||
# This mimics the Python 2.2 method resolution order, which is what the old targets.py module used
|
""" Return the order in which target descriptions are searched for
|
||||||
# For more details, check http://makina-corpus.com/blog/metier/2014/python-tutorial-understanding-python-mro-class-search-path
|
attributes. This mimics the Python 2.2 method resolution order, which
|
||||||
# The resolution order contains (name, level) tuples, where "name" is the name of the class and "level"
|
is what the old targets.py module used. For more details, check
|
||||||
# is the level in the inheritance hierarchy (the target itself is at level 0, its first parent at level 1,
|
http://makina-corpus.com/blog/metier/2014/python-tutorial-understanding-python-mro-class-search-path
|
||||||
# its parent's parent at level 1 and so on)
|
The resolution order contains (name, level) tuples, where "name" is the
|
||||||
def __get_resolution_order(self, target_name, order, level = 0):
|
name of the class and "level" is the level in the inheritance hierarchy
|
||||||
if not target_name in [l[0] for l in order]: # the resolution order can't contain duplicate target names
|
(the target itself is at level 0, its first parent at level 1, its
|
||||||
|
parent's parent at level 2 and so on)
|
||||||
|
"""
|
||||||
|
# the resolution order can't contain duplicate target names
|
||||||
|
if not target_name in [l[0] for l in order]:
|
||||||
order.append((target_name, level))
|
order.append((target_name, level))
|
||||||
parents = self.get_json_target_data()[target_name].get("inherits", [])
|
parents = self.get_json_target_data()[target_name].get("inherits", [])
|
||||||
for p in parents:
|
for par in parents:
|
||||||
order = self.__get_resolution_order(p, order, level + 1)
|
order = self.__get_resolution_order(par, order, level + 1)
|
||||||
return order
|
return order
|
||||||
|
|
||||||
# Modify the exporter specification ("progen") by changing all "template" keys to full paths
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __add_paths_to_progen(data):
|
def __add_paths_to_progen(data):
|
||||||
|
"""Modify the exporter specification ("progen") by changing all
|
||||||
|
"template" keys to full paths
|
||||||
|
"""
|
||||||
out = {}
|
out = {}
|
||||||
for key, value in data.items():
|
for key, val in data.items():
|
||||||
if isinstance(value, dict):
|
if isinstance(val, dict):
|
||||||
out[key] = Target.__add_paths_to_progen(value)
|
out[key] = Target.__add_paths_to_progen(val)
|
||||||
elif key == "template":
|
elif key == "template":
|
||||||
out[key] = [os.path.join(os.path.dirname(__file__), 'export', v) for v in value]
|
out[key] = [os.path.join(os.path.dirname(__file__), 'export', v)
|
||||||
|
for v in val]
|
||||||
else:
|
else:
|
||||||
out[key] = value
|
out[key] = val
|
||||||
return out
|
return out
|
||||||
|
|
||||||
# Comute the value of a given target attribute
|
def __getattr_cumulative(self, attrname):
|
||||||
def __getattr_helper(self, attrname):
|
"""Look for the attribute in the class and its parents, as defined by
|
||||||
|
the resolution order
|
||||||
|
"""
|
||||||
tdata = self.get_json_target_data()
|
tdata = self.get_json_target_data()
|
||||||
if attrname in self.__cumulative_attributes:
|
# For a cumulative attribute, figure out when it was defined the
|
||||||
# For a cumulative attribute, figure out when it was defined the last time (in attribute
|
# last time (in attribute resolution order) then follow the "_add"
|
||||||
# resolution order) then follow the "_add" and "_remove" data fields
|
# and "_remove" data fields
|
||||||
for idx, t in enumerate(self.resolution_order):
|
for idx, target in enumerate(self.resolution_order):
|
||||||
if attrname in tdata[t[0]]: # the attribute was defined at this level in the resolution order
|
# the attribute was defined at this level in the resolution
|
||||||
def_idx = idx
|
# order
|
||||||
break
|
if attrname in tdata[target[0]]:
|
||||||
else:
|
def_idx = idx
|
||||||
raise AttributeError("Attribute '%s' not found in target '%s'" % (attrname, self.name))
|
|
||||||
# Get the starting value of the attribute
|
|
||||||
v = (tdata[self.resolution_order[def_idx][0]][attrname] or [])[:]
|
|
||||||
# Traverse the resolution list in high inheritance to low inheritance level, left to right order
|
|
||||||
# to figure out all the other classes that change the definition by adding or removing elements
|
|
||||||
for idx in xrange(self.resolution_order[def_idx][1] - 1, -1, -1):
|
|
||||||
same_level_targets = [t[0] for t in self.resolution_order if t[1] == idx]
|
|
||||||
for t in same_level_targets:
|
|
||||||
data = tdata[t]
|
|
||||||
# Do we have anything to add ?
|
|
||||||
if data.has_key(attrname + "_add"):
|
|
||||||
v.extend(data[attrname + "_add"])
|
|
||||||
# Do we have anything to remove ?
|
|
||||||
if data.has_key(attrname + "_remove"):
|
|
||||||
# Macros can be defined either without a value (MACRO) or with a value (MACRO=10).
|
|
||||||
# When removing, we specify only the name of the macro, without the value. So we need
|
|
||||||
# to create a mapping between the macro name and its value. This will work for
|
|
||||||
# extra_labels and other type of arrays as well, since they fall into the "macros
|
|
||||||
# without a value" category (simple definitions without a value).
|
|
||||||
name_def_map = {}
|
|
||||||
for crtv in v:
|
|
||||||
if crtv.find('=') != -1:
|
|
||||||
temp = crtv.split('=')
|
|
||||||
if len(temp) != 2:
|
|
||||||
raise ValueError("Invalid macro definition '%s'" % crtv)
|
|
||||||
name_def_map[temp[0]] = crtv
|
|
||||||
else:
|
|
||||||
name_def_map[crtv] = crtv
|
|
||||||
for e in data[attrname + "_remove"]:
|
|
||||||
if not e in name_def_map:
|
|
||||||
raise ValueError("Unable to remove '%s' in '%s.%s' since it doesn't exist" % (e, self.name, attrname))
|
|
||||||
v.remove(name_def_map[e])
|
|
||||||
return v
|
|
||||||
# Look for the attribute in the class and its parents, as defined by the resolution order
|
|
||||||
v = None
|
|
||||||
for t in self.resolution_order:
|
|
||||||
data = tdata[t[0]]
|
|
||||||
if data.has_key(attrname):
|
|
||||||
v = data[attrname]
|
|
||||||
break
|
break
|
||||||
else: # Attribute not found
|
else:
|
||||||
raise AttributeError("Attribute '%s' not found in target '%s'" % (attrname, self.name))
|
raise AttributeError("Attribute '%s' not found in target '%s'"
|
||||||
# 'progen' needs the full path to the template (the path in JSON is relative to tools/export)
|
% (attrname, self.name))
|
||||||
return v if attrname != "progen" else self.__add_paths_to_progen(v)
|
# Get the starting value of the attribute
|
||||||
|
starting_value = (tdata[self.resolution_order[def_idx][0]][attrname]
|
||||||
|
or [])[:]
|
||||||
|
# Traverse the resolution list in high inheritance to low
|
||||||
|
# inheritance level, left to right order to figure out all the
|
||||||
|
# other classes that change the definition by adding or removing
|
||||||
|
# elements
|
||||||
|
for idx in xrange(self.resolution_order[def_idx][1] - 1, -1, -1):
|
||||||
|
same_level_targets = [tar[0] for tar in self.resolution_order
|
||||||
|
if tar[1] == idx]
|
||||||
|
for tar in same_level_targets:
|
||||||
|
data = tdata[tar]
|
||||||
|
# Do we have anything to add ?
|
||||||
|
if data.has_key(attrname + "_add"):
|
||||||
|
starting_value.extend(data[attrname + "_add"])
|
||||||
|
# Do we have anything to remove ?
|
||||||
|
if data.has_key(attrname + "_remove"):
|
||||||
|
# Macros can be defined either without a value (MACRO)
|
||||||
|
# or with a value (MACRO=10). When removing, we specify
|
||||||
|
# only the name of the macro, without the value. So we
|
||||||
|
# need to create a mapping between the macro name and
|
||||||
|
# its value. This will work for extra_labels and other
|
||||||
|
# type of arrays as well, since they fall into the
|
||||||
|
# "macros without a value" category (simple definitions
|
||||||
|
# without a value).
|
||||||
|
name_def_map = {}
|
||||||
|
for crtv in starting_value:
|
||||||
|
if crtv.find('=') != -1:
|
||||||
|
temp = crtv.split('=')
|
||||||
|
if len(temp) != 2:
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid macro definition '%s'" % crtv)
|
||||||
|
name_def_map[temp[0]] = crtv
|
||||||
|
else:
|
||||||
|
name_def_map[crtv] = crtv
|
||||||
|
for element in data[attrname + "_remove"]:
|
||||||
|
if not element in name_def_map:
|
||||||
|
raise ValueError(
|
||||||
|
("Unable to remove '%s' in '%s.%s' since "
|
||||||
|
% (element, self.name, attrname)) +
|
||||||
|
"it doesn't exist")
|
||||||
|
starting_value.remove(name_def_map[element])
|
||||||
|
return starting_value
|
||||||
|
|
||||||
|
def __getattr_helper(self, attrname):
|
||||||
|
"""Compute the value of a given target attribute"""
|
||||||
|
if attrname in self.cumulative_attributes:
|
||||||
|
return self.__getattr_cumulative(attrname)
|
||||||
|
else:
|
||||||
|
tdata = self.get_json_target_data()
|
||||||
|
starting_value = None
|
||||||
|
for target in self.resolution_order:
|
||||||
|
data = tdata[target[0]]
|
||||||
|
if data.has_key(attrname):
|
||||||
|
starting_value = data[attrname]
|
||||||
|
break
|
||||||
|
else: # Attribute not found
|
||||||
|
raise AttributeError(
|
||||||
|
"Attribute '%s' not found in target '%s'"
|
||||||
|
% (attrname, self.name))
|
||||||
|
# 'progen' needs the full path to the template (the path in JSON is
|
||||||
|
# relative to tools/export)
|
||||||
|
if attrname == "progen":
|
||||||
|
return self.__add_paths_to_progen(starting_value)
|
||||||
|
else:
|
||||||
|
return starting_value
|
||||||
|
|
||||||
# Return the value of an attribute
|
|
||||||
# This function only computes the attribute's value once, then adds it to the instance attributes
|
|
||||||
# (in __dict__), so the next time it is returned directly
|
|
||||||
def __getattr__(self, attrname):
|
def __getattr__(self, attrname):
|
||||||
v = self.__getattr_helper(attrname)
|
""" Return the value of an attribute. This function only computes the
|
||||||
self.__dict__[attrname] = v
|
attribute's value once, then adds it to the instance attributes (in
|
||||||
return v
|
__dict__), so the next time it is returned directly
|
||||||
|
"""
|
||||||
|
result = self.__getattr_helper(attrname)
|
||||||
|
self.__dict__[attrname] = result
|
||||||
|
return result
|
||||||
|
|
||||||
# Add one or more new target(s) represented as a Python dictionary in 'new_targets'
|
|
||||||
# It is an error to add a target with a name that already exists.
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_py_targets(new_targets):
|
def add_py_targets(new_targets):
|
||||||
|
"""Add one or more new target(s) represented as a Python dictionary
|
||||||
|
in 'new_targets'. It is an error to add a target with a name that
|
||||||
|
already exists.
|
||||||
|
"""
|
||||||
crt_data = Target.get_json_target_data()
|
crt_data = Target.get_json_target_data()
|
||||||
for tk, tv in new_targets.items():
|
for target_key, target_value in new_targets.items():
|
||||||
if crt_data.has_key(tk):
|
if crt_data.has_key(target_key):
|
||||||
raise Exception("Attempt to add target '%s' that already exists" % tk)
|
raise Exception(
|
||||||
|
"Attempt to add target '%s' that already exists"
|
||||||
|
% target_key)
|
||||||
# Add target data to the internal target dictionary
|
# Add target data to the internal target dictionary
|
||||||
crt_data[tk] = tv
|
crt_data[target_key] = target_value
|
||||||
# Create the new target and add it to the relevant data structures
|
# Create the new target and add it to the relevant data structures
|
||||||
new_target = Target(tk)
|
new_target = Target(target_key)
|
||||||
TARGETS.append(new_target)
|
TARGETS.append(new_target)
|
||||||
TARGET_MAP[tk] = new_target
|
TARGET_MAP[target_key] = new_target
|
||||||
TARGET_NAMES.append(tk)
|
TARGET_NAMES.append(target_key)
|
||||||
|
|
||||||
# Return the target instance starting from the target name
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@cached
|
@cached
|
||||||
def get_target(name):
|
def get_target(target_name):
|
||||||
return Target(name)
|
""" Return the target instance starting from the target name """
|
||||||
|
return Target(target_name)
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, target_name):
|
||||||
self.name = name
|
self.name = target_name
|
||||||
|
|
||||||
# Compute resolution order once (it will be used later in __getattr__)
|
# Compute resolution order once (it will be used later in __getattr__)
|
||||||
self.resolution_order = self.__get_resolution_order(self.name, [])
|
self.resolution_order = self.__get_resolution_order(self.name, [])
|
||||||
# Create also a list with only the names of the targets in the resolution order
|
# Create also a list with only the names of the targets in the
|
||||||
self.resolution_order_names = [t[0] for t in self.resolution_order]
|
# resolution order
|
||||||
|
self.resolution_order_names = [target[0] for target
|
||||||
|
in self.resolution_order]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def program_cycle_s(self):
|
def program_cycle_s(self):
|
||||||
|
"""Special override for program_cycle_s as it's default value depends
|
||||||
|
upon is_disk_virtual
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
return self.__getattr__("program_cycle_s")
|
return self.__getattr__("program_cycle_s")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return 4 if self.is_disk_virtual else 1.5
|
return 4 if self.is_disk_virtual else 1.5
|
||||||
|
|
||||||
def get_labels(self):
|
def get_labels(self):
|
||||||
|
"""Get all possible labels for this target"""
|
||||||
labels = [self.name] + CORE_LABELS[self.core] + self.extra_labels
|
labels = [self.name] + CORE_LABELS[self.core] + self.extra_labels
|
||||||
# Automatically define UVISOR_UNSUPPORTED if the target doesn't specifically
|
# Automatically define UVISOR_UNSUPPORTED if the target doesn't
|
||||||
# define UVISOR_SUPPORTED
|
# specifically define UVISOR_SUPPORTED
|
||||||
if not "UVISOR_SUPPORTED" in labels:
|
if not "UVISOR_SUPPORTED" in labels:
|
||||||
labels.append("UVISOR_UNSUPPORTED")
|
labels.append("UVISOR_UNSUPPORTED")
|
||||||
return labels
|
return labels
|
||||||
|
|
||||||
# For now, this function only allows "post binary" hooks (hooks that are executed after
|
|
||||||
# the binary image is extracted from the executable file)
|
|
||||||
def init_hooks(self, hook, toolchain_name):
|
def init_hooks(self, hook, toolchain_name):
|
||||||
|
"""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)
|
||||||
|
"""
|
||||||
|
|
||||||
# If there's no hook, simply return
|
# If there's no hook, simply return
|
||||||
try:
|
try:
|
||||||
hook_data = self.post_binary_hook
|
hook_data = self.post_binary_hook
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return
|
return
|
||||||
# A hook was found. The hook's name is in the format "classname.functionname"
|
# A hook was found. The hook's name is in the format
|
||||||
|
# "classname.functionname"
|
||||||
temp = hook_data["function"].split(".")
|
temp = hook_data["function"].split(".")
|
||||||
if len(temp) != 2:
|
if len(temp) != 2:
|
||||||
raise HookError("Invalid format for hook '%s' in target '%s' (must be 'class_name.function_name')" % (hook_data["function"], self.name))
|
raise HookError(
|
||||||
|
("Invalid format for hook '%s' in target '%s'"
|
||||||
|
% (hook_data["function"], self.name)) +
|
||||||
|
" (must be 'class_name.function_name')")
|
||||||
class_name, function_name = temp[0], temp[1]
|
class_name, function_name = temp[0], temp[1]
|
||||||
# "class_name" must refer to a class in this file, so check if the class exists
|
# "class_name" must refer to a class in this file, so check if the
|
||||||
|
# class exists
|
||||||
mdata = self.get_module_data()
|
mdata = self.get_module_data()
|
||||||
if not mdata.has_key(class_name) or not inspect.isclass(mdata[class_name]):
|
if not mdata.has_key(class_name) or \
|
||||||
raise HookError("Class '%s' required by '%s' in target '%s' not found in targets.py" % (class_name, hook_data["function"], self.name))
|
not inspect.isclass(mdata[class_name]):
|
||||||
# "function_name" must refer to a static function inside class "class_name"
|
raise HookError(
|
||||||
|
("Class '%s' required by '%s' in target '%s'"
|
||||||
|
% (class_name, hook_data["function"], self.name)) +
|
||||||
|
" not found in targets.py")
|
||||||
|
# "function_name" must refer to a static function inside class
|
||||||
|
# "class_name"
|
||||||
cls = mdata[class_name]
|
cls = mdata[class_name]
|
||||||
if (not hasattr(cls, function_name)) or (not inspect.isfunction(getattr(cls, function_name))):
|
if (not hasattr(cls, function_name)) or \
|
||||||
raise HookError("Static function '%s' required by '%s' in target '%s' not found in class '%s'" % (function_name, hook_data["function"], self.name, class_name))
|
(not inspect.isfunction(getattr(cls, function_name))):
|
||||||
|
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))
|
||||||
# Check if the hook specification also has target restrictions
|
# Check if the hook specification also has target restrictions
|
||||||
toolchain_restrictions = hook_data.get("toolchains", [])
|
toolchain_restrictions = hook_data.get("toolchains", [])
|
||||||
if toolchain_restrictions and (toolchain_name not in toolchain_restrictions):
|
if toolchain_restrictions and \
|
||||||
|
(toolchain_name not in toolchain_restrictions):
|
||||||
return
|
return
|
||||||
# Finally, hook the requested function
|
# Finally, hook the requested function
|
||||||
hook.hook_add_binary("post", getattr(cls, function_name))
|
hook.hook_add_binary("post", getattr(cls, function_name))
|
||||||
|
|
||||||
########################################################################################################################
|
################################################################################
|
||||||
# Target specific code goes in this section
|
# Target specific code goes in this section
|
||||||
# This code can be invoked from the target description using the "post_binary_hook" key
|
# This code can be invoked from the target description using the
|
||||||
|
# "post_binary_hook" key
|
||||||
|
|
||||||
class LPCTargetCode:
|
class LPCTargetCode(object):
|
||||||
|
"""General LPC Target patching code"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def lpc_patch(t_self, resources, elf, binf):
|
def lpc_patch(t_self, resources, elf, binf):
|
||||||
|
"""Patch an elf file"""
|
||||||
t_self.debug("LPC Patch: %s" % os.path.split(binf)[1])
|
t_self.debug("LPC Patch: %s" % os.path.split(binf)[1])
|
||||||
patch(binf)
|
patch(binf)
|
||||||
|
|
||||||
class LPC4088Code:
|
class LPC4088Code(object):
|
||||||
|
"""Code specific to the LPC4088"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def binary_hook(t_self, resources, elf, binf):
|
def binary_hook(t_self, resources, elf, binf):
|
||||||
|
"""Hook to be run after an elf file is built"""
|
||||||
if not os.path.isdir(binf):
|
if not os.path.isdir(binf):
|
||||||
# Regular binary file, nothing to do
|
# Regular binary file, nothing to do
|
||||||
LPCTargetCode.lpc_patch(t_self, resources, elf, binf)
|
LPCTargetCode.lpc_patch(t_self, resources, elf, binf)
|
||||||
|
|
@ -294,19 +374,23 @@ class LPC4088Code:
|
||||||
t_self.debug("Generated custom binary file (internal flash + SPIFI)")
|
t_self.debug("Generated custom binary file (internal flash + SPIFI)")
|
||||||
LPCTargetCode.lpc_patch(t_self, resources, elf, binf)
|
LPCTargetCode.lpc_patch(t_self, resources, elf, binf)
|
||||||
|
|
||||||
class TEENSY3_1Code:
|
class TEENSY3_1Code(object):
|
||||||
|
"""Hooks for the TEENSY3.1"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def binary_hook(t_self, resources, elf, binf):
|
def binary_hook(t_self, resources, elf, binf):
|
||||||
|
"""Hook that is run after elf is generated"""
|
||||||
from intelhex import IntelHex
|
from intelhex import IntelHex
|
||||||
binh = IntelHex()
|
binh = IntelHex()
|
||||||
binh.loadbin(binf, offset = 0)
|
binh.loadbin(binf, offset=0)
|
||||||
|
|
||||||
with open(binf.replace(".bin", ".hex"), "w") as f:
|
with open(binf.replace(".bin", ".hex"), "w") as file_desc:
|
||||||
binh.tofile(f, format='hex')
|
binh.tofile(file_desc, format='hex')
|
||||||
|
|
||||||
class MTSCode:
|
class MTSCode(object):
|
||||||
|
"""Generic MTS code"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _combine_bins_helper(target_name, t_self, resources, elf, binf):
|
def _combine_bins_helper(target_name, t_self, resources, elf, binf):
|
||||||
|
"""combine bins with the bootloader for a particular target"""
|
||||||
loader = os.path.join(TOOLS_BOOTLOADERS, target_name, "bootloader.bin")
|
loader = os.path.join(TOOLS_BOOTLOADERS, target_name, "bootloader.bin")
|
||||||
target = binf + ".tmp"
|
target = binf + ".tmp"
|
||||||
if not os.path.exists(loader):
|
if not os.path.exists(loader):
|
||||||
|
|
@ -333,51 +417,64 @@ class MTSCode:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def combine_bins_mts_dot(t_self, resources, elf, binf):
|
def combine_bins_mts_dot(t_self, resources, elf, binf):
|
||||||
MTSCode._combine_bins_helper("MTS_MDOT_F411RE", t_self, resources, elf, binf)
|
"""A hook for the MTS MDOT"""
|
||||||
|
MTSCode._combine_bins_helper("MTS_MDOT_F411RE", t_self, resources, elf,
|
||||||
|
binf)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def combine_bins_mts_dragonfly(t_self, resources, elf, binf):
|
def combine_bins_mts_dragonfly(t_self, resources, elf, binf):
|
||||||
MTSCode._combine_bins_helper("MTS_DRAGONFLY_F411RE", t_self, resources, elf, binf)
|
"""A hoof for the MTS Dragonfly"""
|
||||||
|
MTSCode._combine_bins_helper("MTS_DRAGONFLY_F411RE", t_self, resources,
|
||||||
|
elf, binf)
|
||||||
|
|
||||||
class MCU_NRF51Code:
|
class MCU_NRF51Code(object):
|
||||||
|
"""NRF51 Hooks"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def binary_hook(t_self, resources, elf, binf):
|
def binary_hook(t_self, resources, elf, binf):
|
||||||
|
"""Hook that merges the soft device with the bin file"""
|
||||||
# Scan to find the actual paths of soft device
|
# Scan to find the actual paths of soft device
|
||||||
sdf = None
|
sdf = None
|
||||||
for softdeviceAndOffsetEntry in t_self.target.EXPECTED_SOFTDEVICES_WITH_OFFSETS:
|
for softdevice_and_offset_entry\
|
||||||
|
in t_self.target.EXPECTED_SOFTDEVICES_WITH_OFFSETS:
|
||||||
for hexf in resources.hex_files:
|
for hexf in resources.hex_files:
|
||||||
if hexf.find(softdeviceAndOffsetEntry['name']) != -1:
|
if hexf.find(softdevice_and_offset_entry['name']) != -1:
|
||||||
t_self.debug("SoftDevice file found %s." % softdeviceAndOffsetEntry['name'])
|
t_self.debug("SoftDevice file found %s."
|
||||||
|
% softdevice_and_offset_entry['name'])
|
||||||
sdf = hexf
|
sdf = hexf
|
||||||
|
|
||||||
if sdf is not None: break
|
if sdf is not None:
|
||||||
if sdf is not None: break
|
break
|
||||||
|
if sdf is not None:
|
||||||
|
break
|
||||||
|
|
||||||
if sdf is None:
|
if sdf is None:
|
||||||
t_self.debug("Hex file not found. Aborting.")
|
t_self.debug("Hex file not found. Aborting.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Look for bootloader file that matches this soft device or bootloader override image
|
# Look for bootloader file that matches this soft device or bootloader
|
||||||
|
# override image
|
||||||
blf = None
|
blf = None
|
||||||
if t_self.target.MERGE_BOOTLOADER is True:
|
if t_self.target.MERGE_BOOTLOADER is True:
|
||||||
for hexf in resources.hex_files:
|
for hexf in resources.hex_files:
|
||||||
if hexf.find(t_self.target.OVERRIDE_BOOTLOADER_FILENAME) != -1:
|
if hexf.find(t_self.target.OVERRIDE_BOOTLOADER_FILENAME) != -1:
|
||||||
t_self.debug("Bootloader file found %s." % t_self.target.OVERRIDE_BOOTLOADER_FILENAME)
|
t_self.debug("Bootloader file found %s."
|
||||||
|
% t_self.target.OVERRIDE_BOOTLOADER_FILENAME)
|
||||||
blf = hexf
|
blf = hexf
|
||||||
break
|
break
|
||||||
elif hexf.find(softdeviceAndOffsetEntry['boot']) != -1:
|
elif hexf.find(softdevice_and_offset_entry['boot']) != -1:
|
||||||
t_self.debug("Bootloader file found %s." % softdeviceAndOffsetEntry['boot'])
|
t_self.debug("Bootloader file found %s."
|
||||||
|
% softdevice_and_offset_entry['boot'])
|
||||||
blf = hexf
|
blf = hexf
|
||||||
break
|
break
|
||||||
|
|
||||||
# Merge user code with softdevice
|
# Merge user code with softdevice
|
||||||
from intelhex import IntelHex
|
from intelhex import IntelHex
|
||||||
binh = IntelHex()
|
binh = IntelHex()
|
||||||
binh.loadbin(binf, offset=softdeviceAndOffsetEntry['offset'])
|
binh.loadbin(binf, offset=softdevice_and_offset_entry['offset'])
|
||||||
|
|
||||||
if t_self.target.MERGE_SOFT_DEVICE is True:
|
if t_self.target.MERGE_SOFT_DEVICE is True:
|
||||||
t_self.debug("Merge SoftDevice file %s" % softdeviceAndOffsetEntry['name'])
|
t_self.debug("Merge SoftDevice file %s"
|
||||||
|
% softdevice_and_offset_entry['name'])
|
||||||
sdh = IntelHex(sdf)
|
sdh = IntelHex(sdf)
|
||||||
binh.merge(sdh)
|
binh.merge(sdh)
|
||||||
|
|
||||||
|
|
@ -386,13 +483,15 @@ class MCU_NRF51Code:
|
||||||
blh = IntelHex(blf)
|
blh = IntelHex(blf)
|
||||||
binh.merge(blh)
|
binh.merge(blh)
|
||||||
|
|
||||||
with open(binf.replace(".bin", ".hex"), "w") as f:
|
with open(binf.replace(".bin", ".hex"), "w") as fileout:
|
||||||
binh.tofile(f, format='hex')
|
binh.tofile(fileout, format='hex')
|
||||||
|
|
||||||
########################################################################################################################
|
################################################################################
|
||||||
|
|
||||||
# Instantiate all public targets
|
# Instantiate all public targets
|
||||||
TARGETS = [Target.get_target(name) for name, value in Target.get_json_target_data().items() if value.get("public", True)]
|
TARGETS = [Target.get_target(name) for name, value
|
||||||
|
in Target.get_json_target_data().items()
|
||||||
|
if value.get("public", True)]
|
||||||
|
|
||||||
# Map each target name to its unique instance
|
# Map each target name to its unique instance
|
||||||
TARGET_MAP = dict([(t.name, t) for t in TARGETS])
|
TARGET_MAP = dict([(t.name, t) for t in TARGETS])
|
||||||
|
|
@ -412,14 +511,17 @@ def get_target_detect_codes():
|
||||||
result[detect_code] = target.name
|
result[detect_code] = target.name
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Sets the location of the JSON file that contains the targets
|
|
||||||
def set_targets_json_location(location=None):
|
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
|
# First instruct Target about the new location
|
||||||
Target.set_targets_json_location(location)
|
Target.set_targets_json_location(location)
|
||||||
# Then re-initialize TARGETS, TARGET_MAP and TARGET_NAMES
|
# Then re-initialize TARGETS, TARGET_MAP and TARGET_NAMES. The
|
||||||
# The re-initialization does not create new variables, it keeps the old ones instead
|
# re-initialization does not create new variables, it keeps the old ones
|
||||||
# This ensures compatibility with code that does "from tools.targets import TARGET_NAMES"
|
# instead. This ensures compatibility with code that does
|
||||||
TARGETS[:] = [Target.get_target(name) for name, value in Target.get_json_target_data().items() if value.get("public", True)]
|
# "from tools.targets import TARGET_NAMES"
|
||||||
|
TARGETS[:] = [Target.get_target(name) for name, value
|
||||||
|
in Target.get_json_target_data().items()
|
||||||
|
if value.get("public", True)]
|
||||||
TARGET_MAP.clear()
|
TARGET_MAP.clear()
|
||||||
TARGET_MAP.update(dict([(t.name, t) for t in TARGETS]))
|
TARGET_MAP.update(dict([(target.name, target) for target in TARGETS]))
|
||||||
TARGET_NAMES[:] = TARGET_MAP.keys()
|
TARGET_NAMES[:] = TARGET_MAP.keys()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue