Commit 8c1b74a2 authored by Donald Hunter's avatar Donald Hunter Committed by Jakub Kicinski

doc: netlink: Add hyperlinks to generated Netlink docs

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

Note that there is a single label namespace for all of the kernel docs.
Hyperlinks within a single netlink doc need to be qualified by the
family name to avoid collisions.

The label format is 'family-type-name' which gives, for example,
'rt-link-attribute-set-link-attrs' as the link id.
Signed-off-by: default avatarDonald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240329135021.52534-3-donald.hunter@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 4cc1730a
...@@ -82,9 +82,9 @@ def rst_subsubsection(title: str) -> str: ...@@ -82,9 +82,9 @@ def rst_subsubsection(title: str) -> str:
return f"{title}\n" + "~" * len(title) 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""" """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: def rst_subtitle(title: str) -> str:
...@@ -102,6 +102,17 @@ def rst_list_inline(list_: List[str], level: int = 0) -> 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_) + "]" 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: def rst_header() -> str:
"""The headers for all the auto generated RST files""" """The headers for all the auto generated RST files"""
lines = [] lines = []
...@@ -159,20 +170,24 @@ def parse_do_attributes(attrs: Dict[str, Any], level: int = 0) -> str: ...@@ -159,20 +170,24 @@ def parse_do_attributes(attrs: Dict[str, Any], level: int = 0) -> str:
return "\n".join(lines) 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""" """Parse operations block"""
preprocessed = ["name", "doc", "title", "do", "dump"] preprocessed = ["name", "doc", "title", "do", "dump"]
linkable = ["fixed-header", "attribute-set"]
lines = [] lines = []
for operation in operations: 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") lines.append(rst_paragraph(sanitize(operation["doc"])) + "\n")
for key in operation.keys(): for key in operation.keys():
if key in preprocessed: if key in preprocessed:
# Skip the special fields # Skip the special fields
continue 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: if "do" in operation:
lines.append(rst_paragraph(":do:", 0)) lines.append(rst_paragraph(":do:", 0))
...@@ -212,14 +227,14 @@ def parse_entries(entries: List[Dict[str, Any]], level: int) -> str: ...@@ -212,14 +227,14 @@ def parse_entries(entries: List[Dict[str, Any]], level: int) -> str:
return "\n".join(lines) 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""" """Parse definitions section"""
preprocessed = ["name", "entries", "members"] preprocessed = ["name", "entries", "members"]
ignored = ["render-max"] # This is not printed ignored = ["render-max"] # This is not printed
lines = [] lines = []
for definition in defs: for definition in defs:
lines.append(rst_section(definition["name"])) lines.append(rst_section(namespace, 'definition', definition["name"]))
for k in definition.keys(): for k in definition.keys():
if k in preprocessed + ignored: if k in preprocessed + ignored:
continue continue
...@@ -237,14 +252,15 @@ def parse_definitions(defs: Dict[str, Any]) -> str: ...@@ -237,14 +252,15 @@ def parse_definitions(defs: Dict[str, Any]) -> str:
return "\n".join(lines) 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""" """Parse attribute from attribute-set"""
preprocessed = ["name", "type"] preprocessed = ["name", "type"]
linkable = ["enum", "nested-attributes", "struct", "sub-message"]
ignored = ["checks"] ignored = ["checks"]
lines = [] lines = []
for entry in entries: for entry in entries:
lines.append(rst_section(entry["name"])) lines.append(rst_section(namespace, 'attribute-set', entry["name"]))
for attr in entry["attributes"]: for attr in entry["attributes"]:
type_ = attr.get("type") type_ = attr.get("type")
attr_line = attr["name"] attr_line = attr["name"]
...@@ -257,25 +273,31 @@ def parse_attr_sets(entries: List[Dict[str, Any]]) -> str: ...@@ -257,25 +273,31 @@ def parse_attr_sets(entries: List[Dict[str, Any]]) -> str:
for k in attr.keys(): for k in attr.keys():
if k in preprocessed + ignored: if k in preprocessed + ignored:
continue 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") lines.append("\n")
return "\n".join(lines) 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""" """Parse sub-message definitions"""
lines = [] lines = []
for entry in entries: for entry in entries:
lines.append(rst_section(entry["name"])) lines.append(rst_section(namespace, 'sub-message', entry["name"]))
for fmt in entry["formats"]: for fmt in entry["formats"]:
value = fmt["value"] value = fmt["value"]
lines.append(rst_bullet(bold(value))) lines.append(rst_bullet(bold(value)))
for attr in ['fixed-header', 'attribute-set']: for attr in ['fixed-header', 'attribute-set']:
if attr in fmt: 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") lines.append("\n")
return "\n".join(lines) return "\n".join(lines)
...@@ -289,7 +311,9 @@ def parse_yaml(obj: Dict[str, Any]) -> str: ...@@ -289,7 +311,9 @@ def parse_yaml(obj: Dict[str, Any]) -> str:
lines.append(rst_header()) 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_title(title))
lines.append(rst_paragraph(".. contents:: :depth: 3\n")) lines.append(rst_paragraph(".. contents:: :depth: 3\n"))
...@@ -300,7 +324,7 @@ def parse_yaml(obj: Dict[str, Any]) -> str: ...@@ -300,7 +324,7 @@ def parse_yaml(obj: Dict[str, Any]) -> str:
# Operations # Operations
if "operations" in obj: if "operations" in obj:
lines.append(rst_subtitle("Operations")) lines.append(rst_subtitle("Operations"))
lines.append(parse_operations(obj["operations"]["list"])) lines.append(parse_operations(obj["operations"]["list"], family))
# Multicast groups # Multicast groups
if "mcast-groups" in obj: if "mcast-groups" in obj:
...@@ -310,17 +334,17 @@ def parse_yaml(obj: Dict[str, Any]) -> str: ...@@ -310,17 +334,17 @@ def parse_yaml(obj: Dict[str, Any]) -> str:
# Definitions # Definitions
if "definitions" in obj: if "definitions" in obj:
lines.append(rst_subtitle("Definitions")) lines.append(rst_subtitle("Definitions"))
lines.append(parse_definitions(obj["definitions"])) lines.append(parse_definitions(obj["definitions"], family))
# Attributes set # Attributes set
if "attribute-sets" in obj: if "attribute-sets" in obj:
lines.append(rst_subtitle("Attribute sets")) 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 # Sub-messages
if "sub-messages" in obj: if "sub-messages" in obj:
lines.append(rst_subtitle("Sub-messages")) 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) return "\n".join(lines)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment