def _assemble_config_from_sources_and_lib_files( target_attributes: dict, mbed_lib_files: Iterable[Path], mbed_app_file: Optional[Path] = None) -> Config: previous_cumulative_data = None target_source = Source.from_target(target_attributes) current_cumulative_data = CumulativeData.from_sources([target_source]) while previous_cumulative_data != current_cumulative_data: current_labels = current_cumulative_data.labels | current_cumulative_data.extra_labels filtered_files = _filter_files(mbed_lib_files, current_labels, current_cumulative_data.features, current_cumulative_data.components) mbed_lib_sources = [ Source.from_mbed_lib(file, current_labels) for file in filtered_files ] all_sources = [target_source] + mbed_lib_sources if mbed_app_file: all_sources = all_sources + [ Source.from_mbed_app(mbed_app_file, current_labels) ] previous_cumulative_data = current_cumulative_data current_cumulative_data = CumulativeData.from_sources(all_sources) _update_target_attributes(target_attributes, current_cumulative_data) return Config.from_sources(all_sources)
def test_from_mbed_lib(self): with tempfile.TemporaryDirectory() as directory: data = { "name": "foo", "config": { "a-number": 123, "a-bool": { "help": "Simply a boolean", "value": True } }, "target_overrides": { "*": { "a-number": 456, "target.features_add": ["FOO"] }, "NOT_THIS_TARGET": { "a-string": "foo", "target.features_add": ["BAR"] }, "THIS_TARGET": { "a-bool": False, "other-lib.something-else": "blah" }, }, "macros": ["MACRO=1"], } file = pathlib.Path(directory, "mbed_lib.json") file.write_text(json.dumps(data)) subject = Source.from_mbed_lib(file, ["THIS_TARGET"]) self.assertEqual( subject, Source( human_name=f"File: {file}", config={ "foo.a-number": 123, "foo.a-bool": { "help": "Simply a boolean", "value": True } }, overrides={ "foo.a-number": 456, "foo.a-bool": False, "other-lib.something-else": "blah", "target.features_add": ["FOO"], }, macros=["MACRO=1"], ), )
def test_from_mbed_app(self): with tempfile.TemporaryDirectory() as directory: data = { "config": { "a-bool": False, "a-number": { "help": "Simply a number", "value": 0 } }, "target_overrides": { "*": { "a-bool": True, "target.features_add": ["HAT"] }, "NOT_THIS_TARGET": { "a-number": 999, "target.features_add": ["BOAT"] }, "THIS_TARGET": { "a-number": 2, "some-lib.something-else": "blah" }, }, "macros": ["SOME_MACRO=2"], } file = pathlib.Path(directory, "mbed_app.json") file.write_text(json.dumps(data)) subject = Source.from_mbed_app(file, ["THIS_TARGET"]) self.assertEqual( subject, Source( human_name=f"File: {file}", config={ "app.a-bool": False, "app.a-number": { "help": "Simply a number", "value": 0 } }, overrides={ "app.a-number": 2, "app.a-bool": True, "some-lib.something-else": "blah", "target.features_add": ["HAT"], }, macros=["SOME_MACRO=2"], ), )
def _assemble_config_from_sources_and_lib_files( target_source: Source, mbed_lib_files: Iterable[Path], mbed_app_file: Optional[Path] = None) -> Config: previous_cumulative_data = None current_cumulative_data = CumulativeData.from_sources([target_source]) while previous_cumulative_data != current_cumulative_data: filtered_files = _filter_files(mbed_lib_files, current_cumulative_data) mbed_lib_sources = [ Source.from_mbed_lib(file, current_cumulative_data.labels) for file in filtered_files ] all_sources = [target_source] + mbed_lib_sources if mbed_app_file: all_sources = all_sources + [ Source.from_mbed_app(mbed_app_file, current_cumulative_data.labels) ] previous_cumulative_data = current_cumulative_data current_cumulative_data = CumulativeData.from_sources(all_sources) return Config.from_sources(all_sources)
def test_from_target(self): # Warning: Target is a dataclass and dataclasses provide no type safety when mocking target = dict( features={"feature_1"}, components={"component_1"}, labels={"label_1"}, extra_labels={"label_2"}, config={ "foo": "bar", "target.bool": True }, macros=["MACRO_A"], c_lib="std", printf_lib="minimal-printf", ) subject = Source.from_target(target) self.assertEqual( subject, Source( human_name=f"mbed_target.Target for {target}", config={ "target.foo": "bar", "target.bool": True }, overrides={ "target.features": target["features"], "target.components": target["components"], "target.labels": target["labels"], "target.extra_labels": target["extra_labels"], "target.macros": target["macros"], "target.c_lib": target["c_lib"], "target.printf_lib": target["printf_lib"], }, macros=[], ), )
def assemble_config(mbed_target: str, mbed_program_directory: Path) -> Config: """Assemble Config for given target and program directory. The structure and configuration of MbedOS requires us to do multiple passes over configuration files, as each pass might affect which configuration files should be included in the final configuration. """ target_source = Source.from_target(mbed_target, mbed_program_directory) mbed_lib_files = find_files("mbed_lib.json", mbed_program_directory) mbed_program = MbedProgram.from_existing(mbed_program_directory) mbed_app_file = mbed_program.files.app_config_file return _assemble_config_from_sources_and_lib_files(target_source, mbed_lib_files, mbed_app_file)
def test_assembles_config_using_all_relevant_files(self): target = { "config": { "foo": None }, "macros": {}, "labels": set("A"), "extra_labels": set(), "features": set("RED"), "components": set(), "c_lib": "std", "printf_lib": "minimal-printf", } mbed_lib_files = [ { "path": Path("TARGET_A", "mbed_lib.json"), "json_contents": { "name": "a", "config": { "number": 123 }, "target_overrides": { "*": { "target.features_add": ["RED"] } }, }, }, { "path": Path("subdir", "FEATURE_RED", "mbed_lib.json"), "json_contents": { "name": "red", "config": { "bool": False }, "target_overrides": { "A": { "bool": True, "target.features_add": ["BLUE"], "target.components_add": ["LEG"] } }, "macros": ["RED_MACRO"], }, }, { "path": Path("COMPONENT_LEG", "mbed_lib.json"), "json_contents": { "name": "leg", "config": { "number-of-fingers": 5 }, "macros": ["LEG_MACRO"] }, }, ] unused_mbed_lib_file = { "path": Path("subdir", "FEATURE_BROWN", "mbed_lib.json"), "json_contents": { "name": "brown", "target_overrides": { "*": { "red.bool": "DON'T USE ME" } }, "macros": ["DONT_USE_THIS_MACRO"], }, } mbed_app_file = { "path": Path("mbed_app.json"), "json_contents": { "target_overrides": { "*": { "target.foo": "bar" } } }, } with TemporaryDirectory() as directory: created_mbed_lib_files = create_files(directory, mbed_lib_files) created_mbed_app_file = create_files(directory, [mbed_app_file])[0] create_files(directory, [unused_mbed_lib_file]) subject = _assemble_config_from_sources_and_lib_files( target, find_files("mbed_lib.json", Path(directory)), created_mbed_app_file) mbed_lib_sources = [ Source.from_mbed_lib(Path(directory, file), ["A"]) for file in created_mbed_lib_files ] mbed_app_source = Source.from_mbed_app(created_mbed_app_file, ["A"]) expected_config = Config.from_sources( [Source.from_target(target)] + mbed_lib_sources + [mbed_app_source]) self.assertEqual(subject, expected_config)