Skip to content

Commit ae67298

Browse files
committed
[nrf noup] doc: extensions: api_overview: refactor extension
The extension had a some major design flaws, mainly: - Ignored the `api_overview_doxygen_xml_dir` setting, instead it "sniffed" doxyrunner properties, so violating environment boundaries - Computation of Doxygen HTML path worked because of the hardcoded URL in relative form found in doc/conf.py This patch moves most code to the actual directive, so that context can be obtained from the document being parsed. Also, the only config required now is the Doxygen output dir, obtained from doxyrunner at conf.py level. Signed-off-by: Gerard Marull-Paretas <[email protected]> (cherry picked from commit 35517f6d4202ef9c4ef1218150a1059383cb6ea5)
1 parent 6d04ddb commit ae67298

File tree

2 files changed

+116
-127
lines changed

2 files changed

+116
-127
lines changed

doc/_extensions/zephyr/api_overview.py

Lines changed: 115 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
11
# Copyright (c) 2023 Intel Corporation
22
# SPDX-License-Identifier: Apache-2.0
33

4-
import doxmlparser
4+
import os
5+
from pathlib import Path
6+
from typing import Any
57

8+
import doxmlparser
69
from docutils import nodes
710
from doxmlparser.compound import DoxCompoundKind
8-
from pathlib import Path
9-
from sphinx.application import Sphinx
1011
from sphinx.util.docutils import SphinxDirective
11-
from typing import Any, Dict
12+
13+
14+
def get_group(innergroup, all_groups):
15+
try:
16+
return [
17+
g
18+
for g in all_groups
19+
if g.get_compounddef()[0].get_id() == innergroup.get_refid()
20+
][0]
21+
except IndexError as e:
22+
raise Exception(f"Unexpected group {innergroup.get_refid()}") from e
23+
24+
25+
def parse_xml_dir(dir_name):
26+
groups = []
27+
root = doxmlparser.index.parse(Path(dir_name) / "index.xml", True)
28+
for compound in root.get_compound():
29+
if compound.get_kind() == DoxCompoundKind.GROUP:
30+
file_name = Path(dir_name) / f"{compound.get_refid()}.xml"
31+
groups.append(doxmlparser.compound.parse(file_name, True))
32+
33+
return groups
1234

1335

1436
class ApiOverview(SphinxDirective):
@@ -21,154 +43,121 @@ class ApiOverview(SphinxDirective):
2143
2244
Configuration options:
2345
24-
api_overview_doxygen_xml_dir: Doxygen xml output directory
25-
api_overview_doxygen_base_url: Doxygen base html directory
46+
api_overview_doxygen_out_dir: Doxygen output directory
2647
"""
2748

2849
def run(self):
29-
return [self.env.api_overview_table]
50+
groups = parse_xml_dir(self.config.api_overview_doxygen_out_dir + "/xml")
3051

31-
32-
def get_group(innergroup, all_groups):
33-
try:
34-
return [
52+
toplevel = [
3553
g
36-
for g in all_groups
37-
if g.get_compounddef()[0].get_id() == innergroup.get_refid()
38-
][0]
39-
except IndexError as e:
40-
raise Exception(f"Unexpected group {innergroup.get_refid()}") from e
41-
42-
43-
def visit_group(app, group, all_groups, rows, indent=0):
44-
version = since = ""
45-
github_uri = "https://github.com/zephyrproject-rtos/zephyr/releases/tag/"
46-
cdef = group.get_compounddef()[0]
54+
for g in groups
55+
if g.get_compounddef()[0].get_id()
56+
not in [
57+
i.get_refid()
58+
for h in [j.get_compounddef()[0].get_innergroup() for j in groups]
59+
for i in h
60+
]
61+
]
4762

48-
ssects = [
49-
s for p in cdef.get_detaileddescription().get_para() for s in p.get_simplesect()
50-
]
51-
for sect in ssects:
52-
if sect.get_kind() == "since":
53-
since = sect.get_para()[0].get_valueOf_()
54-
elif sect.get_kind() == "version":
55-
version = sect.get_para()[0].get_valueOf_()
63+
return [self.generate_table(toplevel, groups)]
5664

57-
if since:
58-
since_url = nodes.inline()
59-
reference = nodes.reference(text=f"v{since.strip()}.0", refuri=f"{github_uri}/v{since.strip()}.0")
60-
reference.attributes["internal"] = True
61-
since_url += reference
62-
else:
63-
since_url = nodes.Text("")
65+
def generate_table(self, toplevel, groups):
66+
table = nodes.table()
67+
tgroup = nodes.tgroup()
6468

65-
url_base = Path(app.config.api_overview_doxygen_base_url)
66-
url = url_base / f"{cdef.get_id()}.html"
69+
thead = nodes.thead()
70+
thead_row = nodes.row()
71+
for header_name in ["API", "Version", "Available in Zephyr Since"]:
72+
colspec = nodes.colspec()
73+
tgroup += colspec
6774

68-
title = cdef.get_title()
75+
entry = nodes.entry()
76+
entry += nodes.Text(header_name)
77+
thead_row += entry
78+
thead += thead_row
79+
tgroup += thead
6980

70-
row_node = nodes.row()
81+
rows = []
82+
tbody = nodes.tbody()
83+
for t in toplevel:
84+
self.visit_group(t, groups, rows)
85+
tbody.extend(rows)
86+
tgroup += tbody
7187

72-
# Next entry will contain the spacer and the link with API name
73-
entry = nodes.entry()
74-
span = nodes.Text("".join(["\U000000A0"] * indent))
75-
entry += span
88+
table += tgroup
7689

77-
# API name with link
78-
inline = nodes.inline()
79-
reference = nodes.reference(text=title, refuri=str(url))
80-
reference.attributes["internal"] = True
81-
inline += reference
82-
entry += inline
83-
row_node += entry
90+
return table
8491

85-
version_node = nodes.Text(version)
86-
# Finally, add version and since
87-
for cell in [version_node, since_url]:
88-
entry = nodes.entry()
89-
entry += cell
90-
row_node += entry
91-
rows.append(row_node)
92+
def visit_group(self, group, all_groups, rows, indent=0):
93+
version = since = ""
94+
github_uri = "https://github.com/zephyrproject-rtos/zephyr/releases/tag/"
95+
cdef = group.get_compounddef()[0]
9296

93-
for innergroup in cdef.get_innergroup():
94-
visit_group(
95-
app, get_group(innergroup, all_groups), all_groups, rows, indent + 6
97+
ssects = [
98+
s for p in cdef.get_detaileddescription().get_para() for s in p.get_simplesect()
99+
]
100+
for sect in ssects:
101+
if sect.get_kind() == "since":
102+
since = sect.get_para()[0].get_valueOf_()
103+
elif sect.get_kind() == "version":
104+
version = sect.get_para()[0].get_valueOf_()
105+
106+
if since:
107+
since_url = nodes.inline()
108+
reference = nodes.reference(
109+
text=f"v{since.strip()}.0", refuri=f"{github_uri}/v{since.strip()}.0"
110+
)
111+
reference.attributes["internal"] = True
112+
since_url += reference
113+
else:
114+
since_url = nodes.Text("")
115+
116+
url_base = Path(self.config.api_overview_doxygen_out_dir + "/html")
117+
abs_url = url_base / f"{cdef.get_id()}.html"
118+
doc_dir = os.path.dirname(self.get_source_info()[0])
119+
doc_dest = os.path.join(
120+
self.env.app.outdir,
121+
os.path.relpath(doc_dir, self.env.app.srcdir),
96122
)
123+
url = os.path.relpath(abs_url, doc_dest)
97124

125+
title = cdef.get_title()
98126

99-
def parse_xml_dir(dir_name):
100-
groups = []
101-
root = doxmlparser.index.parse(Path(dir_name) / "index.xml", True)
102-
for compound in root.get_compound():
103-
if compound.get_kind() == DoxCompoundKind.GROUP:
104-
file_name = Path(dir_name) / f"{compound.get_refid()}.xml"
105-
groups.append(doxmlparser.compound.parse(file_name, True))
106-
107-
return groups
127+
row_node = nodes.row()
108128

129+
# Next entry will contain the spacer and the link with API name
130+
entry = nodes.entry()
131+
span = nodes.Text("".join(["\U000000A0"] * indent))
132+
entry += span
109133

110-
def generate_table(app, toplevel, groups):
111-
table = nodes.table()
112-
tgroup = nodes.tgroup()
134+
# API name with link
135+
inline = nodes.inline()
136+
reference = nodes.reference(text=title, refuri=str(url))
137+
reference.attributes["internal"] = True
138+
inline += reference
139+
entry += inline
140+
row_node += entry
113141

114-
thead = nodes.thead()
115-
thead_row = nodes.row()
116-
for header_name in ["API", "Version", "Available in Zephyr Since"]:
117-
colspec = nodes.colspec()
118-
tgroup += colspec
142+
version_node = nodes.Text(version)
143+
# Finally, add version and since
144+
for cell in [version_node, since_url]:
145+
entry = nodes.entry()
146+
entry += cell
147+
row_node += entry
148+
rows.append(row_node)
119149

120-
entry = nodes.entry()
121-
entry += nodes.Text(header_name)
122-
thead_row += entry
123-
thead += thead_row
124-
tgroup += thead
125-
126-
rows = []
127-
tbody = nodes.tbody()
128-
for t in toplevel:
129-
visit_group(app, t, groups, rows)
130-
tbody.extend(rows)
131-
tgroup += tbody
132-
133-
table += tgroup
134-
135-
return table
136-
137-
138-
def sync_contents(app: Sphinx) -> None:
139-
if app.config.doxyrunner_outdir:
140-
doxygen_out_dir = Path(app.config.doxyrunner_outdir)
141-
else:
142-
doxygen_out_dir = Path(app.outdir) / "_doxygen"
143-
144-
if not app.env.doxygen_input_changed:
145-
return
146-
147-
doxygen_xml_dir = doxygen_out_dir / "xml"
148-
groups = parse_xml_dir(doxygen_xml_dir)
149-
150-
toplevel = [
151-
g
152-
for g in groups
153-
if g.get_compounddef()[0].get_id()
154-
not in [
155-
i.get_refid()
156-
for h in [j.get_compounddef()[0].get_innergroup() for j in groups]
157-
for i in h
158-
]
159-
]
160-
161-
app.builder.env.api_overview_table = generate_table(app, toplevel, groups)
150+
for innergroup in cdef.get_innergroup():
151+
self.visit_group(
152+
get_group(innergroup, all_groups), all_groups, rows, indent + 6
153+
)
162154

163155

164-
def setup(app) -> Dict[str, Any]:
165-
app.add_config_value("api_overview_doxygen_xml_dir", "html/doxygen/xml", "env")
166-
app.add_config_value("api_overview_doxygen_base_url", "../../doxygen/html", "env")
156+
def setup(app) -> dict[str, Any]:
157+
app.add_config_value("api_overview_doxygen_out_dir", "", "env")
167158

168159
app.add_directive("api-overview-table", ApiOverview)
169160

170-
app.connect("builder-inited", sync_contents)
171-
172161
return {
173162
"version": "0.1",
174163
"parallel_read_safe": True,

doc/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@
348348

349349
# -- Options for zephyr.api_overview --------------------------------------
350350

351-
api_overview_doxygen_base_url = "../../doxygen/html"
351+
api_overview_doxygen_out_dir = str(doxyrunner_outdir)
352352

353353
def setup(app):
354354
# theme customizations

0 commit comments

Comments
 (0)