mirror of https://github.com/ARMmbed/mbed-os.git
Refactor Target and Config away from global variables
parent
e4a40cd6a7
commit
d4f9820577
|
@ -303,14 +303,8 @@ def prepare_toolchain(src_paths, target, toolchain_name,
|
||||||
src_paths = [src_paths[0]] + list(set(src_paths[1:]))
|
src_paths = [src_paths[0]] + list(set(src_paths[1:]))
|
||||||
|
|
||||||
# If the configuration object was not yet created, create it now
|
# If the configuration object was not yet created, create it now
|
||||||
config = config or Config(target, src_paths, app_config=app_config)
|
config = config or Config(target, src_paths)
|
||||||
|
target = config.target
|
||||||
# If the 'target' argument is a string, convert it to a target instance
|
|
||||||
if isinstance(target, basestring):
|
|
||||||
try:
|
|
||||||
target = TARGET_MAP[target]
|
|
||||||
except KeyError:
|
|
||||||
raise KeyError("Target '%s' not found" % target)
|
|
||||||
|
|
||||||
# Toolchain instance
|
# Toolchain instance
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -15,12 +15,12 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
# Implementation of mbed configuration mechanism
|
# Implementation of mbed configuration mechanism
|
||||||
from tools.utils import json_file_to_dict
|
from tools.utils import json_file_to_dict
|
||||||
from tools.targets import Target
|
from tools.targets import CUMULATIVE_ATTRIBUTES, TARGET_MAP, \
|
||||||
|
generate_py_target, get_resolution_order
|
||||||
|
|
||||||
# Base class for all configuration exceptions
|
# Base class for all configuration exceptions
|
||||||
class ConfigException(Exception):
|
class ConfigException(Exception):
|
||||||
|
@ -350,7 +350,7 @@ class Config(object):
|
||||||
"UVISOR", "BLE", "CLIENT", "IPV4", "IPV6", "COMMON_PAL", "STORAGE"
|
"UVISOR", "BLE", "CLIENT", "IPV4", "IPV6", "COMMON_PAL", "STORAGE"
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, target, top_level_dirs=None, app_config=None):
|
def __init__(self, tgt, top_level_dirs=None, app_config=None):
|
||||||
"""Construct a mbed configuration
|
"""Construct a mbed configuration
|
||||||
|
|
||||||
Positional arguments:
|
Positional arguments:
|
||||||
|
@ -397,16 +397,23 @@ class Config(object):
|
||||||
self.lib_config_data = {}
|
self.lib_config_data = {}
|
||||||
# Make sure that each config is processed only once
|
# Make sure that each config is processed only once
|
||||||
self.processed_configs = {}
|
self.processed_configs = {}
|
||||||
self.target = target if isinstance(target, basestring) else target.name
|
if isinstance(tgt, basestring):
|
||||||
self.target_labels = Target.get_target(self.target).get_labels()
|
if tgt in TARGET_MAP:
|
||||||
|
self.target = TARGET_MAP[tgt]
|
||||||
|
else:
|
||||||
|
self.target = generate_py_target(
|
||||||
|
self.app_config_data.get("custom_targets", {}), tgt)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.target = tgt
|
||||||
|
self.target = deepcopy(self.target)
|
||||||
|
self.target_labels = self.target.labels
|
||||||
|
|
||||||
self.cumulative_overrides = {key: ConfigCumulativeOverride(key)
|
self.cumulative_overrides = {key: ConfigCumulativeOverride(key)
|
||||||
for key in
|
for key in CUMULATIVE_ATTRIBUTES}
|
||||||
Target.cumulative_attributes}
|
|
||||||
|
|
||||||
self._process_config_and_overrides(self.app_config_data, {}, "app",
|
self._process_config_and_overrides(self.app_config_data, {}, "app",
|
||||||
"application")
|
"application")
|
||||||
self.target_labels = Target.get_target(self.target).get_labels()
|
|
||||||
self.config_errors = None
|
self.config_errors = None
|
||||||
|
|
||||||
def add_config_files(self, flist):
|
def add_config_files(self, flist):
|
||||||
|
@ -509,7 +516,7 @@ class Config(object):
|
||||||
label)))))
|
label)))))
|
||||||
|
|
||||||
for cumulatives in self.cumulative_overrides.itervalues():
|
for cumulatives in self.cumulative_overrides.itervalues():
|
||||||
cumulatives.update_target(Target.get_target(self.target))
|
cumulatives.update_target(self.target)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
@ -528,10 +535,10 @@ class Config(object):
|
||||||
|
|
||||||
Arguments: None
|
Arguments: None
|
||||||
"""
|
"""
|
||||||
params, json_data = {}, Target.get_json_target_data()
|
params, json_data = {}, self.target.json_data
|
||||||
resolution_order = [e[0] for e
|
resolution_order = [e[0] for e
|
||||||
in sorted(
|
in sorted(
|
||||||
Target.get_target(self.target).resolution_order,
|
self.target.resolution_order,
|
||||||
key=lambda e: e[1], reverse=True)]
|
key=lambda e: e[1], reverse=True)]
|
||||||
for tname in resolution_order:
|
for tname in resolution_order:
|
||||||
# Read the target data directly from its description
|
# Read the target data directly from its description
|
||||||
|
@ -547,9 +554,11 @@ class Config(object):
|
||||||
# in the target inheritance tree, raise an error We need to use
|
# in the target inheritance tree, raise an error We need to use
|
||||||
# 'defined_by[7:]' to remove the "target:" prefix from
|
# 'defined_by[7:]' to remove the "target:" prefix from
|
||||||
# defined_by
|
# defined_by
|
||||||
|
rel_names = [tgt for tgt, _ in
|
||||||
|
get_resolution_order(self.target.json_data, tname,
|
||||||
|
[])]
|
||||||
if (full_name not in params) or \
|
if (full_name not in params) or \
|
||||||
(params[full_name].defined_by[7:] not in
|
(params[full_name].defined_by[7:] not in rel_names):
|
||||||
Target.get_target(tname).resolution_order_names):
|
|
||||||
raise ConfigException(
|
raise ConfigException(
|
||||||
"Attempt to override undefined parameter '%s' in '%s'"
|
"Attempt to override undefined parameter '%s' in '%s'"
|
||||||
% (name,
|
% (name,
|
||||||
|
@ -680,15 +689,14 @@ class Config(object):
|
||||||
params, _ = self.get_config_data()
|
params, _ = self.get_config_data()
|
||||||
self._check_required_parameters(params)
|
self._check_required_parameters(params)
|
||||||
self.cumulative_overrides['features']\
|
self.cumulative_overrides['features']\
|
||||||
.update_target(Target.get_target(self.target))
|
.update_target(self.target)
|
||||||
features = Target.get_target(self.target).features
|
|
||||||
|
|
||||||
for feature in features:
|
for feature in self.target.features:
|
||||||
if feature not in self.__allowed_features:
|
if feature not in self.__allowed_features:
|
||||||
raise ConfigException(
|
raise ConfigException(
|
||||||
"Feature '%s' is not a supported features" % feature)
|
"Feature '%s' is not a supported features" % feature)
|
||||||
|
|
||||||
return features
|
return self.target.features
|
||||||
|
|
||||||
def validate_config(self):
|
def validate_config(self):
|
||||||
""" Validate configuration settings. This either returns True or
|
""" Validate configuration settings. This either returns True or
|
||||||
|
|
116
tools/targets.py
116
tools/targets.py
|
@ -21,10 +21,15 @@ import struct
|
||||||
import shutil
|
import shutil
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
import sys
|
||||||
|
from collections import namedtuple
|
||||||
from tools.patch import patch
|
from tools.patch import patch
|
||||||
from tools.paths import TOOLS_BOOTLOADERS
|
from tools.paths import TOOLS_BOOTLOADERS
|
||||||
from tools.utils import json_file_to_dict
|
from tools.utils import json_file_to_dict
|
||||||
|
|
||||||
|
__all__ = ["target", "TARGETS", "TARGET_MAP", "TARGET_NAMES", "CORE_LABELS",
|
||||||
|
"HookError", "generate_py_target", "Target",
|
||||||
|
"CUMULATIVE_ATTRIBUTES", "get_resolution_order"]
|
||||||
|
|
||||||
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"],
|
||||||
|
@ -60,11 +65,58 @@ def cached(func):
|
||||||
return CACHES[(func.__name__, args)]
|
return CACHES[(func.__name__, args)]
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
class Target(object):
|
|
||||||
|
# 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']
|
||||||
|
|
||||||
|
|
||||||
|
def get_resolution_order(json_data, target_name, order, level=0):
|
||||||
|
""" Return the order in which target descriptions are searched for
|
||||||
|
attributes. This mimics the Python 2.2 method resolution order, which
|
||||||
|
is what the old targets.py module used. For more details, check
|
||||||
|
http://makina-corpus.com/blog/metier/2014/python-tutorial-understanding-python-mro-class-search-path
|
||||||
|
The resolution order contains (name, level) tuples, where "name" is the
|
||||||
|
name of the class and "level" is the level in the inheritance hierarchy
|
||||||
|
(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 target_name not in [l[0] for l in order]:
|
||||||
|
order.append((target_name, level))
|
||||||
|
parents = json_data[target_name].get("inherits", [])
|
||||||
|
for par in parents:
|
||||||
|
order = get_resolution_order(json_data, par, order, level + 1)
|
||||||
|
return order
|
||||||
|
|
||||||
|
|
||||||
|
def target(name, json_data):
|
||||||
|
"""Construct a target object"""
|
||||||
|
resolution_order = get_resolution_order(json_data, name, [])
|
||||||
|
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)
|
||||||
|
|
||||||
|
def generate_py_target(new_targets, name):
|
||||||
|
"""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.
|
||||||
|
"""
|
||||||
|
base_targets = Target.get_json_target_data()
|
||||||
|
for new_target in new_targets.keys():
|
||||||
|
if new_target in base_targets:
|
||||||
|
raise Exception("Attempt to add target '%s' that already exists"
|
||||||
|
% new_target)
|
||||||
|
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")):
|
||||||
"""An object to represent a Target (MCU/Board)"""
|
"""An object to represent a Target (MCU/Board)"""
|
||||||
# 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']
|
|
||||||
|
|
||||||
# Default location of the 'targets.json' file
|
# Default location of the 'targets.json' file
|
||||||
__targets_json_location_default = os.path.join(
|
__targets_json_location_default = os.path.join(
|
||||||
|
@ -95,24 +147,6 @@ class Target(object):
|
||||||
return dict([(m[0], m[1]) for m in
|
return dict([(m[0], m[1]) for m in
|
||||||
inspect.getmembers(sys.modules[__name__])])
|
inspect.getmembers(sys.modules[__name__])])
|
||||||
|
|
||||||
def __get_resolution_order(self, target_name, order, level=0):
|
|
||||||
""" Return the order in which target descriptions are searched for
|
|
||||||
attributes. This mimics the Python 2.2 method resolution order, which
|
|
||||||
is what the old targets.py module used. For more details, check
|
|
||||||
http://makina-corpus.com/blog/metier/2014/python-tutorial-understanding-python-mro-class-search-path
|
|
||||||
The resolution order contains (name, level) tuples, where "name" is the
|
|
||||||
name of the class and "level" is the level in the inheritance hierarchy
|
|
||||||
(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 target_name not in [l[0] for l in order]:
|
|
||||||
order.append((target_name, level))
|
|
||||||
parents = self.get_json_target_data()[target_name].get("inherits", [])
|
|
||||||
for par in parents:
|
|
||||||
order = self.__get_resolution_order(par, order, level + 1)
|
|
||||||
return order
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __add_paths_to_progen(data):
|
def __add_paths_to_progen(data):
|
||||||
"""Modify the exporter specification ("progen") by changing all
|
"""Modify the exporter specification ("progen") by changing all
|
||||||
|
@ -133,14 +167,14 @@ class Target(object):
|
||||||
"""Look for the attribute in the class and its parents, as defined by
|
"""Look for the attribute in the class and its parents, as defined by
|
||||||
the resolution order
|
the resolution order
|
||||||
"""
|
"""
|
||||||
tdata = self.get_json_target_data()
|
tdata = self.json_data
|
||||||
# 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 resolution order) then follow the "_add"
|
# last time (in attribute resolution order) then follow the "_add"
|
||||||
# and "_remove" data fields
|
# and "_remove" data fields
|
||||||
for idx, target in enumerate(self.resolution_order):
|
for idx, tgt in enumerate(self.resolution_order):
|
||||||
# the attribute was defined at this level in the resolution
|
# the attribute was defined at this level in the resolution
|
||||||
# order
|
# order
|
||||||
if attrname in tdata[target[0]]:
|
if attrname in tdata[tgt[0]]:
|
||||||
def_idx = idx
|
def_idx = idx
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -192,13 +226,13 @@ class Target(object):
|
||||||
|
|
||||||
def __getattr_helper(self, attrname):
|
def __getattr_helper(self, attrname):
|
||||||
"""Compute the value of a given target attribute"""
|
"""Compute the value of a given target attribute"""
|
||||||
if attrname in self.cumulative_attributes:
|
if attrname in CUMULATIVE_ATTRIBUTES:
|
||||||
return self.__getattr_cumulative(attrname)
|
return self.__getattr_cumulative(attrname)
|
||||||
else:
|
else:
|
||||||
tdata = self.get_json_target_data()
|
tdata = self.json_data
|
||||||
starting_value = None
|
starting_value = None
|
||||||
for target in self.resolution_order:
|
for tgt in self.resolution_order:
|
||||||
data = tdata[target[0]]
|
data = tdata[tgt[0]]
|
||||||
if data.has_key(attrname):
|
if data.has_key(attrname):
|
||||||
starting_value = data[attrname]
|
starting_value = data[attrname]
|
||||||
break
|
break
|
||||||
|
@ -226,17 +260,8 @@ class Target(object):
|
||||||
@cached
|
@cached
|
||||||
def get_target(target_name):
|
def get_target(target_name):
|
||||||
""" Return the target instance starting from the target name """
|
""" Return the target instance starting from the target name """
|
||||||
return Target(target_name)
|
return target(target_name, Target.get_json_target_data())
|
||||||
|
|
||||||
def __init__(self, target_name):
|
|
||||||
self.name = target_name
|
|
||||||
|
|
||||||
# Compute resolution order once (it will be used later in __getattr__)
|
|
||||||
self.resolution_order = self.__get_resolution_order(self.name, [])
|
|
||||||
# Create also a list with only the names of the targets in the
|
|
||||||
# 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):
|
||||||
|
@ -248,7 +273,8 @@ class Target(object):
|
||||||
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):
|
@property
|
||||||
|
def labels(self):
|
||||||
"""Get all possible labels for this target"""
|
"""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
|
# Automatically define UVISOR_UNSUPPORTED if the target doesn't
|
||||||
|
@ -487,9 +513,9 @@ def get_target_detect_codes():
|
||||||
""" Returns dictionary mapping detect_code -> platform_name
|
""" Returns dictionary mapping detect_code -> platform_name
|
||||||
"""
|
"""
|
||||||
result = {}
|
result = {}
|
||||||
for target in TARGETS:
|
for tgt in TARGETS:
|
||||||
for detect_code in target.detect_code:
|
for detect_code in tgt.detect_code:
|
||||||
result[detect_code] = target.name
|
result[detect_code] = tgt.name
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def set_targets_json_location(location=None):
|
def set_targets_json_location(location=None):
|
||||||
|
@ -500,9 +526,9 @@ def set_targets_json_location(location=None):
|
||||||
# re-initialization does not create new variables, it keeps the old ones
|
# re-initialization does not create new variables, it keeps the old ones
|
||||||
# instead. This ensures compatibility with code that does
|
# instead. This ensures compatibility with code that does
|
||||||
# "from tools.targets import TARGET_NAMES"
|
# "from tools.targets import TARGET_NAMES"
|
||||||
TARGETS[:] = [Target.get_target(target) for target, obj
|
TARGETS[:] = [Target.get_target(tgt) for tgt, obj
|
||||||
in Target.get_json_target_data().items()
|
in Target.get_json_target_data().items()
|
||||||
if obj.get("public", True)]
|
if obj.get("public", True)]
|
||||||
TARGET_MAP.clear()
|
TARGET_MAP.clear()
|
||||||
TARGET_MAP.update(dict([(target.name, target) for target in TARGETS]))
|
TARGET_MAP.update(dict([(tgt.name, tgt) for tgt in TARGETS]))
|
||||||
TARGET_NAMES[:] = TARGET_MAP.keys()
|
TARGET_NAMES[:] = TARGET_MAP.keys()
|
||||||
|
|
|
@ -27,6 +27,7 @@ def compare_config(cfg, expected):
|
||||||
if cfg[k].value != expected[k]:
|
if cfg[k].value != expected[k]:
|
||||||
return "'%s': expected '%s', got '%s'" % (k, expected[k], cfg[k].value)
|
return "'%s': expected '%s', got '%s'" % (k, expected[k], cfg[k].value)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
raise
|
||||||
return "Unexpected key '%s' in configuration data" % k
|
return "Unexpected key '%s' in configuration data" % k
|
||||||
for k in expected:
|
for k in expected:
|
||||||
if k not in ["desc", "expected_macros", "expected_features"] + cfg.keys():
|
if k not in ["desc", "expected_macros", "expected_features"] + cfg.keys():
|
||||||
|
|
|
@ -428,7 +428,7 @@ class mbedToolchain:
|
||||||
toolchain_labels = [c.__name__ for c in getmro(self.__class__)]
|
toolchain_labels = [c.__name__ for c in getmro(self.__class__)]
|
||||||
toolchain_labels.remove('mbedToolchain')
|
toolchain_labels.remove('mbedToolchain')
|
||||||
self.labels = {
|
self.labels = {
|
||||||
'TARGET': self.target.get_labels() + ["DEBUG" if "debug-info" in self.options else "RELEASE"],
|
'TARGET': self.target.labels + ["DEBUG" if "debug-info" in self.options else "RELEASE"],
|
||||||
'FEATURE': self.target.features,
|
'FEATURE': self.target.features,
|
||||||
'TOOLCHAIN': toolchain_labels
|
'TOOLCHAIN': toolchain_labels
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue