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.
Dependents: mbed-os-example-blinky-gr-lychee GR-Boads_Camera_sample GR-Boards_Audio_Recoder GR-Boads_Camera_DisplayApp ... more
config.py
00001 """ 00002 mbed SDK 00003 Copyright (c) 2016 ARM Limited 00004 00005 Licensed under the Apache License, Version 2.0 (the "License"); 00006 you may not use this file except in compliance with the License. 00007 You may obtain a copy of the License at 00008 00009 http://www.apache.org/licenses/LICENSE-2.0 00010 00011 Unless required by applicable law or agreed to in writing, software 00012 distributed under the License is distributed on an "AS IS" BASIS, 00013 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 See the License for the specific language governing permissions and 00015 limitations under the License. 00016 """ 00017 00018 from copy import deepcopy 00019 import os 00020 import sys 00021 from collections import namedtuple 00022 from os.path import splitext 00023 from intelhex import IntelHex 00024 # Implementation of mbed configuration mechanism 00025 from tools.utils import json_file_to_dict, intelhex_offset 00026 from tools.arm_pack_manager import Cache 00027 from tools.targets import CUMULATIVE_ATTRIBUTES, TARGET_MAP, \ 00028 generate_py_target, get_resolution_order 00029 00030 # Base class for all configuration exceptions 00031 class ConfigException (Exception): 00032 """Config system only exception. Makes it easier to distinguish config 00033 errors""" 00034 pass 00035 00036 class ConfigParameter (object): 00037 """This class keeps information about a single configuration parameter""" 00038 00039 def __init__ (self, name, data, unit_name, unit_kind): 00040 """Construct a ConfigParameter 00041 00042 Positional arguments: 00043 name - the name of the configuration parameter 00044 data - the data associated with the configuration parameter 00045 unit_name - the unit (target/library/application) that defines this 00046 parameter 00047 unit_ kind - the kind of the unit ("target", "library" or "application") 00048 """ 00049 self.name = self.get_full_name (name, unit_name, unit_kind, 00050 allow_prefix=False) 00051 self.defined_by = self.get_display_name (unit_name, unit_kind) 00052 self.set_value (data.get("value", None), unit_name, unit_kind) 00053 self.help_text = data.get("help", None) 00054 self.required = data.get("required", False) 00055 self.macro_name = data.get("macro_name", "MBED_CONF_%s" % 00056 self.sanitize (self.name .upper())) 00057 self.config_errors = [] 00058 00059 @staticmethod 00060 def get_full_name (name, unit_name, unit_kind, label=None, 00061 allow_prefix=True): 00062 """Return the full (prefixed) name of a parameter. If the parameter 00063 already has a prefix, check if it is valid 00064 00065 Positional arguments: 00066 name - the simple (unqualified) name of the parameter 00067 unit_name - the unit (target/library/application) that defines this 00068 parameter 00069 unit_kind - the kind of the unit ("target", "library" or "application") 00070 00071 Keyword arguments: 00072 label - the name of the label in the 'target_config_overrides' section 00073 allow_prefix - True to allow the original name to have a prefix, False 00074 otherwise 00075 """ 00076 if name.find('.') == -1: # the name is not prefixed 00077 if unit_kind == "target": 00078 prefix = "target." 00079 elif unit_kind == "application": 00080 prefix = "app." 00081 else: 00082 prefix = unit_name + '.' 00083 return prefix + name 00084 # The name has a prefix, so check if it is valid 00085 if not allow_prefix: 00086 raise ConfigException("Invalid parameter name '%s' in '%s'" % 00087 (name, ConfigParameter.get_display_name( 00088 unit_name, unit_kind, label))) 00089 temp = name.split(".") 00090 # Check if the parameter syntax is correct (must be 00091 # unit_name.parameter_name) 00092 if len(temp) != 2: 00093 raise ConfigException("Invalid parameter name '%s' in '%s'" % 00094 (name, ConfigParameter.get_display_name( 00095 unit_name, unit_kind, label))) 00096 prefix = temp[0] 00097 # Check if the given parameter prefix matches the expected prefix 00098 if (unit_kind == "library" and prefix != unit_name) or \ 00099 (unit_kind == "target" and prefix != "target"): 00100 raise ConfigException( 00101 "Invalid prefix '%s' for parameter name '%s' in '%s'" % 00102 (prefix, name, ConfigParameter.get_display_name( 00103 unit_name, unit_kind, label))) 00104 return name 00105 00106 @staticmethod 00107 def get_display_name (unit_name, unit_kind, label=None): 00108 """Return the name displayed for a unit when interrogating the origin 00109 and the last set place of a parameter 00110 00111 Positional arguments: 00112 unit_name - the unit (target/library/application) that defines this 00113 parameter 00114 unit_kind - the kind of the unit ("target", "library" or "application") 00115 00116 Keyword arguments: 00117 label - the name of the label in the 'target_config_overrides' section 00118 """ 00119 if unit_kind == "target": 00120 return "target:" + unit_name 00121 elif unit_kind == "application": 00122 return "application%s" % ("[%s]" % label if label else "") 00123 else: # library 00124 return "library:%s%s" % (unit_name, "[%s]" % label if label else "") 00125 00126 @staticmethod 00127 def sanitize (name): 00128 """ "Sanitize" a name so that it is a valid C macro name. Currently it 00129 simply replaces '.' and '-' with '_'. 00130 00131 Positional arguments: 00132 name - the name to make into a valid C macro 00133 """ 00134 return name.replace('.', '_').replace('-', '_') 00135 00136 def set_value (self, value, unit_name, unit_kind, label=None): 00137 """ Sets a value for this parameter, remember the place where it was 00138 set. If the value is a Boolean, it is converted to 1 (for True) or 00139 to 0 (for False). 00140 00141 Positional arguments: 00142 value - the value of the parameter 00143 unit_name - the unit (target/library/application) that defines this 00144 parameter 00145 unit_kind - the kind of the unit ("target", "library" or "application") 00146 00147 Keyword arguments: 00148 label - the name of the label in the 'target_config_overrides' section 00149 (optional) 00150 """ 00151 self.value = int(value) if isinstance(value, bool) else value 00152 self.set_by = self.get_display_name (unit_name, unit_kind, label) 00153 00154 def __str__ (self): 00155 """Return the string representation of this configuration parameter 00156 00157 Arguments: None 00158 """ 00159 if self.value is not None: 00160 return '%s = %s (macro name: "%s")' % \ 00161 (self.name , self.value , self.macro_name ) 00162 else: 00163 return '%s has no value' % self.name 00164 00165 def get_verbose_description (self): 00166 """Return a verbose description of this configuration parameter as a 00167 string 00168 00169 Arguments: None 00170 """ 00171 desc = "Name: %s%s\n" % \ 00172 (self.name , " (required parameter)" if self.required else "") 00173 if self.help_text : 00174 desc += " Description: %s\n" % self.help_text 00175 desc += " Defined by: %s\n" % self.defined_by 00176 if not self.value : 00177 return desc + " No value set" 00178 desc += " Macro name: %s\n" % self.macro_name 00179 desc += " Value: %s (set by %s)" % (self.value , self.set_by ) 00180 return desc 00181 00182 class ConfigMacro (object): 00183 """ A representation of a configuration macro. It handles both macros 00184 without a value (MACRO) and with a value (MACRO=VALUE) 00185 """ 00186 def __init__ (self, name, unit_name, unit_kind): 00187 """Construct a ConfigMacro object 00188 00189 Positional arguments: 00190 name - the macro's name 00191 unit_name - the location where the macro was defined 00192 unit_kind - the type of macro this is 00193 """ 00194 self.name = name 00195 self.defined_by = ConfigParameter.get_display_name(unit_name, unit_kind) 00196 if name.find("=") != -1: 00197 tmp = name.split("=") 00198 if len(tmp) != 2: 00199 raise ValueError("Invalid macro definition '%s' in '%s'" % 00200 (name, self.defined_by )) 00201 self.macro_name = tmp[0] 00202 self.macro_value = tmp[1] 00203 else: 00204 self.macro_name = name 00205 self.macro_value = None 00206 00207 class ConfigCumulativeOverride (object): 00208 """Representation of overrides for cumulative attributes""" 00209 def __init__ (self, name, additions=None, removals=None, strict=False): 00210 """Construct a ConfigCumulativeOverride object 00211 00212 Positional arguments: 00213 name - the name of the config file this came from ? 00214 00215 Keyword arguments: 00216 additions - macros to add to the overrides 00217 removals - macros to remove from the overrides 00218 strict - Boolean indicating that attempting to remove from an override 00219 that does not exist should error 00220 """ 00221 self.name = name 00222 if additions: 00223 self.additions = set(additions) 00224 else: 00225 self.additions = set() 00226 if removals: 00227 self.removals = set(removals) 00228 else: 00229 self.removals = set() 00230 self.strict = strict 00231 00232 def remove_cumulative_overrides (self, overrides): 00233 """Extend the list of override removals. 00234 00235 Positional arguments: 00236 overrides - a list of names that, when the override is evaluated, will 00237 be removed 00238 """ 00239 for override in overrides: 00240 if override in self.additions : 00241 raise ConfigException( 00242 "Configuration conflict. The %s %s both added and removed." 00243 % (self.name [:-1], override)) 00244 00245 self.removals |= set(overrides) 00246 00247 def add_cumulative_overrides (self, overrides): 00248 """Extend the list of override additions. 00249 00250 Positional arguments: 00251 overrides - a list of a names that, when the override is evaluated, will 00252 be added to the list 00253 """ 00254 for override in overrides: 00255 if override in self.removals or \ 00256 (self.strict and override not in self.additions ): 00257 raise ConfigException( 00258 "Configuration conflict. The %s %s both added and removed." 00259 % (self.name [:-1], override)) 00260 00261 self.additions |= set(overrides) 00262 00263 def strict_cumulative_overrides (self, overrides): 00264 """Remove all overrides that are not the specified ones 00265 00266 Positional arguments: 00267 overrides - a list of names that will replace the entire attribute when 00268 this override is evaluated. 00269 """ 00270 self.remove_cumulative_overrides (self.additions - set(overrides)) 00271 self.add_cumulative_overrides (overrides) 00272 self.strict = True 00273 00274 def update_target (self, target): 00275 """Update the attributes of a target based on this override""" 00276 setattr(target, self.name , 00277 list((set(getattr(target, self.name , [])) 00278 | self.additions ) - self.removals )) 00279 00280 00281 def _process_config_parameters(data, params, unit_name, unit_kind): 00282 """Process a "config_parameters" section in either a target, a library, 00283 or the application. 00284 00285 Positional arguments: 00286 data - a dictionary with the configuration parameters 00287 params - storage for the discovered configuration parameters 00288 unit_name - the unit (target/library/application) that defines this 00289 parameter 00290 unit_kind - the kind of the unit ("target", "library" or "application") 00291 """ 00292 for name, val in data.items(): 00293 full_name = ConfigParameter.get_full_name(name, unit_name, unit_kind) 00294 # If the parameter was already defined, raise an error 00295 if full_name in params: 00296 raise ConfigException( 00297 "Parameter name '%s' defined in both '%s' and '%s'" % 00298 (name, ConfigParameter.get_display_name(unit_name, unit_kind), 00299 params[full_name].defined_by)) 00300 # Otherwise add it to the list of known parameters 00301 # If "val" is not a dictionary, this is a shortcut definition, 00302 # otherwise it is a full definition 00303 params[full_name] = ConfigParameter(name, val if isinstance(val, dict) 00304 else {"value": val}, unit_name, 00305 unit_kind) 00306 return params 00307 00308 00309 def _process_macros(mlist, macros, unit_name, unit_kind): 00310 """Process a macro definition and check for incompatible duplicate 00311 definitions. 00312 00313 Positional arguments: 00314 mlist - list of macro names to process 00315 macros - dictionary with currently discovered macros 00316 unit_name - the unit (library/application) that defines this macro 00317 unit_kind - the kind of the unit ("library" or "application") 00318 """ 00319 for mname in mlist: 00320 macro = ConfigMacro(mname, unit_name, unit_kind) 00321 if (macro.macro_name in macros) and \ 00322 (macros[macro.macro_name].name != mname): 00323 # Found an incompatible definition of the macro in another module, 00324 # so raise an error 00325 full_unit_name = ConfigParameter.get_display_name(unit_name, 00326 unit_kind) 00327 raise ConfigException( 00328 ("Macro '%s' defined in both '%s' and '%s'" 00329 % (macro.macro_name, macros[macro.macro_name].defined_by, 00330 full_unit_name)) + 00331 " with incompatible values") 00332 macros[macro.macro_name] = macro 00333 00334 00335 Region = namedtuple("Region", "name start size active filename") 00336 00337 class Config (object): 00338 """'Config' implements the mbed configuration mechanism""" 00339 00340 # Libraries and applications have different names for their configuration 00341 # files 00342 __mbed_app_config_name = "mbed_app.json" 00343 __mbed_lib_config_name = "mbed_lib.json" 00344 00345 # Allowed keys in configuration dictionaries 00346 # (targets can have any kind of keys, so this validation is not applicable 00347 # to them) 00348 __allowed_keys = { 00349 "library": set(["name", "config", "target_overrides", "macros", 00350 "__config_path"]), 00351 "application": set(["config", "target_overrides", 00352 "macros", "__config_path"]) 00353 } 00354 00355 __unused_overrides = set(["target.bootloader_img", "target.restrict_size"]) 00356 00357 # Allowed features in configurations 00358 __allowed_features = [ 00359 "UVISOR", "BLE", "CLIENT", "IPV4", "LWIP", "COMMON_PAL", "STORAGE", "NANOSTACK", 00360 # Nanostack configurations 00361 "LOWPAN_BORDER_ROUTER", "LOWPAN_HOST", "LOWPAN_ROUTER", "NANOSTACK_FULL", "THREAD_BORDER_ROUTER", "THREAD_END_DEVICE", "THREAD_ROUTER", "ETHERNET_HOST" 00362 ] 00363 00364 def __init__ (self, tgt, top_level_dirs=None, app_config=None): 00365 """Construct a mbed configuration 00366 00367 Positional arguments: 00368 target - the name of the mbed target used for this configuration 00369 instance 00370 00371 Keyword argumets: 00372 top_level_dirs - a list of top level source directories (where 00373 mbed_app_config.json could be found) 00374 app_config - location of a chosen mbed_app.json file 00375 00376 NOTE: Construction of a Config object will look for the application 00377 configuration file in top_level_dirs. If found once, it'll parse it. 00378 top_level_dirs may be None (in this case, the constructor will not 00379 search for a configuration file). 00380 """ 00381 app_config_location = app_config 00382 if app_config_location is None: 00383 for directory in top_level_dirs or []: 00384 full_path = os.path.join(directory, self.__mbed_app_config_name ) 00385 if os.path.isfile(full_path): 00386 if app_config_location is not None: 00387 raise ConfigException("Duplicate '%s' file in '%s' and '%s'" 00388 % (self.__mbed_app_config_name , 00389 app_config_location, full_path)) 00390 else: 00391 app_config_location = full_path 00392 try: 00393 self.app_config_data = json_file_to_dict(app_config_location) \ 00394 if app_config_location else {} 00395 except ValueError as exc: 00396 sys.stderr.write(str(exc) + "\n") 00397 self.app_config_data = {} 00398 00399 # Check the keys in the application configuration data 00400 unknown_keys = set(self.app_config_data .keys()) - \ 00401 self.__allowed_keys ["application"] 00402 if unknown_keys: 00403 raise ConfigException("Unknown key(s) '%s' in %s" % 00404 (",".join(unknown_keys), 00405 self.__mbed_app_config_name )) 00406 # Update the list of targets with the ones defined in the application 00407 # config, if applicable 00408 self.lib_config_data = {} 00409 # Make sure that each config is processed only once 00410 self.processed_configs = {} 00411 if isinstance(tgt, basestring): 00412 if tgt in TARGET_MAP: 00413 self.target = TARGET_MAP[tgt] 00414 else: 00415 self.target = generate_py_target( 00416 self.app_config_data .get("custom_targets", {}), tgt) 00417 00418 else: 00419 self.target = tgt 00420 self.target = deepcopy(self.target ) 00421 self.target_labels = self.target .labels 00422 00423 self.cumulative_overrides = {key: ConfigCumulativeOverride(key) 00424 for key in CUMULATIVE_ATTRIBUTES} 00425 00426 self._process_config_and_overrides (self.app_config_data , {}, "app", 00427 "application") 00428 self.config_errors = None 00429 00430 def add_config_files (self, flist): 00431 """Add configuration files 00432 00433 Positional arguments: 00434 flist - a list of files to add to this configuration 00435 """ 00436 for config_file in flist: 00437 if not config_file.endswith(self.__mbed_lib_config_name ): 00438 continue 00439 full_path = os.path.normpath(os.path.abspath(config_file)) 00440 # Check that we didn't already process this file 00441 if self.processed_configs .has_key(full_path): 00442 continue 00443 self.processed_configs [full_path] = True 00444 # Read the library configuration and add a "__full_config_path" 00445 # attribute to it 00446 try: 00447 cfg = json_file_to_dict(config_file) 00448 except ValueError as exc: 00449 sys.stderr.write(str(exc) + "\n") 00450 continue 00451 00452 cfg["__config_path"] = full_path 00453 00454 if "name" not in cfg: 00455 raise ConfigException( 00456 "Library configured at %s has no name field." % full_path) 00457 # If there's already a configuration for a module with the same 00458 # name, exit with error 00459 if self.lib_config_data .has_key(cfg["name"]): 00460 raise ConfigException( 00461 "Library name '%s' is not unique (defined in '%s' and '%s')" 00462 % (cfg["name"], full_path, 00463 self.lib_config_data [cfg["name"]]["__config_path"])) 00464 self.lib_config_data [cfg["name"]] = cfg 00465 00466 @property 00467 def has_regions (self): 00468 """Does this config have regions defined?""" 00469 if 'target_overrides' in self.app_config_data : 00470 target_overrides = self.app_config_data ['target_overrides'].get( 00471 self.target .name, {}) 00472 return ('target.bootloader_img' in target_overrides or 00473 'target.restrict_size' in target_overrides) 00474 else: 00475 return False 00476 00477 @property 00478 def regions (self): 00479 """Generate a list of regions from the config""" 00480 if not self.target .bootloader_supported: 00481 raise ConfigException("Bootloader not supported on this target.") 00482 cmsis_part = Cache(False, False).index[self.target .device_name] 00483 start = 0 00484 target_overrides = self.app_config_data ['target_overrides'].get( 00485 self.target .name, {}) 00486 try: 00487 rom_size = int(cmsis_part['memory']['IROM1']['size'], 0) 00488 rom_start = int(cmsis_part['memory']['IROM1']['start'], 0) 00489 except KeyError: 00490 raise ConfigException("Not enough information in CMSIS packs to " 00491 "build a bootloader project") 00492 if 'target.bootloader_img' in target_overrides: 00493 filename = target_overrides['target.bootloader_img'] 00494 part = intelhex_offset(filename, offset=rom_start) 00495 if part.minaddr() != rom_start: 00496 raise ConfigException("bootloader executable does not " 00497 "start at 0x%x" % rom_start) 00498 part_size = (part.maxaddr() - part.minaddr()) + 1 00499 yield Region("bootloader", rom_start + start, part_size, False, 00500 filename) 00501 start += part_size 00502 if 'target.restrict_size' in target_overrides: 00503 new_size = int(target_overrides['target.restrict_size'], 0) 00504 yield Region("application", rom_start + start, new_size, True, None) 00505 start += new_size 00506 yield Region("post_application", rom_start +start, rom_size - start, 00507 False, None) 00508 else: 00509 yield Region("application", rom_start + start, rom_size - start, 00510 True, None) 00511 if start > rom_size: 00512 raise ConfigException("Not enough memory on device to fit all " 00513 "application regions") 00514 00515 def _process_config_and_overrides(self, data, params, unit_name, unit_kind): 00516 """Process "config_parameters" and "target_config_overrides" into a 00517 given dictionary 00518 00519 Positional arguments: 00520 data - the configuration data of the library/appliation 00521 params - storage for the discovered configuration parameters 00522 unit_name - the unit (library/application) that defines this parameter 00523 unit_kind - the kind of the unit ("library" or "application") 00524 """ 00525 self.config_errors = [] 00526 _process_config_parameters(data.get("config", {}), params, unit_name, 00527 unit_kind) 00528 for label, overrides in data.get("target_overrides", {}).items(): 00529 # If the label is defined by the target or it has the special value 00530 # "*", process the overrides 00531 if (label == '*') or (label in self.target_labels ): 00532 # Check for invalid cumulative overrides in libraries 00533 if (unit_kind == 'library' and 00534 any(attr.startswith('target.extra_labels') for attr 00535 in overrides.iterkeys())): 00536 raise ConfigException( 00537 "Target override 'target.extra_labels' in " + 00538 ConfigParameter.get_display_name(unit_name, unit_kind, 00539 label) + 00540 " is only allowed at the application level") 00541 00542 # Parse out cumulative overrides 00543 for attr, cumulatives in self.cumulative_overrides .iteritems(): 00544 if 'target.'+attr in overrides: 00545 cumulatives.strict_cumulative_overrides( 00546 overrides['target.'+attr]) 00547 del overrides['target.'+attr] 00548 00549 if 'target.'+attr+'_add' in overrides: 00550 cumulatives.add_cumulative_overrides( 00551 overrides['target.'+attr+'_add']) 00552 del overrides['target.'+attr+'_add'] 00553 00554 if 'target.'+attr+'_remove' in overrides: 00555 cumulatives.remove_cumulative_overrides( 00556 overrides['target.'+attr+'_remove']) 00557 del overrides['target.'+attr+'_remove'] 00558 00559 # Consider the others as overrides 00560 for name, val in overrides.items(): 00561 # Get the full name of the parameter 00562 full_name = ConfigParameter.get_full_name(name, unit_name, 00563 unit_kind, label) 00564 if full_name in params: 00565 params[full_name].set_value(val, unit_name, unit_kind, 00566 label) 00567 elif name in self.__unused_overrides : 00568 pass 00569 else: 00570 self.config_errors .append( 00571 ConfigException( 00572 "Attempt to override undefined parameter" + 00573 (" '%s' in '%s'" 00574 % (full_name, 00575 ConfigParameter.get_display_name(unit_name, 00576 unit_kind, 00577 label))))) 00578 00579 for cumulatives in self.cumulative_overrides .itervalues(): 00580 cumulatives.update_target(self.target ) 00581 00582 return params 00583 00584 def get_target_config_data (self): 00585 """Read and interpret configuration data defined by targets. 00586 00587 We consider the resolution order for our target and sort it by level 00588 reversed, so that we first look at the top level target (the parent), 00589 then its direct children, then the children of those children and so on, 00590 until we reach self.target 00591 TODO: this might not work so well in some multiple inheritance scenarios 00592 At each step, look at two keys of the target data: 00593 - config_parameters: used to define new configuration parameters 00594 - config_overrides: used to override already defined configuration 00595 parameters 00596 00597 Arguments: None 00598 """ 00599 params, json_data = {}, self.target .json_data 00600 resolution_order = [e[0] for e 00601 in sorted( 00602 self.target .resolution_order, 00603 key=lambda e: e[1], reverse=True)] 00604 for tname in resolution_order: 00605 # Read the target data directly from its description 00606 target_data = json_data[tname] 00607 # Process definitions first 00608 _process_config_parameters(target_data.get("config", {}), params, 00609 tname, "target") 00610 # Then process overrides 00611 for name, val in target_data.get("overrides", {}).items(): 00612 full_name = ConfigParameter.get_full_name(name, tname, "target") 00613 # If the parameter name is not defined or if there isn't a path 00614 # from this target to the target where the parameter was defined 00615 # in the target inheritance tree, raise an error We need to use 00616 # 'defined_by[7:]' to remove the "target:" prefix from 00617 # defined_by 00618 rel_names = [tgt for tgt, _ in 00619 get_resolution_order(self.target .json_data, tname, 00620 [])] 00621 if full_name in self.__unused_overrides : 00622 continue 00623 if (full_name not in params) or \ 00624 (params[full_name].defined_by[7:] not in rel_names): 00625 raise ConfigException( 00626 "Attempt to override undefined parameter '%s' in '%s'" 00627 % (name, 00628 ConfigParameter.get_display_name(tname, "target"))) 00629 # Otherwise update the value of the parameter 00630 params[full_name].set_value(val, tname, "target") 00631 return params 00632 00633 def get_lib_config_data (self): 00634 """ Read and interpret configuration data defined by libraries. It is 00635 assumed that "add_config_files" above was already called and the library 00636 configuration data exists in self.lib_config_data 00637 00638 Arguments: None 00639 """ 00640 all_params, macros = {}, {} 00641 for lib_name, lib_data in self.lib_config_data .items(): 00642 unknown_keys = set(lib_data.keys()) - self.__allowed_keys ["library"] 00643 if unknown_keys: 00644 raise ConfigException("Unknown key(s) '%s' in %s" % 00645 (",".join(unknown_keys), lib_name)) 00646 all_params.update(self._process_config_and_overrides (lib_data, {}, 00647 lib_name, 00648 "library")) 00649 _process_macros(lib_data.get("macros", []), macros, lib_name, 00650 "library") 00651 return all_params, macros 00652 00653 def get_app_config_data (self, params, macros): 00654 """ Read and interpret the configuration data defined by the target. The 00655 target can override any configuration parameter, as well as define its 00656 own configuration data. 00657 00658 Positional arguments. 00659 params - the dictionary with configuration parameters found so far (in 00660 the target and in libraries) 00661 macros - the list of macros defined in the configuration 00662 """ 00663 app_cfg = self.app_config_data 00664 # The application can have a "config_parameters" and a 00665 # "target_config_overrides" section just like a library 00666 self._process_config_and_overrides (app_cfg, params, "app", 00667 "application") 00668 # The application can also defined macros 00669 _process_macros(app_cfg.get("macros", []), macros, "app", 00670 "application") 00671 00672 def get_config_data (self): 00673 """ Return the configuration data in two parts: (params, macros) 00674 params - a dictionary with mapping a name to a ConfigParam 00675 macros - the list of macros defined with "macros" in libraries and in 00676 the application (as ConfigMacro instances) 00677 00678 Arguments: None 00679 """ 00680 all_params = self.get_target_config_data () 00681 lib_params, macros = self.get_lib_config_data () 00682 all_params.update(lib_params) 00683 self.get_app_config_data (all_params, macros) 00684 return all_params, macros 00685 00686 @staticmethod 00687 def _check_required_parameters(params): 00688 """Check that there are no required parameters without a value 00689 00690 Positional arguments: 00691 params - the list of parameters to check 00692 00693 NOTE: This function does not return. Instead, it throws a 00694 ConfigException when any of the required parameters are missing values 00695 """ 00696 for param in params.values(): 00697 if param.required and (param.value is None): 00698 raise ConfigException("Required parameter '" + param.name + 00699 "' defined by '" + param.defined_by + 00700 "' doesn't have a value") 00701 00702 @staticmethod 00703 def parameters_to_macros (params): 00704 """ Encode the configuration parameters as C macro definitions. 00705 00706 Positional arguments: 00707 params - a dictionary mapping a name to a ConfigParameter 00708 00709 Return: a list of strings that encode the configuration parameters as 00710 C pre-processor macros 00711 """ 00712 return ['%s=%s' % (m.macro_name, m.value) for m in params.values() 00713 if m.value is not None] 00714 00715 @staticmethod 00716 def config_macros_to_macros (macros): 00717 """ Return the macro definitions generated for a dictionary of 00718 ConfigMacros (as returned by get_config_data). 00719 00720 Positional arguments: 00721 params - a dictionary mapping a name to a ConfigMacro instance 00722 00723 Return: a list of strings that are the C pre-processor macros 00724 """ 00725 return [m.name for m in macros.values()] 00726 00727 @staticmethod 00728 def config_to_macros (config): 00729 """Convert the configuration data to a list of C macros 00730 00731 Positional arguments: 00732 config - configuration data as (ConfigParam instances, ConfigMacro 00733 instances) tuple (as returned by get_config_data()) 00734 """ 00735 params, macros = config[0], config[1] 00736 Config._check_required_parameters(params) 00737 return Config.config_macros_to_macros(macros) + \ 00738 Config.parameters_to_macros(params) 00739 00740 def get_config_data_macros (self): 00741 """ Convert a Config object to a list of C macros 00742 00743 Arguments: None 00744 """ 00745 return self.config_to_macros (self.get_config_data ()) 00746 00747 def get_features (self): 00748 """ Extract any features from the configuration data 00749 00750 Arguments: None 00751 """ 00752 params, _ = self.get_config_data () 00753 self._check_required_parameters (params) 00754 self.cumulative_overrides ['features']\ 00755 .update_target(self.target ) 00756 00757 for feature in self.target .features: 00758 if feature not in self.__allowed_features : 00759 raise ConfigException( 00760 "Feature '%s' is not a supported features" % feature) 00761 00762 return self.target .features 00763 00764 def validate_config (self): 00765 """ Validate configuration settings. This either returns True or 00766 raises an exception 00767 00768 Arguments: None 00769 """ 00770 if self.config_errors : 00771 raise self.config_errors [0] 00772 return True 00773 00774 00775 def load_resources (self, resources): 00776 """ Load configuration data from a Resources instance and expand it 00777 based on defined features. 00778 00779 Positional arguments: 00780 resources - the resources object to load from and expand 00781 """ 00782 # Update configuration files until added features creates no changes 00783 prev_features = set() 00784 while True: 00785 # Add/update the configuration with any .json files found while 00786 # scanning 00787 self.add_config_files (resources.json_files) 00788 00789 # Add features while we find new ones 00790 features = set(self.get_features ()) 00791 if features == prev_features: 00792 break 00793 00794 for feature in features: 00795 if feature in resources.features: 00796 resources.add(resources.features[feature]) 00797 00798 prev_features = features 00799 self.validate_config () 00800 00801 return resources 00802 00803 @staticmethod 00804 def config_to_header (config, fname=None): 00805 """ Convert the configuration data to the content of a C header file, 00806 meant to be included to a C/C++ file. The content is returned as a 00807 string. 00808 00809 Positional arguments: 00810 config - configuration data as (ConfigParam instances, ConfigMacro 00811 instances) tuple (as returned by get_config_data()) 00812 00813 Keyword arguments: 00814 fname - also write the content is to the file called "fname". 00815 WARNING: if 'fname' names an existing file, it will be 00816 overwritten! 00817 """ 00818 params, macros = config[0], config[1] 00819 Config._check_required_parameters(params) 00820 header_data = "// Automatically generated configuration file.\n" 00821 header_data += "// DO NOT EDIT, content will be overwritten.\n\n" 00822 header_data += "#ifndef __MBED_CONFIG_DATA__\n" 00823 header_data += "#define __MBED_CONFIG_DATA__\n\n" 00824 # Compute maximum length of macro names for proper alignment 00825 max_param_macro_name_len = (max([len(m.macro_name) for m 00826 in params.values() 00827 if m.value is not None]) 00828 if params else 0) 00829 max_direct_macro_name_len = (max([len(m.macro_name) for m 00830 in macros.values()]) 00831 if macros else 0) 00832 max_macro_name_len = max(max_param_macro_name_len, 00833 max_direct_macro_name_len) 00834 # Compute maximum length of macro values for proper alignment 00835 max_param_macro_val_len = (max([len(str(m.value)) for m 00836 in params.values() 00837 if m.value is not None]) 00838 if params else 0) 00839 max_direct_macro_val_len = max([len(m.macro_value or "") for m 00840 in macros.values()]) if macros else 0 00841 max_macro_val_len = max(max_param_macro_val_len, 00842 max_direct_macro_val_len) 00843 # Generate config parameters first 00844 if params: 00845 header_data += "// Configuration parameters\n" 00846 for macro in params.values(): 00847 if macro.value is not None: 00848 header_data += ("#define {0:<{1}} {2!s:<{3}} " + 00849 "// set by {4}\n")\ 00850 .format(macro.macro_name, max_macro_name_len, 00851 macro.value, max_macro_val_len, macro.set_by) 00852 # Then macros 00853 if macros: 00854 header_data += "// Macros\n" 00855 for macro in macros.values(): 00856 if macro.macro_value: 00857 header_data += ("#define {0:<{1}} {2!s:<{3}}" + 00858 " // defined by {4}\n")\ 00859 .format(macro.macro_name, max_macro_name_len, 00860 macro.macro_value, max_macro_val_len, 00861 macro.defined_by) 00862 else: 00863 header_data += ("#define {0:<{1}}" + 00864 " // defined by {2}\n")\ 00865 .format(macro.macro_name, 00866 max_macro_name_len + max_macro_val_len + 1, 00867 macro.defined_by) 00868 header_data += "\n#endif\n" 00869 # If fname is given, write "header_data" to it 00870 if fname: 00871 with open(fname, "w+") as file_desc: 00872 file_desc.write(header_data) 00873 return header_data 00874 00875 def get_config_data_header (self, fname=None): 00876 """ Convert a Config instance to the content of a C header file, meant 00877 to be included to a C/C++ file. The content is returned as a string. 00878 00879 Keyword arguments: 00880 fname - also write the content to the file called "fname". 00881 WARNING: if 'fname' names an existing file, it will be 00882 overwritten! 00883 """ 00884 return self.config_to_header (self.get_config_data (), fname)
Generated on Tue Jul 12 2022 11:02:22 by
1.7.2