Daiki Kato / mbed-os-lychee

Dependents:   mbed-os-example-blinky-gr-lychee GR-Boads_Camera_sample GR-Boards_Audio_Recoder GR-Boads_Camera_DisplayApp ... more

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