Clone of official tools

Committer:
The Other Jimmy
Date:
Wed Feb 15 13:53:18 2017 -0600
Revision:
35:da9c89f8be7d
Parent:
31:8ea194f6145b
Update tools to mbed-os 5.3.5

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
The Other Jimmy 31:8ea194f6145b 18 from copy import deepcopy
The Other Jimmy 31:8ea194f6145b 19 import os
The Other Jimmy 31:8ea194f6145b 20 import sys
screamer 8:a8ac6ed29081 21 # Implementation of mbed configuration mechanism
screamer 29:1210849dba19 22 from tools.utils import json_file_to_dict
The Other Jimmy 31:8ea194f6145b 23 from tools.targets import CUMULATIVE_ATTRIBUTES, TARGET_MAP, \
The Other Jimmy 31:8ea194f6145b 24 generate_py_target, get_resolution_order
screamer 8:a8ac6ed29081 25
screamer 8:a8ac6ed29081 26 # Base class for all configuration exceptions
screamer 8:a8ac6ed29081 27 class ConfigException(Exception):
screamer 29:1210849dba19 28 """Config system only exception. Makes it easier to distinguish config
screamer 29:1210849dba19 29 errors"""
screamer 8:a8ac6ed29081 30 pass
screamer 8:a8ac6ed29081 31
screamer 29:1210849dba19 32 class ConfigParameter(object):
screamer 29:1210849dba19 33 """This class keeps information about a single configuration parameter"""
screamer 29:1210849dba19 34
screamer 8:a8ac6ed29081 35 def __init__(self, name, data, unit_name, unit_kind):
screamer 29:1210849dba19 36 """Construct a ConfigParameter
screamer 29:1210849dba19 37
screamer 29:1210849dba19 38 Positional arguments:
screamer 29:1210849dba19 39 name - the name of the configuration parameter
screamer 29:1210849dba19 40 data - the data associated with the configuration parameter
screamer 29:1210849dba19 41 unit_name - the unit (target/library/application) that defines this
screamer 29:1210849dba19 42 parameter
screamer 29:1210849dba19 43 unit_ kind - the kind of the unit ("target", "library" or "application")
screamer 29:1210849dba19 44 """
screamer 29:1210849dba19 45 self.name = self.get_full_name(name, unit_name, unit_kind,
screamer 29:1210849dba19 46 allow_prefix=False)
screamer 8:a8ac6ed29081 47 self.defined_by = self.get_display_name(unit_name, unit_kind)
screamer 13:ab47a20b66f0 48 self.set_value(data.get("value", None), unit_name, unit_kind)
screamer 8:a8ac6ed29081 49 self.help_text = data.get("help", None)
screamer 8:a8ac6ed29081 50 self.required = data.get("required", False)
screamer 29:1210849dba19 51 self.macro_name = data.get("macro_name", "MBED_CONF_%s" %
screamer 29:1210849dba19 52 self.sanitize(self.name.upper()))
screamer 13:ab47a20b66f0 53 self.config_errors = []
screamer 8:a8ac6ed29081 54
screamer 8:a8ac6ed29081 55 @staticmethod
screamer 29:1210849dba19 56 def get_full_name(name, unit_name, unit_kind, label=None,
screamer 29:1210849dba19 57 allow_prefix=True):
screamer 29:1210849dba19 58 """Return the full (prefixed) name of a parameter. If the parameter
screamer 29:1210849dba19 59 already has a prefix, check if it is valid
screamer 29:1210849dba19 60
screamer 29:1210849dba19 61 Positional arguments:
screamer 29:1210849dba19 62 name - the simple (unqualified) name of the parameter
screamer 29:1210849dba19 63 unit_name - the unit (target/library/application) that defines this
screamer 29:1210849dba19 64 parameter
screamer 29:1210849dba19 65 unit_kind - the kind of the unit ("target", "library" or "application")
screamer 29:1210849dba19 66
screamer 29:1210849dba19 67 Keyword arguments:
screamer 29:1210849dba19 68 label - the name of the label in the 'target_config_overrides' section
screamer 29:1210849dba19 69 allow_prefix - True to allow the original name to have a prefix, False
screamer 29:1210849dba19 70 otherwise
screamer 29:1210849dba19 71 """
screamer 8:a8ac6ed29081 72 if name.find('.') == -1: # the name is not prefixed
screamer 8:a8ac6ed29081 73 if unit_kind == "target":
screamer 8:a8ac6ed29081 74 prefix = "target."
screamer 8:a8ac6ed29081 75 elif unit_kind == "application":
screamer 8:a8ac6ed29081 76 prefix = "app."
screamer 8:a8ac6ed29081 77 else:
screamer 8:a8ac6ed29081 78 prefix = unit_name + '.'
screamer 8:a8ac6ed29081 79 return prefix + name
screamer 8:a8ac6ed29081 80 # The name has a prefix, so check if it is valid
screamer 8:a8ac6ed29081 81 if not allow_prefix:
screamer 29:1210849dba19 82 raise ConfigException("Invalid parameter name '%s' in '%s'" %
screamer 29:1210849dba19 83 (name, ConfigParameter.get_display_name(
screamer 29:1210849dba19 84 unit_name, unit_kind, label)))
screamer 8:a8ac6ed29081 85 temp = name.split(".")
screamer 29:1210849dba19 86 # Check if the parameter syntax is correct (must be
screamer 29:1210849dba19 87 # unit_name.parameter_name)
screamer 8:a8ac6ed29081 88 if len(temp) != 2:
screamer 29:1210849dba19 89 raise ConfigException("Invalid parameter name '%s' in '%s'" %
screamer 29:1210849dba19 90 (name, ConfigParameter.get_display_name(
screamer 29:1210849dba19 91 unit_name, unit_kind, label)))
screamer 8:a8ac6ed29081 92 prefix = temp[0]
screamer 8:a8ac6ed29081 93 # Check if the given parameter prefix matches the expected prefix
screamer 29:1210849dba19 94 if (unit_kind == "library" and prefix != unit_name) or \
screamer 29:1210849dba19 95 (unit_kind == "target" and prefix != "target"):
screamer 29:1210849dba19 96 raise ConfigException(
screamer 29:1210849dba19 97 "Invalid prefix '%s' for parameter name '%s' in '%s'" %
screamer 29:1210849dba19 98 (prefix, name, ConfigParameter.get_display_name(
screamer 29:1210849dba19 99 unit_name, unit_kind, label)))
screamer 8:a8ac6ed29081 100 return name
screamer 8:a8ac6ed29081 101
screamer 8:a8ac6ed29081 102 @staticmethod
screamer 29:1210849dba19 103 def get_display_name(unit_name, unit_kind, label=None):
screamer 29:1210849dba19 104 """Return the name displayed for a unit when interrogating the origin
screamer 29:1210849dba19 105 and the last set place of a parameter
screamer 29:1210849dba19 106
screamer 29:1210849dba19 107 Positional arguments:
screamer 29:1210849dba19 108 unit_name - the unit (target/library/application) that defines this
screamer 29:1210849dba19 109 parameter
screamer 29:1210849dba19 110 unit_kind - the kind of the unit ("target", "library" or "application")
screamer 29:1210849dba19 111
screamer 29:1210849dba19 112 Keyword arguments:
screamer 29:1210849dba19 113 label - the name of the label in the 'target_config_overrides' section
screamer 29:1210849dba19 114 """
screamer 8:a8ac6ed29081 115 if unit_kind == "target":
screamer 8:a8ac6ed29081 116 return "target:" + unit_name
screamer 8:a8ac6ed29081 117 elif unit_kind == "application":
screamer 8:a8ac6ed29081 118 return "application%s" % ("[%s]" % label if label else "")
screamer 8:a8ac6ed29081 119 else: # library
screamer 8:a8ac6ed29081 120 return "library:%s%s" % (unit_name, "[%s]" % label if label else "")
screamer 8:a8ac6ed29081 121
screamer 8:a8ac6ed29081 122 @staticmethod
screamer 8:a8ac6ed29081 123 def sanitize(name):
screamer 29:1210849dba19 124 """ "Sanitize" a name so that it is a valid C macro name. Currently it
screamer 29:1210849dba19 125 simply replaces '.' and '-' with '_'.
screamer 29:1210849dba19 126
screamer 29:1210849dba19 127 Positional arguments:
screamer 29:1210849dba19 128 name - the name to make into a valid C macro
screamer 29:1210849dba19 129 """
screamer 8:a8ac6ed29081 130 return name.replace('.', '_').replace('-', '_')
screamer 8:a8ac6ed29081 131
screamer 29:1210849dba19 132 def set_value(self, value, unit_name, unit_kind, label=None):
screamer 29:1210849dba19 133 """ Sets a value for this parameter, remember the place where it was
screamer 29:1210849dba19 134 set. If the value is a Boolean, it is converted to 1 (for True) or
screamer 29:1210849dba19 135 to 0 (for False).
screamer 29:1210849dba19 136
screamer 29:1210849dba19 137 Positional arguments:
screamer 29:1210849dba19 138 value - the value of the parameter
screamer 29:1210849dba19 139 unit_name - the unit (target/library/application) that defines this
screamer 29:1210849dba19 140 parameter
screamer 29:1210849dba19 141 unit_kind - the kind of the unit ("target", "library" or "application")
screamer 29:1210849dba19 142
screamer 29:1210849dba19 143 Keyword arguments:
screamer 29:1210849dba19 144 label - the name of the label in the 'target_config_overrides' section
screamer 29:1210849dba19 145 (optional)
screamer 29:1210849dba19 146 """
screamer 13:ab47a20b66f0 147 self.value = int(value) if isinstance(value, bool) else value
screamer 8:a8ac6ed29081 148 self.set_by = self.get_display_name(unit_name, unit_kind, label)
screamer 8:a8ac6ed29081 149
screamer 8:a8ac6ed29081 150 def __str__(self):
screamer 29:1210849dba19 151 """Return the string representation of this configuration parameter
screamer 29:1210849dba19 152
screamer 29:1210849dba19 153 Arguments: None
screamer 29:1210849dba19 154 """
screamer 8:a8ac6ed29081 155 if self.value is not None:
screamer 29:1210849dba19 156 return '%s = %s (macro name: "%s")' % \
screamer 29:1210849dba19 157 (self.name, self.value, self.macro_name)
screamer 8:a8ac6ed29081 158 else:
screamer 8:a8ac6ed29081 159 return '%s has no value' % self.name
screamer 8:a8ac6ed29081 160
screamer 8:a8ac6ed29081 161 def get_verbose_description(self):
screamer 29:1210849dba19 162 """Return a verbose description of this configuration parameter as a
screamer 29:1210849dba19 163 string
screamer 29:1210849dba19 164
screamer 29:1210849dba19 165 Arguments: None
screamer 29:1210849dba19 166 """
screamer 29:1210849dba19 167 desc = "Name: %s%s\n" % \
screamer 29:1210849dba19 168 (self.name, " (required parameter)" if self.required else "")
screamer 8:a8ac6ed29081 169 if self.help_text:
screamer 8:a8ac6ed29081 170 desc += " Description: %s\n" % self.help_text
screamer 8:a8ac6ed29081 171 desc += " Defined by: %s\n" % self.defined_by
screamer 8:a8ac6ed29081 172 if not self.value:
screamer 8:a8ac6ed29081 173 return desc + " No value set"
screamer 8:a8ac6ed29081 174 desc += " Macro name: %s\n" % self.macro_name
screamer 8:a8ac6ed29081 175 desc += " Value: %s (set by %s)" % (self.value, self.set_by)
screamer 8:a8ac6ed29081 176 return desc
screamer 8:a8ac6ed29081 177
screamer 29:1210849dba19 178 class ConfigMacro(object):
screamer 29:1210849dba19 179 """ A representation of a configuration macro. It handles both macros
screamer 29:1210849dba19 180 without a value (MACRO) and with a value (MACRO=VALUE)
screamer 29:1210849dba19 181 """
screamer 8:a8ac6ed29081 182 def __init__(self, name, unit_name, unit_kind):
screamer 29:1210849dba19 183 """Construct a ConfigMacro object
screamer 29:1210849dba19 184
screamer 29:1210849dba19 185 Positional arguments:
screamer 29:1210849dba19 186 name - the macro's name
screamer 29:1210849dba19 187 unit_name - the location where the macro was defined
screamer 29:1210849dba19 188 unit_kind - the type of macro this is
screamer 29:1210849dba19 189 """
screamer 8:a8ac6ed29081 190 self.name = name
screamer 8:a8ac6ed29081 191 self.defined_by = ConfigParameter.get_display_name(unit_name, unit_kind)
screamer 8:a8ac6ed29081 192 if name.find("=") != -1:
screamer 8:a8ac6ed29081 193 tmp = name.split("=")
screamer 8:a8ac6ed29081 194 if len(tmp) != 2:
screamer 29:1210849dba19 195 raise ValueError("Invalid macro definition '%s' in '%s'" %
screamer 29:1210849dba19 196 (name, self.defined_by))
screamer 8:a8ac6ed29081 197 self.macro_name = tmp[0]
screamer 13:ab47a20b66f0 198 self.macro_value = tmp[1]
screamer 8:a8ac6ed29081 199 else:
screamer 8:a8ac6ed29081 200 self.macro_name = name
screamer 13:ab47a20b66f0 201 self.macro_value = None
screamer 8:a8ac6ed29081 202
screamer 29:1210849dba19 203 class ConfigCumulativeOverride(object):
screamer 29:1210849dba19 204 """Representation of overrides for cumulative attributes"""
screamer 29:1210849dba19 205 def __init__(self, name, additions=None, removals=None, strict=False):
screamer 29:1210849dba19 206 """Construct a ConfigCumulativeOverride object
screamer 29:1210849dba19 207
screamer 29:1210849dba19 208 Positional arguments:
screamer 29:1210849dba19 209 name - the name of the config file this came from ?
screamer 29:1210849dba19 210
screamer 29:1210849dba19 211 Keyword arguments:
screamer 29:1210849dba19 212 additions - macros to add to the overrides
screamer 29:1210849dba19 213 removals - macros to remove from the overrides
screamer 29:1210849dba19 214 strict - Boolean indicating that attempting to remove from an override
screamer 29:1210849dba19 215 that does not exist should error
screamer 29:1210849dba19 216 """
screamer 24:25bff2709c20 217 self.name = name
screamer 29:1210849dba19 218 if additions:
screamer 29:1210849dba19 219 self.additions = set(additions)
screamer 29:1210849dba19 220 else:
screamer 29:1210849dba19 221 self.additions = set()
screamer 29:1210849dba19 222 if removals:
screamer 29:1210849dba19 223 self.removals = set(removals)
screamer 29:1210849dba19 224 else:
screamer 29:1210849dba19 225 self.removals = set()
screamer 24:25bff2709c20 226 self.strict = strict
screamer 24:25bff2709c20 227
screamer 24:25bff2709c20 228 def remove_cumulative_overrides(self, overrides):
screamer 29:1210849dba19 229 """Extend the list of override removals.
screamer 29:1210849dba19 230
screamer 29:1210849dba19 231 Positional arguments:
screamer 29:1210849dba19 232 overrides - a list of names that, when the override is evaluated, will
screamer 29:1210849dba19 233 be removed
screamer 29:1210849dba19 234 """
screamer 24:25bff2709c20 235 for override in overrides:
screamer 24:25bff2709c20 236 if override in self.additions:
screamer 29:1210849dba19 237 raise ConfigException(
screamer 29:1210849dba19 238 "Configuration conflict. The %s %s both added and removed."
screamer 29:1210849dba19 239 % (self.name[:-1], override))
screamer 24:25bff2709c20 240
screamer 24:25bff2709c20 241 self.removals |= set(overrides)
screamer 24:25bff2709c20 242
screamer 24:25bff2709c20 243 def add_cumulative_overrides(self, overrides):
screamer 29:1210849dba19 244 """Extend the list of override additions.
screamer 29:1210849dba19 245
screamer 29:1210849dba19 246 Positional arguments:
screamer 29:1210849dba19 247 overrides - a list of a names that, when the override is evaluated, will
screamer 29:1210849dba19 248 be added to the list
screamer 29:1210849dba19 249 """
screamer 24:25bff2709c20 250 for override in overrides:
screamer 29:1210849dba19 251 if override in self.removals or \
screamer 29:1210849dba19 252 (self.strict and override not in self.additions):
screamer 29:1210849dba19 253 raise ConfigException(
screamer 29:1210849dba19 254 "Configuration conflict. The %s %s both added and removed."
screamer 29:1210849dba19 255 % (self.name[:-1], override))
screamer 24:25bff2709c20 256
screamer 24:25bff2709c20 257 self.additions |= set(overrides)
screamer 24:25bff2709c20 258
screamer 24:25bff2709c20 259 def strict_cumulative_overrides(self, overrides):
screamer 29:1210849dba19 260 """Remove all overrides that are not the specified ones
screamer 29:1210849dba19 261
screamer 29:1210849dba19 262 Positional arguments:
screamer 29:1210849dba19 263 overrides - a list of names that will replace the entire attribute when
screamer 29:1210849dba19 264 this override is evaluated.
screamer 29:1210849dba19 265 """
screamer 24:25bff2709c20 266 self.remove_cumulative_overrides(self.additions - set(overrides))
screamer 24:25bff2709c20 267 self.add_cumulative_overrides(overrides)
screamer 24:25bff2709c20 268 self.strict = True
screamer 24:25bff2709c20 269
screamer 24:25bff2709c20 270 def update_target(self, target):
screamer 29:1210849dba19 271 """Update the attributes of a target based on this override"""
screamer 29:1210849dba19 272 setattr(target, self.name,
screamer 29:1210849dba19 273 list((set(getattr(target, self.name, []))
screamer 29:1210849dba19 274 | self.additions) - self.removals))
screamer 24:25bff2709c20 275
screamer 24:25bff2709c20 276
screamer 29:1210849dba19 277 def _process_config_parameters(data, params, unit_name, unit_kind):
screamer 29:1210849dba19 278 """Process a "config_parameters" section in either a target, a library,
screamer 29:1210849dba19 279 or the application.
screamer 24:25bff2709c20 280
screamer 29:1210849dba19 281 Positional arguments:
screamer 29:1210849dba19 282 data - a dictionary with the configuration parameters
screamer 29:1210849dba19 283 params - storage for the discovered configuration parameters
screamer 29:1210849dba19 284 unit_name - the unit (target/library/application) that defines this
screamer 29:1210849dba19 285 parameter
screamer 29:1210849dba19 286 unit_kind - the kind of the unit ("target", "library" or "application")
screamer 29:1210849dba19 287 """
screamer 29:1210849dba19 288 for name, val in data.items():
screamer 29:1210849dba19 289 full_name = ConfigParameter.get_full_name(name, unit_name, unit_kind)
screamer 29:1210849dba19 290 # If the parameter was already defined, raise an error
screamer 29:1210849dba19 291 if full_name in params:
screamer 29:1210849dba19 292 raise ConfigException(
screamer 29:1210849dba19 293 "Parameter name '%s' defined in both '%s' and '%s'" %
screamer 29:1210849dba19 294 (name, ConfigParameter.get_display_name(unit_name, unit_kind),
screamer 29:1210849dba19 295 params[full_name].defined_by))
screamer 29:1210849dba19 296 # Otherwise add it to the list of known parameters
screamer 29:1210849dba19 297 # If "val" is not a dictionary, this is a shortcut definition,
screamer 29:1210849dba19 298 # otherwise it is a full definition
screamer 29:1210849dba19 299 params[full_name] = ConfigParameter(name, val if isinstance(val, dict)
screamer 29:1210849dba19 300 else {"value": val}, unit_name,
screamer 29:1210849dba19 301 unit_kind)
screamer 29:1210849dba19 302 return params
screamer 29:1210849dba19 303
screamer 29:1210849dba19 304
screamer 29:1210849dba19 305 def _process_macros(mlist, macros, unit_name, unit_kind):
screamer 29:1210849dba19 306 """Process a macro definition and check for incompatible duplicate
screamer 29:1210849dba19 307 definitions.
screamer 29:1210849dba19 308
screamer 29:1210849dba19 309 Positional arguments:
screamer 29:1210849dba19 310 mlist - list of macro names to process
screamer 29:1210849dba19 311 macros - dictionary with currently discovered macros
screamer 29:1210849dba19 312 unit_name - the unit (library/application) that defines this macro
screamer 29:1210849dba19 313 unit_kind - the kind of the unit ("library" or "application")
screamer 29:1210849dba19 314 """
screamer 29:1210849dba19 315 for mname in mlist:
screamer 29:1210849dba19 316 macro = ConfigMacro(mname, unit_name, unit_kind)
screamer 29:1210849dba19 317 if (macro.macro_name in macros) and \
screamer 29:1210849dba19 318 (macros[macro.macro_name].name != mname):
screamer 29:1210849dba19 319 # Found an incompatible definition of the macro in another module,
screamer 29:1210849dba19 320 # so raise an error
screamer 29:1210849dba19 321 full_unit_name = ConfigParameter.get_display_name(unit_name,
screamer 29:1210849dba19 322 unit_kind)
screamer 29:1210849dba19 323 raise ConfigException(
screamer 29:1210849dba19 324 ("Macro '%s' defined in both '%s' and '%s'"
screamer 29:1210849dba19 325 % (macro.macro_name, macros[macro.macro_name].defined_by,
screamer 29:1210849dba19 326 full_unit_name)) +
screamer 29:1210849dba19 327 " with incompatible values")
screamer 29:1210849dba19 328 macros[macro.macro_name] = macro
screamer 29:1210849dba19 329
screamer 29:1210849dba19 330
screamer 29:1210849dba19 331 class Config(object):
screamer 29:1210849dba19 332 """'Config' implements the mbed configuration mechanism"""
screamer 29:1210849dba19 333
screamer 29:1210849dba19 334 # Libraries and applications have different names for their configuration
screamer 29:1210849dba19 335 # files
screamer 8:a8ac6ed29081 336 __mbed_app_config_name = "mbed_app.json"
screamer 8:a8ac6ed29081 337 __mbed_lib_config_name = "mbed_lib.json"
screamer 8:a8ac6ed29081 338
screamer 8:a8ac6ed29081 339 # Allowed keys in configuration dictionaries
screamer 29:1210849dba19 340 # (targets can have any kind of keys, so this validation is not applicable
screamer 29:1210849dba19 341 # to them)
screamer 8:a8ac6ed29081 342 __allowed_keys = {
screamer 29:1210849dba19 343 "library": set(["name", "config", "target_overrides", "macros",
screamer 29:1210849dba19 344 "__config_path"]),
The Other Jimmy 31:8ea194f6145b 345 "application": set(["config", "target_overrides",
screamer 29:1210849dba19 346 "macros", "__config_path"])
screamer 8:a8ac6ed29081 347 }
screamer 8:a8ac6ed29081 348
screamer 13:ab47a20b66f0 349 # Allowed features in configurations
screamer 13:ab47a20b66f0 350 __allowed_features = [
The Other Jimmy 31:8ea194f6145b 351 "UVISOR", "BLE", "CLIENT", "IPV4", "LWIP", "COMMON_PAL", "STORAGE", "NANOSTACK",
The Other Jimmy 31:8ea194f6145b 352 # Nanostack configurations
The Other Jimmy 35:da9c89f8be7d 353 "LOWPAN_BORDER_ROUTER", "LOWPAN_HOST", "LOWPAN_ROUTER", "NANOSTACK_FULL", "THREAD_BORDER_ROUTER", "THREAD_END_DEVICE", "THREAD_ROUTER", "ETHERNET_HOST"
The Other Jimmy 31:8ea194f6145b 354 ]
screamer 13:ab47a20b66f0 355
The Other Jimmy 31:8ea194f6145b 356 def __init__(self, tgt, top_level_dirs=None, app_config=None):
screamer 29:1210849dba19 357 """Construct a mbed configuration
screamer 29:1210849dba19 358
screamer 29:1210849dba19 359 Positional arguments:
screamer 29:1210849dba19 360 target - the name of the mbed target used for this configuration
screamer 29:1210849dba19 361 instance
screamer 29:1210849dba19 362
screamer 29:1210849dba19 363 Keyword argumets:
screamer 29:1210849dba19 364 top_level_dirs - a list of top level source directories (where
The Other Jimmy 31:8ea194f6145b 365 mbed_app_config.json could be found)
The Other Jimmy 31:8ea194f6145b 366 app_config - location of a chosen mbed_app.json file
screamer 29:1210849dba19 367
screamer 29:1210849dba19 368 NOTE: Construction of a Config object will look for the application
The Other Jimmy 31:8ea194f6145b 369 configuration file in top_level_dirs. If found once, it'll parse it.
The Other Jimmy 31:8ea194f6145b 370 top_level_dirs may be None (in this case, the constructor will not
The Other Jimmy 31:8ea194f6145b 371 search for a configuration file).
screamer 29:1210849dba19 372 """
The Other Jimmy 31:8ea194f6145b 373 app_config_location = app_config
The Other Jimmy 31:8ea194f6145b 374 if app_config_location is None:
The Other Jimmy 31:8ea194f6145b 375 for directory in top_level_dirs or []:
The Other Jimmy 31:8ea194f6145b 376 full_path = os.path.join(directory, self.__mbed_app_config_name)
The Other Jimmy 31:8ea194f6145b 377 if os.path.isfile(full_path):
The Other Jimmy 31:8ea194f6145b 378 if app_config_location is not None:
The Other Jimmy 31:8ea194f6145b 379 raise ConfigException("Duplicate '%s' file in '%s' and '%s'"
The Other Jimmy 31:8ea194f6145b 380 % (self.__mbed_app_config_name,
The Other Jimmy 31:8ea194f6145b 381 app_config_location, full_path))
The Other Jimmy 31:8ea194f6145b 382 else:
The Other Jimmy 31:8ea194f6145b 383 app_config_location = full_path
The Other Jimmy 31:8ea194f6145b 384 try:
The Other Jimmy 31:8ea194f6145b 385 self.app_config_data = json_file_to_dict(app_config_location) \
The Other Jimmy 31:8ea194f6145b 386 if app_config_location else {}
The Other Jimmy 31:8ea194f6145b 387 except ValueError as exc:
The Other Jimmy 31:8ea194f6145b 388 sys.stderr.write(str(exc) + "\n")
The Other Jimmy 31:8ea194f6145b 389 self.app_config_data = {}
The Other Jimmy 31:8ea194f6145b 390
screamer 8:a8ac6ed29081 391 # Check the keys in the application configuration data
screamer 29:1210849dba19 392 unknown_keys = set(self.app_config_data.keys()) - \
screamer 29:1210849dba19 393 self.__allowed_keys["application"]
screamer 8:a8ac6ed29081 394 if unknown_keys:
screamer 29:1210849dba19 395 raise ConfigException("Unknown key(s) '%s' in %s" %
screamer 29:1210849dba19 396 (",".join(unknown_keys),
screamer 29:1210849dba19 397 self.__mbed_app_config_name))
screamer 29:1210849dba19 398 # Update the list of targets with the ones defined in the application
screamer 29:1210849dba19 399 # config, if applicable
screamer 8:a8ac6ed29081 400 self.lib_config_data = {}
screamer 8:a8ac6ed29081 401 # Make sure that each config is processed only once
screamer 8:a8ac6ed29081 402 self.processed_configs = {}
The Other Jimmy 31:8ea194f6145b 403 if isinstance(tgt, basestring):
The Other Jimmy 31:8ea194f6145b 404 if tgt in TARGET_MAP:
The Other Jimmy 31:8ea194f6145b 405 self.target = TARGET_MAP[tgt]
The Other Jimmy 31:8ea194f6145b 406 else:
The Other Jimmy 31:8ea194f6145b 407 self.target = generate_py_target(
The Other Jimmy 31:8ea194f6145b 408 self.app_config_data.get("custom_targets", {}), tgt)
The Other Jimmy 31:8ea194f6145b 409
The Other Jimmy 31:8ea194f6145b 410 else:
The Other Jimmy 31:8ea194f6145b 411 self.target = tgt
The Other Jimmy 31:8ea194f6145b 412 self.target = deepcopy(self.target)
The Other Jimmy 31:8ea194f6145b 413 self.target_labels = self.target.labels
screamer 24:25bff2709c20 414
screamer 29:1210849dba19 415 self.cumulative_overrides = {key: ConfigCumulativeOverride(key)
The Other Jimmy 31:8ea194f6145b 416 for key in CUMULATIVE_ATTRIBUTES}
screamer 24:25bff2709c20 417
screamer 29:1210849dba19 418 self._process_config_and_overrides(self.app_config_data, {}, "app",
screamer 29:1210849dba19 419 "application")
screamer 29:1210849dba19 420 self.config_errors = None
screamer 8:a8ac6ed29081 421
screamer 8:a8ac6ed29081 422 def add_config_files(self, flist):
screamer 29:1210849dba19 423 """Add configuration files
screamer 29:1210849dba19 424
screamer 29:1210849dba19 425 Positional arguments:
screamer 29:1210849dba19 426 flist - a list of files to add to this configuration
screamer 29:1210849dba19 427 """
screamer 29:1210849dba19 428 for config_file in flist:
screamer 29:1210849dba19 429 if not config_file.endswith(self.__mbed_lib_config_name):
screamer 8:a8ac6ed29081 430 continue
screamer 29:1210849dba19 431 full_path = os.path.normpath(os.path.abspath(config_file))
screamer 8:a8ac6ed29081 432 # Check that we didn't already process this file
screamer 8:a8ac6ed29081 433 if self.processed_configs.has_key(full_path):
screamer 8:a8ac6ed29081 434 continue
screamer 8:a8ac6ed29081 435 self.processed_configs[full_path] = True
screamer 29:1210849dba19 436 # Read the library configuration and add a "__full_config_path"
screamer 29:1210849dba19 437 # attribute to it
The Other Jimmy 31:8ea194f6145b 438 try:
The Other Jimmy 31:8ea194f6145b 439 cfg = json_file_to_dict(config_file)
The Other Jimmy 31:8ea194f6145b 440 except ValueError as exc:
The Other Jimmy 31:8ea194f6145b 441 sys.stderr.write(str(exc) + "\n")
The Other Jimmy 31:8ea194f6145b 442 continue
The Other Jimmy 31:8ea194f6145b 443
screamer 8:a8ac6ed29081 444 cfg["__config_path"] = full_path
screamer 29:1210849dba19 445
screamer 29:1210849dba19 446 if "name" not in cfg:
screamer 29:1210849dba19 447 raise ConfigException(
screamer 29:1210849dba19 448 "Library configured at %s has no name field." % full_path)
screamer 29:1210849dba19 449 # If there's already a configuration for a module with the same
screamer 29:1210849dba19 450 # name, exit with error
screamer 8:a8ac6ed29081 451 if self.lib_config_data.has_key(cfg["name"]):
screamer 29:1210849dba19 452 raise ConfigException(
screamer 29:1210849dba19 453 "Library name '%s' is not unique (defined in '%s' and '%s')"
screamer 29:1210849dba19 454 % (cfg["name"], full_path,
screamer 29:1210849dba19 455 self.lib_config_data[cfg["name"]]["__config_path"]))
screamer 8:a8ac6ed29081 456 self.lib_config_data[cfg["name"]] = cfg
screamer 8:a8ac6ed29081 457
screamer 29:1210849dba19 458
screamer 29:1210849dba19 459 def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
screamer 29:1210849dba19 460 """Process "config_parameters" and "target_config_overrides" into a
screamer 29:1210849dba19 461 given dictionary
screamer 8:a8ac6ed29081 462
screamer 29:1210849dba19 463 Positional arguments:
screamer 29:1210849dba19 464 data - the configuration data of the library/appliation
screamer 29:1210849dba19 465 params - storage for the discovered configuration parameters
screamer 29:1210849dba19 466 unit_name - the unit (library/application) that defines this parameter
screamer 29:1210849dba19 467 unit_kind - the kind of the unit ("library" or "application")
screamer 29:1210849dba19 468 """
screamer 13:ab47a20b66f0 469 self.config_errors = []
screamer 29:1210849dba19 470 _process_config_parameters(data.get("config", {}), params, unit_name,
screamer 29:1210849dba19 471 unit_kind)
screamer 8:a8ac6ed29081 472 for label, overrides in data.get("target_overrides", {}).items():
screamer 29:1210849dba19 473 # If the label is defined by the target or it has the special value
screamer 29:1210849dba19 474 # "*", process the overrides
screamer 8:a8ac6ed29081 475 if (label == '*') or (label in self.target_labels):
screamer 24:25bff2709c20 476 # Check for invalid cumulative overrides in libraries
screamer 29:1210849dba19 477 if (unit_kind == 'library' and
screamer 29:1210849dba19 478 any(attr.startswith('target.extra_labels') for attr
screamer 29:1210849dba19 479 in overrides.iterkeys())):
screamer 29:1210849dba19 480 raise ConfigException(
screamer 29:1210849dba19 481 "Target override 'target.extra_labels' in " +
screamer 29:1210849dba19 482 ConfigParameter.get_display_name(unit_name, unit_kind,
screamer 29:1210849dba19 483 label) +
screamer 29:1210849dba19 484 " is only allowed at the application level")
Screamer@Y5070-M.virtuoso 10:2511036308b8 485
screamer 24:25bff2709c20 486 # Parse out cumulative overrides
screamer 24:25bff2709c20 487 for attr, cumulatives in self.cumulative_overrides.iteritems():
screamer 24:25bff2709c20 488 if 'target.'+attr in overrides:
screamer 29:1210849dba19 489 cumulatives.strict_cumulative_overrides(
screamer 29:1210849dba19 490 overrides['target.'+attr])
screamer 24:25bff2709c20 491 del overrides['target.'+attr]
Screamer@Y5070-M.virtuoso 10:2511036308b8 492
screamer 24:25bff2709c20 493 if 'target.'+attr+'_add' in overrides:
screamer 29:1210849dba19 494 cumulatives.add_cumulative_overrides(
screamer 29:1210849dba19 495 overrides['target.'+attr+'_add'])
screamer 24:25bff2709c20 496 del overrides['target.'+attr+'_add']
screamer 24:25bff2709c20 497
screamer 24:25bff2709c20 498 if 'target.'+attr+'_remove' in overrides:
screamer 29:1210849dba19 499 cumulatives.remove_cumulative_overrides(
screamer 29:1210849dba19 500 overrides['target.'+attr+'_remove'])
screamer 24:25bff2709c20 501 del overrides['target.'+attr+'_remove']
Screamer@Y5070-M.virtuoso 10:2511036308b8 502
Screamer@Y5070-M.virtuoso 10:2511036308b8 503 # Consider the others as overrides
screamer 29:1210849dba19 504 for name, val in overrides.items():
screamer 8:a8ac6ed29081 505 # Get the full name of the parameter
screamer 29:1210849dba19 506 full_name = ConfigParameter.get_full_name(name, unit_name,
screamer 29:1210849dba19 507 unit_kind, label)
screamer 13:ab47a20b66f0 508 if full_name in params:
screamer 29:1210849dba19 509 params[full_name].set_value(val, unit_name, unit_kind,
screamer 29:1210849dba19 510 label)
screamer 13:ab47a20b66f0 511 else:
screamer 29:1210849dba19 512 self.config_errors.append(
screamer 29:1210849dba19 513 ConfigException(
screamer 29:1210849dba19 514 "Attempt to override undefined parameter" +
screamer 29:1210849dba19 515 (" '%s' in '%s'"
screamer 29:1210849dba19 516 % (full_name,
screamer 29:1210849dba19 517 ConfigParameter.get_display_name(unit_name,
screamer 29:1210849dba19 518 unit_kind,
screamer 29:1210849dba19 519 label)))))
screamer 24:25bff2709c20 520
screamer 24:25bff2709c20 521 for cumulatives in self.cumulative_overrides.itervalues():
The Other Jimmy 31:8ea194f6145b 522 cumulatives.update_target(self.target)
screamer 24:25bff2709c20 523
screamer 8:a8ac6ed29081 524 return params
screamer 8:a8ac6ed29081 525
screamer 8:a8ac6ed29081 526 def get_target_config_data(self):
screamer 29:1210849dba19 527 """Read and interpret configuration data defined by targets.
screamer 29:1210849dba19 528
screamer 29:1210849dba19 529 We consider the resolution order for our target and sort it by level
screamer 29:1210849dba19 530 reversed, so that we first look at the top level target (the parent),
screamer 29:1210849dba19 531 then its direct children, then the children of those children and so on,
screamer 29:1210849dba19 532 until we reach self.target
screamer 29:1210849dba19 533 TODO: this might not work so well in some multiple inheritance scenarios
screamer 29:1210849dba19 534 At each step, look at two keys of the target data:
screamer 29:1210849dba19 535 - config_parameters: used to define new configuration parameters
screamer 29:1210849dba19 536 - config_overrides: used to override already defined configuration
screamer 29:1210849dba19 537 parameters
screamer 29:1210849dba19 538
screamer 29:1210849dba19 539 Arguments: None
screamer 29:1210849dba19 540 """
The Other Jimmy 31:8ea194f6145b 541 params, json_data = {}, self.target.json_data
screamer 29:1210849dba19 542 resolution_order = [e[0] for e
screamer 29:1210849dba19 543 in sorted(
The Other Jimmy 31:8ea194f6145b 544 self.target.resolution_order,
screamer 29:1210849dba19 545 key=lambda e: e[1], reverse=True)]
screamer 8:a8ac6ed29081 546 for tname in resolution_order:
screamer 8:a8ac6ed29081 547 # Read the target data directly from its description
screamer 29:1210849dba19 548 target_data = json_data[tname]
screamer 8:a8ac6ed29081 549 # Process definitions first
screamer 29:1210849dba19 550 _process_config_parameters(target_data.get("config", {}), params,
screamer 29:1210849dba19 551 tname, "target")
screamer 8:a8ac6ed29081 552 # Then process overrides
screamer 29:1210849dba19 553 for name, val in target_data.get("overrides", {}).items():
screamer 8:a8ac6ed29081 554 full_name = ConfigParameter.get_full_name(name, tname, "target")
screamer 29:1210849dba19 555 # If the parameter name is not defined or if there isn't a path
screamer 29:1210849dba19 556 # from this target to the target where the parameter was defined
screamer 29:1210849dba19 557 # in the target inheritance tree, raise an error We need to use
screamer 29:1210849dba19 558 # 'defined_by[7:]' to remove the "target:" prefix from
screamer 29:1210849dba19 559 # defined_by
The Other Jimmy 31:8ea194f6145b 560 rel_names = [tgt for tgt, _ in
The Other Jimmy 31:8ea194f6145b 561 get_resolution_order(self.target.json_data, tname,
The Other Jimmy 31:8ea194f6145b 562 [])]
screamer 29:1210849dba19 563 if (full_name not in params) or \
The Other Jimmy 31:8ea194f6145b 564 (params[full_name].defined_by[7:] not in rel_names):
screamer 29:1210849dba19 565 raise ConfigException(
screamer 29:1210849dba19 566 "Attempt to override undefined parameter '%s' in '%s'"
screamer 29:1210849dba19 567 % (name,
screamer 29:1210849dba19 568 ConfigParameter.get_display_name(tname, "target")))
screamer 8:a8ac6ed29081 569 # Otherwise update the value of the parameter
screamer 29:1210849dba19 570 params[full_name].set_value(val, tname, "target")
screamer 8:a8ac6ed29081 571 return params
screamer 8:a8ac6ed29081 572
screamer 29:1210849dba19 573 def get_lib_config_data(self):
screamer 29:1210849dba19 574 """ Read and interpret configuration data defined by libraries. It is
screamer 29:1210849dba19 575 assumed that "add_config_files" above was already called and the library
screamer 29:1210849dba19 576 configuration data exists in self.lib_config_data
screamer 8:a8ac6ed29081 577
screamer 29:1210849dba19 578 Arguments: None
screamer 29:1210849dba19 579 """
screamer 8:a8ac6ed29081 580 all_params, macros = {}, {}
screamer 8:a8ac6ed29081 581 for lib_name, lib_data in self.lib_config_data.items():
screamer 8:a8ac6ed29081 582 unknown_keys = set(lib_data.keys()) - self.__allowed_keys["library"]
screamer 8:a8ac6ed29081 583 if unknown_keys:
screamer 29:1210849dba19 584 raise ConfigException("Unknown key(s) '%s' in %s" %
screamer 29:1210849dba19 585 (",".join(unknown_keys), lib_name))
screamer 29:1210849dba19 586 all_params.update(self._process_config_and_overrides(lib_data, {},
screamer 29:1210849dba19 587 lib_name,
screamer 29:1210849dba19 588 "library"))
screamer 29:1210849dba19 589 _process_macros(lib_data.get("macros", []), macros, lib_name,
screamer 29:1210849dba19 590 "library")
screamer 8:a8ac6ed29081 591 return all_params, macros
screamer 8:a8ac6ed29081 592
screamer 8:a8ac6ed29081 593 def get_app_config_data(self, params, macros):
screamer 29:1210849dba19 594 """ Read and interpret the configuration data defined by the target. The
screamer 29:1210849dba19 595 target can override any configuration parameter, as well as define its
screamer 29:1210849dba19 596 own configuration data.
screamer 29:1210849dba19 597
screamer 29:1210849dba19 598 Positional arguments.
screamer 29:1210849dba19 599 params - the dictionary with configuration parameters found so far (in
screamer 29:1210849dba19 600 the target and in libraries)
screamer 29:1210849dba19 601 macros - the list of macros defined in the configuration
screamer 29:1210849dba19 602 """
screamer 8:a8ac6ed29081 603 app_cfg = self.app_config_data
screamer 29:1210849dba19 604 # The application can have a "config_parameters" and a
screamer 29:1210849dba19 605 # "target_config_overrides" section just like a library
screamer 29:1210849dba19 606 self._process_config_and_overrides(app_cfg, params, "app",
screamer 29:1210849dba19 607 "application")
screamer 8:a8ac6ed29081 608 # The application can also defined macros
screamer 29:1210849dba19 609 _process_macros(app_cfg.get("macros", []), macros, "app",
screamer 29:1210849dba19 610 "application")
screamer 8:a8ac6ed29081 611
screamer 8:a8ac6ed29081 612 def get_config_data(self):
screamer 29:1210849dba19 613 """ Return the configuration data in two parts: (params, macros)
screamer 29:1210849dba19 614 params - a dictionary with mapping a name to a ConfigParam
screamer 29:1210849dba19 615 macros - the list of macros defined with "macros" in libraries and in
screamer 29:1210849dba19 616 the application (as ConfigMacro instances)
screamer 29:1210849dba19 617
screamer 29:1210849dba19 618 Arguments: None
screamer 29:1210849dba19 619 """
screamer 8:a8ac6ed29081 620 all_params = self.get_target_config_data()
screamer 8:a8ac6ed29081 621 lib_params, macros = self.get_lib_config_data()
screamer 8:a8ac6ed29081 622 all_params.update(lib_params)
screamer 8:a8ac6ed29081 623 self.get_app_config_data(all_params, macros)
screamer 13:ab47a20b66f0 624 return all_params, macros
screamer 8:a8ac6ed29081 625
screamer 13:ab47a20b66f0 626 @staticmethod
screamer 13:ab47a20b66f0 627 def _check_required_parameters(params):
screamer 29:1210849dba19 628 """Check that there are no required parameters without a value
screamer 29:1210849dba19 629
screamer 29:1210849dba19 630 Positional arguments:
screamer 29:1210849dba19 631 params - the list of parameters to check
screamer 8:a8ac6ed29081 632
screamer 29:1210849dba19 633 NOTE: This function does not return. Instead, it throws a
screamer 29:1210849dba19 634 ConfigException when any of the required parameters are missing values
screamer 29:1210849dba19 635 """
screamer 29:1210849dba19 636 for param in params.values():
screamer 29:1210849dba19 637 if param.required and (param.value is None):
screamer 29:1210849dba19 638 raise ConfigException("Required parameter '" + param.name +
screamer 29:1210849dba19 639 "' defined by '" + param.defined_by +
screamer 29:1210849dba19 640 "' doesn't have a value")
screamer 29:1210849dba19 641
screamer 8:a8ac6ed29081 642 @staticmethod
screamer 8:a8ac6ed29081 643 def parameters_to_macros(params):
screamer 29:1210849dba19 644 """ Encode the configuration parameters as C macro definitions.
screamer 29:1210849dba19 645
screamer 29:1210849dba19 646 Positional arguments:
screamer 29:1210849dba19 647 params - a dictionary mapping a name to a ConfigParameter
screamer 8:a8ac6ed29081 648
screamer 29:1210849dba19 649 Return: a list of strings that encode the configuration parameters as
screamer 29:1210849dba19 650 C pre-processor macros
screamer 29:1210849dba19 651 """
screamer 29:1210849dba19 652 return ['%s=%s' % (m.macro_name, m.value) for m in params.values()
screamer 29:1210849dba19 653 if m.value is not None]
screamer 29:1210849dba19 654
screamer 13:ab47a20b66f0 655 @staticmethod
screamer 13:ab47a20b66f0 656 def config_macros_to_macros(macros):
screamer 29:1210849dba19 657 """ Return the macro definitions generated for a dictionary of
screamer 29:1210849dba19 658 ConfigMacros (as returned by get_config_data).
screamer 29:1210849dba19 659
screamer 29:1210849dba19 660 Positional arguments:
screamer 29:1210849dba19 661 params - a dictionary mapping a name to a ConfigMacro instance
screamer 29:1210849dba19 662
screamer 29:1210849dba19 663 Return: a list of strings that are the C pre-processor macros
screamer 29:1210849dba19 664 """
screamer 13:ab47a20b66f0 665 return [m.name for m in macros.values()]
screamer 13:ab47a20b66f0 666
screamer 13:ab47a20b66f0 667 @staticmethod
screamer 13:ab47a20b66f0 668 def config_to_macros(config):
screamer 29:1210849dba19 669 """Convert the configuration data to a list of C macros
screamer 29:1210849dba19 670
screamer 29:1210849dba19 671 Positional arguments:
screamer 29:1210849dba19 672 config - configuration data as (ConfigParam instances, ConfigMacro
screamer 29:1210849dba19 673 instances) tuple (as returned by get_config_data())
screamer 29:1210849dba19 674 """
screamer 13:ab47a20b66f0 675 params, macros = config[0], config[1]
screamer 13:ab47a20b66f0 676 Config._check_required_parameters(params)
screamer 29:1210849dba19 677 return Config.config_macros_to_macros(macros) + \
screamer 29:1210849dba19 678 Config.parameters_to_macros(params)
screamer 13:ab47a20b66f0 679
screamer 8:a8ac6ed29081 680 def get_config_data_macros(self):
screamer 29:1210849dba19 681 """ Convert a Config object to a list of C macros
screamer 29:1210849dba19 682
screamer 29:1210849dba19 683 Arguments: None
screamer 29:1210849dba19 684 """
screamer 13:ab47a20b66f0 685 return self.config_to_macros(self.get_config_data())
screamer 13:ab47a20b66f0 686
screamer 13:ab47a20b66f0 687 def get_features(self):
screamer 29:1210849dba19 688 """ Extract any features from the configuration data
screamer 29:1210849dba19 689
screamer 29:1210849dba19 690 Arguments: None
screamer 29:1210849dba19 691 """
screamer 13:ab47a20b66f0 692 params, _ = self.get_config_data()
screamer 8:a8ac6ed29081 693 self._check_required_parameters(params)
screamer 29:1210849dba19 694 self.cumulative_overrides['features']\
The Other Jimmy 31:8ea194f6145b 695 .update_target(self.target)
screamer 13:ab47a20b66f0 696
The Other Jimmy 31:8ea194f6145b 697 return self.target.features
screamer 13:ab47a20b66f0 698
screamer 13:ab47a20b66f0 699 def validate_config(self):
screamer 29:1210849dba19 700 """ Validate configuration settings. This either returns True or
screamer 29:1210849dba19 701 raises an exception
screamer 29:1210849dba19 702
screamer 29:1210849dba19 703 Arguments: None
screamer 29:1210849dba19 704 """
screamer 13:ab47a20b66f0 705 if self.config_errors:
screamer 13:ab47a20b66f0 706 raise self.config_errors[0]
screamer 13:ab47a20b66f0 707 return True
screamer 13:ab47a20b66f0 708
screamer 13:ab47a20b66f0 709
screamer 13:ab47a20b66f0 710 def load_resources(self, resources):
screamer 29:1210849dba19 711 """ Load configuration data from a Resources instance and expand it
screamer 29:1210849dba19 712 based on defined features.
screamer 29:1210849dba19 713
screamer 29:1210849dba19 714 Positional arguments:
screamer 29:1210849dba19 715 resources - the resources object to load from and expand
screamer 29:1210849dba19 716 """
screamer 13:ab47a20b66f0 717 # Update configuration files until added features creates no changes
screamer 13:ab47a20b66f0 718 prev_features = set()
screamer 13:ab47a20b66f0 719 while True:
screamer 29:1210849dba19 720 # Add/update the configuration with any .json files found while
screamer 29:1210849dba19 721 # scanning
screamer 13:ab47a20b66f0 722 self.add_config_files(resources.json_files)
screamer 13:ab47a20b66f0 723
screamer 13:ab47a20b66f0 724 # Add features while we find new ones
screamer 29:1210849dba19 725 features = set(self.get_features())
screamer 13:ab47a20b66f0 726 if features == prev_features:
screamer 13:ab47a20b66f0 727 break
screamer 13:ab47a20b66f0 728
screamer 13:ab47a20b66f0 729 for feature in features:
screamer 13:ab47a20b66f0 730 if feature in resources.features:
screamer 13:ab47a20b66f0 731 resources.add(resources.features[feature])
screamer 13:ab47a20b66f0 732
screamer 13:ab47a20b66f0 733 prev_features = features
screamer 13:ab47a20b66f0 734 self.validate_config()
screamer 13:ab47a20b66f0 735
screamer 13:ab47a20b66f0 736 return resources
screamer 13:ab47a20b66f0 737
screamer 13:ab47a20b66f0 738 @staticmethod
screamer 29:1210849dba19 739 def config_to_header(config, fname=None):
screamer 29:1210849dba19 740 """ Convert the configuration data to the content of a C header file,
screamer 29:1210849dba19 741 meant to be included to a C/C++ file. The content is returned as a
screamer 29:1210849dba19 742 string.
screamer 29:1210849dba19 743
screamer 29:1210849dba19 744 Positional arguments:
screamer 29:1210849dba19 745 config - configuration data as (ConfigParam instances, ConfigMacro
screamer 29:1210849dba19 746 instances) tuple (as returned by get_config_data())
screamer 29:1210849dba19 747
screamer 29:1210849dba19 748 Keyword arguments:
screamer 29:1210849dba19 749 fname - also write the content is to the file called "fname".
screamer 29:1210849dba19 750 WARNING: if 'fname' names an existing file, it will be
screamer 29:1210849dba19 751 overwritten!
screamer 29:1210849dba19 752 """
screamer 13:ab47a20b66f0 753 params, macros = config[0], config[1]
screamer 13:ab47a20b66f0 754 Config._check_required_parameters(params)
screamer 29:1210849dba19 755 header_data = "// Automatically generated configuration file.\n"
screamer 13:ab47a20b66f0 756 header_data += "// DO NOT EDIT, content will be overwritten.\n\n"
screamer 13:ab47a20b66f0 757 header_data += "#ifndef __MBED_CONFIG_DATA__\n"
screamer 13:ab47a20b66f0 758 header_data += "#define __MBED_CONFIG_DATA__\n\n"
screamer 13:ab47a20b66f0 759 # Compute maximum length of macro names for proper alignment
screamer 29:1210849dba19 760 max_param_macro_name_len = (max([len(m.macro_name) for m
screamer 29:1210849dba19 761 in params.values()
screamer 29:1210849dba19 762 if m.value is not None])
screamer 29:1210849dba19 763 if params else 0)
screamer 29:1210849dba19 764 max_direct_macro_name_len = (max([len(m.macro_name) for m
screamer 29:1210849dba19 765 in macros.values()])
screamer 29:1210849dba19 766 if macros else 0)
screamer 29:1210849dba19 767 max_macro_name_len = max(max_param_macro_name_len,
screamer 29:1210849dba19 768 max_direct_macro_name_len)
screamer 13:ab47a20b66f0 769 # Compute maximum length of macro values for proper alignment
screamer 29:1210849dba19 770 max_param_macro_val_len = (max([len(str(m.value)) for m
screamer 29:1210849dba19 771 in params.values()
screamer 29:1210849dba19 772 if m.value is not None])
screamer 29:1210849dba19 773 if params else 0)
screamer 29:1210849dba19 774 max_direct_macro_val_len = max([len(m.macro_value or "") for m
screamer 29:1210849dba19 775 in macros.values()]) if macros else 0
screamer 29:1210849dba19 776 max_macro_val_len = max(max_param_macro_val_len,
screamer 29:1210849dba19 777 max_direct_macro_val_len)
screamer 13:ab47a20b66f0 778 # Generate config parameters first
screamer 13:ab47a20b66f0 779 if params:
screamer 13:ab47a20b66f0 780 header_data += "// Configuration parameters\n"
screamer 29:1210849dba19 781 for macro in params.values():
screamer 29:1210849dba19 782 if macro.value is not None:
screamer 29:1210849dba19 783 header_data += ("#define {0:<{1}} {2!s:<{3}} " +
screamer 29:1210849dba19 784 "// set by {4}\n")\
screamer 29:1210849dba19 785 .format(macro.macro_name, max_macro_name_len,
screamer 29:1210849dba19 786 macro.value, max_macro_val_len, macro.set_by)
screamer 13:ab47a20b66f0 787 # Then macros
screamer 13:ab47a20b66f0 788 if macros:
screamer 13:ab47a20b66f0 789 header_data += "// Macros\n"
screamer 29:1210849dba19 790 for macro in macros.values():
screamer 29:1210849dba19 791 if macro.macro_value:
screamer 29:1210849dba19 792 header_data += ("#define {0:<{1}} {2!s:<{3}}" +
screamer 29:1210849dba19 793 " // defined by {4}\n")\
The Other Jimmy 31:8ea194f6145b 794 .format(macro.macro_name, max_macro_name_len,
The Other Jimmy 31:8ea194f6145b 795 macro.macro_value, max_macro_val_len,
The Other Jimmy 31:8ea194f6145b 796 macro.defined_by)
screamer 13:ab47a20b66f0 797 else:
screamer 29:1210849dba19 798 header_data += ("#define {0:<{1}}" +
screamer 29:1210849dba19 799 " // defined by {2}\n")\
The Other Jimmy 31:8ea194f6145b 800 .format(macro.macro_name,
screamer 29:1210849dba19 801 max_macro_name_len + max_macro_val_len + 1,
The Other Jimmy 31:8ea194f6145b 802 macro.defined_by)
screamer 13:ab47a20b66f0 803 header_data += "\n#endif\n"
screamer 13:ab47a20b66f0 804 # If fname is given, write "header_data" to it
screamer 13:ab47a20b66f0 805 if fname:
screamer 29:1210849dba19 806 with open(fname, "w+") as file_desc:
screamer 29:1210849dba19 807 file_desc.write(header_data)
screamer 13:ab47a20b66f0 808 return header_data
screamer 13:ab47a20b66f0 809
screamer 29:1210849dba19 810 def get_config_data_header(self, fname=None):
screamer 29:1210849dba19 811 """ Convert a Config instance to the content of a C header file, meant
screamer 29:1210849dba19 812 to be included to a C/C++ file. The content is returned as a string.
screamer 29:1210849dba19 813
screamer 29:1210849dba19 814 Keyword arguments:
screamer 29:1210849dba19 815 fname - also write the content to the file called "fname".
screamer 29:1210849dba19 816 WARNING: if 'fname' names an existing file, it will be
screamer 29:1210849dba19 817 overwritten!
screamer 29:1210849dba19 818 """
screamer 13:ab47a20b66f0 819 return self.config_to_header(self.get_config_data(), fname)