mirror of
https://github.com/torvalds/linux
synced 2024-10-03 01:43:05 +00:00
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: Donald Hunter <donald.hunter@gmail.com> Link: https://lore.kernel.org/r/20240329135021.52534-3-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
4cc1730a90
commit
8c1b74a26d
|
@ -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:
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
|
|
||||||
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:
|
||||||
# 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:
|
||||||
# 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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue