From 49c6ab689b7691ba7a0db3c5f049a47c8099a168 Mon Sep 17 00:00:00 2001 From: Kayce Basques Date: Sat, 24 Jun 2023 01:06:42 +0000 Subject: [PATCH] docs: Auto-generate the Zephyr Kconfig reference Creates the MVP reference documentation for Zephyr Kconfig. The extension recursively walks the Kconfig tree and generates documentation for each option. The type and source code URL are shown for each option. Optional fields like default values and help text are also shown if available. Bug: b/288127315 Change-Id: I0ff30a576b4cd84dc68536e4b59ccd438aabead9 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/152533 Reviewed-by: Kayce Basques Reviewed-by: Anthony DiGirolamo Commit-Queue: Kayce Basques Presubmit-Verified: CQ Bot Account Pigweed-Auto-Submit: Kayce Basques --- docs/BUILD.gn | 1 + docs/conf.py | 1 + docs/os/zephyr/index.rst | 7 + docs/os/zephyr/kconfig.rst | 12 ++ pw_docgen/py/BUILD.gn | 1 + pw_docgen/py/pw_docgen/sphinx/kconfig.py | 137 ++++++++++++++++++ .../virtualenv_setup/constraint.list | 1 + .../pigweed_upstream_requirements.txt | 1 + 8 files changed, 161 insertions(+) create mode 100644 docs/os/zephyr/kconfig.rst create mode 100644 pw_docgen/py/pw_docgen/sphinx/kconfig.py diff --git a/docs/BUILD.gn b/docs/BUILD.gn index 7c854c4893..815f6d861e 100644 --- a/docs/BUILD.gn +++ b/docs/BUILD.gn @@ -51,6 +51,7 @@ pw_doc_group("core_docs") { "module_structure.rst", "os/index.rst", "os/zephyr/index.rst", + "os/zephyr/kconfig.rst", "size_optimizations.rst", "style_guide.rst", ] diff --git a/docs/conf.py b/docs/conf.py index 46b71875c1..ea5590622b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -41,6 +41,7 @@ extensions = [ 'pw_docgen.sphinx.google_analytics', # Enables optional Google Analytics + 'pw_docgen.sphinx.kconfig', 'pw_docgen.sphinx.module_metadata', 'sphinx.ext.autodoc', # Automatic documentation for Python code 'sphinx.ext.napoleon', # Parses Google-style docstrings diff --git a/docs/os/zephyr/index.rst b/docs/os/zephyr/index.rst index d3b1622048..0d791ca6d1 100644 --- a/docs/os/zephyr/index.rst +++ b/docs/os/zephyr/index.rst @@ -22,6 +22,8 @@ Get started with Zephyr and Pigweed 1. Complete the `Zephyr Getting Started Guide`_. 2. Check out the `zds2023`_ repository for an example of a Zephyr starter project that has been set up to use Pigweed. +3. See :ref:`docs-os-zephyr-kconfig` to find the Kconfig options for + enabling individual Pigweed modules and features. ------- Testing @@ -38,3 +40,8 @@ Once set up, simply invoke: .. _Zephyr Getting Started Guide: https://docs.zephyrproject.org/latest/develop/getting_started/index.html#getting-started-guide .. _zds2023: https://github.com/yperess/zds2023 + +.. toctree:: + :hidden: + + kconfig diff --git a/docs/os/zephyr/kconfig.rst b/docs/os/zephyr/kconfig.rst new file mode 100644 index 0000000000..bff8460384 --- /dev/null +++ b/docs/os/zephyr/kconfig.rst @@ -0,0 +1,12 @@ +.. _docs-os-zephyr-kconfig: + +======================== +Zephyr Kconfig reference +======================== +This page is an auto-generated reference of all of the Kconfig-related +options that are available when you use Pigweed with Zephyr. + +.. caution:: + This document is a work in progress. If something looks incorrect or + incomplete, use the source links to double-check the information in the + Kconfig source code. diff --git a/pw_docgen/py/BUILD.gn b/pw_docgen/py/BUILD.gn index 55c48aaeb6..efa2c9ce39 100644 --- a/pw_docgen/py/BUILD.gn +++ b/pw_docgen/py/BUILD.gn @@ -27,6 +27,7 @@ pw_python_package("py") { "pw_docgen/docgen.py", "pw_docgen/sphinx/__init__.py", "pw_docgen/sphinx/google_analytics.py", + "pw_docgen/sphinx/kconfig.py", "pw_docgen/sphinx/module_metadata.py", ] pylintrc = "$dir_pigweed/.pylintrc" diff --git a/pw_docgen/py/pw_docgen/sphinx/kconfig.py b/pw_docgen/py/pw_docgen/sphinx/kconfig.py new file mode 100644 index 0000000000..60713e0848 --- /dev/null +++ b/pw_docgen/py/pw_docgen/sphinx/kconfig.py @@ -0,0 +1,137 @@ +# Copyright 2023 The Pigweed Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +"""Auto-generate the Kconfig reference in //docs/os/zephyr/kconfig.rst""" + + +import os +import re +from typing import Iterable, Dict + +import docutils +from docutils.core import publish_doctree +from sphinx.application import Sphinx +from sphinx.addnodes import document + + +try: + import kconfiglib # type: ignore + + KCONFIGLIB_AVAILABLE = True +except ImportError: + KCONFIGLIB_AVAILABLE = False + + +def rst_to_doctree(rst: str) -> Iterable[docutils.nodes.Node]: + """Convert raw reStructuredText into doctree nodes.""" + # TODO(b/288127315): Properly resolve references within the rst so that + # links are generated more robustly. + while ':ref:`module-' in rst: + rst = re.sub( + r':ref:`module-(.*?)`', r'`\1 `_', rst + ) + doctree = publish_doctree(rst) + return doctree.children + + +def create_source_paragraph(name_and_loc: str) -> Iterable[docutils.nodes.Node]: + """Convert kconfiglib's name and location string into a source code link.""" + start = name_and_loc.index('pw_') + end = name_and_loc.index(':') + file_path = name_and_loc[start:end] + url = f'https://cs.opensource.google/pigweed/pigweed/+/main:{file_path}' + link = f'`//{file_path} <{url}>`_' + return rst_to_doctree(f'Source: {link}') + + +def process_node( + node: kconfiglib.MenuNode, parent: docutils.nodes.Node +) -> None: + """Recursively generate documentation for the Kconfig nodes.""" + while node: + if node.item == kconfiglib.MENU: + name = node.prompt[0] + # All auto-generated sections must have an ID or else the + # get_secnumber() function in Sphinx's HTML5 writer throws an + # IndexError. + menu_section = docutils.nodes.section(ids=[name]) + menu_section += docutils.nodes.title(text=f'{name} options') + if node.list: + process_node(node.list, menu_section) + parent += menu_section + elif isinstance(node.item, kconfiglib.Symbol): + name = f'CONFIG_{node.item.name}' + symbol_section = docutils.nodes.section(ids=[name]) + symbol_section += docutils.nodes.title(text=name) + symbol_section += docutils.nodes.paragraph( + text=f'Type: {kconfiglib.TYPE_TO_STR[node.item.type]}' + ) + if node.item.defaults: + try: + default_value = node.item.defaults[0][0].str_value + symbol_section += docutils.nodes.paragraph( + text=f'Default value: {default_value}' + ) + # If the data wasn't found, just contine trying to process + # rest of the documentation for the node. + except IndexError: + pass + if node.item.ranges: + try: + low = node.item.ranges[0][0].str_value + high = node.item.ranges[0][1].str_value + symbol_section += docutils.nodes.paragraph( + text=f'Range of valid values: {low} to {high}' + ) + except IndexError: + pass + if node.prompt: + try: + symbol_section += docutils.nodes.paragraph( + text=f'Description: {node.prompt[0]}' + ) + except IndexError: + pass + if node.help: + symbol_section += rst_to_doctree(node.help) + if node.list: + process_node(node.list, symbol_section) + symbol_section += create_source_paragraph(node.item.name_and_loc) + parent += symbol_section + # TODO(b/288127315): Render choices? + # elif isinstance(node.item, kconfiglib.Choice): + node = node.next + + +def generate_kconfig_reference(_, doctree: document, docname: str) -> None: + """Parse the Kconfig and kick off the doc generation process.""" + if 'docs/os/zephyr/kconfig' not in docname: + return + # Assume that the new content should be appended to the last section + # in the doctree. + for child in doctree.children: + if isinstance(child, docutils.nodes.section): + root = child + pw_root = os.environ['PW_ROOT'] + file_path = f'{pw_root}/Kconfig.zephyr' + kconfig = kconfiglib.Kconfig(file_path) + # There's no need to render kconfig.top_node (the main menu) or + # kconfig.top_node.list (ZEPHYR_PIGWEED_MODULE). + process_node(kconfig.top_node.list.next, root) + + +def setup(app: Sphinx) -> Dict[str, bool]: + """Initialize the Sphinx extension.""" + if KCONFIGLIB_AVAILABLE: + app.connect('doctree-resolved', generate_kconfig_reference) + return {'parallel_read_safe': True, 'parallel_write_safe': True} diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list index dcbd48eabe..d2db510309 100644 --- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list +++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list @@ -32,6 +32,7 @@ isort==5.10.1 jedi==0.18.1 Jinja2==3.0.3 json5==0.9.11 +kconfiglib==14.1.0 lazy-object-proxy==1.9.0 MarkupSafe==2.0.1 matplotlib-inline==0.1.3 diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/pigweed_upstream_requirements.txt b/pw_env_setup/py/pw_env_setup/virtualenv_setup/pigweed_upstream_requirements.txt index cc1136e46a..9691d90506 100644 --- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/pigweed_upstream_requirements.txt +++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/pigweed_upstream_requirements.txt @@ -20,6 +20,7 @@ sphinx-copybutton==0.5.1 sphinx-tabs==3.4.1 myst-parser==0.18.1 breathe==4.34.0 +kconfiglib==14.1.0 # Renode requirements psutil==5.9.4 robotframework==6.0.2