From acbac0174de3b8f18cdc549fa92cdf5957775086 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 5 May 2023 14:41:18 +0200 Subject: items: Add proxy item type --- config.yml | 3 ++- rtemsspec/interface.py | 5 ++-- rtemsspec/items.py | 43 ++++++++++++++++++++++++++++++ rtemsspec/tests/spec-item-cache/d/c.yml | 3 +++ rtemsspec/tests/spec-item-cache/p.yml | 5 +++- rtemsspec/tests/spec-item-cache/proxy.yml | 3 +++ rtemsspec/tests/spec-item-cache/proxy2.yml | 5 ++++ rtemsspec/tests/spec-item-cache/q.yml | 6 +++++ rtemsspec/tests/spec-item-cache/r.yml | 3 +++ rtemsspec/tests/spec-item-cache/s.yml | 6 +++++ rtemsspec/tests/spec/proxy.yml | 28 +++++++++++++++++++ rtemsspec/tests/test_items_itemcache.py | 20 +++++++++++--- spec-spec/spec/proxy-member.yml | 23 ++++++++++++++++ spec-spec/spec/proxy.yml | 28 +++++++++++++++++++ spec2modules.py | 4 +-- 15 files changed, 174 insertions(+), 11 deletions(-) create mode 100644 rtemsspec/tests/spec-item-cache/proxy.yml create mode 100644 rtemsspec/tests/spec-item-cache/proxy2.yml create mode 100644 rtemsspec/tests/spec-item-cache/q.yml create mode 100644 rtemsspec/tests/spec-item-cache/r.yml create mode 100644 rtemsspec/tests/spec-item-cache/s.yml create mode 100644 rtemsspec/tests/spec/proxy.yml create mode 100644 spec-spec/spec/proxy-member.yml create mode 100644 spec-spec/spec/proxy.yml diff --git a/config.yml b/config.yml index 16105bf1..1b96b7e7 100644 --- a/config.yml +++ b/config.yml @@ -60,12 +60,13 @@ build: workspace-directory: workspace/rtems spec: cache-directory: cache - spec-type-root-uid: /spec/root paths: - spec-spec - spec - spec-glossary - modules/rtems/spec + resolve-proxies: true + spec-type-root-uid: /spec/root glossary: project-groups: - /glossary-general diff --git a/rtemsspec/interface.py b/rtemsspec/interface.py index 85a6781a..d31bcb0e 100644 --- a/rtemsspec/interface.py +++ b/rtemsspec/interface.py @@ -871,6 +871,5 @@ def generate(config: dict, item_cache: ItemCache) -> None: enabled = config["enabled"] enabled_by_defined = _gather_enabled_by_defined( config["item-level-interfaces"], item_cache) - for item in item_cache.all.values(): - if item.type == "interface/header-file": - _generate_header_file(item, domains, enabled_by_defined, enabled) + for item in item_cache.items_by_type.get("interface/header-file", []): + _generate_header_file(item, domains, enabled_by_defined, enabled) diff --git a/rtemsspec/items.py b/rtemsspec/items.py index d9cf8801..1adbcdc7 100644 --- a/rtemsspec/items.py +++ b/rtemsspec/items.py @@ -203,6 +203,7 @@ class Item: self._data = data self._links_to_parents: List[Link] = [] self._links_to_children: List[Link] = [] + self._resolved_proxy = False def __eq__(self, other: Any) -> bool: if not isinstance(other, Item): @@ -420,6 +421,11 @@ class Item: """ Returns true if the item is enabled, otherwise returns false. """ return self._data["_enabled"] + @property + def resolved_proxy(self) -> bool: + """ Is true if the item is a resolved proxy, otherwise false. """ + return self._resolved_proxy + @property def data(self) -> Any: """ The item data. """ @@ -707,6 +713,33 @@ def item_is_enabled(_enabled: List[str], _item: Item) -> bool: return True +def _resolve_proxy(proxy: Item, is_link_enabled: Callable[[Link], + bool]) -> None: + + # pylint: disable=protected-access + try: + member = proxy.child("proxy-member", is_link_enabled=is_link_enabled) + except IndexError: + pass + else: + member._links_to_parents.extend(proxy._links_to_parents) + member._links_to_children.extend(proxy._links_to_children) + proxy._data = member._data + proxy._ident = member._ident + proxy._resolved_proxy = True + proxy._uid = member._uid + for link in proxy._links_to_parents: + for link_2 in link.item._links_to_children: + if link_2.item == proxy: + link_2._item = member + for link in proxy._links_to_children: + for link_2 in link.item._links_to_parents: + if link_2.item == proxy: + link_2._item = member + proxy._links_to_children = member._links_to_children + proxy._links_to_parents = member._links_to_parents + + class ItemCache: """ This class provides a cache of specification items. """ @@ -734,6 +767,8 @@ class ItemCache: for item in self._items.values(): self._set_type(item) item["_enabled"] = is_item_enabled(self._enabled, item) + if config.get("resolve-proxies", False): + self.resolve_proxies() def __getitem__(self, uid: str) -> Item: return self._items[uid] @@ -774,6 +809,14 @@ class ItemCache: for item in self._items.values(): item["_enabled"] = is_item_enabled(enabled, item) + def resolve_proxies( + self, + is_link_enabled: Callable[[Link], + bool] = _is_link_enabled) -> None: + """ Resolves each proxy item to the its first enabled member. """ + for item in self.items_by_type.get("proxy", []): + _resolve_proxy(item, is_link_enabled) + def add_volatile_item(self, uid: str, data: Any) -> Item: """ Adds an item with the specified data to the cache and returns it. diff --git a/rtemsspec/tests/spec-item-cache/d/c.yml b/rtemsspec/tests/spec-item-cache/d/c.yml index b31a8978..786b66cd 100644 --- a/rtemsspec/tests/spec-item-cache/d/c.yml +++ b/rtemsspec/tests/spec-item-cache/d/c.yml @@ -12,5 +12,8 @@ enabled-by: true links: - role: null uid: ../p +- role: null + uid: ../r +type: other v: c r6: ${../p:/r7} diff --git a/rtemsspec/tests/spec-item-cache/p.yml b/rtemsspec/tests/spec-item-cache/p.yml index d2d69d33..ca1545e7 100644 --- a/rtemsspec/tests/spec-item-cache/p.yml +++ b/rtemsspec/tests/spec-item-cache/p.yml @@ -1,6 +1,9 @@ enabled-by: not: foobar -links: [] +links: +- role: proxy-member + uid: proxy +type: other v: p x: y: z diff --git a/rtemsspec/tests/spec-item-cache/proxy.yml b/rtemsspec/tests/spec-item-cache/proxy.yml new file mode 100644 index 00000000..5c068b28 --- /dev/null +++ b/rtemsspec/tests/spec-item-cache/proxy.yml @@ -0,0 +1,3 @@ +enabled-by: true +links: [] +type: proxy diff --git a/rtemsspec/tests/spec-item-cache/proxy2.yml b/rtemsspec/tests/spec-item-cache/proxy2.yml new file mode 100644 index 00000000..7507739d --- /dev/null +++ b/rtemsspec/tests/spec-item-cache/proxy2.yml @@ -0,0 +1,5 @@ +enabled-by: true +links: +- role: xyz + uid: r +type: proxy diff --git a/rtemsspec/tests/spec-item-cache/q.yml b/rtemsspec/tests/spec-item-cache/q.yml new file mode 100644 index 00000000..c33c3a6b --- /dev/null +++ b/rtemsspec/tests/spec-item-cache/q.yml @@ -0,0 +1,6 @@ +enabled-by: blub +links: +- role: proxy-member + uid: proxy +type: other +v: q diff --git a/rtemsspec/tests/spec-item-cache/r.yml b/rtemsspec/tests/spec-item-cache/r.yml new file mode 100644 index 00000000..2678ce82 --- /dev/null +++ b/rtemsspec/tests/spec-item-cache/r.yml @@ -0,0 +1,3 @@ +enabled-by: true +links: [] +type: other diff --git a/rtemsspec/tests/spec-item-cache/s.yml b/rtemsspec/tests/spec-item-cache/s.yml new file mode 100644 index 00000000..cc19b614 --- /dev/null +++ b/rtemsspec/tests/spec-item-cache/s.yml @@ -0,0 +1,6 @@ +enabled-by: foobar +links: +- role: proxy-member + uid: proxy2 +type: other +v: s diff --git a/rtemsspec/tests/spec/proxy.yml b/rtemsspec/tests/spec/proxy.yml new file mode 100644 index 00000000..f5cbbfb3 --- /dev/null +++ b/rtemsspec/tests/spec/proxy.yml @@ -0,0 +1,28 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2023 embedded brains GmbH (http://www.embedded-brains.de) +enabled-by: true +links: +- role: spec-member + uid: root +- role: spec-refinement + spec-key: type + spec-value: proxy + uid: root +spec-description: null +spec-example: null +spec-info: + dict: + attributes: {} + description: | + Items of similar characteristics may link to a proxy item through links + with the ${proxy-member:/spec-name}. A proxy item resolves to the first + member item which is enabled. Proxies may be used to provide an + interface with a common name and implementations which depend on + configuration options. For example, in one configuration a constant + could be a compile time constant and in another configuration it could be + a read-only object. + mandatory-attributes: all +spec-name: Proxy Item Types +spec-type: proxy +type: spec diff --git a/rtemsspec/tests/test_items_itemcache.py b/rtemsspec/tests/test_items_itemcache.py index 07cec0bf..ff75db12 100644 --- a/rtemsspec/tests/test_items_itemcache.py +++ b/rtemsspec/tests/test_items_itemcache.py @@ -64,7 +64,7 @@ def test_load(tmpdir): assert p.map("/p") == p assert p.map("p") == p a = item_cache.all - assert len(a) == 2 + assert len(a) == 7 assert a["/p"]["v"] == "p" assert a["/d/c"]["v"] == "c" item_cache.set_enabled([]) @@ -125,7 +125,11 @@ def get_value_dict(ctx): def test_item_mapper(tmpdir): - config = create_item_cache_config_and_copy_spec(tmpdir, "spec-item-cache") + config = create_item_cache_config_and_copy_spec(tmpdir, + "spec-item-cache", + with_spec_types=True) + config["enabled"] = ["foobar"] + config["resolve-proxies"] = True item_cache = ItemCache(config) item = item_cache["/p"] base_mapper = ItemMapper(item) @@ -146,10 +150,18 @@ def test_item_mapper(tmpdir): with mapper.prefix("y"): assert mapper[".:."] == "z" assert mapper["."] == "/p" + match = r"cannot get value for '/v' of spec:/proxy specified by 'proxy:/v" + with pytest.raises(ValueError, match=match): + mapper["proxy:/v"] + assert not item_cache["/proxy"].resolved_proxy + assert item_cache["/proxy2"].resolved_proxy + assert not item_cache["/r"].resolved_proxy + assert mapper["proxy2:/v"] == "s" + assert item_cache["/r"].child("xyz").uid == "/s" assert mapper["d/c"] == "/d/c" assert mapper["d/c:v"] == "c" assert mapper["d/c:a/b"] == "e" - mapper.add_get_value(":/a/x-to-b", get_x_to_b_value) + mapper.add_get_value("other:/a/x-to-b", get_x_to_b_value) assert mapper["d/c:a/x-to-b:args:0:%"] == "eargs:0:%" assert mapper["d/c:a/f[1]"] == 2 assert mapper["d/c:a/../a/f[3]/g[0]"] == 4 @@ -157,7 +169,7 @@ def test_item_mapper(tmpdir): assert item_3 == item assert key_path_3 == "/v" assert value_3 == "p" - mapper.add_get_value_dictionary(":/dict", get_value_dict) + mapper.add_get_value_dictionary("other:/dict", get_value_dict) assert mapper["d/c:/dict/some-arbitrary-key"] == "some-arbitrary-key" recursive_mapper = ItemMapper(item, recursive=True) assert recursive_mapper.substitute("${.:/r1/r2/r3}") == "foobar" diff --git a/spec-spec/spec/proxy-member.yml b/spec-spec/spec/proxy-member.yml new file mode 100644 index 00000000..5265c07c --- /dev/null +++ b/spec-spec/spec/proxy-member.yml @@ -0,0 +1,23 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2023 embedded brains GmbH (http://www.embedded-brains.de) +enabled-by: true +links: +- role: spec-member + uid: root +- role: spec-refinement + spec-key: role + spec-value: proxy-member + uid: link +spec-description: null +spec-example: null +spec-info: + dict: + attributes: {} + description: | + It defines the proxy member role of links. Items may use this role to + link to ${proxy:/spec-name} items. + mandatory-attributes: all +spec-name: Proxy Member Link Role +spec-type: proxy-member +type: spec diff --git a/spec-spec/spec/proxy.yml b/spec-spec/spec/proxy.yml new file mode 100644 index 00000000..f5cbbfb3 --- /dev/null +++ b/spec-spec/spec/proxy.yml @@ -0,0 +1,28 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2023 embedded brains GmbH (http://www.embedded-brains.de) +enabled-by: true +links: +- role: spec-member + uid: root +- role: spec-refinement + spec-key: type + spec-value: proxy + uid: root +spec-description: null +spec-example: null +spec-info: + dict: + attributes: {} + description: | + Items of similar characteristics may link to a proxy item through links + with the ${proxy-member:/spec-name}. A proxy item resolves to the first + member item which is enabled. Proxies may be used to provide an + interface with a common name and implementations which depend on + configuration options. For example, in one configuration a constant + could be a compile time constant and in another configuration it could be + a read-only object. + mandatory-attributes: all +spec-name: Proxy Item Types +spec-type: proxy +type: spec diff --git a/spec2modules.py b/spec2modules.py index 16df8cf8..8c86d396 100755 --- a/spec2modules.py +++ b/spec2modules.py @@ -68,8 +68,8 @@ def main() -> None: if args.diff: rtemsspec.content.Content.write = _diff # type: ignore config = rtemsspec.util.load_config("config.yml") - item_cache = rtemsspec.items.ItemCache( - config["spec"], is_item_enabled=rtemsspec.items.item_is_enabled) + item_cache = rtemsspec.items.ItemCache(config["spec"]) + item_cache.set_enabled([], rtemsspec.items.item_is_enabled) rtemsspec.validation.generate(config["validation"], item_cache, args.targets) -- cgit v1.2.3