Commit 513bebc4 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'doc-netlink-add-hyperlinks-to-generated-docs'

Donald Hunter says:

====================
doc: netlink: Add hyperlinks to generated docs

Extend ynl-gen-rst to generate hyperlinks to definitions, attribute sets
and sub-messages from all the places that reference them.
====================

Link: https://lore.kernel.org/r/20240329135021.52534-1-donald.hunter@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 3b4cf29b 2dddf8aa
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
@@ -1099,6 +1099,19 @@ definitions:
      -
        name: offmask
        type: s32
  -
    name: tc-u32-mark
    type: struct
    members:
      -
        name: val
        type: u32
      -
        name: mask
        type: u32
      -
        name: success
        type: u32
  -
    name: tc-u32-sel
    type: struct
@@ -1774,6 +1787,44 @@ attribute-sets:
      -
        name: key-ex
        type: binary
  -
    name: tc-act-police-attrs
    attributes:
      -
        name: tbf
        type: binary
        struct: tc-police
      -
        name: rate
        type: binary # TODO
      -
        name: peakrate
        type: binary # TODO
      -
        name: avrate
        type: u32
      -
        name: result
        type: u32
      -
        name: tm
        type: binary
        struct: tcf-t
      -
        name: pad
        type: pad
      -
        name: rate64
        type: u64
      -
        name: peakrate64
        type: u64
      -
        name: pktrate64
        type: u64
      -
        name: pktburst64
        type: u64
  -
    name: tc-act-simple-attrs
    attributes:
+43 −19
Original line number Diff line number Diff line
@@ -82,9 +82,9 @@ def rst_subsubsection(title: str) -> str:
    return f"{title}\n" + "~" * len(title)


def rst_section(title: str) -> str:
def rst_section(namespace: str, prefix: str, title: str) -> str:
    """Add a section to the document"""
    return f"\n{title}\n" + "=" * len(title)
    return f".. _{namespace}-{prefix}-{title}:\n\n{title}\n" + "=" * len(title)


def rst_subtitle(title: str) -> str:
@@ -102,6 +102,17 @@ def rst_list_inline(list_: List[str], level: int = 0) -> str:
    return headroom(level) + "[" + ", ".join(inline(i) for i in list_) + "]"


def rst_ref(namespace: str, prefix: str, name: str) -> str:
    """Add a hyperlink to the document"""
    mappings = {'enum': 'definition',
                'fixed-header': 'definition',
                'nested-attributes': 'attribute-set',
                'struct': 'definition'}
    if prefix in mappings:
        prefix = mappings[prefix]
    return f":ref:`{namespace}-{prefix}-{name}`"


def rst_header() -> str:
    """The headers for all the auto generated RST files"""
    lines = []
@@ -159,20 +170,24 @@ def parse_do_attributes(attrs: Dict[str, Any], level: int = 0) -> str:
    return "\n".join(lines)


def parse_operations(operations: List[Dict[str, Any]]) -> str:
def parse_operations(operations: List[Dict[str, Any]], namespace: str) -> str:
    """Parse operations block"""
    preprocessed = ["name", "doc", "title", "do", "dump"]
    linkable = ["fixed-header", "attribute-set"]
    lines = []

    for operation in operations:
        lines.append(rst_section(operation["name"]))
        lines.append(rst_section(namespace, 'operation', operation["name"]))
        lines.append(rst_paragraph(sanitize(operation["doc"])) + "\n")

        for key in operation.keys():
            if key in preprocessed:
                # Skip the special fields
                continue
            lines.append(rst_fields(key, operation[key], 0))
            value = operation[key]
            if key in linkable:
                value = rst_ref(namespace, key, value)
            lines.append(rst_fields(key, value, 0))

        if "do" in operation:
            lines.append(rst_paragraph(":do:", 0))
@@ -212,14 +227,14 @@ def parse_entries(entries: List[Dict[str, Any]], level: int) -> str:
    return "\n".join(lines)


def parse_definitions(defs: Dict[str, Any]) -> str:
def parse_definitions(defs: Dict[str, Any], namespace: str) -> str:
    """Parse definitions section"""
    preprocessed = ["name", "entries", "members"]
    ignored = ["render-max"]  # This is not printed
    lines = []

    for definition in defs:
        lines.append(rst_section(definition["name"]))
        lines.append(rst_section(namespace, 'definition', definition["name"]))
        for k in definition.keys():
            if k in preprocessed + ignored:
                continue
@@ -237,14 +252,15 @@ def parse_definitions(defs: Dict[str, Any]) -> str:
    return "\n".join(lines)


def parse_attr_sets(entries: List[Dict[str, Any]]) -> str:
def parse_attr_sets(entries: List[Dict[str, Any]], namespace: str) -> str:
    """Parse attribute from attribute-set"""
    preprocessed = ["name", "type"]
    linkable = ["enum", "nested-attributes", "struct", "sub-message"]
    ignored = ["checks"]
    lines = []

    for entry in entries:
        lines.append(rst_section(entry["name"]))
        lines.append(rst_section(namespace, 'attribute-set', entry["name"]))
        for attr in entry["attributes"]:
            type_ = attr.get("type")
            attr_line = attr["name"]
@@ -257,25 +273,31 @@ def parse_attr_sets(entries: List[Dict[str, Any]]) -> str:
            for k in attr.keys():
                if k in preprocessed + ignored:
                    continue
                lines.append(rst_fields(k, sanitize(attr[k]), 0))
                if k in linkable:
                    value = rst_ref(namespace, k, attr[k])
                else:
                    value = sanitize(attr[k])
                lines.append(rst_fields(k, value, 0))
            lines.append("\n")

    return "\n".join(lines)


def parse_sub_messages(entries: List[Dict[str, Any]]) -> str:
def parse_sub_messages(entries: List[Dict[str, Any]], namespace: str) -> str:
    """Parse sub-message definitions"""
    lines = []

    for entry in entries:
        lines.append(rst_section(entry["name"]))
        lines.append(rst_section(namespace, 'sub-message', entry["name"]))
        for fmt in entry["formats"]:
            value = fmt["value"]

            lines.append(rst_bullet(bold(value)))
            for attr in ['fixed-header', 'attribute-set']:
                if attr in fmt:
                    lines.append(rst_fields(attr, fmt[attr], 1))
                    lines.append(rst_fields(attr,
                                            rst_ref(namespace, attr, fmt[attr]),
                                            1))
            lines.append("\n")

    return "\n".join(lines)
@@ -289,9 +311,11 @@ def parse_yaml(obj: Dict[str, Any]) -> str:

    lines.append(rst_header())

    title = f"Family ``{obj['name']}`` netlink specification"
    family = obj['name']

    title = f"Family ``{family}`` netlink specification"
    lines.append(rst_title(title))
    lines.append(rst_paragraph(".. contents::\n"))
    lines.append(rst_paragraph(".. contents:: :depth: 3\n"))

    if "doc" in obj:
        lines.append(rst_subtitle("Summary"))
@@ -300,7 +324,7 @@ def parse_yaml(obj: Dict[str, Any]) -> str:
    # Operations
    if "operations" in obj:
        lines.append(rst_subtitle("Operations"))
        lines.append(parse_operations(obj["operations"]["list"]))
        lines.append(parse_operations(obj["operations"]["list"], family))

    # Multicast groups
    if "mcast-groups" in obj:
@@ -310,17 +334,17 @@ def parse_yaml(obj: Dict[str, Any]) -> str:
    # Definitions
    if "definitions" in obj:
        lines.append(rst_subtitle("Definitions"))
        lines.append(parse_definitions(obj["definitions"]))
        lines.append(parse_definitions(obj["definitions"], family))

    # Attributes set
    if "attribute-sets" in obj:
        lines.append(rst_subtitle("Attribute sets"))
        lines.append(parse_attr_sets(obj["attribute-sets"]))
        lines.append(parse_attr_sets(obj["attribute-sets"], family))

    # Sub-messages
    if "sub-messages" in obj:
        lines.append(rst_subtitle("Sub-messages"))
        lines.append(parse_sub_messages(obj["sub-messages"]))
        lines.append(parse_sub_messages(obj["sub-messages"], family))

    return "\n".join(lines)