joey shelton / LED_Demo

Dependencies:   MAX44000 PWM_Tone_Library nexpaq_mdk

Fork of LED_Demo by Maxim nexpaq

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 import os
00019 import sys
00020 
00021 # Implementation of mbed configuration mechanism
00022 from tools.utils import json_file_to_dict
00023 from tools.targets import Target
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", "custom_targets", "target_overrides",
00345                             "macros", "__config_path"])
00346     }
00347 
00348     # Allowed features in configurations
00349     __allowed_features = [
00350         "UVISOR", "BLE", "CLIENT", "IPV4", "IPV6", "COMMON_PAL", "STORAGE"
00351     ]
00352 
00353     def __init__ (self, target, top_level_dirs=None, app_config=None):
00354         """Construct a mbed configuration
00355 
00356         Positional arguments:
00357         target - the name of the mbed target used for this configuration
00358                  instance
00359 
00360         Keyword argumets:
00361         top_level_dirs - a list of top level source directories (where
00362                          mbed_app_config.json could be found)
00363         app_config - location of a chosen mbed_app.json file
00364 
00365         NOTE: Construction of a Config object will look for the application
00366         configuration file in top_level_dirs. If found once, it'll parse it and
00367         check if it has a custom_targets function. If it does, it'll update the
00368         list of targets as needed. If more than one config file is found, an
00369         exception is raised. top_level_dirs may be None (in this case,
00370         the constructor will not 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         Target.add_py_targets(self.app_config_data .get("custom_targets", {}))
00400         self.lib_config_data  = {}
00401         # Make sure that each config is processed only once
00402         self.processed_configs  = {}
00403         self.target  = target if isinstance(target, basestring) else target.name
00404         self.target_labels  = Target.get_target(self.target ).get_labels()
00405 
00406         self.cumulative_overrides  = {key: ConfigCumulativeOverride(key)
00407                                      for key in
00408                                      Target.cumulative_attributes}
00409 
00410         self._process_config_and_overrides (self.app_config_data , {}, "app",
00411                                            "application")
00412         self.target_labels  = Target.get_target(self.target ).get_labels()
00413         self.config_errors  = None
00414 
00415     def add_config_files (self, flist):
00416         """Add configuration files
00417 
00418         Positional arguments:
00419         flist - a list of files to add to this configuration
00420         """
00421         for config_file in flist:
00422             if not config_file.endswith(self.__mbed_lib_config_name ):
00423                 continue
00424             full_path = os.path.normpath(os.path.abspath(config_file))
00425             # Check that we didn't already process this file
00426             if self.processed_configs .has_key(full_path):
00427                 continue
00428             self.processed_configs [full_path] = True
00429             # Read the library configuration and add a "__full_config_path"
00430             # attribute to it
00431             try:
00432                 cfg = json_file_to_dict(config_file)
00433             except ValueError as exc:
00434                 sys.stderr.write(str(exc) + "\n")
00435                 continue
00436 
00437             cfg["__config_path"] = full_path
00438 
00439             if "name" not in cfg:
00440                 raise ConfigException(
00441                     "Library configured at %s has no name field." % full_path)
00442             # If there's already a configuration for a module with the same
00443             # name, exit with error
00444             if self.lib_config_data .has_key(cfg["name"]):
00445                 raise ConfigException(
00446                     "Library name '%s' is not unique (defined in '%s' and '%s')"
00447                     % (cfg["name"], full_path,
00448                        self.lib_config_data [cfg["name"]]["__config_path"]))
00449             self.lib_config_data [cfg["name"]] = cfg
00450 
00451 
00452     def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
00453         """Process "config_parameters" and "target_config_overrides" into a
00454         given dictionary
00455 
00456         Positional arguments:
00457         data - the configuration data of the library/appliation
00458         params - storage for the discovered configuration parameters
00459         unit_name - the unit (library/application) that defines this parameter
00460         unit_kind - the kind of the unit ("library" or "application")
00461         """
00462         self.config_errors  = []
00463         _process_config_parameters(data.get("config", {}), params, unit_name,
00464                                    unit_kind)
00465         for label, overrides in data.get("target_overrides", {}).items():
00466             # If the label is defined by the target or it has the special value
00467             # "*", process the overrides
00468             if (label == '*') or (label in self.target_labels ):
00469                 # Check for invalid cumulative overrides in libraries
00470                 if (unit_kind == 'library' and
00471                     any(attr.startswith('target.extra_labels') for attr
00472                         in overrides.iterkeys())):
00473                     raise ConfigException(
00474                         "Target override 'target.extra_labels' in " +
00475                         ConfigParameter.get_display_name(unit_name, unit_kind,
00476                                                          label) +
00477                         " is only allowed at the application level")
00478 
00479                 # Parse out cumulative overrides
00480                 for attr, cumulatives in self.cumulative_overrides .iteritems():
00481                     if 'target.'+attr in overrides:
00482                         cumulatives.strict_cumulative_overrides(
00483                             overrides['target.'+attr])
00484                         del overrides['target.'+attr]
00485 
00486                     if 'target.'+attr+'_add' in overrides:
00487                         cumulatives.add_cumulative_overrides(
00488                             overrides['target.'+attr+'_add'])
00489                         del overrides['target.'+attr+'_add']
00490 
00491                     if 'target.'+attr+'_remove' in overrides:
00492                         cumulatives.remove_cumulative_overrides(
00493                             overrides['target.'+attr+'_remove'])
00494                         del overrides['target.'+attr+'_remove']
00495 
00496                 # Consider the others as overrides
00497                 for name, val in overrides.items():
00498                     # Get the full name of the parameter
00499                     full_name = ConfigParameter.get_full_name(name, unit_name,
00500                                                               unit_kind, label)
00501                     if full_name in params:
00502                         params[full_name].set_value(val, unit_name, unit_kind,
00503                                                     label)
00504                     else:
00505                         self.config_errors .append(
00506                             ConfigException(
00507                                 "Attempt to override undefined parameter" +
00508                                 (" '%s' in '%s'"
00509                                  % (full_name,
00510                                     ConfigParameter.get_display_name(unit_name,
00511                                                                      unit_kind,
00512                                                                      label)))))
00513 
00514         for cumulatives in self.cumulative_overrides .itervalues():
00515             cumulatives.update_target(Target.get_target(self.target ))
00516 
00517         return params
00518 
00519     def get_target_config_data (self):
00520         """Read and interpret configuration data defined by targets.
00521 
00522         We consider the resolution order for our target and sort it by level
00523         reversed, so that we first look at the top level target (the parent),
00524         then its direct children, then the children of those children and so on,
00525         until we reach self.target
00526         TODO: this might not work so well in some multiple inheritance scenarios
00527         At each step, look at two keys of the target data:
00528           - config_parameters: used to define new configuration parameters
00529           - config_overrides: used to override already defined configuration
00530                               parameters
00531 
00532         Arguments: None
00533         """
00534         params, json_data = {}, Target.get_json_target_data()
00535         resolution_order = [e[0] for e
00536                             in sorted(
00537                                 Target.get_target(self.target ).resolution_order,
00538                                 key=lambda e: e[1], reverse=True)]
00539         for tname in resolution_order:
00540             # Read the target data directly from its description
00541             target_data = json_data[tname]
00542             # Process definitions first
00543             _process_config_parameters(target_data.get("config", {}), params,
00544                                        tname, "target")
00545             # Then process overrides
00546             for name, val in target_data.get("overrides", {}).items():
00547                 full_name = ConfigParameter.get_full_name(name, tname, "target")
00548                 # If the parameter name is not defined or if there isn't a path
00549                 # from this target to the target where the parameter was defined
00550                 # in the target inheritance tree, raise an error We need to use
00551                 # 'defined_by[7:]' to remove the "target:" prefix from
00552                 # defined_by
00553                 if (full_name not in params) or \
00554                    (params[full_name].defined_by[7:] not in
00555                     Target.get_target(tname).resolution_order_names):
00556                     raise ConfigException(
00557                         "Attempt to override undefined parameter '%s' in '%s'"
00558                         % (name,
00559                            ConfigParameter.get_display_name(tname, "target")))
00560                 # Otherwise update the value of the parameter
00561                 params[full_name].set_value(val, tname, "target")
00562         return params
00563 
00564     def get_lib_config_data (self):
00565         """ Read and interpret configuration data defined by libraries. It is
00566         assumed that "add_config_files" above was already called and the library
00567         configuration data exists in self.lib_config_data
00568 
00569         Arguments: None
00570         """
00571         all_params, macros = {}, {}
00572         for lib_name, lib_data in self.lib_config_data .items():
00573             unknown_keys = set(lib_data.keys()) - self.__allowed_keys ["library"]
00574             if unknown_keys:
00575                 raise ConfigException("Unknown key(s) '%s' in %s" %
00576                                       (",".join(unknown_keys), lib_name))
00577             all_params.update(self._process_config_and_overrides (lib_data, {},
00578                                                                  lib_name,
00579                                                                  "library"))
00580             _process_macros(lib_data.get("macros", []), macros, lib_name,
00581                             "library")
00582         return all_params, macros
00583 
00584     def get_app_config_data (self, params, macros):
00585         """ Read and interpret the configuration data defined by the target. The
00586         target can override any configuration parameter, as well as define its
00587         own configuration data.
00588 
00589         Positional arguments.
00590         params - the dictionary with configuration parameters found so far (in
00591                  the target and in libraries)
00592         macros - the list of macros defined in the configuration
00593         """
00594         app_cfg = self.app_config_data 
00595         # The application can have a "config_parameters" and a
00596         # "target_config_overrides" section just like a library
00597         self._process_config_and_overrides (app_cfg, params, "app",
00598                                            "application")
00599         # The application can also defined macros
00600         _process_macros(app_cfg.get("macros", []), macros, "app",
00601                         "application")
00602 
00603     def get_config_data (self):
00604         """ Return the configuration data in two parts: (params, macros)
00605         params - a dictionary with mapping a name to a ConfigParam
00606         macros - the list of macros defined with "macros" in libraries and in
00607                  the application (as ConfigMacro instances)
00608 
00609         Arguments: None
00610         """
00611         all_params = self.get_target_config_data ()
00612         lib_params, macros = self.get_lib_config_data ()
00613         all_params.update(lib_params)
00614         self.get_app_config_data (all_params, macros)
00615         return all_params, macros
00616 
00617     @staticmethod
00618     def _check_required_parameters(params):
00619         """Check that there are no required parameters without a value
00620 
00621         Positional arguments:
00622         params - the list of parameters to check
00623 
00624         NOTE: This function does not return. Instead, it throws a
00625         ConfigException when any of the required parameters are missing values
00626         """
00627         for param in params.values():
00628             if param.required and (param.value is None):
00629                 raise ConfigException("Required parameter '" + param.name +
00630                                       "' defined by '" + param.defined_by +
00631                                       "' doesn't have a value")
00632 
00633     @staticmethod
00634     def parameters_to_macros (params):
00635         """ Encode the configuration parameters as C macro definitions.
00636 
00637         Positional arguments:
00638         params - a dictionary mapping a name to a ConfigParameter
00639 
00640         Return: a list of strings that encode the configuration parameters as
00641         C pre-processor macros
00642         """
00643         return ['%s=%s' % (m.macro_name, m.value) for m in params.values()
00644                 if m.value is not None]
00645 
00646     @staticmethod
00647     def config_macros_to_macros (macros):
00648         """ Return the macro definitions generated for a dictionary of
00649         ConfigMacros (as returned by get_config_data).
00650 
00651         Positional arguments:
00652         params - a dictionary mapping a name to a ConfigMacro instance
00653 
00654         Return: a list of strings that are the C pre-processor macros
00655         """
00656         return [m.name for m in macros.values()]
00657 
00658     @staticmethod
00659     def config_to_macros (config):
00660         """Convert the configuration data to a list of C macros
00661 
00662         Positional arguments:
00663         config - configuration data as (ConfigParam instances, ConfigMacro
00664                  instances) tuple (as returned by get_config_data())
00665         """
00666         params, macros = config[0], config[1]
00667         Config._check_required_parameters(params)
00668         return Config.config_macros_to_macros(macros) + \
00669             Config.parameters_to_macros(params)
00670 
00671     def get_config_data_macros (self):
00672         """ Convert a Config object to a list of C macros
00673 
00674         Arguments: None
00675         """
00676         return self.config_to_macros (self.get_config_data ())
00677 
00678     def get_features (self):
00679         """ Extract any features from the configuration data
00680 
00681         Arguments: None
00682         """
00683         params, _ = self.get_config_data ()
00684         self._check_required_parameters (params)
00685         self.cumulative_overrides ['features']\
00686             .update_target(Target.get_target(self.target ))
00687         features = Target.get_target(self.target ).features
00688 
00689         for feature in features:
00690             if feature not in self.__allowed_features :
00691                 raise ConfigException(
00692                     "Feature '%s' is not a supported features" % feature)
00693 
00694         return features
00695 
00696     def validate_config (self):
00697         """ Validate configuration settings. This either returns True or
00698         raises an exception
00699 
00700         Arguments: None
00701         """
00702         if self.config_errors :
00703             raise self.config_errors [0]
00704         return True
00705 
00706 
00707     def load_resources (self, resources):
00708         """ Load configuration data from a Resources instance and expand it
00709         based on defined features.
00710 
00711         Positional arguments:
00712         resources - the resources object to load from and expand
00713         """
00714         # Update configuration files until added features creates no changes
00715         prev_features = set()
00716         while True:
00717             # Add/update the configuration with any .json files found while
00718             # scanning
00719             self.add_config_files (resources.json_files)
00720 
00721             # Add features while we find new ones
00722             features = set(self.get_features ())
00723             if features == prev_features:
00724                 break
00725 
00726             for feature in features:
00727                 if feature in resources.features:
00728                     resources.add(resources.features[feature])
00729 
00730             prev_features = features
00731         self.validate_config ()
00732 
00733         return resources
00734 
00735     @staticmethod
00736     def config_to_header (config, fname=None):
00737         """ Convert the configuration data to the content of a C header file,
00738         meant to be included to a C/C++ file. The content is returned as a
00739         string.
00740 
00741         Positional arguments:
00742         config - configuration data as (ConfigParam instances, ConfigMacro
00743                  instances) tuple (as returned by get_config_data())
00744 
00745         Keyword arguments:
00746         fname -  also write the content is to the file called "fname".
00747                  WARNING: if 'fname' names an existing file, it will be
00748                  overwritten!
00749         """
00750         params, macros = config[0], config[1]
00751         Config._check_required_parameters(params)
00752         header_data = "// Automatically generated configuration file.\n"
00753         header_data += "// DO NOT EDIT, content will be overwritten.\n\n"
00754         header_data += "#ifndef __MBED_CONFIG_DATA__\n"
00755         header_data += "#define __MBED_CONFIG_DATA__\n\n"
00756         # Compute maximum length of macro names for proper alignment
00757         max_param_macro_name_len = (max([len(m.macro_name) for m
00758                                          in params.values()
00759                                          if m.value is not None])
00760                                     if params else 0)
00761         max_direct_macro_name_len = (max([len(m.macro_name) for m
00762                                          in macros.values()])
00763                                      if macros else 0)
00764         max_macro_name_len = max(max_param_macro_name_len,
00765                                  max_direct_macro_name_len)
00766         # Compute maximum length of macro values for proper alignment
00767         max_param_macro_val_len = (max([len(str(m.value)) for m
00768                                        in params.values()
00769                                        if m.value is not None])
00770                                    if params else 0)
00771         max_direct_macro_val_len = max([len(m.macro_value or "") for m
00772                                         in macros.values()]) if macros else 0
00773         max_macro_val_len = max(max_param_macro_val_len,
00774                                 max_direct_macro_val_len)
00775         # Generate config parameters first
00776         if params:
00777             header_data += "// Configuration parameters\n"
00778             for macro in params.values():
00779                 if macro.value is not None:
00780                     header_data += ("#define {0:<{1}} {2!s:<{3}} " +
00781                                     "// set by {4}\n")\
00782                         .format(macro.macro_name, max_macro_name_len,
00783                                 macro.value, max_macro_val_len, macro.set_by)
00784         # Then macros
00785         if macros:
00786             header_data += "// Macros\n"
00787             for macro in macros.values():
00788                 if macro.macro_value:
00789                     header_data += ("#define {0:<{1}} {2!s:<{3}}" +
00790                                     " // defined by {4}\n")\
00791                         .format(macro.macro_name, max_macro_name_len,
00792                                 macro.macro_value, max_macro_val_len,
00793                                 macro.defined_by)
00794                 else:
00795                     header_data += ("#define {0:<{1}}" +
00796                                     " // defined by {2}\n")\
00797                         .format(macro.macro_name,
00798                                 max_macro_name_len + max_macro_val_len + 1,
00799                                 macro.defined_by)
00800         header_data += "\n#endif\n"
00801         # If fname is given, write "header_data" to it
00802         if fname:
00803             with open(fname, "w+") as file_desc:
00804                 file_desc.write(header_data)
00805         return header_data
00806 
00807     def get_config_data_header (self, fname=None):
00808         """ Convert a Config instance to the content of a C header file, meant
00809         to be included to a C/C++ file. The content is returned as a string.
00810 
00811         Keyword arguments:
00812         fname - also write the content to the file called "fname".
00813                 WARNING: if 'fname' names an existing file, it will be
00814                 overwritten!
00815         """
00816         return self.config_to_header (self.get_config_data (), fname)