mirror of https://github.com/ARMmbed/mbed-os.git
Add JSON schema based validation to mbed config script
- Added app and lib JSON schema definition files which specify the valid keys and values that can be used in mbed library and application configuration files. The primary different between the app and lib schema is that the lib config requires a name key. - Modified the expected error code in some of the test cases. The error message is now issued by the JSON validator. - Added some validation code to the config script which checks the validity of the mbed_app.json file when it is initially loaded. - Added some validation code to config script which checks each of the mbed_lib.json scripts when they are loaded. - Removed manual checks for allowable config keys from within the mbed_app and mbed_lib json files. - Removed the check_dict_types() function which was no longer being called.pull/5022/head
parent
10c81775b4
commit
fd208e6751
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
from os.path import dirname, abspath, exists, join, isabs
|
from os.path import dirname, abspath, exists, join, isabs
|
||||||
import sys
|
import sys
|
||||||
|
@ -24,6 +25,7 @@ from os.path import splitext, relpath
|
||||||
from intelhex import IntelHex
|
from intelhex import IntelHex
|
||||||
from jinja2 import FileSystemLoader, StrictUndefined
|
from jinja2 import FileSystemLoader, StrictUndefined
|
||||||
from jinja2.environment import Environment
|
from jinja2.environment import Environment
|
||||||
|
from jsonschema import validate, Draft4Validator
|
||||||
# Implementation of mbed configuration mechanism
|
# Implementation of mbed configuration mechanism
|
||||||
from tools.utils import json_file_to_dict, intelhex_offset
|
from tools.utils import json_file_to_dict, intelhex_offset
|
||||||
from tools.arm_pack_manager import Cache
|
from tools.arm_pack_manager import Cache
|
||||||
|
@ -341,12 +343,6 @@ def _process_macros(mlist, macros, unit_name, unit_kind):
|
||||||
macros[macro.macro_name] = macro
|
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")
|
Region = namedtuple("Region", "name start size active filename")
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
|
@ -357,17 +353,8 @@ class Config(object):
|
||||||
__mbed_app_config_name = "mbed_app.json"
|
__mbed_app_config_name = "mbed_app.json"
|
||||||
__mbed_lib_config_name = "mbed_lib.json"
|
__mbed_lib_config_name = "mbed_lib.json"
|
||||||
|
|
||||||
# Allowed keys in configuration dictionaries, and their types
|
__unused_overrides = set(["target.bootloader_img", "target.restrict_size",
|
||||||
# (targets can have any kind of keys, so this validation is not applicable
|
"target.mbed_app_start", "target.mbed_app_size"])
|
||||||
# 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}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Allowed features in configurations
|
# Allowed features in configurations
|
||||||
__allowed_features = [
|
__allowed_features = [
|
||||||
|
@ -420,15 +407,15 @@ class Config(object):
|
||||||
ConfigException("Could not parse mbed app configuration from %s"
|
ConfigException("Could not parse mbed app configuration from %s"
|
||||||
% self.app_config_location))
|
% self.app_config_location))
|
||||||
|
|
||||||
# Check the keys in the application configuration data
|
# Validate the format of the JSON file based on the schema_app.json
|
||||||
unknown_keys = set(self.app_config_data.keys()) - \
|
schema_path = os.path.join(os.path.dirname(__file__), "schema_app.json")
|
||||||
set(self.__allowed_keys["application"].keys())
|
validator = Draft4Validator(json_file_to_dict(schema_path))
|
||||||
if unknown_keys:
|
|
||||||
raise ConfigException("Unknown key(s) '%s' in %s" %
|
errors = sorted(validator.iter_errors(self.app_config_data))
|
||||||
(",".join(unknown_keys),
|
|
||||||
self.__mbed_app_config_name))
|
if errors:
|
||||||
check_dict_types(self.app_config_data, self.__allowed_keys["application"],
|
raise ConfigException(",".join(x.message for x in errors))
|
||||||
"app-config")
|
|
||||||
# Update the list of targets with the ones defined in the application
|
# Update the list of targets with the ones defined in the application
|
||||||
# config, if applicable
|
# config, if applicable
|
||||||
self.lib_config_data = {}
|
self.lib_config_data = {}
|
||||||
|
@ -480,9 +467,16 @@ class Config(object):
|
||||||
|
|
||||||
cfg["__config_path"] = full_path
|
cfg["__config_path"] = full_path
|
||||||
|
|
||||||
if "name" not in cfg:
|
# Validate the format of the JSON file based on the schema_lib.json
|
||||||
raise ConfigException(
|
schema_path = os.path.join(os.path.dirname(__file__),
|
||||||
"Library configured at %s has no name field." % full_path)
|
"schema_lib.json")
|
||||||
|
validator = Draft4Validator(json_file_to_dict(schema_path))
|
||||||
|
|
||||||
|
errors = sorted(validator.iter_errors(cfg))
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise ConfigException(",".join(x.message for x in errors))
|
||||||
|
|
||||||
# If there's already a configuration for a module with the same
|
# If there's already a configuration for a module with the same
|
||||||
# name, exit with error
|
# name, exit with error
|
||||||
if self.lib_config_data.has_key(cfg["name"]):
|
if self.lib_config_data.has_key(cfg["name"]):
|
||||||
|
@ -781,12 +775,6 @@ class Config(object):
|
||||||
"""
|
"""
|
||||||
all_params, macros = {}, {}
|
all_params, macros = {}, {}
|
||||||
for lib_name, lib_data in self.lib_config_data.items():
|
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, {},
|
all_params.update(self._process_config_and_overrides(lib_data, {},
|
||||||
lib_name,
|
lib_name,
|
||||||
"library"))
|
"library"))
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-06/schema#",
|
||||||
|
"title": "Mbed Library Schema",
|
||||||
|
"description": "Configuration file for an mbed library",
|
||||||
|
"type": "object",
|
||||||
|
"$id": "http://example.com/root.json",
|
||||||
|
"definitions": {
|
||||||
|
"config_parameter_long": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"help": {
|
||||||
|
"description": "An optional help message that describes the purpose of the parameter",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"description": "An optional field that defines the value of the parameter",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"string",
|
||||||
|
"boolean"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"required": {
|
||||||
|
"description": "An optional field that specifies whether the parameter must be given a value before compiling the code. (False by default)",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"macro_name": {
|
||||||
|
"description": "An optional field for the macro defined at compile time for this configuration parameter. The system will automatically figure out the macro name from the configuration parameter, but this field will override it",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config_parameter_short": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"integer",
|
||||||
|
"boolean"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"config_parameter_base": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/config_parameter_long"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/config_parameter_short"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"target_override_entry": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^\\S+$": {}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Name of the library",
|
||||||
|
"type": "string",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"description": "List of configuration parameters",
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[^ ]+$": {
|
||||||
|
"$ref": "#/definitions/config_parameter_base"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"target_overrides": {
|
||||||
|
"description": "List of overrides for specific targets",
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"\\*": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
".*\\..*": {}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"^\\S+$": {
|
||||||
|
"$ref": "#/definitions/target_override_entry"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"macros": {
|
||||||
|
"description": "A list of extra macros that will be defined when compiling a project that includes this library.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^([A-Za-z0-9_]+|[A-Za-z0-9_]+=[0-9]+|[A-Za-z0-9_]+=\\\".*\\\")$"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"__config_path": {
|
||||||
|
"description": "Path to configuration file",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"artifact_name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-06/schema#",
|
||||||
|
"title": "Mbed Library Schema",
|
||||||
|
"description": "Configuration file for an mbed library",
|
||||||
|
"type": "object",
|
||||||
|
"$id": "http://example.com/root.json",
|
||||||
|
"definitions": {
|
||||||
|
"config_parameter_long": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"help": {
|
||||||
|
"description": "An optional help message that describes the purpose of the parameter",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"description": "An optional field that defines the value of the parameter",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"string",
|
||||||
|
"boolean"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"required": {
|
||||||
|
"description": "An optional field that specifies whether the parameter must be given a value before compiling the code. (False by default)",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"macro_name": {
|
||||||
|
"description": "An optional field for the macro defined at compile time for this configuration parameter. The system will automatically figure out the macro name from the configuration parameter, but this field will override it",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config_parameter_short": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"integer",
|
||||||
|
"boolean"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"config_parameter_base": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/config_parameter_long"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/config_parameter_short"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"target_override_entry": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^\\S+$": {}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Name of the library",
|
||||||
|
"type": "string",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"description": "List of configuration parameters",
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[^ ]+$": {
|
||||||
|
"$ref": "#/definitions/config_parameter_base"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"target_overrides": {
|
||||||
|
"description": "List of overrides for specific targets",
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"\\*": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
".*\\..*": {}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"^\\S+$": {
|
||||||
|
"$ref": "#/definitions/target_override_entry"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"macros": {
|
||||||
|
"description": "A list of extra macros that will be defined when compiling a project that includes this library.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^([A-Za-z0-9_]+|[A-Za-z0-9_]+=[0-9]+|[A-Za-z0-9_]+=\\\".*\\\")$"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"__config_path": {
|
||||||
|
"description": "Path to configuration file",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
Loading…
Reference in New Issue