Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Jakub Kicinski <kuba@kernel.org>

+94 -19
+51
Documentation/netlink/specs/tc.yaml
··· 1100 1100 name: offmask 1101 1101 type: s32 1102 1102 - 1103 + name: tc-u32-mark 1104 + type: struct 1105 + members: 1106 + - 1107 + name: val 1108 + type: u32 1109 + - 1110 + name: mask 1111 + type: u32 1112 + - 1113 + name: success 1114 + type: u32 1115 + - 1103 1116 name: tc-u32-sel 1104 1117 type: struct 1105 1118 members: ··· 1787 1774 - 1788 1775 name: key-ex 1789 1776 type: binary 1777 + - 1778 + name: tc-act-police-attrs 1779 + attributes: 1780 + - 1781 + name: tbf 1782 + type: binary 1783 + struct: tc-police 1784 + - 1785 + name: rate 1786 + type: binary # TODO 1787 + - 1788 + name: peakrate 1789 + type: binary # TODO 1790 + - 1791 + name: avrate 1792 + type: u32 1793 + - 1794 + name: result 1795 + type: u32 1796 + - 1797 + name: tm 1798 + type: binary 1799 + struct: tcf-t 1800 + - 1801 + name: pad 1802 + type: pad 1803 + - 1804 + name: rate64 1805 + type: u64 1806 + - 1807 + name: peakrate64 1808 + type: u64 1809 + - 1810 + name: pktrate64 1811 + type: u64 1812 + - 1813 + name: pktburst64 1814 + type: u64 1790 1815 - 1791 1816 name: tc-act-simple-attrs 1792 1817 attributes:
+43 -19
tools/net/ynl/ynl-gen-rst.py
··· 82 82 return f"{title}\n" + "~" * len(title) 83 83 84 84 85 - def rst_section(title: str) -> str: 85 + def rst_section(namespace: str, prefix: str, title: str) -> str: 86 86 """Add a section to the document""" 87 - return f"\n{title}\n" + "=" * len(title) 87 + return f".. _{namespace}-{prefix}-{title}:\n\n{title}\n" + "=" * len(title) 88 88 89 89 90 90 def rst_subtitle(title: str) -> str: ··· 100 100 def rst_list_inline(list_: List[str], level: int = 0) -> str: 101 101 """Format a list using inlines""" 102 102 return headroom(level) + "[" + ", ".join(inline(i) for i in list_) + "]" 103 + 104 + 105 + def rst_ref(namespace: str, prefix: str, name: str) -> str: 106 + """Add a hyperlink to the document""" 107 + mappings = {'enum': 'definition', 108 + 'fixed-header': 'definition', 109 + 'nested-attributes': 'attribute-set', 110 + 'struct': 'definition'} 111 + if prefix in mappings: 112 + prefix = mappings[prefix] 113 + return f":ref:`{namespace}-{prefix}-{name}`" 103 114 104 115 105 116 def rst_header() -> str: ··· 170 159 return "\n".join(lines) 171 160 172 161 173 - def parse_operations(operations: List[Dict[str, Any]]) -> str: 162 + def parse_operations(operations: List[Dict[str, Any]], namespace: str) -> str: 174 163 """Parse operations block""" 175 164 preprocessed = ["name", "doc", "title", "do", "dump"] 165 + linkable = ["fixed-header", "attribute-set"] 176 166 lines = [] 177 167 178 168 for operation in operations: 179 - lines.append(rst_section(operation["name"])) 169 + lines.append(rst_section(namespace, 'operation', operation["name"])) 180 170 lines.append(rst_paragraph(sanitize(operation["doc"])) + "\n") 181 171 182 172 for key in operation.keys(): 183 173 if key in preprocessed: 184 174 # Skip the special fields 185 175 continue 186 - lines.append(rst_fields(key, operation[key], 0)) 176 + value = operation[key] 177 + if key in linkable: 178 + value = rst_ref(namespace, key, value) 179 + lines.append(rst_fields(key, value, 0)) 187 180 188 181 if "do" in operation: 189 182 lines.append(rst_paragraph(":do:", 0)) ··· 227 212 return "\n".join(lines) 228 213 229 214 230 - def parse_definitions(defs: Dict[str, Any]) -> str: 215 + def parse_definitions(defs: Dict[str, Any], namespace: str) -> str: 231 216 """Parse definitions section""" 232 217 preprocessed = ["name", "entries", "members"] 233 218 ignored = ["render-max"] # This is not printed 234 219 lines = [] 235 220 236 221 for definition in defs: 237 - lines.append(rst_section(definition["name"])) 222 + lines.append(rst_section(namespace, 'definition', definition["name"])) 238 223 for k in definition.keys(): 239 224 if k in preprocessed + ignored: 240 225 continue ··· 252 237 return "\n".join(lines) 253 238 254 239 255 - def parse_attr_sets(entries: List[Dict[str, Any]]) -> str: 240 + def parse_attr_sets(entries: List[Dict[str, Any]], namespace: str) -> str: 256 241 """Parse attribute from attribute-set""" 257 242 preprocessed = ["name", "type"] 243 + linkable = ["enum", "nested-attributes", "struct", "sub-message"] 258 244 ignored = ["checks"] 259 245 lines = [] 260 246 261 247 for entry in entries: 262 - lines.append(rst_section(entry["name"])) 248 + lines.append(rst_section(namespace, 'attribute-set', entry["name"])) 263 249 for attr in entry["attributes"]: 264 250 type_ = attr.get("type") 265 251 attr_line = attr["name"] ··· 273 257 for k in attr.keys(): 274 258 if k in preprocessed + ignored: 275 259 continue 276 - lines.append(rst_fields(k, sanitize(attr[k]), 0)) 260 + if k in linkable: 261 + value = rst_ref(namespace, k, attr[k]) 262 + else: 263 + value = sanitize(attr[k]) 264 + lines.append(rst_fields(k, value, 0)) 277 265 lines.append("\n") 278 266 279 267 return "\n".join(lines) 280 268 281 269 282 - def parse_sub_messages(entries: List[Dict[str, Any]]) -> str: 270 + def parse_sub_messages(entries: List[Dict[str, Any]], namespace: str) -> str: 283 271 """Parse sub-message definitions""" 284 272 lines = [] 285 273 286 274 for entry in entries: 287 - lines.append(rst_section(entry["name"])) 275 + lines.append(rst_section(namespace, 'sub-message', entry["name"])) 288 276 for fmt in entry["formats"]: 289 277 value = fmt["value"] 290 278 291 279 lines.append(rst_bullet(bold(value))) 292 280 for attr in ['fixed-header', 'attribute-set']: 293 281 if attr in fmt: 294 - lines.append(rst_fields(attr, fmt[attr], 1)) 282 + lines.append(rst_fields(attr, 283 + rst_ref(namespace, attr, fmt[attr]), 284 + 1)) 295 285 lines.append("\n") 296 286 297 287 return "\n".join(lines) ··· 311 289 312 290 lines.append(rst_header()) 313 291 314 - title = f"Family ``{obj['name']}`` netlink specification" 292 + family = obj['name'] 293 + 294 + title = f"Family ``{family}`` netlink specification" 315 295 lines.append(rst_title(title)) 316 - lines.append(rst_paragraph(".. contents::\n")) 296 + lines.append(rst_paragraph(".. contents:: :depth: 3\n")) 317 297 318 298 if "doc" in obj: 319 299 lines.append(rst_subtitle("Summary")) ··· 324 300 # Operations 325 301 if "operations" in obj: 326 302 lines.append(rst_subtitle("Operations")) 327 - lines.append(parse_operations(obj["operations"]["list"])) 303 + lines.append(parse_operations(obj["operations"]["list"], family)) 328 304 329 305 # Multicast groups 330 306 if "mcast-groups" in obj: ··· 334 310 # Definitions 335 311 if "definitions" in obj: 336 312 lines.append(rst_subtitle("Definitions")) 337 - lines.append(parse_definitions(obj["definitions"])) 313 + lines.append(parse_definitions(obj["definitions"], family)) 338 314 339 315 # Attributes set 340 316 if "attribute-sets" in obj: 341 317 lines.append(rst_subtitle("Attribute sets")) 342 - lines.append(parse_attr_sets(obj["attribute-sets"])) 318 + lines.append(parse_attr_sets(obj["attribute-sets"], family)) 343 319 344 320 # Sub-messages 345 321 if "sub-messages" in obj: 346 322 lines.append(rst_subtitle("Sub-messages")) 347 - lines.append(parse_sub_messages(obj["sub-messages"])) 323 + lines.append(parse_sub_messages(obj["sub-messages"], family)) 348 324 349 325 return "\n".join(lines) 350 326