Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-sdk-tools by
config.py@31:182518299918, 2017-01-04 (annotated)
- Committer:
- The Other Jimmy
- Date:
- Wed Jan 04 11:58:24 2017 -0600
- Revision:
- 31:182518299918
- Parent:
- 30:f12ce67666d0
Update tools to follow mbed-os tools release 5.3.1
Who changed what in which revision?
User | Revision | Line number | New 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:182518299918 | 18 | from copy import deepcopy |
The Other Jimmy |
31:182518299918 | 19 | import os |
The Other Jimmy |
31:182518299918 | 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:182518299918 | 23 | from tools.targets import CUMULATIVE_ATTRIBUTES, TARGET_MAP, \ |
The Other Jimmy |
31:182518299918 | 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:182518299918 | 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:182518299918 | 351 | "UVISOR", "BLE", "CLIENT", "IPV4", "LWIP", "COMMON_PAL", "STORAGE", "NANOSTACK", |
The Other Jimmy |
31:182518299918 | 352 | # Nanostack configurations |
The Other Jimmy |
31:182518299918 | 353 | "LOWPAN_BORDER_ROUTER", "LOWPAN_HOST", "LOWPAN_ROUTER", "NANOSTACK_FULL", "THREAD_BORDER_ROUTER", "THREAD_END_DEVICE", "THREAD_ROUTER" |
The Other Jimmy |
31:182518299918 | 354 | ] |
screamer | 13:ab47a20b66f0 | 355 | |
The Other Jimmy |
31:182518299918 | 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:182518299918 | 365 | mbed_app_config.json could be found) |
The Other Jimmy |
31:182518299918 | 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:182518299918 | 369 | configuration file in top_level_dirs. If found once, it'll parse it. |
The Other Jimmy |
31:182518299918 | 370 | top_level_dirs may be None (in this case, the constructor will not |
The Other Jimmy |
31:182518299918 | 371 | search for a configuration file). |
screamer | 29:1210849dba19 | 372 | """ |
The Other Jimmy |
31:182518299918 | 373 | app_config_location = app_config |
The Other Jimmy |
31:182518299918 | 374 | if app_config_location is None: |
The Other Jimmy |
31:182518299918 | 375 | for directory in top_level_dirs or []: |
The Other Jimmy |
31:182518299918 | 376 | full_path = os.path.join(directory, self.__mbed_app_config_name) |
The Other Jimmy |
31:182518299918 | 377 | if os.path.isfile(full_path): |
The Other Jimmy |
31:182518299918 | 378 | if app_config_location is not None: |
The Other Jimmy |
31:182518299918 | 379 | raise ConfigException("Duplicate '%s' file in '%s' and '%s'" |
The Other Jimmy |
31:182518299918 | 380 | % (self.__mbed_app_config_name, |
The Other Jimmy |
31:182518299918 | 381 | app_config_location, full_path)) |
The Other Jimmy |
31:182518299918 | 382 | else: |
The Other Jimmy |
31:182518299918 | 383 | app_config_location = full_path |
The Other Jimmy |
31:182518299918 | 384 | try: |
The Other Jimmy |
31:182518299918 | 385 | self.app_config_data = json_file_to_dict(app_config_location) \ |
The Other Jimmy |
31:182518299918 | 386 | if app_config_location else {} |
The Other Jimmy |
31:182518299918 | 387 | except ValueError as exc: |
The Other Jimmy |
31:182518299918 | 388 | sys.stderr.write(str(exc) + "\n") |
The Other Jimmy |
31:182518299918 | 389 | self.app_config_data = {} |
The Other Jimmy |
31:182518299918 | 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:182518299918 | 403 | if isinstance(tgt, basestring): |
The Other Jimmy |
31:182518299918 | 404 | if tgt in TARGET_MAP: |
The Other Jimmy |
31:182518299918 | 405 | self.target = TARGET_MAP[tgt] |
The Other Jimmy |
31:182518299918 | 406 | else: |
The Other Jimmy |
31:182518299918 | 407 | self.target = generate_py_target( |
The Other Jimmy |
31:182518299918 | 408 | self.app_config_data.get("custom_targets", {}), tgt) |
The Other Jimmy |
31:182518299918 | 409 | |
The Other Jimmy |
31:182518299918 | 410 | else: |
The Other Jimmy |
31:182518299918 | 411 | self.target = tgt |
The Other Jimmy |
31:182518299918 | 412 | self.target = deepcopy(self.target) |
The Other Jimmy |
31:182518299918 | 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:182518299918 | 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:182518299918 | 438 | try: |
The Other Jimmy |
31:182518299918 | 439 | cfg = json_file_to_dict(config_file) |
The Other Jimmy |
31:182518299918 | 440 | except ValueError as exc: |
The Other Jimmy |
31:182518299918 | 441 | sys.stderr.write(str(exc) + "\n") |
The Other Jimmy |
31:182518299918 | 442 | continue |
The Other Jimmy |
31:182518299918 | 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:182518299918 | 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:182518299918 | 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:182518299918 | 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:182518299918 | 560 | rel_names = [tgt for tgt, _ in |
The Other Jimmy |
31:182518299918 | 561 | get_resolution_order(self.target.json_data, tname, |
The Other Jimmy |
31:182518299918 | 562 | [])] |
screamer | 29:1210849dba19 | 563 | if (full_name not in params) or \ |
The Other Jimmy |
31:182518299918 | 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:182518299918 | 695 | .update_target(self.target) |
screamer | 13:ab47a20b66f0 | 696 | |
The Other Jimmy |
31:182518299918 | 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:182518299918 | 794 | .format(macro.macro_name, max_macro_name_len, |
The Other Jimmy |
31:182518299918 | 795 | macro.macro_value, max_macro_val_len, |
The Other Jimmy |
31:182518299918 | 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:182518299918 | 800 | .format(macro.macro_name, |
screamer | 29:1210849dba19 | 801 | max_macro_name_len + max_macro_val_len + 1, |
The Other Jimmy |
31:182518299918 | 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) |