Format targets.py

pull/2458/head
Jimmy Brisson 2016-08-12 16:52:26 -05:00
parent 9d09f8e049
commit bb43f8d37a
1 changed files with 268 additions and 166 deletions

View File

@ -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()