the other jimmy / mbed-sdk-tools

Fork of mbed-sdk-tools by mbed official

Committer:
screamer
Date:
Mon Aug 01 09:10:17 2016 +0100
Revision:
24:25bff2709c20
Parent:
13:ab47a20b66f0
Child:
29:1210849dba19
Major update to tools from ARMmbed/mbed-os

Who changed what in which revision?

UserRevisionLine numberNew contents of line
screamer 8:a8ac6ed29081 1 """
screamer 8:a8ac6ed29081 2 mbed SDK
screamer 8:a8ac6ed29081 3 Copyright (c) 2016 ARM Limited
screamer 8:a8ac6ed29081 4
screamer 8:a8ac6ed29081 5 Licensed under the Apache License, Version 2.0 (the "License");
screamer 8:a8ac6ed29081 6 you may not use this file except in compliance with the License.
screamer 8:a8ac6ed29081 7 You may obtain a copy of the License at
screamer 8:a8ac6ed29081 8
screamer 8:a8ac6ed29081 9 http://www.apache.org/licenses/LICENSE-2.0
screamer 8:a8ac6ed29081 10
screamer 8:a8ac6ed29081 11 Unless required by applicable law or agreed to in writing, software
screamer 8:a8ac6ed29081 12 distributed under the License is distributed on an "AS IS" BASIS,
screamer 8:a8ac6ed29081 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
screamer 8:a8ac6ed29081 14 See the License for the specific language governing permissions and
screamer 8:a8ac6ed29081 15 limitations under the License.
screamer 8:a8ac6ed29081 16 """
screamer 8:a8ac6ed29081 17
screamer 8:a8ac6ed29081 18 # Implementation of mbed configuration mechanism
screamer 8:a8ac6ed29081 19 from copy import deepcopy
screamer 8:a8ac6ed29081 20 from collections import OrderedDict
screamer 8:a8ac6ed29081 21 from tools.utils import json_file_to_dict, ToolException
screamer 8:a8ac6ed29081 22 from tools.targets import Target
screamer 8:a8ac6ed29081 23 import os
screamer 8:a8ac6ed29081 24
screamer 8:a8ac6ed29081 25 # Base class for all configuration exceptions
screamer 8:a8ac6ed29081 26 class ConfigException(Exception):
screamer 8:a8ac6ed29081 27 pass
screamer 8:a8ac6ed29081 28
screamer 8:a8ac6ed29081 29 # This class keeps information about a single configuration parameter
screamer 8:a8ac6ed29081 30 class ConfigParameter:
screamer 8:a8ac6ed29081 31 # name: the name of the configuration parameter
screamer 8:a8ac6ed29081 32 # data: the data associated with the configuration parameter
screamer 8:a8ac6ed29081 33 # unit_name: the unit (target/library/application) that defines this parameter
screamer 8:a8ac6ed29081 34 # unit_ kind: the kind of the unit ("target", "library" or "application")
screamer 8:a8ac6ed29081 35 def __init__(self, name, data, unit_name, unit_kind):
screamer 8:a8ac6ed29081 36 self.name = self.get_full_name(name, unit_name, unit_kind, allow_prefix = False)
screamer 8:a8ac6ed29081 37 self.defined_by = self.get_display_name(unit_name, unit_kind)
screamer 13:ab47a20b66f0 38 self.set_value(data.get("value", None), unit_name, unit_kind)
screamer 8:a8ac6ed29081 39 self.help_text = data.get("help", None)
screamer 8:a8ac6ed29081 40 self.required = data.get("required", False)
screamer 8:a8ac6ed29081 41 self.macro_name = data.get("macro_name", "MBED_CONF_%s" % self.sanitize(self.name.upper()))
screamer 13:ab47a20b66f0 42 self.config_errors = []
screamer 8:a8ac6ed29081 43
screamer 8:a8ac6ed29081 44 # Return the full (prefixed) name of a parameter.
screamer 8:a8ac6ed29081 45 # If the parameter already has a prefix, check if it is valid
screamer 8:a8ac6ed29081 46 # name: the simple (unqualified) name of the parameter
screamer 8:a8ac6ed29081 47 # unit_name: the unit (target/library/application) that defines this parameter
screamer 8:a8ac6ed29081 48 # unit_kind: the kind of the unit ("target", "library" or "application")
screamer 8:a8ac6ed29081 49 # label: the name of the label in the 'target_config_overrides' section (optional)
screamer 8:a8ac6ed29081 50 # allow_prefix: True to allo the original name to have a prefix, False otherwise
screamer 8:a8ac6ed29081 51 @staticmethod
screamer 8:a8ac6ed29081 52 def get_full_name(name, unit_name, unit_kind, label = None, allow_prefix = True):
screamer 8:a8ac6ed29081 53 if name.find('.') == -1: # the name is not prefixed
screamer 8:a8ac6ed29081 54 if unit_kind == "target":
screamer 8:a8ac6ed29081 55 prefix = "target."
screamer 8:a8ac6ed29081 56 elif unit_kind == "application":
screamer 8:a8ac6ed29081 57 prefix = "app."
screamer 8:a8ac6ed29081 58 else:
screamer 8:a8ac6ed29081 59 prefix = unit_name + '.'
screamer 8:a8ac6ed29081 60 return prefix + name
screamer 8:a8ac6ed29081 61 # The name has a prefix, so check if it is valid
screamer 8:a8ac6ed29081 62 if not allow_prefix:
screamer 8:a8ac6ed29081 63 raise ConfigException("Invalid parameter name '%s' in '%s'" % (name, ConfigParameter.get_display_name(unit_name, unit_kind, label)))
screamer 8:a8ac6ed29081 64 temp = name.split(".")
screamer 8:a8ac6ed29081 65 # Check if the parameter syntax is correct (must be unit_name.parameter_name)
screamer 8:a8ac6ed29081 66 if len(temp) != 2:
screamer 8:a8ac6ed29081 67 raise ConfigException("Invalid parameter name '%s' in '%s'" % (name, ConfigParameter.get_display_name(unit_name, unit_kind, label)))
screamer 8:a8ac6ed29081 68 prefix = temp[0]
screamer 8:a8ac6ed29081 69 # Check if the given parameter prefix matches the expected prefix
screamer 8:a8ac6ed29081 70 if (unit_kind == "library" and prefix != unit_name) or (unit_kind == "target" and prefix != "target"):
screamer 8:a8ac6ed29081 71 raise ConfigException("Invalid prefix '%s' for parameter name '%s' in '%s'" % (prefix, name, ConfigParameter.get_display_name(unit_name, unit_kind, label)))
screamer 8:a8ac6ed29081 72 return name
screamer 8:a8ac6ed29081 73
screamer 8:a8ac6ed29081 74 # Return the name displayed for a unit when interogating the origin
screamer 8:a8ac6ed29081 75 # and the last set place of a parameter
screamer 8:a8ac6ed29081 76 # unit_name: the unit (target/library/application) that defines this parameter
screamer 8:a8ac6ed29081 77 # unit_kind: the kind of the unit ("target", "library" or "application")
screamer 8:a8ac6ed29081 78 # label: the name of the label in the 'target_config_overrides' section (optional)
screamer 8:a8ac6ed29081 79 @staticmethod
screamer 8:a8ac6ed29081 80 def get_display_name(unit_name, unit_kind, label = None):
screamer 8:a8ac6ed29081 81 if unit_kind == "target":
screamer 8:a8ac6ed29081 82 return "target:" + unit_name
screamer 8:a8ac6ed29081 83 elif unit_kind == "application":
screamer 8:a8ac6ed29081 84 return "application%s" % ("[%s]" % label if label else "")
screamer 8:a8ac6ed29081 85 else: # library
screamer 8:a8ac6ed29081 86 return "library:%s%s" % (unit_name, "[%s]" % label if label else "")
screamer 8:a8ac6ed29081 87
screamer 8:a8ac6ed29081 88 # "Sanitize" a name so that it is a valid C macro name
screamer 8:a8ac6ed29081 89 # Currently it simply replaces '.' and '-' with '_'
screamer 8:a8ac6ed29081 90 # name: the un-sanitized name.
screamer 8:a8ac6ed29081 91 @staticmethod
screamer 8:a8ac6ed29081 92 def sanitize(name):
screamer 8:a8ac6ed29081 93 return name.replace('.', '_').replace('-', '_')
screamer 8:a8ac6ed29081 94
screamer 13:ab47a20b66f0 95 # Sets a value for this parameter, remember the place where it was set.
screamer 13:ab47a20b66f0 96 # If the value is a boolean, it is converted to 1 (for True) or to 0 (for False).
screamer 8:a8ac6ed29081 97 # value: the value of the parameter
screamer 8:a8ac6ed29081 98 # unit_name: the unit (target/library/application) that defines this parameter
screamer 8:a8ac6ed29081 99 # unit_ kind: the kind of the unit ("target", "library" or "application")
screamer 8:a8ac6ed29081 100 # label: the name of the label in the 'target_config_overrides' section (optional)
screamer 8:a8ac6ed29081 101 def set_value(self, value, unit_name, unit_kind, label = None):
screamer 13:ab47a20b66f0 102 self.value = int(value) if isinstance(value, bool) else value
screamer 8:a8ac6ed29081 103 self.set_by = self.get_display_name(unit_name, unit_kind, label)
screamer 8:a8ac6ed29081 104
screamer 8:a8ac6ed29081 105 # Return the string representation of this configuration parameter
screamer 8:a8ac6ed29081 106 def __str__(self):
screamer 8:a8ac6ed29081 107 if self.value is not None:
screamer 8:a8ac6ed29081 108 return '%s = %s (macro name: "%s")' % (self.name, self.value, self.macro_name)
screamer 8:a8ac6ed29081 109 else:
screamer 8:a8ac6ed29081 110 return '%s has no value' % self.name
screamer 8:a8ac6ed29081 111
screamer 8:a8ac6ed29081 112 # Return a verbose description of this configuration paramater as a string
screamer 8:a8ac6ed29081 113 def get_verbose_description(self):
screamer 8:a8ac6ed29081 114 desc = "Name: %s%s\n" % (self.name, " (required parameter)" if self.required else "")
screamer 8:a8ac6ed29081 115 if self.help_text:
screamer 8:a8ac6ed29081 116 desc += " Description: %s\n" % self.help_text
screamer 8:a8ac6ed29081 117 desc += " Defined by: %s\n" % self.defined_by
screamer 8:a8ac6ed29081 118 if not self.value:
screamer 8:a8ac6ed29081 119 return desc + " No value set"
screamer 8:a8ac6ed29081 120 desc += " Macro name: %s\n" % self.macro_name
screamer 8:a8ac6ed29081 121 desc += " Value: %s (set by %s)" % (self.value, self.set_by)
screamer 8:a8ac6ed29081 122 return desc
screamer 8:a8ac6ed29081 123
screamer 8:a8ac6ed29081 124 # A representation of a configuration macro. It handles both macros without a value (MACRO)
screamer 8:a8ac6ed29081 125 # and with a value (MACRO=VALUE)
screamer 8:a8ac6ed29081 126 class ConfigMacro:
screamer 8:a8ac6ed29081 127 def __init__(self, name, unit_name, unit_kind):
screamer 8:a8ac6ed29081 128 self.name = name
screamer 8:a8ac6ed29081 129 self.defined_by = ConfigParameter.get_display_name(unit_name, unit_kind)
screamer 8:a8ac6ed29081 130 if name.find("=") != -1:
screamer 8:a8ac6ed29081 131 tmp = name.split("=")
screamer 8:a8ac6ed29081 132 if len(tmp) != 2:
screamer 8:a8ac6ed29081 133 raise ValueError("Invalid macro definition '%s' in '%s'" % (name, self.defined_by))
screamer 8:a8ac6ed29081 134 self.macro_name = tmp[0]
screamer 13:ab47a20b66f0 135 self.macro_value = tmp[1]
screamer 8:a8ac6ed29081 136 else:
screamer 8:a8ac6ed29081 137 self.macro_name = name
screamer 13:ab47a20b66f0 138 self.macro_value = None
screamer 8:a8ac6ed29081 139
screamer 24:25bff2709c20 140 # Representation of overrides for cumulative attributes
screamer 24:25bff2709c20 141 class ConfigCumulativeOverride:
screamer 24:25bff2709c20 142 def __init__(self, name, additions=set(), removals=set(), strict=False):
screamer 24:25bff2709c20 143 self.name = name
screamer 24:25bff2709c20 144 self.additions = set(additions)
screamer 24:25bff2709c20 145 self.removals = set(removals)
screamer 24:25bff2709c20 146 self.strict = strict
screamer 24:25bff2709c20 147
screamer 24:25bff2709c20 148 # Add attr to the cumulative override
screamer 24:25bff2709c20 149 def remove_cumulative_overrides(self, overrides):
screamer 24:25bff2709c20 150 for override in overrides:
screamer 24:25bff2709c20 151 if override in self.additions:
screamer 24:25bff2709c20 152 raise ConfigException("Configuration conflict. The %s %s both added and removed." % (self.name[:-1], override))
screamer 24:25bff2709c20 153
screamer 24:25bff2709c20 154 self.removals |= set(overrides)
screamer 24:25bff2709c20 155
screamer 24:25bff2709c20 156 # Remove attr from the cumulative overrides
screamer 24:25bff2709c20 157 def add_cumulative_overrides(self, overrides):
screamer 24:25bff2709c20 158 for override in overrides:
screamer 24:25bff2709c20 159 if (override in self.removals or (self.strict and override not in self.additions)):
screamer 24:25bff2709c20 160 raise ConfigException("Configuration conflict. The %s %s both added and removed." % (self.name[:-1], override))
screamer 24:25bff2709c20 161
screamer 24:25bff2709c20 162 self.additions |= set(overrides)
screamer 24:25bff2709c20 163
screamer 24:25bff2709c20 164 # Enable strict set of cumulative overrides for the specified attr
screamer 24:25bff2709c20 165 def strict_cumulative_overrides(self, overrides):
screamer 24:25bff2709c20 166 self.remove_cumulative_overrides(self.additions - set(overrides))
screamer 24:25bff2709c20 167 self.add_cumulative_overrides(overrides)
screamer 24:25bff2709c20 168 self.strict = True
screamer 24:25bff2709c20 169
screamer 24:25bff2709c20 170 def update_target(self, target):
screamer 24:25bff2709c20 171 setattr(target, self.name, list(
screamer 24:25bff2709c20 172 (set(getattr(target, self.name, [])) | self.additions) - self.removals))
screamer 24:25bff2709c20 173
screamer 24:25bff2709c20 174
screamer 24:25bff2709c20 175
screamer 8:a8ac6ed29081 176 # 'Config' implements the mbed configuration mechanism
screamer 8:a8ac6ed29081 177 class Config:
screamer 8:a8ac6ed29081 178 # Libraries and applications have different names for their configuration files
screamer 8:a8ac6ed29081 179 __mbed_app_config_name = "mbed_app.json"
screamer 8:a8ac6ed29081 180 __mbed_lib_config_name = "mbed_lib.json"
screamer 8:a8ac6ed29081 181
screamer 8:a8ac6ed29081 182 # Allowed keys in configuration dictionaries
screamer 8:a8ac6ed29081 183 # (targets can have any kind of keys, so this validation is not applicable to them)
screamer 8:a8ac6ed29081 184 __allowed_keys = {
screamer 8:a8ac6ed29081 185 "library": set(["name", "config", "target_overrides", "macros", "__config_path"]),
screamer 8:a8ac6ed29081 186 "application": set(["config", "custom_targets", "target_overrides", "macros", "__config_path"])
screamer 8:a8ac6ed29081 187 }
screamer 8:a8ac6ed29081 188
screamer 13:ab47a20b66f0 189 # Allowed features in configurations
screamer 13:ab47a20b66f0 190 __allowed_features = [
screamer 24:25bff2709c20 191 "UVISOR", "BLE", "CLIENT", "IPV4", "IPV6", "COMMON_PAL", "STORAGE"
screamer 13:ab47a20b66f0 192 ]
screamer 13:ab47a20b66f0 193
screamer 8:a8ac6ed29081 194 # The initialization arguments for Config are:
screamer 8:a8ac6ed29081 195 # target: the name of the mbed target used for this configuration instance
screamer 8:a8ac6ed29081 196 # top_level_dirs: a list of top level source directories (where mbed_abb_config.json could be found)
screamer 8:a8ac6ed29081 197 # __init__ will look for the application configuration file in top_level_dirs.
screamer 8:a8ac6ed29081 198 # If found once, it'll parse it and check if it has a custom_targets function.
screamer 8:a8ac6ed29081 199 # If it does, it'll update the list of targets if need.
screamer 8:a8ac6ed29081 200 # If found more than once, an exception is raised
screamer 8:a8ac6ed29081 201 # top_level_dirs can be None (in this case, mbed_app_config.json will not be searched)
screamer 8:a8ac6ed29081 202 def __init__(self, target, top_level_dirs = []):
screamer 8:a8ac6ed29081 203 app_config_location = None
screamer 8:a8ac6ed29081 204 for s in (top_level_dirs or []):
screamer 8:a8ac6ed29081 205 full_path = os.path.join(s, self.__mbed_app_config_name)
screamer 8:a8ac6ed29081 206 if os.path.isfile(full_path):
screamer 8:a8ac6ed29081 207 if app_config_location is not None:
screamer 8:a8ac6ed29081 208 raise ConfigException("Duplicate '%s' file in '%s' and '%s'" % (self.__mbed_app_config_name, app_config_location, full_path))
screamer 8:a8ac6ed29081 209 else:
screamer 8:a8ac6ed29081 210 app_config_location = full_path
screamer 8:a8ac6ed29081 211 self.app_config_data = json_file_to_dict(app_config_location) if app_config_location else {}
screamer 8:a8ac6ed29081 212 # Check the keys in the application configuration data
screamer 8:a8ac6ed29081 213 unknown_keys = set(self.app_config_data.keys()) - self.__allowed_keys["application"]
screamer 8:a8ac6ed29081 214 if unknown_keys:
screamer 8:a8ac6ed29081 215 raise ConfigException("Unknown key(s) '%s' in %s" % (",".join(unknown_keys), self.__mbed_app_config_name))
screamer 8:a8ac6ed29081 216 # Update the list of targets with the ones defined in the application config, if applicable
screamer 8:a8ac6ed29081 217 Target.add_py_targets(self.app_config_data.get("custom_targets", {}))
screamer 8:a8ac6ed29081 218 self.lib_config_data = {}
screamer 8:a8ac6ed29081 219 # Make sure that each config is processed only once
screamer 8:a8ac6ed29081 220 self.processed_configs = {}
screamer 13:ab47a20b66f0 221 self.target = target if isinstance(target, basestring) else target.name
screamer 8:a8ac6ed29081 222 self.target_labels = Target.get_target(self.target).get_labels()
screamer 24:25bff2709c20 223
screamer 24:25bff2709c20 224 self.cumulative_overrides = { key: ConfigCumulativeOverride(key)
screamer 24:25bff2709c20 225 for key in Target._Target__cumulative_attributes }
screamer 24:25bff2709c20 226
screamer 24:25bff2709c20 227 self._process_config_and_overrides(self.app_config_data, {}, "app", "application")
screamer 24:25bff2709c20 228 self.target_labels = Target.get_target(self.target).get_labels()
screamer 8:a8ac6ed29081 229
screamer 8:a8ac6ed29081 230 # Add one or more configuration files
screamer 8:a8ac6ed29081 231 def add_config_files(self, flist):
screamer 8:a8ac6ed29081 232 for f in flist:
screamer 8:a8ac6ed29081 233 if not f.endswith(self.__mbed_lib_config_name):
screamer 8:a8ac6ed29081 234 continue
screamer 8:a8ac6ed29081 235 full_path = os.path.normpath(os.path.abspath(f))
screamer 8:a8ac6ed29081 236 # Check that we didn't already process this file
screamer 8:a8ac6ed29081 237 if self.processed_configs.has_key(full_path):
screamer 8:a8ac6ed29081 238 continue
screamer 8:a8ac6ed29081 239 self.processed_configs[full_path] = True
screamer 8:a8ac6ed29081 240 # Read the library configuration and add a "__full_config_path" attribute to it
screamer 8:a8ac6ed29081 241 cfg = json_file_to_dict(f)
screamer 8:a8ac6ed29081 242 cfg["__config_path"] = full_path
screamer 8:a8ac6ed29081 243 # If there's already a configuration for a module with the same name, exit with error
screamer 8:a8ac6ed29081 244 if self.lib_config_data.has_key(cfg["name"]):
screamer 8:a8ac6ed29081 245 raise ConfigException("Library name '%s' is not unique (defined in '%s' and '%s')" % (cfg["name"], full_path, self.lib_config_data[cfg["name"]]["__config_path"]))
screamer 8:a8ac6ed29081 246 self.lib_config_data[cfg["name"]] = cfg
screamer 8:a8ac6ed29081 247
screamer 8:a8ac6ed29081 248 # Helper function: process a "config_parameters" section in either a target, a library or the application
screamer 8:a8ac6ed29081 249 # data: a dictionary with the configuration parameters
screamer 8:a8ac6ed29081 250 # params: storage for the discovered configuration parameters
screamer 8:a8ac6ed29081 251 # unit_name: the unit (target/library/application) that defines this parameter
screamer 8:a8ac6ed29081 252 # unit_kind: the kind of the unit ("target", "library" or "application")
screamer 8:a8ac6ed29081 253 def _process_config_parameters(self, data, params, unit_name, unit_kind):
screamer 8:a8ac6ed29081 254 for name, v in data.items():
screamer 8:a8ac6ed29081 255 full_name = ConfigParameter.get_full_name(name, unit_name, unit_kind)
screamer 8:a8ac6ed29081 256 # If the parameter was already defined, raise an error
screamer 8:a8ac6ed29081 257 if full_name in params:
screamer 8:a8ac6ed29081 258 raise ConfigException("Parameter name '%s' defined in both '%s' and '%s'" % (name, ConfigParameter.get_display_name(unit_name, unit_kind), params[full_name].defined_by))
screamer 8:a8ac6ed29081 259 # Otherwise add it to the list of known parameters
screamer 8:a8ac6ed29081 260 # If "v" is not a dictionary, this is a shortcut definition, otherwise it is a full definition
screamer 8:a8ac6ed29081 261 params[full_name] = ConfigParameter(name, v if isinstance(v, dict) else {"value": v}, unit_name, unit_kind)
screamer 8:a8ac6ed29081 262 return params
screamer 8:a8ac6ed29081 263
screamer 8:a8ac6ed29081 264 # Helper function: process "config_parameters" and "target_config_overrides" in a given dictionary
screamer 8:a8ac6ed29081 265 # data: the configuration data of the library/appliation
screamer 8:a8ac6ed29081 266 # params: storage for the discovered configuration parameters
screamer 8:a8ac6ed29081 267 # unit_name: the unit (library/application) that defines this parameter
screamer 8:a8ac6ed29081 268 # unit_kind: the kind of the unit ("library" or "application")
screamer 8:a8ac6ed29081 269 def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
screamer 13:ab47a20b66f0 270 self.config_errors = []
screamer 8:a8ac6ed29081 271 self._process_config_parameters(data.get("config", {}), params, unit_name, unit_kind)
screamer 8:a8ac6ed29081 272 for label, overrides in data.get("target_overrides", {}).items():
screamer 8:a8ac6ed29081 273 # If the label is defined by the target or it has the special value "*", process the overrides
screamer 8:a8ac6ed29081 274 if (label == '*') or (label in self.target_labels):
screamer 24:25bff2709c20 275 # Check for invalid cumulative overrides in libraries
screamer 24:25bff2709c20 276 if (unit_kind == 'library' and
screamer 24:25bff2709c20 277 any(attr.startswith('target.extra_labels') for attr in overrides.iterkeys())):
screamer 24:25bff2709c20 278 raise ConfigException("Target override '%s' in '%s' is only allowed at the application level"
screamer 24:25bff2709c20 279 % ("target.extra_labels", ConfigParameter.get_display_name(unit_name, unit_kind, label)))
Screamer@Y5070-M.virtuoso 10:2511036308b8 280
screamer 24:25bff2709c20 281 # Parse out cumulative overrides
screamer 24:25bff2709c20 282 for attr, cumulatives in self.cumulative_overrides.iteritems():
screamer 24:25bff2709c20 283 if 'target.'+attr in overrides:
screamer 24:25bff2709c20 284 cumulatives.strict_cumulative_overrides(overrides['target.'+attr])
screamer 24:25bff2709c20 285 del overrides['target.'+attr]
Screamer@Y5070-M.virtuoso 10:2511036308b8 286
screamer 24:25bff2709c20 287 if 'target.'+attr+'_add' in overrides:
screamer 24:25bff2709c20 288 cumulatives.add_cumulative_overrides(overrides['target.'+attr+'_add'])
screamer 24:25bff2709c20 289 del overrides['target.'+attr+'_add']
screamer 24:25bff2709c20 290
screamer 24:25bff2709c20 291 if 'target.'+attr+'_remove' in overrides:
screamer 24:25bff2709c20 292 cumulatives.remove_cumulative_overrides(overrides['target.'+attr+'_remove'])
screamer 24:25bff2709c20 293 del overrides['target.'+attr+'_remove']
Screamer@Y5070-M.virtuoso 10:2511036308b8 294
Screamer@Y5070-M.virtuoso 10:2511036308b8 295 # Consider the others as overrides
screamer 8:a8ac6ed29081 296 for name, v in overrides.items():
screamer 8:a8ac6ed29081 297 # Get the full name of the parameter
screamer 8:a8ac6ed29081 298 full_name = ConfigParameter.get_full_name(name, unit_name, unit_kind, label)
screamer 13:ab47a20b66f0 299 if full_name in params:
screamer 13:ab47a20b66f0 300 params[full_name].set_value(v, unit_name, unit_kind, label)
screamer 13:ab47a20b66f0 301 else:
screamer 13:ab47a20b66f0 302 self.config_errors.append(ConfigException("Attempt to override undefined parameter '%s' in '%s'"
screamer 13:ab47a20b66f0 303 % (full_name, ConfigParameter.get_display_name(unit_name, unit_kind, label))))
screamer 24:25bff2709c20 304
screamer 24:25bff2709c20 305 for cumulatives in self.cumulative_overrides.itervalues():
screamer 24:25bff2709c20 306 cumulatives.update_target(Target.get_target(self.target))
screamer 24:25bff2709c20 307
screamer 8:a8ac6ed29081 308 return params
screamer 8:a8ac6ed29081 309
screamer 8:a8ac6ed29081 310 # Read and interpret configuration data defined by targets
screamer 8:a8ac6ed29081 311 def get_target_config_data(self):
screamer 8:a8ac6ed29081 312 # We consider the resolution order for our target and sort it by level reversed,
screamer 8:a8ac6ed29081 313 # so that we first look at the top level target (the parent), then its direct children,
screamer 8:a8ac6ed29081 314 # then the children's children and so on, until we reach self.target
screamer 8:a8ac6ed29081 315 # TODO: this might not work so well in some multiple inheritance scenarios
screamer 8:a8ac6ed29081 316 # At each step, look at two keys of the target data:
screamer 8:a8ac6ed29081 317 # - config_parameters: used to define new configuration parameters
screamer 8:a8ac6ed29081 318 # - config_overrides: used to override already defined configuration parameters
screamer 8:a8ac6ed29081 319 params, json_data = {}, Target.get_json_target_data()
screamer 8:a8ac6ed29081 320 resolution_order = [e[0] for e in sorted(Target.get_target(self.target).resolution_order, key = lambda e: e[1], reverse = True)]
screamer 8:a8ac6ed29081 321 for tname in resolution_order:
screamer 8:a8ac6ed29081 322 # Read the target data directly from its description
screamer 8:a8ac6ed29081 323 t = json_data[tname]
screamer 8:a8ac6ed29081 324 # Process definitions first
screamer 8:a8ac6ed29081 325 self._process_config_parameters(t.get("config", {}), params, tname, "target")
screamer 8:a8ac6ed29081 326 # Then process overrides
screamer 8:a8ac6ed29081 327 for name, v in t.get("overrides", {}).items():
screamer 8:a8ac6ed29081 328 full_name = ConfigParameter.get_full_name(name, tname, "target")
screamer 8:a8ac6ed29081 329 # If the parameter name is not defined or if there isn't a path from this target to the target where the
screamer 8:a8ac6ed29081 330 # parameter was defined in the target inheritance tree, raise an error
screamer 8:a8ac6ed29081 331 # We need to use 'defined_by[7:]' to remove the "target:" prefix from defined_by
screamer 8:a8ac6ed29081 332 if (not full_name in params) or (not params[full_name].defined_by[7:] in Target.get_target(tname).resolution_order_names):
screamer 8:a8ac6ed29081 333 raise ConfigException("Attempt to override undefined parameter '%s' in '%s'" % (name, ConfigParameter.get_display_name(tname, "target")))
screamer 8:a8ac6ed29081 334 # Otherwise update the value of the parameter
screamer 8:a8ac6ed29081 335 params[full_name].set_value(v, tname, "target")
screamer 8:a8ac6ed29081 336 return params
screamer 8:a8ac6ed29081 337
screamer 8:a8ac6ed29081 338 # Helper function: process a macro definition, checking for incompatible duplicate definitions
screamer 8:a8ac6ed29081 339 # mlist: list of macro names to process
screamer 8:a8ac6ed29081 340 # macros: dictionary with currently discovered macros
screamer 8:a8ac6ed29081 341 # unit_name: the unit (library/application) that defines this macro
screamer 8:a8ac6ed29081 342 # unit_kind: the kind of the unit ("library" or "application")
screamer 8:a8ac6ed29081 343 def _process_macros(self, mlist, macros, unit_name, unit_kind):
screamer 8:a8ac6ed29081 344 for mname in mlist:
screamer 8:a8ac6ed29081 345 m = ConfigMacro(mname, unit_name, unit_kind)
screamer 8:a8ac6ed29081 346 if (m.macro_name in macros) and (macros[m.macro_name].name != mname):
screamer 8:a8ac6ed29081 347 # Found an incompatible definition of the macro in another module, so raise an error
screamer 8:a8ac6ed29081 348 full_unit_name = ConfigParameter.get_display_name(unit_name, unit_kind)
screamer 8:a8ac6ed29081 349 raise ConfigException("Macro '%s' defined in both '%s' and '%s' with incompatible values" % (m.macro_name, macros[m.macro_name].defined_by, full_unit_name))
screamer 8:a8ac6ed29081 350 macros[m.macro_name] = m
screamer 8:a8ac6ed29081 351
screamer 8:a8ac6ed29081 352 # Read and interpret configuration data defined by libs
screamer 8:a8ac6ed29081 353 # It is assumed that "add_config_files" above was already called and the library configuration data
screamer 8:a8ac6ed29081 354 # exists in self.lib_config_data
screamer 8:a8ac6ed29081 355 def get_lib_config_data(self):
screamer 8:a8ac6ed29081 356 all_params, macros = {}, {}
screamer 8:a8ac6ed29081 357 for lib_name, lib_data in self.lib_config_data.items():
screamer 8:a8ac6ed29081 358 unknown_keys = set(lib_data.keys()) - self.__allowed_keys["library"]
screamer 8:a8ac6ed29081 359 if unknown_keys:
screamer 8:a8ac6ed29081 360 raise ConfigException("Unknown key(s) '%s' in %s" % (",".join(unknown_keys), lib_name))
screamer 8:a8ac6ed29081 361 all_params.update(self._process_config_and_overrides(lib_data, {}, lib_name, "library"))
screamer 8:a8ac6ed29081 362 self._process_macros(lib_data.get("macros", []), macros, lib_name, "library")
screamer 8:a8ac6ed29081 363 return all_params, macros
screamer 8:a8ac6ed29081 364
screamer 8:a8ac6ed29081 365 # Read and interpret the configuration data defined by the target
screamer 8:a8ac6ed29081 366 # The target can override any configuration parameter, as well as define its own configuration data
screamer 8:a8ac6ed29081 367 # params: the dictionary with configuration parameters found so far (in the target and in libraries)
screamer 8:a8ac6ed29081 368 # macros: the list of macros defined in the configuration
screamer 8:a8ac6ed29081 369 def get_app_config_data(self, params, macros):
screamer 8:a8ac6ed29081 370 app_cfg = self.app_config_data
screamer 8:a8ac6ed29081 371 # The application can have a "config_parameters" and a "target_config_overrides" section just like a library
screamer 8:a8ac6ed29081 372 self._process_config_and_overrides(app_cfg, params, "app", "application")
screamer 8:a8ac6ed29081 373 # The application can also defined macros
screamer 8:a8ac6ed29081 374 self._process_macros(app_cfg.get("macros", []), macros, "app", "application")
screamer 8:a8ac6ed29081 375
screamer 8:a8ac6ed29081 376 # Return the configuration data in two parts:
screamer 8:a8ac6ed29081 377 # - params: a dictionary with (name, ConfigParam) entries
screamer 13:ab47a20b66f0 378 # - macros: the list of macros defined with "macros" in libraries and in the application (as ConfigMacro instances)
screamer 8:a8ac6ed29081 379 def get_config_data(self):
screamer 8:a8ac6ed29081 380 all_params = self.get_target_config_data()
screamer 8:a8ac6ed29081 381 lib_params, macros = self.get_lib_config_data()
screamer 8:a8ac6ed29081 382 all_params.update(lib_params)
screamer 8:a8ac6ed29081 383 self.get_app_config_data(all_params, macros)
screamer 13:ab47a20b66f0 384 return all_params, macros
screamer 8:a8ac6ed29081 385
screamer 8:a8ac6ed29081 386 # Helper: verify if there are any required parameters without a value in 'params'
screamer 13:ab47a20b66f0 387 @staticmethod
screamer 13:ab47a20b66f0 388 def _check_required_parameters(params):
screamer 8:a8ac6ed29081 389 for p in params.values():
screamer 8:a8ac6ed29081 390 if p.required and (p.value is None):
screamer 8:a8ac6ed29081 391 raise ConfigException("Required parameter '%s' defined by '%s' doesn't have a value" % (p.name, p.defined_by))
screamer 8:a8ac6ed29081 392
screamer 8:a8ac6ed29081 393 # Return the macro definitions generated for a dictionary of configuration parameters
screamer 8:a8ac6ed29081 394 # params: a dictionary of (name, ConfigParameters instance) mappings
screamer 8:a8ac6ed29081 395 @staticmethod
screamer 8:a8ac6ed29081 396 def parameters_to_macros(params):
screamer 8:a8ac6ed29081 397 return ['%s=%s' % (m.macro_name, m.value) for m in params.values() if m.value is not None]
screamer 8:a8ac6ed29081 398
screamer 13:ab47a20b66f0 399 # Return the macro definitions generated for a dictionary of ConfigMacros (as returned by get_config_data)
screamer 13:ab47a20b66f0 400 # params: a dictionary of (name, ConfigMacro instance) mappings
screamer 13:ab47a20b66f0 401 @staticmethod
screamer 13:ab47a20b66f0 402 def config_macros_to_macros(macros):
screamer 13:ab47a20b66f0 403 return [m.name for m in macros.values()]
screamer 13:ab47a20b66f0 404
screamer 13:ab47a20b66f0 405 # Return the configuration data converted to a list of C macros
screamer 13:ab47a20b66f0 406 # config - configuration data as (ConfigParam instances, ConfigMacro instances) tuple
screamer 13:ab47a20b66f0 407 # (as returned by get_config_data())
screamer 13:ab47a20b66f0 408 @staticmethod
screamer 13:ab47a20b66f0 409 def config_to_macros(config):
screamer 13:ab47a20b66f0 410 params, macros = config[0], config[1]
screamer 13:ab47a20b66f0 411 Config._check_required_parameters(params)
screamer 13:ab47a20b66f0 412 return Config.config_macros_to_macros(macros) + Config.parameters_to_macros(params)
screamer 13:ab47a20b66f0 413
screamer 8:a8ac6ed29081 414 # Return the configuration data converted to a list of C macros
screamer 8:a8ac6ed29081 415 def get_config_data_macros(self):
screamer 13:ab47a20b66f0 416 return self.config_to_macros(self.get_config_data())
screamer 13:ab47a20b66f0 417
screamer 13:ab47a20b66f0 418 # Returns any features in the configuration data
screamer 13:ab47a20b66f0 419 def get_features(self):
screamer 13:ab47a20b66f0 420 params, _ = self.get_config_data()
screamer 8:a8ac6ed29081 421 self._check_required_parameters(params)
screamer 24:25bff2709c20 422 self.cumulative_overrides['features'].update_target(Target.get_target(self.target))
screamer 24:25bff2709c20 423 features = Target.get_target(self.target).features
screamer 13:ab47a20b66f0 424
screamer 13:ab47a20b66f0 425 for feature in features:
screamer 13:ab47a20b66f0 426 if feature not in self.__allowed_features:
screamer 13:ab47a20b66f0 427 raise ConfigException("Feature '%s' is not a supported features" % feature)
screamer 13:ab47a20b66f0 428
screamer 13:ab47a20b66f0 429 return features
screamer 13:ab47a20b66f0 430
screamer 13:ab47a20b66f0 431 # Validate configuration settings. This either returns True or raises an exception
screamer 13:ab47a20b66f0 432 def validate_config(self):
screamer 13:ab47a20b66f0 433 if self.config_errors:
screamer 13:ab47a20b66f0 434 raise self.config_errors[0]
screamer 13:ab47a20b66f0 435 return True
screamer 13:ab47a20b66f0 436
screamer 13:ab47a20b66f0 437
screamer 13:ab47a20b66f0 438 # Loads configuration data from resources. Also expands resources based on defined features settings
screamer 13:ab47a20b66f0 439 def load_resources(self, resources):
screamer 13:ab47a20b66f0 440 # Update configuration files until added features creates no changes
screamer 13:ab47a20b66f0 441 prev_features = set()
screamer 13:ab47a20b66f0 442 while True:
screamer 13:ab47a20b66f0 443 # Add/update the configuration with any .json files found while scanning
screamer 13:ab47a20b66f0 444 self.add_config_files(resources.json_files)
screamer 13:ab47a20b66f0 445
screamer 13:ab47a20b66f0 446 # Add features while we find new ones
screamer 13:ab47a20b66f0 447 features = self.get_features()
screamer 13:ab47a20b66f0 448 if features == prev_features:
screamer 13:ab47a20b66f0 449 break
screamer 13:ab47a20b66f0 450
screamer 13:ab47a20b66f0 451 for feature in features:
screamer 13:ab47a20b66f0 452 if feature in resources.features:
screamer 13:ab47a20b66f0 453 resources.add(resources.features[feature])
screamer 13:ab47a20b66f0 454
screamer 13:ab47a20b66f0 455 prev_features = features
screamer 13:ab47a20b66f0 456 self.validate_config()
screamer 13:ab47a20b66f0 457
screamer 13:ab47a20b66f0 458 return resources
screamer 13:ab47a20b66f0 459
screamer 13:ab47a20b66f0 460 # Return the configuration data converted to the content of a C header file,
screamer 13:ab47a20b66f0 461 # meant to be included to a C/C++ file. The content is returned as a string.
screamer 13:ab47a20b66f0 462 # If 'fname' is given, the content is also written to the file called "fname".
screamer 13:ab47a20b66f0 463 # WARNING: if 'fname' names an existing file, that file will be overwritten!
screamer 13:ab47a20b66f0 464 # config - configuration data as (ConfigParam instances, ConfigMacro instances) tuple
screamer 13:ab47a20b66f0 465 # (as returned by get_config_data())
screamer 13:ab47a20b66f0 466 @staticmethod
screamer 13:ab47a20b66f0 467 def config_to_header(config, fname = None):
screamer 13:ab47a20b66f0 468 params, macros = config[0], config[1]
screamer 13:ab47a20b66f0 469 Config._check_required_parameters(params)
screamer 13:ab47a20b66f0 470 header_data = "// Automatically generated configuration file.\n"
screamer 13:ab47a20b66f0 471 header_data += "// DO NOT EDIT, content will be overwritten.\n\n"
screamer 13:ab47a20b66f0 472 header_data += "#ifndef __MBED_CONFIG_DATA__\n"
screamer 13:ab47a20b66f0 473 header_data += "#define __MBED_CONFIG_DATA__\n\n"
screamer 13:ab47a20b66f0 474 # Compute maximum length of macro names for proper alignment
screamer 13:ab47a20b66f0 475 max_param_macro_name_len = max([len(m.macro_name) for m in params.values() if m.value is not None]) if params else 0
screamer 13:ab47a20b66f0 476 max_direct_macro_name_len = max([len(m.macro_name) for m in macros.values()]) if macros else 0
screamer 13:ab47a20b66f0 477 max_macro_name_len = max(max_param_macro_name_len, max_direct_macro_name_len)
screamer 13:ab47a20b66f0 478 # Compute maximum length of macro values for proper alignment
screamer 13:ab47a20b66f0 479 max_param_macro_val_len = max([len(str(m.value)) for m in params.values() if m.value is not None]) if params else 0
screamer 13:ab47a20b66f0 480 max_direct_macro_val_len = max([len(m.macro_value or "") for m in macros.values()]) if macros else 0
screamer 13:ab47a20b66f0 481 max_macro_val_len = max(max_param_macro_val_len, max_direct_macro_val_len)
screamer 13:ab47a20b66f0 482 # Generate config parameters first
screamer 13:ab47a20b66f0 483 if params:
screamer 13:ab47a20b66f0 484 header_data += "// Configuration parameters\n"
screamer 13:ab47a20b66f0 485 for m in params.values():
screamer 13:ab47a20b66f0 486 if m.value is not None:
screamer 13:ab47a20b66f0 487 header_data += "#define {0:<{1}} {2!s:<{3}} // set by {4}\n".format(m.macro_name, max_macro_name_len, m.value, max_macro_val_len, m.set_by)
screamer 13:ab47a20b66f0 488 # Then macros
screamer 13:ab47a20b66f0 489 if macros:
screamer 13:ab47a20b66f0 490 header_data += "// Macros\n"
screamer 13:ab47a20b66f0 491 for m in macros.values():
screamer 13:ab47a20b66f0 492 if m.macro_value:
screamer 13:ab47a20b66f0 493 header_data += "#define {0:<{1}} {2!s:<{3}} // defined by {4}\n".format(m.macro_name, max_macro_name_len, m.macro_value, max_macro_val_len, m.defined_by)
screamer 13:ab47a20b66f0 494 else:
screamer 13:ab47a20b66f0 495 header_data += "#define {0:<{1}} // defined by {2}\n".format(m.macro_name, max_macro_name_len + max_macro_val_len + 1, m.defined_by)
screamer 13:ab47a20b66f0 496 header_data += "\n#endif\n"
screamer 13:ab47a20b66f0 497 # If fname is given, write "header_data" to it
screamer 13:ab47a20b66f0 498 if fname:
screamer 13:ab47a20b66f0 499 with open(fname, "wt") as f:
screamer 13:ab47a20b66f0 500 f.write(header_data)
screamer 13:ab47a20b66f0 501 return header_data
screamer 13:ab47a20b66f0 502
screamer 13:ab47a20b66f0 503 # Return the configuration data converted to the content of a C header file,
screamer 13:ab47a20b66f0 504 # meant to be included to a C/C++ file. The content is returned as a string.
screamer 13:ab47a20b66f0 505 # If 'fname' is given, the content is also written to the file called "fname".
screamer 13:ab47a20b66f0 506 # WARNING: if 'fname' names an existing file, that file will be overwritten!
screamer 13:ab47a20b66f0 507 def get_config_data_header(self, fname = None):
screamer 13:ab47a20b66f0 508 return self.config_to_header(self.get_config_data(), fname)