Clone of official tools

Committer:
screamer
Date:
Thu Jul 14 20:21:19 2016 +0100
Revision:
13:ab47a20b66f0
Parent:
10:2511036308b8
Child:
24:25bff2709c20
Apply latest tools

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 8:a8ac6ed29081 140 # 'Config' implements the mbed configuration mechanism
screamer 8:a8ac6ed29081 141 class Config:
screamer 8:a8ac6ed29081 142 # Libraries and applications have different names for their configuration files
screamer 8:a8ac6ed29081 143 __mbed_app_config_name = "mbed_app.json"
screamer 8:a8ac6ed29081 144 __mbed_lib_config_name = "mbed_lib.json"
screamer 8:a8ac6ed29081 145
screamer 8:a8ac6ed29081 146 # Allowed keys in configuration dictionaries
screamer 8:a8ac6ed29081 147 # (targets can have any kind of keys, so this validation is not applicable to them)
screamer 8:a8ac6ed29081 148 __allowed_keys = {
screamer 8:a8ac6ed29081 149 "library": set(["name", "config", "target_overrides", "macros", "__config_path"]),
screamer 8:a8ac6ed29081 150 "application": set(["config", "custom_targets", "target_overrides", "macros", "__config_path"])
screamer 8:a8ac6ed29081 151 }
screamer 8:a8ac6ed29081 152
screamer 13:ab47a20b66f0 153 # Allowed features in configurations
screamer 13:ab47a20b66f0 154 __allowed_features = [
screamer 13:ab47a20b66f0 155 "UVISOR", "BLE", "CLIENT", "IPV4", "IPV6"
screamer 13:ab47a20b66f0 156 ]
screamer 13:ab47a20b66f0 157
screamer 8:a8ac6ed29081 158 # The initialization arguments for Config are:
screamer 8:a8ac6ed29081 159 # target: the name of the mbed target used for this configuration instance
screamer 8:a8ac6ed29081 160 # top_level_dirs: a list of top level source directories (where mbed_abb_config.json could be found)
screamer 8:a8ac6ed29081 161 # __init__ will look for the application configuration file in top_level_dirs.
screamer 8:a8ac6ed29081 162 # If found once, it'll parse it and check if it has a custom_targets function.
screamer 8:a8ac6ed29081 163 # If it does, it'll update the list of targets if need.
screamer 8:a8ac6ed29081 164 # If found more than once, an exception is raised
screamer 8:a8ac6ed29081 165 # top_level_dirs can be None (in this case, mbed_app_config.json will not be searched)
screamer 8:a8ac6ed29081 166 def __init__(self, target, top_level_dirs = []):
screamer 8:a8ac6ed29081 167 app_config_location = None
screamer 8:a8ac6ed29081 168 for s in (top_level_dirs or []):
screamer 8:a8ac6ed29081 169 full_path = os.path.join(s, self.__mbed_app_config_name)
screamer 8:a8ac6ed29081 170 if os.path.isfile(full_path):
screamer 8:a8ac6ed29081 171 if app_config_location is not None:
screamer 8:a8ac6ed29081 172 raise ConfigException("Duplicate '%s' file in '%s' and '%s'" % (self.__mbed_app_config_name, app_config_location, full_path))
screamer 8:a8ac6ed29081 173 else:
screamer 8:a8ac6ed29081 174 app_config_location = full_path
screamer 8:a8ac6ed29081 175 self.app_config_data = json_file_to_dict(app_config_location) if app_config_location else {}
screamer 8:a8ac6ed29081 176 # Check the keys in the application configuration data
screamer 8:a8ac6ed29081 177 unknown_keys = set(self.app_config_data.keys()) - self.__allowed_keys["application"]
screamer 8:a8ac6ed29081 178 if unknown_keys:
screamer 8:a8ac6ed29081 179 raise ConfigException("Unknown key(s) '%s' in %s" % (",".join(unknown_keys), self.__mbed_app_config_name))
screamer 8:a8ac6ed29081 180 # Update the list of targets with the ones defined in the application config, if applicable
screamer 8:a8ac6ed29081 181 Target.add_py_targets(self.app_config_data.get("custom_targets", {}))
screamer 8:a8ac6ed29081 182 self.lib_config_data = {}
screamer 8:a8ac6ed29081 183 # Make sure that each config is processed only once
screamer 8:a8ac6ed29081 184 self.processed_configs = {}
screamer 13:ab47a20b66f0 185 self.target = target if isinstance(target, basestring) else target.name
screamer 8:a8ac6ed29081 186 self.target_labels = Target.get_target(self.target).get_labels()
screamer 13:ab47a20b66f0 187 self.added_features = set()
screamer 13:ab47a20b66f0 188 self.removed_features = set()
screamer 13:ab47a20b66f0 189 self.removed_unecessary_features = False
screamer 8:a8ac6ed29081 190
screamer 8:a8ac6ed29081 191 # Add one or more configuration files
screamer 8:a8ac6ed29081 192 def add_config_files(self, flist):
screamer 8:a8ac6ed29081 193 for f in flist:
screamer 8:a8ac6ed29081 194 if not f.endswith(self.__mbed_lib_config_name):
screamer 8:a8ac6ed29081 195 continue
screamer 8:a8ac6ed29081 196 full_path = os.path.normpath(os.path.abspath(f))
screamer 8:a8ac6ed29081 197 # Check that we didn't already process this file
screamer 8:a8ac6ed29081 198 if self.processed_configs.has_key(full_path):
screamer 8:a8ac6ed29081 199 continue
screamer 8:a8ac6ed29081 200 self.processed_configs[full_path] = True
screamer 8:a8ac6ed29081 201 # Read the library configuration and add a "__full_config_path" attribute to it
screamer 8:a8ac6ed29081 202 cfg = json_file_to_dict(f)
screamer 8:a8ac6ed29081 203 cfg["__config_path"] = full_path
screamer 8:a8ac6ed29081 204 # If there's already a configuration for a module with the same name, exit with error
screamer 8:a8ac6ed29081 205 if self.lib_config_data.has_key(cfg["name"]):
screamer 8:a8ac6ed29081 206 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 207 self.lib_config_data[cfg["name"]] = cfg
screamer 8:a8ac6ed29081 208
screamer 8:a8ac6ed29081 209 # Helper function: process a "config_parameters" section in either a target, a library or the application
screamer 8:a8ac6ed29081 210 # data: a dictionary with the configuration parameters
screamer 8:a8ac6ed29081 211 # params: storage for the discovered configuration parameters
screamer 8:a8ac6ed29081 212 # unit_name: the unit (target/library/application) that defines this parameter
screamer 8:a8ac6ed29081 213 # unit_kind: the kind of the unit ("target", "library" or "application")
screamer 8:a8ac6ed29081 214 def _process_config_parameters(self, data, params, unit_name, unit_kind):
screamer 8:a8ac6ed29081 215 for name, v in data.items():
screamer 8:a8ac6ed29081 216 full_name = ConfigParameter.get_full_name(name, unit_name, unit_kind)
screamer 8:a8ac6ed29081 217 # If the parameter was already defined, raise an error
screamer 8:a8ac6ed29081 218 if full_name in params:
screamer 8:a8ac6ed29081 219 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 220 # Otherwise add it to the list of known parameters
screamer 8:a8ac6ed29081 221 # If "v" is not a dictionary, this is a shortcut definition, otherwise it is a full definition
screamer 8:a8ac6ed29081 222 params[full_name] = ConfigParameter(name, v if isinstance(v, dict) else {"value": v}, unit_name, unit_kind)
screamer 8:a8ac6ed29081 223 return params
screamer 8:a8ac6ed29081 224
screamer 13:ab47a20b66f0 225 # Add features to the available features
screamer 13:ab47a20b66f0 226 def remove_features(self, features):
screamer 13:ab47a20b66f0 227 for feature in features:
screamer 13:ab47a20b66f0 228 if feature in self.added_features:
screamer 13:ab47a20b66f0 229 raise ConfigException("Configuration conflict. Feature %s both added and removed." % feature)
screamer 13:ab47a20b66f0 230
screamer 13:ab47a20b66f0 231 self.removed_features |= set(features)
screamer 13:ab47a20b66f0 232
screamer 13:ab47a20b66f0 233 # Remove features from the available features
screamer 13:ab47a20b66f0 234 def add_features(self, features):
screamer 13:ab47a20b66f0 235 for feature in features:
screamer 13:ab47a20b66f0 236 if (feature in self.removed_features
screamer 13:ab47a20b66f0 237 or (self.removed_unecessary_features and feature not in self.added_features)):
screamer 13:ab47a20b66f0 238 raise ConfigException("Configuration conflict. Feature %s both added and removed." % feature)
screamer 13:ab47a20b66f0 239
screamer 13:ab47a20b66f0 240 self.added_features |= set(features)
screamer 13:ab47a20b66f0 241
screamer 8:a8ac6ed29081 242 # Helper function: process "config_parameters" and "target_config_overrides" in a given dictionary
screamer 8:a8ac6ed29081 243 # data: the configuration data of the library/appliation
screamer 8:a8ac6ed29081 244 # params: storage for the discovered configuration parameters
screamer 8:a8ac6ed29081 245 # unit_name: the unit (library/application) that defines this parameter
screamer 8:a8ac6ed29081 246 # unit_kind: the kind of the unit ("library" or "application")
screamer 8:a8ac6ed29081 247 def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
screamer 13:ab47a20b66f0 248 self.config_errors = []
screamer 8:a8ac6ed29081 249 self._process_config_parameters(data.get("config", {}), params, unit_name, unit_kind)
screamer 8:a8ac6ed29081 250 for label, overrides in data.get("target_overrides", {}).items():
screamer 8:a8ac6ed29081 251 # If the label is defined by the target or it has the special value "*", process the overrides
screamer 8:a8ac6ed29081 252 if (label == '*') or (label in self.target_labels):
screamer 13:ab47a20b66f0 253 # Parse out features
screamer 13:ab47a20b66f0 254 if 'target.features' in overrides:
screamer 13:ab47a20b66f0 255 features = overrides['target.features']
screamer 13:ab47a20b66f0 256 self.remove_features(self.added_features - set(features))
screamer 13:ab47a20b66f0 257 self.add_features(features)
screamer 13:ab47a20b66f0 258 self.removed_unecessary_features = True
screamer 13:ab47a20b66f0 259 del overrides['target.features']
Screamer@Y5070-M.virtuoso 10:2511036308b8 260
screamer 13:ab47a20b66f0 261 if 'target.features_add' in overrides:
screamer 13:ab47a20b66f0 262 self.add_features(overrides['target.features_add'])
screamer 13:ab47a20b66f0 263 del overrides['target.features_add']
Screamer@Y5070-M.virtuoso 10:2511036308b8 264
screamer 13:ab47a20b66f0 265 if 'target.features_remove' in overrides:
screamer 13:ab47a20b66f0 266 self.remove_features(overrides['target.features_remove'])
screamer 13:ab47a20b66f0 267 del overrides['target.features_remove']
Screamer@Y5070-M.virtuoso 10:2511036308b8 268
Screamer@Y5070-M.virtuoso 10:2511036308b8 269 # Consider the others as overrides
screamer 8:a8ac6ed29081 270 for name, v in overrides.items():
screamer 8:a8ac6ed29081 271 # Get the full name of the parameter
screamer 8:a8ac6ed29081 272 full_name = ConfigParameter.get_full_name(name, unit_name, unit_kind, label)
screamer 13:ab47a20b66f0 273 if full_name in params:
screamer 13:ab47a20b66f0 274 params[full_name].set_value(v, unit_name, unit_kind, label)
screamer 13:ab47a20b66f0 275 else:
screamer 13:ab47a20b66f0 276 self.config_errors.append(ConfigException("Attempt to override undefined parameter '%s' in '%s'"
screamer 13:ab47a20b66f0 277 % (full_name, ConfigParameter.get_display_name(unit_name, unit_kind, label))))
screamer 8:a8ac6ed29081 278 return params
screamer 8:a8ac6ed29081 279
screamer 8:a8ac6ed29081 280 # Read and interpret configuration data defined by targets
screamer 8:a8ac6ed29081 281 def get_target_config_data(self):
screamer 8:a8ac6ed29081 282 # We consider the resolution order for our target and sort it by level reversed,
screamer 8:a8ac6ed29081 283 # so that we first look at the top level target (the parent), then its direct children,
screamer 8:a8ac6ed29081 284 # then the children's children and so on, until we reach self.target
screamer 8:a8ac6ed29081 285 # TODO: this might not work so well in some multiple inheritance scenarios
screamer 8:a8ac6ed29081 286 # At each step, look at two keys of the target data:
screamer 8:a8ac6ed29081 287 # - config_parameters: used to define new configuration parameters
screamer 8:a8ac6ed29081 288 # - config_overrides: used to override already defined configuration parameters
screamer 8:a8ac6ed29081 289 params, json_data = {}, Target.get_json_target_data()
screamer 8:a8ac6ed29081 290 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 291 for tname in resolution_order:
screamer 8:a8ac6ed29081 292 # Read the target data directly from its description
screamer 8:a8ac6ed29081 293 t = json_data[tname]
screamer 8:a8ac6ed29081 294 # Process definitions first
screamer 8:a8ac6ed29081 295 self._process_config_parameters(t.get("config", {}), params, tname, "target")
screamer 8:a8ac6ed29081 296 # Then process overrides
screamer 8:a8ac6ed29081 297 for name, v in t.get("overrides", {}).items():
screamer 8:a8ac6ed29081 298 full_name = ConfigParameter.get_full_name(name, tname, "target")
screamer 8:a8ac6ed29081 299 # 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 300 # parameter was defined in the target inheritance tree, raise an error
screamer 8:a8ac6ed29081 301 # We need to use 'defined_by[7:]' to remove the "target:" prefix from defined_by
screamer 8:a8ac6ed29081 302 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 303 raise ConfigException("Attempt to override undefined parameter '%s' in '%s'" % (name, ConfigParameter.get_display_name(tname, "target")))
screamer 8:a8ac6ed29081 304 # Otherwise update the value of the parameter
screamer 8:a8ac6ed29081 305 params[full_name].set_value(v, tname, "target")
screamer 8:a8ac6ed29081 306 return params
screamer 8:a8ac6ed29081 307
screamer 8:a8ac6ed29081 308 # Helper function: process a macro definition, checking for incompatible duplicate definitions
screamer 8:a8ac6ed29081 309 # mlist: list of macro names to process
screamer 8:a8ac6ed29081 310 # macros: dictionary with currently discovered macros
screamer 8:a8ac6ed29081 311 # unit_name: the unit (library/application) that defines this macro
screamer 8:a8ac6ed29081 312 # unit_kind: the kind of the unit ("library" or "application")
screamer 8:a8ac6ed29081 313 def _process_macros(self, mlist, macros, unit_name, unit_kind):
screamer 8:a8ac6ed29081 314 for mname in mlist:
screamer 8:a8ac6ed29081 315 m = ConfigMacro(mname, unit_name, unit_kind)
screamer 8:a8ac6ed29081 316 if (m.macro_name in macros) and (macros[m.macro_name].name != mname):
screamer 8:a8ac6ed29081 317 # Found an incompatible definition of the macro in another module, so raise an error
screamer 8:a8ac6ed29081 318 full_unit_name = ConfigParameter.get_display_name(unit_name, unit_kind)
screamer 8:a8ac6ed29081 319 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 320 macros[m.macro_name] = m
screamer 8:a8ac6ed29081 321
screamer 8:a8ac6ed29081 322 # Read and interpret configuration data defined by libs
screamer 8:a8ac6ed29081 323 # It is assumed that "add_config_files" above was already called and the library configuration data
screamer 8:a8ac6ed29081 324 # exists in self.lib_config_data
screamer 8:a8ac6ed29081 325 def get_lib_config_data(self):
screamer 8:a8ac6ed29081 326 all_params, macros = {}, {}
screamer 8:a8ac6ed29081 327 for lib_name, lib_data in self.lib_config_data.items():
screamer 8:a8ac6ed29081 328 unknown_keys = set(lib_data.keys()) - self.__allowed_keys["library"]
screamer 8:a8ac6ed29081 329 if unknown_keys:
screamer 8:a8ac6ed29081 330 raise ConfigException("Unknown key(s) '%s' in %s" % (",".join(unknown_keys), lib_name))
screamer 8:a8ac6ed29081 331 all_params.update(self._process_config_and_overrides(lib_data, {}, lib_name, "library"))
screamer 8:a8ac6ed29081 332 self._process_macros(lib_data.get("macros", []), macros, lib_name, "library")
screamer 8:a8ac6ed29081 333 return all_params, macros
screamer 8:a8ac6ed29081 334
screamer 8:a8ac6ed29081 335 # Read and interpret the configuration data defined by the target
screamer 8:a8ac6ed29081 336 # The target can override any configuration parameter, as well as define its own configuration data
screamer 8:a8ac6ed29081 337 # params: the dictionary with configuration parameters found so far (in the target and in libraries)
screamer 8:a8ac6ed29081 338 # macros: the list of macros defined in the configuration
screamer 8:a8ac6ed29081 339 def get_app_config_data(self, params, macros):
screamer 8:a8ac6ed29081 340 app_cfg = self.app_config_data
screamer 8:a8ac6ed29081 341 # The application can have a "config_parameters" and a "target_config_overrides" section just like a library
screamer 8:a8ac6ed29081 342 self._process_config_and_overrides(app_cfg, params, "app", "application")
screamer 8:a8ac6ed29081 343 # The application can also defined macros
screamer 8:a8ac6ed29081 344 self._process_macros(app_cfg.get("macros", []), macros, "app", "application")
screamer 8:a8ac6ed29081 345
screamer 8:a8ac6ed29081 346 # Return the configuration data in two parts:
screamer 8:a8ac6ed29081 347 # - params: a dictionary with (name, ConfigParam) entries
screamer 13:ab47a20b66f0 348 # - macros: the list of macros defined with "macros" in libraries and in the application (as ConfigMacro instances)
screamer 8:a8ac6ed29081 349 def get_config_data(self):
screamer 8:a8ac6ed29081 350 all_params = self.get_target_config_data()
screamer 8:a8ac6ed29081 351 lib_params, macros = self.get_lib_config_data()
screamer 8:a8ac6ed29081 352 all_params.update(lib_params)
screamer 8:a8ac6ed29081 353 self.get_app_config_data(all_params, macros)
screamer 13:ab47a20b66f0 354 return all_params, macros
screamer 8:a8ac6ed29081 355
screamer 8:a8ac6ed29081 356 # Helper: verify if there are any required parameters without a value in 'params'
screamer 13:ab47a20b66f0 357 @staticmethod
screamer 13:ab47a20b66f0 358 def _check_required_parameters(params):
screamer 8:a8ac6ed29081 359 for p in params.values():
screamer 8:a8ac6ed29081 360 if p.required and (p.value is None):
screamer 8:a8ac6ed29081 361 raise ConfigException("Required parameter '%s' defined by '%s' doesn't have a value" % (p.name, p.defined_by))
screamer 8:a8ac6ed29081 362
screamer 8:a8ac6ed29081 363 # Return the macro definitions generated for a dictionary of configuration parameters
screamer 8:a8ac6ed29081 364 # params: a dictionary of (name, ConfigParameters instance) mappings
screamer 8:a8ac6ed29081 365 @staticmethod
screamer 8:a8ac6ed29081 366 def parameters_to_macros(params):
screamer 8:a8ac6ed29081 367 return ['%s=%s' % (m.macro_name, m.value) for m in params.values() if m.value is not None]
screamer 8:a8ac6ed29081 368
screamer 13:ab47a20b66f0 369 # Return the macro definitions generated for a dictionary of ConfigMacros (as returned by get_config_data)
screamer 13:ab47a20b66f0 370 # params: a dictionary of (name, ConfigMacro instance) mappings
screamer 13:ab47a20b66f0 371 @staticmethod
screamer 13:ab47a20b66f0 372 def config_macros_to_macros(macros):
screamer 13:ab47a20b66f0 373 return [m.name for m in macros.values()]
screamer 13:ab47a20b66f0 374
screamer 13:ab47a20b66f0 375 # Return the configuration data converted to a list of C macros
screamer 13:ab47a20b66f0 376 # config - configuration data as (ConfigParam instances, ConfigMacro instances) tuple
screamer 13:ab47a20b66f0 377 # (as returned by get_config_data())
screamer 13:ab47a20b66f0 378 @staticmethod
screamer 13:ab47a20b66f0 379 def config_to_macros(config):
screamer 13:ab47a20b66f0 380 params, macros = config[0], config[1]
screamer 13:ab47a20b66f0 381 Config._check_required_parameters(params)
screamer 13:ab47a20b66f0 382 return Config.config_macros_to_macros(macros) + Config.parameters_to_macros(params)
screamer 13:ab47a20b66f0 383
screamer 8:a8ac6ed29081 384 # Return the configuration data converted to a list of C macros
screamer 8:a8ac6ed29081 385 def get_config_data_macros(self):
screamer 13:ab47a20b66f0 386 return self.config_to_macros(self.get_config_data())
screamer 13:ab47a20b66f0 387
screamer 13:ab47a20b66f0 388 # Returns any features in the configuration data
screamer 13:ab47a20b66f0 389 def get_features(self):
screamer 13:ab47a20b66f0 390 params, _ = self.get_config_data()
screamer 8:a8ac6ed29081 391 self._check_required_parameters(params)
screamer 13:ab47a20b66f0 392 features = ((set(Target.get_target(self.target).features)
screamer 13:ab47a20b66f0 393 | self.added_features) - self.removed_features)
screamer 13:ab47a20b66f0 394
screamer 13:ab47a20b66f0 395 for feature in features:
screamer 13:ab47a20b66f0 396 if feature not in self.__allowed_features:
screamer 13:ab47a20b66f0 397 raise ConfigException("Feature '%s' is not a supported features" % feature)
screamer 13:ab47a20b66f0 398
screamer 13:ab47a20b66f0 399 return features
screamer 13:ab47a20b66f0 400
screamer 13:ab47a20b66f0 401 # Validate configuration settings. This either returns True or raises an exception
screamer 13:ab47a20b66f0 402 def validate_config(self):
screamer 13:ab47a20b66f0 403 if self.config_errors:
screamer 13:ab47a20b66f0 404 raise self.config_errors[0]
screamer 13:ab47a20b66f0 405 return True
screamer 13:ab47a20b66f0 406
screamer 13:ab47a20b66f0 407
screamer 13:ab47a20b66f0 408 # Loads configuration data from resources. Also expands resources based on defined features settings
screamer 13:ab47a20b66f0 409 def load_resources(self, resources):
screamer 13:ab47a20b66f0 410 # Update configuration files until added features creates no changes
screamer 13:ab47a20b66f0 411 prev_features = set()
screamer 13:ab47a20b66f0 412 while True:
screamer 13:ab47a20b66f0 413 # Add/update the configuration with any .json files found while scanning
screamer 13:ab47a20b66f0 414 self.add_config_files(resources.json_files)
screamer 13:ab47a20b66f0 415
screamer 13:ab47a20b66f0 416 # Add features while we find new ones
screamer 13:ab47a20b66f0 417 features = self.get_features()
screamer 13:ab47a20b66f0 418 if features == prev_features:
screamer 13:ab47a20b66f0 419 break
screamer 13:ab47a20b66f0 420
screamer 13:ab47a20b66f0 421 for feature in features:
screamer 13:ab47a20b66f0 422 if feature in resources.features:
screamer 13:ab47a20b66f0 423 resources.add(resources.features[feature])
screamer 13:ab47a20b66f0 424
screamer 13:ab47a20b66f0 425 prev_features = features
screamer 13:ab47a20b66f0 426 self.validate_config()
screamer 13:ab47a20b66f0 427
screamer 13:ab47a20b66f0 428 return resources
screamer 13:ab47a20b66f0 429
screamer 13:ab47a20b66f0 430 # Return the configuration data converted to the content of a C header file,
screamer 13:ab47a20b66f0 431 # meant to be included to a C/C++ file. The content is returned as a string.
screamer 13:ab47a20b66f0 432 # If 'fname' is given, the content is also written to the file called "fname".
screamer 13:ab47a20b66f0 433 # WARNING: if 'fname' names an existing file, that file will be overwritten!
screamer 13:ab47a20b66f0 434 # config - configuration data as (ConfigParam instances, ConfigMacro instances) tuple
screamer 13:ab47a20b66f0 435 # (as returned by get_config_data())
screamer 13:ab47a20b66f0 436 @staticmethod
screamer 13:ab47a20b66f0 437 def config_to_header(config, fname = None):
screamer 13:ab47a20b66f0 438 params, macros = config[0], config[1]
screamer 13:ab47a20b66f0 439 Config._check_required_parameters(params)
screamer 13:ab47a20b66f0 440 header_data = "// Automatically generated configuration file.\n"
screamer 13:ab47a20b66f0 441 header_data += "// DO NOT EDIT, content will be overwritten.\n\n"
screamer 13:ab47a20b66f0 442 header_data += "#ifndef __MBED_CONFIG_DATA__\n"
screamer 13:ab47a20b66f0 443 header_data += "#define __MBED_CONFIG_DATA__\n\n"
screamer 13:ab47a20b66f0 444 # Compute maximum length of macro names for proper alignment
screamer 13:ab47a20b66f0 445 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 446 max_direct_macro_name_len = max([len(m.macro_name) for m in macros.values()]) if macros else 0
screamer 13:ab47a20b66f0 447 max_macro_name_len = max(max_param_macro_name_len, max_direct_macro_name_len)
screamer 13:ab47a20b66f0 448 # Compute maximum length of macro values for proper alignment
screamer 13:ab47a20b66f0 449 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 450 max_direct_macro_val_len = max([len(m.macro_value or "") for m in macros.values()]) if macros else 0
screamer 13:ab47a20b66f0 451 max_macro_val_len = max(max_param_macro_val_len, max_direct_macro_val_len)
screamer 13:ab47a20b66f0 452 # Generate config parameters first
screamer 13:ab47a20b66f0 453 if params:
screamer 13:ab47a20b66f0 454 header_data += "// Configuration parameters\n"
screamer 13:ab47a20b66f0 455 for m in params.values():
screamer 13:ab47a20b66f0 456 if m.value is not None:
screamer 13:ab47a20b66f0 457 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 458 # Then macros
screamer 13:ab47a20b66f0 459 if macros:
screamer 13:ab47a20b66f0 460 header_data += "// Macros\n"
screamer 13:ab47a20b66f0 461 for m in macros.values():
screamer 13:ab47a20b66f0 462 if m.macro_value:
screamer 13:ab47a20b66f0 463 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 464 else:
screamer 13:ab47a20b66f0 465 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 466 header_data += "\n#endif\n"
screamer 13:ab47a20b66f0 467 # If fname is given, write "header_data" to it
screamer 13:ab47a20b66f0 468 if fname:
screamer 13:ab47a20b66f0 469 with open(fname, "wt") as f:
screamer 13:ab47a20b66f0 470 f.write(header_data)
screamer 13:ab47a20b66f0 471 return header_data
screamer 13:ab47a20b66f0 472
screamer 13:ab47a20b66f0 473 # Return the configuration data converted to the content of a C header file,
screamer 13:ab47a20b66f0 474 # meant to be included to a C/C++ file. The content is returned as a string.
screamer 13:ab47a20b66f0 475 # If 'fname' is given, the content is also written to the file called "fname".
screamer 13:ab47a20b66f0 476 # WARNING: if 'fname' names an existing file, that file will be overwritten!
screamer 13:ab47a20b66f0 477 def get_config_data_header(self, fname = None):
screamer 13:ab47a20b66f0 478 return self.config_to_header(self.get_config_data(), fname)