summaryrefslogblamecommitdiffstats
path: root/rtemsspec/interfacedoc.py
blob: 81e1d3c612bcb42c503654b381c7e51ef30575be (plain) (tree)




























                                                                             
                                            
 

                                                                              
                 
                                                                            












                                                                            
                                                          

 



                                                                    
                              













                                                                           

































































































































                                                                               
# 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}"))


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)