Skip to content

Commit fd208e6

Browse files
author
Steven Cartmell
committed
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.
1 parent 10c8177 commit fd208e6

File tree

3 files changed

+243
-35
lines changed

3 files changed

+243
-35
lines changed

tools/config/__init__.py

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"""
1717

1818
from copy import deepcopy
19+
import json
1920
import os
2021
from os.path import dirname, abspath, exists, join, isabs
2122
import sys
@@ -24,6 +25,7 @@
2425
from intelhex import IntelHex
2526
from jinja2 import FileSystemLoader, StrictUndefined
2627
from jinja2.environment import Environment
28+
from jsonschema import validate, Draft4Validator
2729
# Implementation of mbed configuration mechanism
2830
from tools.utils import json_file_to_dict, intelhex_offset
2931
from tools.arm_pack_manager import Cache
@@ -341,12 +343,6 @@ def _process_macros(mlist, macros, unit_name, unit_kind):
341343
macros[macro.macro_name] = macro
342344

343345

344-
def check_dict_types(dict, type_dict, dict_loc):
345-
for key, value in dict.iteritems():
346-
if not isinstance(value, type_dict[key]):
347-
raise ConfigException("The value of %s.%s is not of type %s" %
348-
(dict_loc, key, type_dict[key].__name__))
349-
350346
Region = namedtuple("Region", "name start size active filename")
351347

352348
class Config(object):
@@ -357,17 +353,8 @@ class Config(object):
357353
__mbed_app_config_name = "mbed_app.json"
358354
__mbed_lib_config_name = "mbed_lib.json"
359355

360-
# Allowed keys in configuration dictionaries, and their types
361-
# (targets can have any kind of keys, so this validation is not applicable
362-
# to them)
363-
__allowed_keys = {
364-
"library": {"name": str, "config": dict, "target_overrides": dict,
365-
"macros": list, "__config_path": str},
366-
"application": {"config": dict, "target_overrides": dict,
367-
"macros": list, "__config_path": str,
368-
"artifact_name": str}
369-
}
370-
356+
__unused_overrides = set(["target.bootloader_img", "target.restrict_size",
357+
"target.mbed_app_start", "target.mbed_app_size"])
371358

372359
# Allowed features in configurations
373360
__allowed_features = [
@@ -420,15 +407,15 @@ def __init__(self, tgt, top_level_dirs=None, app_config=None):
420407
ConfigException("Could not parse mbed app configuration from %s"
421408
% self.app_config_location))
422409

423-
# Check the keys in the application configuration data
424-
unknown_keys = set(self.app_config_data.keys()) - \
425-
set(self.__allowed_keys["application"].keys())
426-
if unknown_keys:
427-
raise ConfigException("Unknown key(s) '%s' in %s" %
428-
(",".join(unknown_keys),
429-
self.__mbed_app_config_name))
430-
check_dict_types(self.app_config_data, self.__allowed_keys["application"],
431-
"app-config")
410+
# Validate the format of the JSON file based on the schema_app.json
411+
schema_path = os.path.join(os.path.dirname(__file__), "schema_app.json")
412+
validator = Draft4Validator(json_file_to_dict(schema_path))
413+
414+
errors = sorted(validator.iter_errors(self.app_config_data))
415+
416+
if errors:
417+
raise ConfigException(",".join(x.message for x in errors))
418+
432419
# Update the list of targets with the ones defined in the application
433420
# config, if applicable
434421
self.lib_config_data = {}
@@ -480,9 +467,16 @@ def add_config_files(self, flist):
480467

481468
cfg["__config_path"] = full_path
482469

483-
if "name" not in cfg:
484-
raise ConfigException(
485-
"Library configured at %s has no name field." % full_path)
470+
# Validate the format of the JSON file based on the schema_lib.json
471+
schema_path = os.path.join(os.path.dirname(__file__),
472+
"schema_lib.json")
473+
validator = Draft4Validator(json_file_to_dict(schema_path))
474+
475+
errors = sorted(validator.iter_errors(cfg))
476+
477+
if errors:
478+
raise ConfigException(",".join(x.message for x in errors))
479+
486480
# If there's already a configuration for a module with the same
487481
# name, exit with error
488482
if self.lib_config_data.has_key(cfg["name"]):
@@ -781,12 +775,6 @@ def get_lib_config_data(self):
781775
"""
782776
all_params, macros = {}, {}
783777
for lib_name, lib_data in self.lib_config_data.items():
784-
unknown_keys = (set(lib_data.keys()) -
785-
set(self.__allowed_keys["library"].keys()))
786-
if unknown_keys:
787-
raise ConfigException("Unknown key(s) '%s' in %s" %
788-
(",".join(unknown_keys), lib_name))
789-
check_dict_types(lib_data, self.__allowed_keys["library"], lib_name)
790778
all_params.update(self._process_config_and_overrides(lib_data, {},
791779
lib_name,
792780
"library"))

tools/config/schema_app.json

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-06/schema#",
3+
"title": "Mbed Library Schema",
4+
"description": "Configuration file for an mbed library",
5+
"type": "object",
6+
"$id": "http://example.com/root.json",
7+
"definitions": {
8+
"config_parameter_long": {
9+
"type": "object",
10+
"properties": {
11+
"help": {
12+
"description": "An optional help message that describes the purpose of the parameter",
13+
"type": "string"
14+
},
15+
"value": {
16+
"description": "An optional field that defines the value of the parameter",
17+
"type": [
18+
"integer",
19+
"string",
20+
"boolean"
21+
]
22+
},
23+
"required": {
24+
"description": "An optional field that specifies whether the parameter must be given a value before compiling the code. (False by default)",
25+
"type": "boolean"
26+
},
27+
"macro_name": {
28+
"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",
29+
"type": "string"
30+
}
31+
}
32+
},
33+
"config_parameter_short": {
34+
"type": [
35+
"string",
36+
"integer",
37+
"boolean"
38+
]
39+
},
40+
"config_parameter_base": {
41+
"oneOf": [
42+
{
43+
"$ref": "#/definitions/config_parameter_long"
44+
},
45+
{
46+
"$ref": "#/definitions/config_parameter_short"
47+
}
48+
]
49+
},
50+
"target_override_entry": {
51+
"type": "object",
52+
"patternProperties": {
53+
"^\\S+$": {}
54+
},
55+
"additionalProperties": false
56+
}
57+
},
58+
"properties": {
59+
"name": {
60+
"description": "Name of the library",
61+
"type": "string",
62+
"items": {
63+
"type": "string"
64+
}
65+
},
66+
"config": {
67+
"description": "List of configuration parameters",
68+
"type": "object",
69+
"patternProperties": {
70+
"^[^ ]+$": {
71+
"$ref": "#/definitions/config_parameter_base"
72+
}
73+
},
74+
"additionalProperties": false
75+
},
76+
"target_overrides": {
77+
"description": "List of overrides for specific targets",
78+
"type": "object",
79+
"patternProperties": {
80+
"\\*": {
81+
"type": "object",
82+
"patternProperties": {
83+
".*\\..*": {}
84+
},
85+
"additionalProperties": false
86+
},
87+
"^\\S+$": {
88+
"$ref": "#/definitions/target_override_entry"
89+
}
90+
},
91+
"additionalProperties": false
92+
},
93+
"macros": {
94+
"description": "A list of extra macros that will be defined when compiling a project that includes this library.",
95+
"type": "array",
96+
"items": {
97+
"type": "string",
98+
"pattern": "^([A-Za-z0-9_]+|[A-Za-z0-9_]+=[0-9]+|[A-Za-z0-9_]+=\\\".*\\\")$"
99+
}
100+
},
101+
"__config_path": {
102+
"description": "Path to configuration file",
103+
"type": "string"
104+
},
105+
"artifact_name": {
106+
"type": "string"
107+
}
108+
},
109+
"additionalProperties": false
110+
}

tools/config/schema_lib.json

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-06/schema#",
3+
"title": "Mbed Library Schema",
4+
"description": "Configuration file for an mbed library",
5+
"type": "object",
6+
"$id": "http://example.com/root.json",
7+
"definitions": {
8+
"config_parameter_long": {
9+
"type": "object",
10+
"properties": {
11+
"help": {
12+
"description": "An optional help message that describes the purpose of the parameter",
13+
"type": "string"
14+
},
15+
"value": {
16+
"description": "An optional field that defines the value of the parameter",
17+
"type": [
18+
"integer",
19+
"string",
20+
"boolean"
21+
]
22+
},
23+
"required": {
24+
"description": "An optional field that specifies whether the parameter must be given a value before compiling the code. (False by default)",
25+
"type": "boolean"
26+
},
27+
"macro_name": {
28+
"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",
29+
"type": "string"
30+
}
31+
}
32+
},
33+
"config_parameter_short": {
34+
"type": [
35+
"string",
36+
"integer",
37+
"boolean"
38+
]
39+
},
40+
"config_parameter_base": {
41+
"oneOf": [
42+
{
43+
"$ref": "#/definitions/config_parameter_long"
44+
},
45+
{
46+
"$ref": "#/definitions/config_parameter_short"
47+
}
48+
]
49+
},
50+
"target_override_entry": {
51+
"type": "object",
52+
"patternProperties": {
53+
"^\\S+$": {}
54+
},
55+
"additionalProperties": false
56+
}
57+
},
58+
"properties": {
59+
"name": {
60+
"description": "Name of the library",
61+
"type": "string",
62+
"items": {
63+
"type": "string"
64+
}
65+
},
66+
"config": {
67+
"description": "List of configuration parameters",
68+
"type": "object",
69+
"patternProperties": {
70+
"^[^ ]+$": {
71+
"$ref": "#/definitions/config_parameter_base"
72+
}
73+
},
74+
"additionalProperties": false
75+
},
76+
"target_overrides": {
77+
"description": "List of overrides for specific targets",
78+
"type": "object",
79+
"patternProperties": {
80+
"\\*": {
81+
"type": "object",
82+
"patternProperties": {
83+
".*\\..*": {}
84+
},
85+
"additionalProperties": false
86+
},
87+
"^\\S+$": {
88+
"$ref": "#/definitions/target_override_entry"
89+
}
90+
},
91+
"additionalProperties": false
92+
},
93+
"macros": {
94+
"description": "A list of extra macros that will be defined when compiling a project that includes this library.",
95+
"type": "array",
96+
"items": {
97+
"type": "string",
98+
"pattern": "^([A-Za-z0-9_]+|[A-Za-z0-9_]+=[0-9]+|[A-Za-z0-9_]+=\\\".*\\\")$"
99+
}
100+
},
101+
"__config_path": {
102+
"description": "Path to configuration file",
103+
"type": "string"
104+
}
105+
},
106+
"required": [
107+
"name"
108+
],
109+
"additionalProperties": false
110+
}

0 commit comments

Comments
 (0)