This makes all Python scripts in doc/_extensions compliant w.r.t current Ruff rules Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
233 lines
6.8 KiB
Python
233 lines
6.8 KiB
Python
"""
|
|
Copyright (c) 2021 Nordic Semiconductor ASA
|
|
Copyright (c) 2024 The Linux Foundation
|
|
SPDX-License-Identifier: Apache-2.0
|
|
"""
|
|
|
|
import concurrent.futures
|
|
import os
|
|
from typing import Any
|
|
|
|
import doxmlparser
|
|
from docutils import nodes
|
|
from doxmlparser.compound import DoxCompoundKind, DoxMemberKind
|
|
from sphinx import addnodes
|
|
from sphinx.application import Sphinx
|
|
from sphinx.domains.c import CXRefRole
|
|
from sphinx.transforms.post_transforms import SphinxPostTransform
|
|
from sphinx.util import logging
|
|
from sphinx.util.docutils import SphinxDirective
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
KIND_D2S = {
|
|
DoxMemberKind.DEFINE: "macro",
|
|
DoxMemberKind.VARIABLE: "var",
|
|
DoxMemberKind.TYPEDEF: "type",
|
|
DoxMemberKind.ENUM: "enum",
|
|
DoxMemberKind.FUNCTION: "func",
|
|
}
|
|
|
|
|
|
class DoxygenGroupDirective(SphinxDirective):
|
|
has_content = False
|
|
required_arguments = 1
|
|
optional_arguments = 0
|
|
|
|
def run(self):
|
|
|
|
desc_node = addnodes.desc()
|
|
desc_node["domain"] = "c"
|
|
desc_node["objtype"] = "group"
|
|
|
|
title_signode = addnodes.desc_signature()
|
|
group_xref = addnodes.pending_xref(
|
|
"",
|
|
refdomain="c",
|
|
reftype="group",
|
|
reftarget=self.arguments[0],
|
|
refwarn=True,
|
|
)
|
|
group_xref += nodes.Text(self.arguments[0])
|
|
title_signode += group_xref
|
|
|
|
desc_node.append(title_signode)
|
|
|
|
return [desc_node]
|
|
|
|
|
|
class DoxygenReferencer(SphinxPostTransform):
|
|
"""Mapping between Doxygen memberdef kind and Sphinx kinds"""
|
|
|
|
default_priority = 5
|
|
|
|
def run(self, **kwargs: Any) -> None:
|
|
for node in self.document.traverse(addnodes.pending_xref):
|
|
if node.get("refdomain") != "c":
|
|
continue
|
|
|
|
reftype = node.get("reftype")
|
|
|
|
# "member", "data" and "var" are equivalent as per Sphinx documentation for C domain
|
|
if reftype in ("member", "data"):
|
|
reftype = "var"
|
|
|
|
entry = self.app.env.doxybridge_cache.get(reftype)
|
|
if not entry:
|
|
continue
|
|
|
|
reftarget = node.get("reftarget").replace(".", "::").rstrip("()")
|
|
id = entry.get(reftarget)
|
|
if not id:
|
|
if reftype == "func":
|
|
# macros are sometimes referenced as functions, so try that
|
|
id = self.app.env.doxybridge_cache.get("macro").get(reftarget)
|
|
if not id:
|
|
continue
|
|
else:
|
|
continue
|
|
|
|
if reftype in ("struct", "union", "group"):
|
|
doxygen_target = f"{id}.html"
|
|
else:
|
|
split = id.split("_")
|
|
doxygen_target = f"{'_'.join(split[:-1])}.html#{split[-1][1:]}"
|
|
|
|
doxygen_target = str(self.app.config.doxybridge_dir) + "/html/" + doxygen_target
|
|
|
|
doc_dir = os.path.dirname(self.document.get("source"))
|
|
doc_dest = os.path.join(
|
|
self.app.outdir,
|
|
os.path.relpath(doc_dir, self.app.srcdir),
|
|
)
|
|
rel_uri = os.path.relpath(doxygen_target, doc_dest)
|
|
|
|
refnode = nodes.reference("", "", internal=True, refuri=rel_uri, reftitle="")
|
|
|
|
refnode.append(node[0].deepcopy())
|
|
|
|
if reftype == "group":
|
|
refnode["classes"].append("doxygroup")
|
|
title = self.app.env.doxybridge_group_titles.get(reftarget, "group")
|
|
refnode[0] = nodes.Text(title)
|
|
|
|
node.replace_self([refnode])
|
|
|
|
|
|
def parse_members(sectiondef):
|
|
cache = {}
|
|
|
|
for memberdef in sectiondef.get_memberdef():
|
|
kind = KIND_D2S.get(memberdef.get_kind())
|
|
if not kind:
|
|
continue
|
|
|
|
id = memberdef.get_id()
|
|
if memberdef.get_kind() == DoxMemberKind.VARIABLE:
|
|
name = memberdef.get_qualifiedname() or memberdef.get_name()
|
|
else:
|
|
name = memberdef.get_name()
|
|
|
|
cache.setdefault(kind, {})[name] = id
|
|
|
|
if memberdef.get_kind() == DoxMemberKind.ENUM:
|
|
for enumvalue in memberdef.get_enumvalue():
|
|
enumname = enumvalue.get_name()
|
|
enumid = enumvalue.get_id()
|
|
cache.setdefault("enumerator", {})[enumname] = enumid
|
|
|
|
return cache
|
|
|
|
|
|
def parse_sections(compounddef):
|
|
cache = {}
|
|
|
|
for sectiondef in compounddef.get_sectiondef():
|
|
members = parse_members(sectiondef)
|
|
for kind, data in members.items():
|
|
cache.setdefault(kind, {}).update(data)
|
|
|
|
return cache
|
|
|
|
|
|
def parse_compound(inDirName, baseName) -> dict:
|
|
rootObj = doxmlparser.compound.parse(inDirName + "/" + baseName + ".xml", True)
|
|
cache = {}
|
|
group_titles = {}
|
|
|
|
for compounddef in rootObj.get_compounddef():
|
|
name = compounddef.get_compoundname()
|
|
id = compounddef.get_id()
|
|
kind = None
|
|
if compounddef.get_kind() == DoxCompoundKind.STRUCT:
|
|
kind = "struct"
|
|
elif compounddef.get_kind() == DoxCompoundKind.UNION:
|
|
kind = "union"
|
|
elif compounddef.get_kind() == DoxCompoundKind.GROUP:
|
|
kind = "group"
|
|
group_titles[name] = compounddef.get_title()
|
|
|
|
if kind:
|
|
cache.setdefault(kind, {})[name] = id
|
|
|
|
sections = parse_sections(compounddef)
|
|
for kind, data in sections.items():
|
|
cache.setdefault(kind, {}).update(data)
|
|
|
|
return cache, group_titles
|
|
|
|
|
|
def parse_index(app: Sphinx, inDirName):
|
|
rootObj = doxmlparser.index.parse(inDirName + "/index.xml", True)
|
|
compounds = rootObj.get_compound()
|
|
|
|
with concurrent.futures.ProcessPoolExecutor() as executor:
|
|
futures = [
|
|
executor.submit(parse_compound, inDirName, compound.get_refid())
|
|
for compound in compounds
|
|
]
|
|
for future in concurrent.futures.as_completed(futures):
|
|
cache, group_titles = future.result()
|
|
for kind, data in cache.items():
|
|
app.env.doxybridge_cache.setdefault(kind, {}).update(data)
|
|
app.env.doxybridge_group_titles.update(group_titles)
|
|
|
|
|
|
def doxygen_parse(app: Sphinx) -> None:
|
|
if not app.env.doxygen_input_changed:
|
|
return
|
|
|
|
app.env.doxybridge_cache = {
|
|
"macro": {},
|
|
"var": {},
|
|
"type": {},
|
|
"enum": {},
|
|
"enumerator": {},
|
|
"func": {},
|
|
"union": {},
|
|
"struct": {},
|
|
"group": {},
|
|
}
|
|
|
|
app.env.doxybridge_group_titles = {}
|
|
|
|
parse_index(app, str(app.config.doxybridge_dir / "xml"))
|
|
|
|
|
|
def setup(app: Sphinx) -> dict[str, Any]:
|
|
app.add_config_value("doxybridge_dir", None, "env")
|
|
|
|
app.add_directive("doxygengroup", DoxygenGroupDirective)
|
|
|
|
app.add_role_to_domain("c", "group", CXRefRole())
|
|
|
|
app.add_post_transform(DoxygenReferencer)
|
|
app.connect("builder-inited", doxygen_parse)
|
|
|
|
return {
|
|
"version": "0.1",
|
|
"parallel_read_safe": True,
|
|
"parallel_write_safe": True,
|
|
}
|