mbed-os

Dependents:   cobaLCDJoyMotor_Thread odometry_omni_3roda_v3 odometry_omni_3roda_v1 odometry_omni_3roda_v2 ... more

Committer:
be_bryan
Date:
Mon Dec 11 17:54:04 2017 +0000
Revision:
0:b74591d5ab33
motor ++

Who changed what in which revision?

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