mirror of https://github.com/ARMmbed/mbed-os.git
Improvements to the targets implementation
- added a method that can be used to new targets dynamically (this will be used by the configuration mechanism). - the JSON parser now keeps the order of the keys read from the JSON file (will also be used by the configuration mechanism). - there's now a global target cache in targets.py, so that a target with a given name will only be created once.
parent
8dc9b0b226
commit
7c920259cc
|
@ -37,7 +37,7 @@ from paths import TOOLS_BOOTLOADERS
|
||||||
import json
|
import json
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
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
|
||||||
|
@ -60,27 +60,17 @@ class Target:
|
||||||
# need to be computed differently than regular attributes
|
# need to be computed differently than regular attributes
|
||||||
__cumulative_attributes = ['extra_labels', 'macros']
|
__cumulative_attributes = ['extra_labels', 'macros']
|
||||||
|
|
||||||
# Utility function: traverse a dictionary and change all the strings in the dictionary to
|
# {target_name: target_instance} map for all the targets in the system
|
||||||
# ASCII from Unicode. Needed because the original mbed target definitions were written in
|
__target_map = {}
|
||||||
# Python and used only ASCII strings, but the Python JSON decoder always returns Unicode
|
|
||||||
# Based on http://stackoverflow.com/a/13105359
|
# List of targets that were added dynamically using "add_py_targets" (see below)
|
||||||
@staticmethod
|
__py_targets = set()
|
||||||
def to_ascii(input):
|
|
||||||
if isinstance(input, dict):
|
|
||||||
return dict([(Target.to_ascii(key), Target.to_ascii(value)) for key, value in input.iteritems()])
|
|
||||||
elif isinstance(input, list):
|
|
||||||
return [Target.to_ascii(element) for element in input]
|
|
||||||
elif isinstance(input, unicode):
|
|
||||||
return input.encode('ascii')
|
|
||||||
else:
|
|
||||||
return input
|
|
||||||
|
|
||||||
# Load the description of JSON target data
|
# Load the description of JSON target data
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@cached
|
@cached
|
||||||
def get_json_target_data():
|
def get_json_target_data():
|
||||||
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../mbed/hal/targets.json"), "rt") as f:
|
return json_file_to_dict(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../mbed/hal/targets.json"))
|
||||||
return Target.to_ascii(json.load(f))
|
|
||||||
|
|
||||||
# Get the members of this module using Python's "inspect" module
|
# Get the members of this module using Python's "inspect" module
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -172,21 +162,58 @@ class Target:
|
||||||
return v if attrname != "progen" else self.__add_paths_to_progen(v)
|
return v if attrname != "progen" else self.__add_paths_to_progen(v)
|
||||||
|
|
||||||
# Return the value of an attribute
|
# Return the value of an attribute
|
||||||
# This function only looks for the attribute's value in the cache, the real work of computing the
|
# This function only computes the attribute's value once, then adds it to the instance attributes
|
||||||
# attribute's value is done in the function above (__getattr_helper)
|
# (in __dict__), so the next time it is returned directly
|
||||||
def __getattr__(self, attrname):
|
def __getattr__(self, attrname):
|
||||||
if not self.attr_cache.has_key(attrname):
|
v = self.__getattr_helper(attrname)
|
||||||
self.attr_cache[attrname] = self.__getattr_helper(attrname)
|
self.__dict__[attrname] = v
|
||||||
return self.attr_cache[attrname]
|
return v
|
||||||
|
|
||||||
|
# Add one or more new target(s) represented as a Python dictionary in 'new_targets'
|
||||||
|
# It it an error to add a target with a name that exists in "targets.json"
|
||||||
|
# However, it is OK to add a target that was previously added via "add_py_targets"
|
||||||
|
# (this makes testing easier without changing the regular semantics)
|
||||||
|
@staticmethod
|
||||||
|
def add_py_targets(new_targets):
|
||||||
|
crt_data = Target.get_json_target_data()
|
||||||
|
# First add all elemnts to the internal dictionary
|
||||||
|
for tk, tv in new_targets.items():
|
||||||
|
if crt_data.has_key(tk) and (not tk in Target.__py_targets):
|
||||||
|
raise Exception("Attempt to add target '%s' that already exists" % tk)
|
||||||
|
crt_data[tk] = tv
|
||||||
|
Target.__py_targets.add(tk)
|
||||||
|
# Then create the new instances and update global variables if needed
|
||||||
|
for tk, tv in new_targets.items():
|
||||||
|
# Is the target already created?
|
||||||
|
old_target = Target.__target_map.get(tk, None)
|
||||||
|
# Instantiate this target. If it is public, update the data in
|
||||||
|
# in TARGETS, TARGET_MAP, TARGET_NAMES
|
||||||
|
new_target = Target(tk)
|
||||||
|
if tv.get("public", True):
|
||||||
|
if old_target: # remove the old target from TARGETS and TARGET_NAMES
|
||||||
|
TARGETS.remove(old_target)
|
||||||
|
TARGET_NAMES.remove(tk)
|
||||||
|
# Add the new target
|
||||||
|
TARGETS.append(new_target)
|
||||||
|
TARGET_MAP[tk] = new_target
|
||||||
|
TARGET_NAMES.append(tk)
|
||||||
|
# Update the target cache
|
||||||
|
Target.__target_map[tk] = new_target
|
||||||
|
|
||||||
|
# Return the target instance starting from the target name
|
||||||
|
@staticmethod
|
||||||
|
def get_target(name):
|
||||||
|
if not Target.__target_map.has_key(name):
|
||||||
|
Target.__target_map[name] = Target(name)
|
||||||
|
return Target.__target_map[name]
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = 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
|
||||||
# Attribute cache: once an attribute's value is computed, don't compute it again
|
self.resolution_order_names = [t[0] for t in self.resolution_order]
|
||||||
self.attr_cache = {}
|
|
||||||
|
|
||||||
def program_cycle_s(self):
|
def program_cycle_s(self):
|
||||||
try:
|
try:
|
||||||
|
@ -364,7 +391,7 @@ class MCU_NRF51Code:
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
# Instantiate all public targets
|
# Instantiate all public targets
|
||||||
TARGETS = [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])
|
||||||
|
|
|
@ -21,7 +21,8 @@ from os import listdir, remove, makedirs
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from os.path import isdir, join, exists, split, relpath, splitext
|
from os.path import isdir, join, exists, split, relpath, splitext
|
||||||
from subprocess import Popen, PIPE, STDOUT, call
|
from subprocess import Popen, PIPE, STDOUT, call
|
||||||
|
import json
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
def cmd(l, check=True, verbose=False, shell=False, cwd=None):
|
def cmd(l, check=True, verbose=False, shell=False, cwd=None):
|
||||||
text = l if shell else ' '.join(l)
|
text = l if shell else ' '.join(l)
|
||||||
|
@ -174,3 +175,23 @@ def check_required_modules(required_modules, verbose=True):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# Utility function: traverse a dictionary and change all the strings in the dictionary to
|
||||||
|
# ASCII from Unicode. Useful when reading ASCII JSON data, because the JSON decoder always
|
||||||
|
# returns Unicode string.
|
||||||
|
# Based on http://stackoverflow.com/a/13105359
|
||||||
|
def dict_to_ascii(input):
|
||||||
|
if isinstance(input, dict):
|
||||||
|
return OrderedDict([(dict_to_ascii(key), dict_to_ascii(value)) for key, value in input.iteritems()])
|
||||||
|
elif isinstance(input, list):
|
||||||
|
return [dict_to_ascii(element) for element in input]
|
||||||
|
elif isinstance(input, unicode):
|
||||||
|
return input.encode('ascii')
|
||||||
|
else:
|
||||||
|
return input
|
||||||
|
|
||||||
|
# Read a JSON file and return its Python representation, transforming all the strings from Unicode
|
||||||
|
# to ASCII. The order of keys in the JSON file is preserved.
|
||||||
|
def json_file_to_dict(fname):
|
||||||
|
with open(fname, "rt") as f:
|
||||||
|
return dict_to_ascii(json.load(f, object_pairs_hook=OrderedDict))
|
||||||
|
|
Loading…
Reference in New Issue