Clone of official tools
Diff: config/__init__.py
- Revision:
- 43:2a7da56ebd24
- Parent:
- 36:96847d42f010
--- a/config/__init__.py Mon Nov 06 13:17:14 2017 -0600 +++ b/config/__init__.py Tue Sep 25 13:43:09 2018 -0500 @@ -14,21 +14,62 @@ See the License for the specific language governing permissions and limitations under the License. """ +from __future__ import print_function, division, absolute_import from copy import deepcopy +from six import moves +import json +import six import os -from os.path import dirname, abspath, exists, join +from os.path import dirname, abspath, exists, join, isabs import sys from collections import namedtuple from os.path import splitext, relpath from intelhex import IntelHex from jinja2 import FileSystemLoader, StrictUndefined from jinja2.environment import Environment -# Implementation of mbed configuration mechanism -from tools.utils import json_file_to_dict, intelhex_offset -from tools.arm_pack_manager import Cache -from tools.targets import CUMULATIVE_ATTRIBUTES, TARGET_MAP, \ - generate_py_target, get_resolution_order +from jsonschema import Draft4Validator, RefResolver + +from ..resources import FileType +from ..utils import (json_file_to_dict, intelhex_offset, integer, + NotSupportedException) +from ..arm_pack_manager import Cache +from ..targets import (CUMULATIVE_ATTRIBUTES, TARGET_MAP, generate_py_target, + get_resolution_order, Target) + +try: + unicode +except NameError: + unicode = str +PATH_OVERRIDES = set(["target.bootloader_img"]) +ROM_OVERRIDES = set([ + # managed BL + "target.bootloader_img", "target.restrict_size", + "target.header_format", "target.header_offset", + "target.app_offset", + + # unmanaged BL + "target.mbed_app_start", "target.mbed_app_size", + + # both + "target.mbed_rom_start", "target.mbed_rom_size", +]) +RAM_OVERRIDES = set([ + # both + "target.mbed_ram_start", "target.mbed_ram_size", +]) + +BOOTLOADER_OVERRIDES = ROM_OVERRIDES | RAM_OVERRIDES + + +ALLOWED_FEATURES = [ + "BOOTLOADER","UVISOR", "BLE", "CLIENT", "IPV4", "LWIP", "COMMON_PAL", "STORAGE", + "NANOSTACK","CRYPTOCELL310", + # Nanostack configurations + "LOWPAN_BORDER_ROUTER", "LOWPAN_HOST", "LOWPAN_ROUTER", "NANOSTACK_FULL", + "THREAD_BORDER_ROUTER", "THREAD_END_DEVICE", "THREAD_ROUTER", + "ETHERNET_HOST", +] # Base class for all configuration exceptions class ConfigException(Exception): @@ -36,6 +77,19 @@ errors""" pass +class UndefinedParameter(ConfigException): + def __init__(self, param, name, kind, label): + self.param = param + self.name = name + self.kind = kind + self.label = label + + def __str__(self): + return "Attempt to override undefined parameter '{}' in '{}'".format( + self.param, + ConfigParameter.get_display_name(self.name, self.kind, self.label), + ) + class ConfigParameter(object): """This class keeps information about a single configuration parameter""" @@ -84,6 +138,8 @@ else: prefix = unit_name + '.' return prefix + name + if name in BOOTLOADER_OVERRIDES: + return name # The name has a prefix, so check if it is valid if not allow_prefix: raise ConfigException("Invalid parameter name '%s' in '%s'" % @@ -98,7 +154,7 @@ unit_name, unit_kind, label))) prefix = temp[0] # Check if the given parameter prefix matches the expected prefix - if (unit_kind == "library" and prefix != unit_name) or \ + if (unit_kind == "library" and prefix not in [unit_name, "target"]) or \ (unit_kind == "target" and prefix != "target"): raise ConfigException( "Invalid prefix '%s' for parameter name '%s' in '%s'" % @@ -335,13 +391,8 @@ macros[macro.macro_name] = macro -def check_dict_types(dict, type_dict, dict_loc): - for key, value in dict.iteritems(): - if not isinstance(value, type_dict[key]): - raise ConfigException("The value of %s.%s is not of type %s" % - (dict_loc, key, type_dict[key].__name__)) - Region = namedtuple("Region", "name start size active filename") +RamRegion = namedtuple("RamRegion", "name start size active") class Config(object): """'Config' implements the mbed configuration mechanism""" @@ -351,26 +402,30 @@ __mbed_app_config_name = "mbed_app.json" __mbed_lib_config_name = "mbed_lib.json" - # Allowed keys in configuration dictionaries, and their types - # (targets can have any kind of keys, so this validation is not applicable - # to them) - __allowed_keys = { - "library": {"name": str, "config": dict, "target_overrides": dict, - "macros": list, "__config_path": str}, - "application": {"config": dict, "target_overrides": dict, - "macros": list, "__config_path": str, - "artifact_name": str} - } - __unused_overrides = set(["target.bootloader_img", "target.restrict_size", "target.mbed_app_start", "target.mbed_app_size"]) - # Allowed features in configurations - __allowed_features = [ - "UVISOR", "BLE", "CLIENT", "IPV4", "LWIP", "COMMON_PAL", "STORAGE", "NANOSTACK", - # Nanostack configurations - "LOWPAN_BORDER_ROUTER", "LOWPAN_HOST", "LOWPAN_ROUTER", "NANOSTACK_FULL", "THREAD_BORDER_ROUTER", "THREAD_END_DEVICE", "THREAD_ROUTER", "ETHERNET_HOST" - ] + @classmethod + def find_app_config(cls, top_level_dirs): + app_config_location = None + for directory in top_level_dirs: + full_path = os.path.join(directory, cls.__mbed_app_config_name) + if os.path.isfile(full_path): + if app_config_location is not None: + raise ConfigException("Duplicate '%s' file in '%s' and '%s'" + % (cls.__mbed_app_config_name, + cls.app_config_location, full_path)) + else: + app_config_location = full_path + return app_config_location + + def format_validation_error(self, error, path): + if error.context: + return self.format_validation_error(error.context[0], path) + else: + return "in {} element {}: {}".format( + path, ".".join(p for p in error.absolute_path), + error.message.replace('u\'','\'')) def __init__(self, tgt, top_level_dirs=None, app_config=None): """Construct a mbed configuration @@ -390,17 +445,10 @@ search for a configuration file). """ config_errors = [] + self.config_errors = [] self.app_config_location = app_config - if self.app_config_location is None: - for directory in top_level_dirs or []: - full_path = os.path.join(directory, self.__mbed_app_config_name) - if os.path.isfile(full_path): - if self.app_config_location is not None: - raise ConfigException("Duplicate '%s' file in '%s' and '%s'" - % (self.__mbed_app_config_name, - self.app_config_location, full_path)) - else: - self.app_config_location = full_path + if self.app_config_location is None and top_level_dirs: + self.app_config_location = self.find_app_config(top_level_dirs) try: self.app_config_data = json_file_to_dict(self.app_config_location) \ if self.app_config_location else {} @@ -410,31 +458,44 @@ ConfigException("Could not parse mbed app configuration from %s" % self.app_config_location)) - # Check the keys in the application configuration data - unknown_keys = set(self.app_config_data.keys()) - \ - set(self.__allowed_keys["application"].keys()) - if unknown_keys: - raise ConfigException("Unknown key(s) '%s' in %s" % - (",".join(unknown_keys), - self.__mbed_app_config_name)) - check_dict_types(self.app_config_data, self.__allowed_keys["application"], - "app-config") + + if self.app_config_location is not None: + # Validate the format of the JSON file based on schema_app.json + schema_root = os.path.dirname(os.path.abspath(__file__)) + schema_path = os.path.join(schema_root, "schema_app.json") + schema = json_file_to_dict(schema_path) + + url = moves.urllib.request.pathname2url(schema_path) + uri = moves.urllib_parse.urljoin("file://", url) + + resolver = RefResolver(uri, schema) + validator = Draft4Validator(schema, resolver=resolver) + + errors = sorted(validator.iter_errors(self.app_config_data)) + + if errors: + raise ConfigException("; ".join( + self.format_validation_error(x, self.app_config_location) + for x in errors)) + # Update the list of targets with the ones defined in the application # config, if applicable self.lib_config_data = {} # Make sure that each config is processed only once self.processed_configs = {} - if isinstance(tgt, basestring): + if isinstance(tgt, Target): + self.target = tgt + else: if tgt in TARGET_MAP: self.target = TARGET_MAP[tgt] else: self.target = generate_py_target( self.app_config_data.get("custom_targets", {}), tgt) - - else: - self.target = tgt self.target = deepcopy(self.target) self.target_labels = self.target.labels + for override in BOOTLOADER_OVERRIDES: + _, attr = override.split(".") + setattr(self.target, attr, None) self.cumulative_overrides = {key: ConfigCumulativeOverride(key) for key in CUMULATIVE_ATTRIBUTES} @@ -454,7 +515,7 @@ continue full_path = os.path.normpath(os.path.abspath(config_file)) # Check that we didn't already process this file - if self.processed_configs.has_key(full_path): + if full_path in self.processed_configs: continue self.processed_configs[full_path] = True # Read the library configuration and add a "__full_config_path" @@ -462,17 +523,31 @@ try: cfg = json_file_to_dict(config_file) except ValueError as exc: - sys.stderr.write(str(exc) + "\n") - continue + raise ConfigException(str(exc)) + + # Validate the format of the JSON file based on the schema_lib.json + schema_root = os.path.dirname(os.path.abspath(__file__)) + schema_path = os.path.join(schema_root, "schema_lib.json") + schema_file = json_file_to_dict(schema_path) + + url = moves.urllib.request.pathname2url(schema_path) + uri = moves.urllib_parse.urljoin("file://", url) + + resolver = RefResolver(uri, schema_file) + validator = Draft4Validator(schema_file, resolver=resolver) + + errors = sorted(validator.iter_errors(cfg)) + + if errors: + raise ConfigException("; ".join( + self.format_validation_error(x, config_file) + for x in errors)) cfg["__config_path"] = full_path - if "name" not in cfg: - raise ConfigException( - "Library configured at %s has no name field." % full_path) # If there's already a configuration for a module with the same # name, exit with error - if self.lib_config_data.has_key(cfg["name"]): + if cfg["name"] in self.lib_config_data: raise ConfigException( "Library name '%s' is not unique (defined in '%s' and '%s')" % (cfg["name"], full_path, @@ -482,20 +557,37 @@ @property def has_regions(self): """Does this config have regions defined?""" - if 'target_overrides' in self.app_config_data: - target_overrides = self.app_config_data['target_overrides'].get( - self.target.name, {}) - return ('target.bootloader_img' in target_overrides or - 'target.restrict_size' in target_overrides or - 'target.mbed_app_start' in target_overrides or - 'target.mbed_app_size' in target_overrides) - else: - return False + for override in ROM_OVERRIDES: + _, attr = override.split(".") + if getattr(self.target, attr, None): + return True + return False + + @property + def has_ram_regions(self): + """Does this config have regions defined?""" + for override in RAM_OVERRIDES: + _, attr = override.split(".") + if getattr(self.target, attr, None): + return True + return False @property - def regions(self): - """Generate a list of regions from the config""" - if not self.target.bootloader_supported: + def sectors(self): + """Return a list of tuples of sector start,size""" + cache = Cache(False, False) + if self.target.device_name not in cache.index: + raise ConfigException("Bootloader not supported on this target: " + "targets.json `device_name` not found in " + "arm_pack_manager index.") + cmsis_part = cache.index[self.target.device_name] + sectors = cmsis_part['sectors'] + if sectors: + return sectors + raise ConfigException("No sector info available") + + def _get_cmsis_part(self): + if not getattr(self.target, "bootloader_supported", False): raise ConfigException("Bootloader not supported on this target.") if not hasattr(self.target, "device_name"): raise ConfigException("Bootloader not supported on this target: " @@ -505,40 +597,113 @@ raise ConfigException("Bootloader not supported on this target: " "targets.json `device_name` not found in " "arm_pack_manager index.") - cmsis_part = cache.index[self.target.device_name] - target_overrides = self.app_config_data['target_overrides'].get( - self.target.name, {}) - if (('target.bootloader_img' in target_overrides or - 'target.restrict_size' in target_overrides) and - ('target.mbed_app_start' in target_overrides or - 'target.mbed_app_size' in target_overrides)): + return cache.index[self.target.device_name] + + def _get_mem_specs(self, memories, cmsis_part, exception_text): + for memory in memories: + try: + size = cmsis_part['memory'][memory]['size'] + start = cmsis_part['memory'][memory]['start'] + return (start, size) + except KeyError: + continue + raise ConfigException(exception_text) + + @property + def rom(self): + """Get rom information as a pair of start_addr, size""" + # Override rom_start/rom_size + # + # This is usually done for a target which: + # 1. Doesn't support CMSIS pack, or + # 2. Supports TrustZone and user needs to change its flash partition + cmsis_part = self._get_cmsis_part() + rom_start, rom_size = self._get_mem_specs( + ["IROM1", "PROGRAM_FLASH"], + cmsis_part, + "Not enough information in CMSIS packs to build a bootloader " + "project" + ) + rom_start = int(getattr(self.target, "mbed_rom_start", False) or rom_start, 0) + rom_size = int(getattr(self.target, "mbed_rom_size", False) or rom_size, 0) + return (rom_start, rom_size) + + @property + def ram_regions(self): + """Generate a list of ram regions from the config""" + cmsis_part = self._get_cmsis_part() + ram_start, ram_size = self._get_mem_specs( + ["IRAM1", "SRAM0"], + cmsis_part, + "Not enough information in CMSIS packs to build a ram sharing project" + ) + # Override ram_start/ram_size + # + # This is usually done for a target which: + # 1. Doesn't support CMSIS pack, or + # 2. Supports TrustZone and user needs to change its flash partition + ram_start = getattr(self.target, "mbed_ram_start", False) or ram_start + ram_size = getattr(self.target, "mbed_ram_size", False) or ram_size + return [RamRegion("application_ram", int(ram_start, 0), int(ram_size, 0), True)] + + @property + def regions(self): + """Generate a list of regions from the config""" + if ((self.target.bootloader_img or self.target.restrict_size) and + (self.target.mbed_app_start or self.target.mbed_app_size)): raise ConfigException( "target.bootloader_img and target.restirct_size are " "incompatible with target.mbed_app_start and " "target.mbed_app_size") + if self.target.bootloader_img or self.target.restrict_size: + return self._generate_bootloader_build(*self.rom) + else: + return self._generate_linker_overrides(*self.rom) + + @staticmethod + def header_member_size(member): + _, _, subtype, _ = member try: - rom_size = int(cmsis_part['memory']['IROM1']['size'], 0) - rom_start = int(cmsis_part['memory']['IROM1']['start'], 0) - except KeyError: - raise ConfigException("Not enough information in CMSIS packs to " - "build a bootloader project") - if ('target.bootloader_img' in target_overrides or - 'target.restrict_size' in target_overrides): - return self._generate_booloader_build(target_overrides, - rom_start, rom_size) - elif ('target.mbed_app_start' in target_overrides or - 'target.mbed_app_size' in target_overrides): - return self._generate_linker_overrides(target_overrides, - rom_start, rom_size) - else: + return int(subtype[:-2]) // 8 + except: + if subtype.startswith("CRCITT32"): + return 32 // 8 + elif subtype == "SHA256": + return 256 // 8 + elif subtype == "SHA512": + return 512 // 8 + else: + raise ValueError("target.header_format: subtype %s is not " + "understood" % subtype) + + @staticmethod + def _header_size(format): + return sum(Config.header_member_size(m) for m in format) + + def _make_header_region(self, start, header_format, offset=None): + size = self._header_size(header_format) + region = Region("header", start, size, False, None) + start += size + start = ((start + 7) // 8) * 8 + return (start, region) + + @staticmethod + def _assign_new_offset(rom_start, start, new_offset, region_name): + newstart = rom_start + integer(new_offset, 0) + if newstart < start: raise ConfigException( - "Bootloader build requested but no bootlader configuration") + "Can not place % region inside previous region" % region_name) + return newstart - def _generate_booloader_build(self, target_overrides, rom_start, rom_size): - start = 0 - if 'target.bootloader_img' in target_overrides: - basedir = abspath(dirname(self.app_config_location)) - filename = join(basedir, target_overrides['target.bootloader_img']) + def _generate_bootloader_build(self, rom_start, rom_size): + start = rom_start + rom_end = rom_start + rom_size + if self.target.bootloader_img: + if isabs(self.target.bootloader_img): + filename = self.target.bootloader_img + else: + basedir = abspath(dirname(self.app_config_location)) + filename = join(basedir, self.target.bootloader_img) if not exists(filename): raise ConfigException("Bootloader %s not found" % filename) part = intelhex_offset(filename, offset=rom_start) @@ -546,35 +711,81 @@ raise ConfigException("bootloader executable does not " "start at 0x%x" % rom_start) part_size = (part.maxaddr() - part.minaddr()) + 1 - yield Region("bootloader", rom_start + start, part_size, False, + part_size = Config._align_ceiling(rom_start + part_size, self.sectors) - rom_start + yield Region("bootloader", rom_start, part_size, False, filename) - start += part_size - if 'target.restrict_size' in target_overrides: - new_size = int(target_overrides['target.restrict_size'], 0) - yield Region("application", rom_start + start, new_size, True, None) + start = rom_start + part_size + if self.target.header_format: + if self.target.header_offset: + start = self._assign_new_offset( + rom_start, start, self.target.header_offset, "header") + start, region = self._make_header_region( + start, self.target.header_format) + yield region._replace(filename=self.target.header_format) + if self.target.restrict_size is not None: + new_size = int(self.target.restrict_size, 0) + new_size = Config._align_floor(start + new_size, self.sectors) - start + yield Region("application", start, new_size, True, None) start += new_size - yield Region("post_application", rom_start +start, rom_size - start, + if self.target.header_format and not self.target.bootloader_img: + if self.target.header_offset: + start = self._assign_new_offset( + rom_start, start, self.target.header_offset, "header") + start, region = self._make_header_region( + start, self.target.header_format) + yield region + if self.target.app_offset: + start = self._assign_new_offset( + rom_start, start, self.target.app_offset, "application") + yield Region("post_application", start, rom_end - start, False, None) else: - yield Region("application", rom_start + start, rom_size - start, + if self.target.app_offset: + start = self._assign_new_offset( + rom_start, start, self.target.app_offset, "application") + yield Region("application", start, rom_end - start, True, None) - if start > rom_size: + if start > rom_start + rom_size: raise ConfigException("Not enough memory on device to fit all " "application regions") + + @staticmethod + def _find_sector(address, sectors): + target_size = -1 + target_start = -1 + for (start, size) in sectors: + if address < start: + break + target_start = start + target_size = size + if (target_size < 0): + raise ConfigException("No valid sector found") + return target_start, target_size + + @staticmethod + def _align_floor(address, sectors): + target_start, target_size = Config._find_sector(address, sectors) + sector_num = (address - target_start) // target_size + return target_start + (sector_num * target_size) + + @staticmethod + def _align_ceiling(address, sectors): + target_start, target_size = Config._find_sector(address, sectors) + sector_num = ((address - target_start) + target_size - 1) // target_size + return target_start + (sector_num * target_size) @property def report(self): return {'app_config': self.app_config_location, 'library_configs': map(relpath, self.processed_configs.keys())} - @staticmethod - def _generate_linker_overrides(target_overrides, rom_start, rom_size): - if 'target.mbed_app_start' in target_overrides: - start = int(target_overrides['target.mbed_app_start'], 0) + def _generate_linker_overrides(self, rom_start, rom_size): + if self.target.mbed_app_start is not None: + start = int(self.target.mbed_app_start, 0) else: start = rom_start - if 'target.mbed_app_size' in target_overrides: - size = int(target_overrides['target.mbed_app_size'], 0) + if self.target.mbed_app_size is not None: + size = int(self.target.mbed_app_size, 0) else: size = (rom_size + rom_start) - start if start < rom_start: @@ -593,7 +804,6 @@ unit_name - the unit (library/application) that defines this parameter unit_kind - the kind of the unit ("library" or "application") """ - self.config_errors = [] _process_config_parameters(data.get("config", {}), params, unit_name, unit_kind) for label, overrides in data.get("target_overrides", {}).items(): @@ -603,7 +813,7 @@ # Check for invalid cumulative overrides in libraries if (unit_kind == 'library' and any(attr.startswith('target.extra_labels') for attr - in overrides.iterkeys())): + in overrides.keys())): raise ConfigException( "Target override 'target.extra_labels' in " + ConfigParameter.get_display_name(unit_name, unit_kind, @@ -611,7 +821,7 @@ " is only allowed at the application level") # Parse out cumulative overrides - for attr, cumulatives in self.cumulative_overrides.iteritems(): + for attr, cumulatives in self.cumulative_overrides.items(): if 'target.'+attr in overrides: key = 'target.' + attr if not isinstance(overrides[key], list): @@ -644,29 +854,28 @@ # Consider the others as overrides for name, val in overrides.items(): + if (name in PATH_OVERRIDES and "__config_path" in data): + val = os.path.join( + os.path.dirname(data["__config_path"]), val) + # Get the full name of the parameter full_name = ConfigParameter.get_full_name(name, unit_name, unit_kind, label) if full_name in params: params[full_name].set_value(val, unit_name, unit_kind, label) - elif name in self.__unused_overrides: - pass elif (name.startswith("target.") and - unit_kind is "application"): + (unit_kind is "application" or + name in BOOTLOADER_OVERRIDES)): _, attribute = name.split(".") setattr(self.target, attribute, val) + continue else: self.config_errors.append( - ConfigException( - "Attempt to override undefined parameter" + - (" '%s' in '%s'" - % (full_name, - ConfigParameter.get_display_name(unit_name, - unit_kind, - label))))) + UndefinedParameter( + full_name, unit_name, unit_kind, label)) - for cumulatives in self.cumulative_overrides.itervalues(): + for cumulatives in self.cumulative_overrides.values(): cumulatives.update_target(self.target) return params @@ -708,39 +917,29 @@ rel_names = [tgt for tgt, _ in get_resolution_order(self.target.json_data, tname, [])] - if full_name in self.__unused_overrides: + if full_name in BOOTLOADER_OVERRIDES: continue if (full_name not in params) or \ (params[full_name].defined_by[7:] not in rel_names): - raise ConfigException( - "Attempt to override undefined parameter '%s' in '%s'" - % (name, - ConfigParameter.get_display_name(tname, "target"))) + raise UndefinedParameter(name, tname, "target", "") # Otherwise update the value of the parameter params[full_name].set_value(val, tname, "target") return params - def get_lib_config_data(self): + def get_lib_config_data(self, target_data): """ Read and interpret configuration data defined by libraries. It is assumed that "add_config_files" above was already called and the library configuration data exists in self.lib_config_data Arguments: None """ - all_params, macros = {}, {} + macros = {} for lib_name, lib_data in self.lib_config_data.items(): - unknown_keys = (set(lib_data.keys()) - - set(self.__allowed_keys["library"].keys())) - if unknown_keys: - raise ConfigException("Unknown key(s) '%s' in %s" % - (",".join(unknown_keys), lib_name)) - check_dict_types(lib_data, self.__allowed_keys["library"], lib_name) - all_params.update(self._process_config_and_overrides(lib_data, {}, - lib_name, - "library")) + self._process_config_and_overrides( + lib_data, target_data, lib_name, "library") _process_macros(lib_data.get("macros", []), macros, lib_name, "library") - return all_params, macros + return target_data, macros def get_app_config_data(self, params, macros): """ Read and interpret the configuration data defined by the target. The @@ -770,10 +969,9 @@ Arguments: None """ all_params = self.get_target_config_data() - lib_params, macros = self.get_lib_config_data() - all_params.update(lib_params) - self.get_app_config_data(all_params, macros) - return all_params, macros + lib_params, macros = self.get_lib_config_data(all_params) + self.get_app_config_data(lib_params, macros) + return lib_params, macros @staticmethod def _check_required_parameters(params): @@ -854,8 +1052,13 @@ Arguments: None """ - if self.config_errors: - raise self.config_errors[0] + params, _ = self.get_config_data() + for error in self.config_errors: + if (isinstance(error, UndefinedParameter) and + error.param in params): + continue + else: + raise error return True @@ -875,25 +1078,27 @@ """ # Update configuration files until added features creates no changes prev_features = set() - self.validate_config() while True: # Add/update the configuration with any .json files found while # scanning - self.add_config_files(resources.json_files) + self.add_config_files( + f.path for f in resources.get_file_refs(FileType.JSON) + ) # Add features while we find new ones features = set(self.get_features()) if features == prev_features: break - for feature in features: - if feature in resources.features: - resources.add(resources.features[feature]) + resources.add_features(features) prev_features = features self.validate_config() - return resources + if (hasattr(self.target, "release_versions") and + "5" not in self.target.release_versions and + "rtos" in self.lib_config_data): + raise NotSupportedException("Target does not support mbed OS 5") @staticmethod def config_to_header(config, fname=None): @@ -914,10 +1119,14 @@ Config._check_required_parameters(params) params_with_values = [p for p in params.values() if p.value is not None] ctx = { - "cfg_params" : [(p.macro_name, str(p.value), p.set_by) - for p in params_with_values], - "macros": [(m.macro_name, str(m.macro_value or ""), m.defined_by) - for m in macros.values()], + "cfg_params": sorted([ + (p.macro_name, str(p.value), p.set_by) + for p in params_with_values + ]), + "macros": sorted([ + (m.macro_name, str(m.macro_value or ""), m.defined_by) + for m in macros.values() + ]), "name_len": max([len(m.macro_name) for m in macros.values()] + [len(m.macro_name) for m in params_with_values] + [0]),