ON Semiconductor / mbed-os

Dependents:   mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers config.py Source File

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)