Browse Source

doc: boards: extensions: introduce zephyr:board role and directive

A new zephyr:board:: Sphinx directive allows  to flag a documentation
page as being the documentation for a specific board, allowing to
auto-populate some of the contents, ex. by adding a board overview a la
Wikipedia, and later things like supported HW features, etc.

A corresponding :zephyr:board: role allows to link to a board doc page.

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
pull/80235/head
Benjamin Cabé 9 months ago committed by Alberto Escolar
parent
commit
ecb7c875dd
  1. 132
      doc/_extensions/zephyr/domain/__init__.py
  2. 55
      doc/_static/css/custom.css
  3. 17
      doc/contribute/documentation/guidelines.rst
  4. 18
      doc/templates/board.tmpl

132
doc/_extensions/zephyr/domain/__init__.py

@ -15,12 +15,14 @@ Directives @@ -15,12 +15,14 @@ Directives
- ``zephyr:code-sample-category::`` - Defines a category for grouping code samples.
- ``zephyr:code-sample-listing::`` - Shows a listing of code samples found in a given category.
- ``zephyr:board-catalog::`` - Shows a listing of boards supported by Zephyr.
- ``zephyr:board::`` - Flags a document as being the documentation page for a board.
Roles
-----
- ``:zephyr:code-sample:`` - References a code sample.
- ``:zephyr:code-sample-category:`` - References a code sample category.
- ``:zephyr:board:`` - References a board.
"""
@ -85,6 +87,10 @@ class CodeSampleListingNode(nodes.Element): @@ -85,6 +87,10 @@ class CodeSampleListingNode(nodes.Element):
pass
class BoardNode(nodes.Element):
pass
class ConvertCodeSampleNode(SphinxTransform):
default_priority = 100
@ -213,6 +219,65 @@ class ConvertCodeSampleCategoryNode(SphinxTransform): @@ -213,6 +219,65 @@ class ConvertCodeSampleCategoryNode(SphinxTransform):
node.replace_self(node.children[0])
class ConvertBoardNode(SphinxTransform):
default_priority = 100
def apply(self):
matcher = NodeMatcher(BoardNode)
for node in self.document.traverse(matcher):
self.convert_node(node)
def convert_node(self, node):
parent = node.parent
siblings_to_move = []
if parent is not None:
index = parent.index(node)
siblings_to_move = parent.children[index + 1 :]
new_section = nodes.section(ids=[node["id"]])
new_section += nodes.title(text=node["full_name"])
# create a sidebar with all the board details
sidebar = nodes.sidebar(classes=["board-overview"])
new_section += sidebar
sidebar += nodes.title(text="Board Overview")
if node["image"] is not None:
figure = nodes.figure()
# set a scale of 100% to indicate we want a link to the full-size image
figure += nodes.image(uri=f"/{node['image']}", scale=100)
figure += nodes.caption(text=node["full_name"])
sidebar += figure
field_list = nodes.field_list()
sidebar += field_list
details = [
("Vendor", node["vendor"]),
("Architecture", ", ".join(node["archs"])),
("SoC", ", ".join(node["socs"])),
]
for property_name, value in details:
field = nodes.field()
field_name = nodes.field_name(text=property_name)
field_body = nodes.field_body()
field_body += nodes.paragraph(text=value)
field += field_name
field += field_body
field_list += field
# Move the sibling nodes under the new section
new_section.extend(siblings_to_move)
# Replace the custom node with the new section
node.replace_self(new_section)
# Remove the moved siblings from their original parent
for sibling in siblings_to_move:
parent.remove(sibling)
class CodeSampleCategoriesTocPatching(SphinxPostTransform):
default_priority = 5 # needs to run *before* ReferencesResolver
@ -569,6 +634,45 @@ class CodeSampleListingDirective(SphinxDirective): @@ -569,6 +634,45 @@ class CodeSampleListingDirective(SphinxDirective):
return [code_sample_listing_node]
class BoardDirective(SphinxDirective):
has_content = False
required_arguments = 1
optional_arguments = 0
def run(self):
# board_name is passed as the directive argument
board_name = self.arguments[0]
boards = self.env.domaindata["zephyr"]["boards"]
vendors = self.env.domaindata["zephyr"]["vendors"]
if board_name not in boards:
logger.warning(
f"Board {board_name} does not seem to be a valid board name.",
location=(self.env.docname, self.lineno),
)
return []
elif "docname" in boards[board_name]:
logger.warning(
f"Board {board_name} is already documented in {boards[board_name]['docname']}.",
location=(self.env.docname, self.lineno),
)
return []
else:
board = boards[board_name]
# flag board in the domain data as now having a documentation page so that it can be
# cross-referenced etc.
board["docname"] = self.env.docname
board_node = BoardNode(id=board_name)
board_node["full_name"] = board["full_name"]
board_node["vendor"] = vendors.get(board["vendor"], board["vendor"])
board_node["archs"] = board["archs"]
board_node["socs"] = board["socs"]
board_node["image"] = board["image"]
return [board_node]
class BoardCatalogDirective(SphinxDirective):
has_content = False
required_arguments = 0
@ -602,6 +706,7 @@ class ZephyrDomain(Domain): @@ -602,6 +706,7 @@ class ZephyrDomain(Domain):
roles = {
"code-sample": XRefRole(innernodeclass=nodes.inline, warn_dangling=True),
"code-sample-category": XRefRole(innernodeclass=nodes.inline, warn_dangling=True),
"board": XRefRole(innernodeclass=nodes.inline, warn_dangling=True),
}
directives = {
@ -609,11 +714,13 @@ class ZephyrDomain(Domain): @@ -609,11 +714,13 @@ class ZephyrDomain(Domain):
"code-sample-listing": CodeSampleListingDirective,
"code-sample-category": CodeSampleCategoryDirective,
"board-catalog": BoardCatalogDirective,
"board": BoardDirective,
}
object_types: Dict[str, ObjType] = {
"code-sample": ObjType("code sample", "code-sample"),
"code-sample-category": ObjType("code sample category", "code-sample-category"),
"board": ObjType("board", "board"),
}
initial_data: Dict[str, Any] = {
@ -647,6 +754,12 @@ class ZephyrDomain(Domain): @@ -647,6 +754,12 @@ class ZephyrDomain(Domain):
self.data["code-samples"].update(otherdata["code-samples"])
self.data["code-samples-categories"].update(otherdata["code-samples-categories"])
# self.data["boards"] contains all the boards right from builder-inited time, but it still # potentially needs merging since a board's docname property is set by BoardDirective to
# indicate the board is documented in a specific document.
for board_name, board in otherdata["boards"].items():
if "docname" in board:
self.data["boards"][board_name]["docname"] = board["docname"]
# merge category trees by adding all the categories found in the "other" tree that to
# self tree
other_tree = otherdata["code-samples-categories-tree"]
@ -689,6 +802,18 @@ class ZephyrDomain(Domain): @@ -689,6 +802,18 @@ class ZephyrDomain(Domain):
1,
)
for _, board in self.data["boards"].items():
# only boards that do have a documentation page are to be considered as valid objects
if "docname" in board:
yield (
board["name"],
board["full_name"],
"board",
board["docname"],
board["name"],
1,
)
# used by Sphinx Immaterial theme
def get_object_synopses(self) -> Iterator[Tuple[Tuple[str, str], str]]:
for _, code_sample in self.data["code-samples"].items():
@ -702,18 +827,20 @@ class ZephyrDomain(Domain): @@ -702,18 +827,20 @@ class ZephyrDomain(Domain):
elem = self.data["code-samples"].get(target)
elif type == "code-sample-category":
elem = self.data["code-samples-categories"].get(target)
elif type == "board":
elem = self.data["boards"].get(target)
else:
return
if elem:
if not node.get("refexplicit"):
contnode = [nodes.Text(elem["name"])]
contnode = [nodes.Text(elem["name"] if type != "board" else elem["full_name"])]
return make_refnode(
builder,
fromdocname,
elem["docname"],
elem["id"],
elem["id"] if type != "board" else elem["name"],
contnode,
elem["description"].astext() if type == "code-sample" else None,
)
@ -821,6 +948,7 @@ def setup(app): @@ -821,6 +948,7 @@ def setup(app):
app.add_transform(ConvertCodeSampleNode)
app.add_transform(ConvertCodeSampleCategoryNode)
app.add_transform(ConvertBoardNode)
app.add_post_transform(ProcessCodeSampleListingNode)
app.add_post_transform(CodeSampleCategoriesTocPatching)

55
doc/_static/css/custom.css vendored

@ -1110,4 +1110,57 @@ li>a.code-sample-link.reference.internal { @@ -1110,4 +1110,57 @@ li>a.code-sample-link.reference.internal {
li>a.code-sample-link.reference.internal.current {
text-decoration: underline;
}
}
/* Board overview "card" on board documentation pages */
.sidebar.board-overview {
border-radius: 12px;
padding: 0px;
background: var(--admonition-note-background-color);
color: var(--admonition-note-title-color);
border-color: var(--admonition-note-title-background-color);
}
@media screen and (max-width: 480px) {
.sidebar.board-overview {
float: none;
margin-left: 0;
}
}
.sidebar.board-overview .sidebar-title {
font-family: var(--header-font-family);
background: var(--admonition-note-title-background-color);
color: var(--admonition-note-title-color);
border-radius: 12px 12px 0px 0px;
margin: 0px;
text-align: center;
}
.sidebar.board-overview * {
color: var(--admonition-note-color);
}
.sidebar.board-overview figure {
padding: 1rem;
margin-bottom: -1rem;
}
.sidebar.board-overview figure img {
height: auto !important;
}
.sidebar.board-overview figure figcaption p {
margin-bottom: 0px;
}
.sidebar.board-overview dl.field-list {
align-items: center;
margin-top: 12px !important;
margin-bottom: 12px !important;
grid-template-columns: auto 1fr !important;
}
.sidebar.board-overview dl.field-list > dt {
background: transparent !important;
}

17
doc/contribute/documentation/guidelines.rst

@ -1184,6 +1184,23 @@ Code samples @@ -1184,6 +1184,23 @@ Code samples
Boards
======
.. rst:directive:: .. zephyr:board:: name
This directive is used at the beginning of a document to indicate it is the main documentation
page for a board whose name is given as the directive argument.
For example::
.. zephyr:board:: wio_terminal
The metadata for the board is read from various config files and used to automatically populate
some sections of the board documentation. A board documentation page that uses this directive
can be linked to using the :rst:role:`zephyr:board` role.
.. rst:role:: zephyr:board
This role is used to reference a board documented using :rst:dir:`zephyr:board`.
.. rst:directive:: .. zephyr:board-catalog::
This directive is used to generate a catalog of Zephyr-supported boards that can be used to

18
doc/templates/board.tmpl vendored

@ -1,20 +1,16 @@ @@ -1,20 +1,16 @@
.. _boardname_linkname:
.. zephyr:board:: board_name
[Board Name]
#############
.. To ensure the board documentation page displays correctly, it is highly
recommended to include a picture alongside the documentation page.
The picture should be named after the board (e.g., "board_name.webp")
and preferably be in webp format. Alternatively, png or jpg formats
are also accepted.
Overview
********
[A short description about the board, its main features and availability]
.. figure:: board_name.png
:width: 800px
:align: center
:alt: Board Name
Board Name (Credit: <owner>)
Hardware
********
[General Hardware information]

Loading…
Cancel
Save