# SPDX-License-Identifier: BSD-2-Clause """ This module provides functions for the generation of interface documentation. """ # Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import os from typing import Any, Callable, Dict, List from rtemsspec.content import CContent, enabled_by_to_exp, ExpressionMapper from rtemsspec.sphinxcontent import get_label, get_reference, SphinxContent, \ SphinxMapper from rtemsspec.items import Item, ItemCache, ItemGetValueContext, ItemMapper ItemMap = Dict[str, Item] AddDefinition = Callable[[CContent, ItemMapper, Item, Dict[str, Any]], None] INTERFACE = "Interface" def _forward_declaration(item: Item) -> str: target = next(item.parents("interface-target")) return f"{target['interface-type']} {target['name']}" def _get_reference(name: str) -> str: return get_reference(get_label(f"{INTERFACE}{name}"), f"{name}()") def _get_value_forward_declaration(ctx: ItemGetValueContext) -> Any: return _forward_declaration(ctx.item) class _CodeMapper(ItemMapper): def __init__(self, item: Item): super().__init__(item) self.add_get_value("interface/forward-declaration:/name", _get_value_forward_declaration) def _get_value_function(ctx: ItemGetValueContext) -> Any: return _get_reference(ctx.value[ctx.key]) class _Mapper(SphinxMapper): def __init__(self, item: Item): super().__init__(item) self.add_get_value("interface/function:/name", _get_value_function) def _generate_introduction(target: str, group: Item, items: List[Item]) -> None: content = SphinxContent() content.register_license_and_copyrights_of_item(group) group_name = group["name"] with content.section("Introduction", get_label(group_name)): content.append("") content.gap = False content.wrap(group["brief"]) content.wrap(group["description"]) content.paste(f"The directives provided by the {group_name} are:") for item in items: content.register_license_and_copyrights_of_item(item) name = item["name"] brief = item["brief"] if brief: brief = f" - {brief}" else: brief = "" ref = _get_reference(name) content.add_list_item(f"{ref}{brief}") content.add_licence_and_copyrights() content.write(target) def _add_function_definition(content: CContent, mapper: ItemMapper, item: Item, value: Dict[str, Any]) -> None: name = item["name"] ret = mapper.substitute(value["return"]) params = [mapper.substitute(param) for param in value["params"]] content.declare_function(ret, name, params) def _add_definition(content: CContent, mapper: ItemMapper, item: Item, prefix: str, value: Dict[str, Any], add_definition: AddDefinition) -> None: # pylint: disable=too-many-arguments assert item["interface-type"] == "function" default = value["default"] variants = value["variants"] if variants: ifelse = "#if " with mapper.prefix(os.path.join(prefix, "variants")): for variant in variants: enabled_by = enabled_by_to_exp(variant["enabled-by"], ExpressionMapper()) content.append(f"{ifelse}{enabled_by}") with content.indent(): add_definition(content, mapper, item, variant["definition"]) ifelse = "#elif " if default is not None: content.append("#else") with mapper.prefix(os.path.join(prefix, "default")): with content.indent(): add_definition(content, mapper, item, default) content.append("#endif") else: with mapper.prefix(os.path.join(prefix, "default")): add_definition(content, mapper, item, default) def _generate_directives(target: str, group: Item, items: List[Item]) -> None: content = SphinxContent() content.register_license_and_copyrights_of_item(group) group_name = group["name"] with content.section("Directives", get_label(group_name)): for item in items: content.register_license_and_copyrights_of_item(item) name = item["name"] code_mapper = _CodeMapper(item) mapper = _Mapper(item) with content.section(f"{name}()", "Interface"): content.wrap(item["brief"]) with content.definition_item("CALLING SEQUENCE:"): with content.directive("code-block", "c"): code = CContent() _add_definition(code, code_mapper, item, "definition", item["definition"], _add_function_definition) content.add(code) if item["params"]: with content.definition_item("DIRECTIVE PARAMETERS:"): for param in item["params"]: content.add_definition_item( mapper.substitute(param["name"]), mapper.substitute( f"This parameter {param['description']}"), wrap=True) ret = item["return"] if ret["return"] or ret["return-values"]: with content.definition_item("DIRECTIVE RETURN VALUES:"): if ret["return-values"]: for retval in ret["return-values"]: content.add_definition_item( mapper.substitute(str(retval["value"])), mapper.substitute(retval["description"]), wrap=True) content.wrap(mapper.substitute(ret["return"])) content.add_definition_item("DESCRIPTION:", mapper.substitute( item["description"]), wrap=True) content.add_definition_item("NOTES:", mapper.substitute(item["notes"]), wrap=True) content.add_licence_and_copyrights() content.write(target) def generate(config: list, item_cache: ItemCache) -> None: """ Generates interface documentation according to the configuration. :param config: A dictionary with configuration entries. :param item_cache: The specification item cache containing the interfaces. """ for doc_config in config: items = [] # type: List[Item] group = item_cache[doc_config["group"]] assert group["type"] == "interface" assert group["interface-type"] == "group" for child in group.children("interface-ingroup"): if child["interface-type"] in ["function"]: items.append(child) items.sort(key=lambda x: x["name"]) _generate_introduction(doc_config["introduction-target"], group, items) _generate_directives(doc_config["directives-target"], group, items)