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