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