From 896096535caf0b643f4aa7dbe0f376d8a04d2f72 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Fri, 25 Aug 2023 13:58:52 -0700 Subject: [PATCH] doc: Generate develop/api/overview.rst API table from doxygen A new extension, api_overview.py, is used to, leveraging doxygen's Python module doxmlparser, parse the doxygen generated XML files. All groups ('defgroup' and 'addtogroup' tags) are collected, alongside their 'version' and 'since' info. From there, a new Sphinx directive `api-overview-table` is populated, including the name of the group, and if available, their 'since' and 'version' information. Signed-off-by: Ederson de Souza Signed-off-by: Anas Nashif --- doc/_extensions/zephyr/api_overview.py | 172 +++++++++++ doc/conf.py | 4 + doc/develop/api/api_lifecycle.rst | 25 ++ doc/develop/api/overview.rst | 406 ++----------------------- 4 files changed, 233 insertions(+), 374 deletions(-) create mode 100644 doc/_extensions/zephyr/api_overview.py diff --git a/doc/_extensions/zephyr/api_overview.py b/doc/_extensions/zephyr/api_overview.py new file mode 100644 index 00000000000000..9d4b2292a0a988 --- /dev/null +++ b/doc/_extensions/zephyr/api_overview.py @@ -0,0 +1,172 @@ +# Copyright (c) 2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import doxmlparser + +from docutils import nodes +from doxmlparser.compound import DoxCompoundKind +from pathlib import Path +from sphinx.application import Sphinx +from sphinx.util.docutils import SphinxDirective +from typing import Any, Dict + + +class ApiOverview(SphinxDirective): + """ + This is a Zephyr directive to generate a table containing an overview + of all APIs. This table will show the API name, version and since which + version it is present - all information extracted from Doxygen XML output. + + It is exclusively used by the doc/develop/api/overview.rst page. + + Configuration options: + + api_overview_doxygen_xml_dir: Doxygen xml output directory + api_overview_doxygen_base_url: Doxygen base html directory + """ + + def run(self): + return [self.env.api_overview_table] + + +def get_group(innergroup, all_groups): + try: + return [ + g + for g in all_groups + if g.get_compounddef()[0].get_id() == innergroup.get_refid() + ][0] + except IndexError as e: + raise Exception(f"Unexpected group {innergroup.get_refid()}") from e + + +def visit_group(app, group, all_groups, rows, indent=0): + version = since = "" + cdef = group.get_compounddef()[0] + + ssects = [ + s for p in cdef.get_detaileddescription().get_para() for s in p.get_simplesect() + ] + for sect in ssects: + if sect.get_kind() == "since": + since = sect.get_para()[0].get_valueOf_() + elif sect.get_kind() == "version": + version = sect.get_para()[0].get_valueOf_() + + if since: + since_url = nodes.inline() + reference = nodes.reference(text=f"v{since.strip()}.0", refuri=f"https://github.com/zephyrproject-rtos/zephyr/releases/tag/v{since.strip()}.0") + reference.attributes["internal"] = True + since_url += reference + else: + since_url = nodes.Text("") + + url_base = Path(app.config.api_overview_doxygen_base_url) + url = url_base / f"{cdef.get_id()}.html" + + title = cdef.get_title() + + row_node = nodes.row() + + # Next entry will contain the spacer and the link with API name + entry = nodes.entry() + span = nodes.Text("".join(["_"] * indent) + " ") + entry += span + + # API name with link + inline = nodes.inline() + reference = nodes.reference(text=title, refuri=str(url)) + reference.attributes["internal"] = True + inline += reference + entry += inline + row_node += entry + + version_node = nodes.Text(version) + # Finally, add version and since + for cell in [version_node, since_url]: + entry = nodes.entry() + entry += cell + row_node += entry + rows.append(row_node) + + for innergroup in cdef.get_innergroup(): + visit_group( + app, get_group(innergroup, all_groups), all_groups, rows, indent + 2 + ) + + +def parse_xml_dir(dir_name): + groups = [] + root = doxmlparser.index.parse(Path(dir_name) / "index.xml", True) + for compound in root.get_compound(): + if compound.get_kind() == DoxCompoundKind.GROUP: + file_name = Path(dir_name) / f"{compound.get_refid()}.xml" + groups.append(doxmlparser.compound.parse(file_name, True)) + + return groups + + +def generate_table(app, toplevel, groups): + table = nodes.table() + tgroup = nodes.tgroup() + + thead = nodes.thead() + thead_row = nodes.row() + for header_name in ["API", "Version", "Available in Zephyr Since"]: + colspec = nodes.colspec() + tgroup += colspec + + entry = nodes.entry() + entry += nodes.Text(header_name) + thead_row += entry + thead += thead_row + tgroup += thead + + rows = [] + tbody = nodes.tbody() + for t in toplevel: + visit_group(app, t, groups, rows) + tbody.extend(rows) + tgroup += tbody + + table += tgroup + + return table + + +def sync_contents(app: Sphinx) -> None: + if app.config.doxyrunner_outdir: + doxygen_out_dir = Path(app.config.doxyrunner_outdir) + else: + doxygen_out_dir = Path(app.outdir) / "_doxygen" + + doxygen_xml_dir = doxygen_out_dir / "xml" + groups = parse_xml_dir(doxygen_xml_dir) + + toplevel = [ + g + for g in groups + if g.get_compounddef()[0].get_id() + not in [ + i.get_refid() + for h in [j.get_compounddef()[0].get_innergroup() for j in groups] + for i in h + ] + ] + + app.builder.env.api_overview_table = generate_table(app, toplevel, groups) + + +def setup(app) -> Dict[str, Any]: + app.add_config_value("api_overview_doxygen_xml_dir", "html/doxygen/xml", "env") + app.add_config_value("api_overview_doxygen_base_url", "../../doxygen/html", "env") + + app.add_directive("api-overview-table", ApiOverview) + + app.connect("builder-inited", sync_contents) + + return { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/doc/conf.py b/doc/conf.py index c9b8453a28b2b1..5af6ce51fce154 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -95,6 +95,7 @@ "sphinx_togglebutton", "zephyr.external_content", "zephyr.domain", + "zephyr.api_overview", ] # Only use SVG converter when it is really needed, e.g. LaTeX. @@ -364,6 +365,9 @@ linkcheck_workers = 10 linkcheck_anchors = False +# -- Options for zephyr.api_overview -------------------------------------- + +api_overview_doxygen_base_url = "../../doxygen/html" def setup(app): # theme customizations diff --git a/doc/develop/api/api_lifecycle.rst b/doc/develop/api/api_lifecycle.rst index b34832655c8507..e641609c4b275d 100644 --- a/doc/develop/api/api_lifecycle.rst +++ b/doc/develop/api/api_lifecycle.rst @@ -20,6 +20,9 @@ no longer optimal or supported by the underlying platforms. An up-to-date table of all APIs and their maturity level can be found in the :ref:`api_overview` page. + +.. _api_lifecycle_experimental: + Experimental ************* @@ -36,6 +39,10 @@ The following requirements apply to all new APIs: of said API (in the case of peripheral APIs, this corresponds to one driver) - At least one sample using the new API (may only build on one single board) +When introducing a new and experimental API, mark the API version in the headers +where the API is defined. An experimental API shall have a version where the minor +version is up to one (0.1.z). (see `api overview `) + Peripheral APIs (Hardware Related) ================================== @@ -46,6 +53,8 @@ the Architecture working group consisting of representatives from different vend The API shall be promoted to ``unstable`` when it has at least two implementations on different hardware platforms. +.. _api_lifecycle_unstable: + Unstable ******** @@ -53,6 +62,10 @@ The API is in the process of settling, but has not yet had sufficient real-world testing to be considered stable. The API is considered generic in nature and can be used on different hardware platforms. +When the API changes status to unstable API, mark the API version in the headers +where the API is defined. Unstable APIs shall have a version where the minor +version is larger than one (0.y.z | y > 1 ). (see `api overview `) + .. note:: Changes will not be announced. @@ -69,6 +82,8 @@ Hardware Agnostic APIs For hardware agnostic APIs, multiple applications using it are required to promote an API from ``experimental`` to ``unstable``. +.. _api_lifecycle_stable: + Stable ******* @@ -94,6 +109,11 @@ In order to declare an API ``stable``, the following steps need to be followed: `Zephyr Architecture meeting`_ where, barring any objections, the Pull Request will be merged + +When the API changes status to stable API, mark the API version in the headers +where the API is defined. Stable APIs shall have a version where the major +version is one or larger (x.y.z | x >= 1 ). (see `api overview `) + .. _breaking_api_changes: Introducing breaking API changes @@ -175,6 +195,11 @@ for it to be discussed and ultimately even voted on in the `Zephyr TSC meeting`_ If the Pull Request is merged then an email must be sent to the ``devel`` and ``user`` mailing lists informing them of the change. +The API version shall be changed to signal backward incompatible changes. This +is achieved by incrementing the major version (X.y.z | X > 1). It MAY also +include minor and patch level changes. Patch and minor versions MUST be reset to +0 when major version is incremented. (see `api overview `) + .. note:: Breaking API changes will be listed and described in the migration guide. diff --git a/doc/develop/api/overview.rst b/doc/develop/api/overview.rst index 62343f2192cc7f..956df605231edd 100644 --- a/doc/develop/api/overview.rst +++ b/doc/develop/api/overview.rst @@ -7,388 +7,46 @@ The table lists Zephyr's APIs and information about them, including their current :ref:`stability level `. More details about API changes between major releases are available in the :ref:`zephyr_release_notes`. -.. Keep this list sorted by the name of the API as it appears - in the HTML, *NOT* the :ref: label +The version column uses `semantic version `_, and has the +following expectations: -.. list-table:: - :header-rows: 1 + * Major version zero (0.y.z) is for initial development. Anything MAY + change at any time. The public API SHOULD NOT be considered stable. - * - API - - Status - - Version Introduced + * If minor version is up to one (0.1.z), API is considered + `experimental `. + * If minor version is larger than one (0.y.z | y > 1), API is considered + `unstable `. - * - :ref:`adc_api` - - Stable - - 1.0 + * Version 1.0.0 defines the public API. The way in which the version number + is incremented after this release is dependent on this public API and how it + changes. - * - :ref:`audio_codec_api` - - Experimental - - 1.13 + * APIs with major versions equal or larger than one (x.y.z | x >= 1 ) are + considered `stable `. + * All existing stable APIs in Zephyr will be start with version 1.0.0. - * - :ref:`audio_dmic_api` - - Experimental - - 1.13 + * Patch version Z (x.y.Z | x > 0) MUST be incremented if only backwards + compatible bug fixes are introduced. A bug fix is defined as an internal + change that fixes incorrect behavior. - * - :ref:`auxdisplay_api` - - Experimental - - 3.4 + * Minor version Y (x.Y.z | x > 0) MUST be incremented if new, backwards + compatible functionality is introduced to the public API. It MUST be + incremented if any public API functionality is marked as deprecated. It MAY + be incremented if substantial new functionality or improvements are + introduced within the private code. It MAY include patch level changes. + Patch version MUST be reset to 0 when minor version is incremented. - * - :ref:`barriers_api` - - Experimental - - 3.4 + * Major version X (x.Y.z | x > 0) MUST be incremented if a compatibility + breaking change was made to the API. - * - :ref:`blinfo_api` - - Experimental - - 3.5 +.. note:: + Version for existing APIs are initially set based on the current state of the + APIs. 0.1.0 denotes an experimental API, 0.8.0 denote an unstable API and + finally 1.0.0 is for stable APIs. - * - :ref:`bluetooth_api` - - Stable - - 1.0 + Changes to APIs in the future will require adapting the version following the + guidelines above. - * - :ref:`clock_control_api` - - Stable - - 1.0 - * - :ref:`coap_sock_interface` - - Unstable - - 1.10 - - * - :ref:`conn_mgr_docs` - - Experimental - - 3.4.0 - - * - :ref:`can_api` - - Stable - - 1.14 - - * - :ref:`can_transceiver_api` - - Experimental - - 3.1 - - * - :ref:`charger_api` - - Experimental - - 3.5 - - * - :ref:`counter_api` - - Unstable - - 1.14 - - * - :ref:`crypto_api` - - Stable - - 1.7 - - * - :ref:`dac_api` - - Unstable - - 2.3 - - * - :ref:`dai_api` - - Experimental - - 3.1 - - * - :ref:`dma_api` - - Stable - - 1.5 - - * - :ref:`device_model_api` - - Stable - - 1.0 - - * - :ref:`devicetree_api` - - Stable - - 2.2 - - * - :ref:`disk_access_api` - - Stable - - 1.6 - - * - :ref:`display_api` - - Unstable - - 1.14 - - * - :ref:`ec_host_cmd_backend_api` - - Experimental - - 2.4 - - * - :ref:`edac_api` - - Unstable - - 2.5 - - * - :ref:`eeprom_api` - - Stable - - 2.1 - - * - :ref:`entropy_api` - - Stable - - 1.10 - - * - :ref:`file_system_api` - - Stable - - 1.5 - - * - :ref:`flash_api` - - Stable - - 1.2 - - * - :ref:`fcb_api` - - Stable - - 1.11 - - * - :ref:`fuel_gauge_api` - - Experimental - - 3.3 - - * - :ref:`flash_map_api` - - Stable - - 1.11 - - * - :ref:`gnss_api` - - Experimental - - 3.6 - - * - :ref:`gpio_api` - - Stable - - 1.0 - - * - :ref:`hwinfo_api` - - Stable - - 1.14 - - * - :ref:`i2c_eeprom_target_api` - - Stable - - 1.13 - - * - :ref:`i2c_api` - - Stable - - 1.0 - - * - :ref:`i2c-target-api` - - Experimental - - 1.12 - - * - :ref:`i2s_api` - - Stable - - 1.9 - - * - :ref:`i3c_api` - - Experimental - - 3.2 - - * - :ref:`ieee802154_driver_api` - - Unstable - - 1.0 - - * - :ref:`ieee802154_l2_api` - - Unstable - - 1.0 - - * - :ref:`ieee802154_mgmt_api` - - Unstable - - 1.0 - - * - :ref:`input` - - Experimental - - 3.4 - - * - :ref:`ipm_api` - - Stable - - 1.0 - - * - :ref:`kscan_api` - - Stable - - 2.1 - - * - :ref:`kernel_api` - - Stable - - 1.0 - - * - :ref:`led_api` - - Stable - - 1.12 - - * - :ref:`lwm2m_interface` - - Unstable - - 1.9 - - * - :ref:`llext` - - Experimental - - 3.5 - - * - :ref:`logging_api` - - Stable - - 1.13 - - * - :ref:`lora_api` - - Experimental - - 2.2 - - * - :ref:`lorawan_api` - - Experimental - - 2.5 - - * - :ref:`mbox_api` - - Experimental - - 1.0 - - * - :ref:`mcu_mgr` - - Stable - - 1.11 - - * - :ref:`modem` - - Experimental - - 3.5 - - * - :ref:`mqtt_socket_interface` - - Unstable - - 1.14 - - * - :ref:`mipi_dbi_api` - - Experimental - - 3.6 - - * - :ref:`mipi_dsi_api` - - Experimental - - 3.1 - - * - :ref:`misc_api` - - Stable - - 1.0 - - * - :ref:`networking_api` - - Stable - - 1.0 - - * - :ref:`nvs_api` - - Stable - - 1.12 - - * - :ref:`peci_api` - - Stable - - 2.1 - - * - :ref:`ps2_api` - - Stable - - 2.1 - - * - :ref:`pwm_api` - - Stable - - 1.0 - - * - :ref:`pinctrl_api` - - Experimental - - 3.0 - - * - :ref:`pm_api` - - Experimental - - 1.2 - - * - :ref:`random_api` - - Stable - - 1.0 - - * - :ref:`regulator_api` - - Experimental - - 2.4 - - * - :ref:`reset_api` - - Experimental - - 3.1 - - * - :ref:`retained_mem_api` - - Unstable - - 3.4 - - * - :ref:`retention_api` - - Experimental - - 3.4 - - * - :ref:`rtc_api` - - Experimental - - 3.4 - - * - :ref:`rtio_api` - - Experimental - - 3.2 - - * - :ref:`smbus_api` - - Experimental - - 3.4 - - * - :ref:`spi_api` - - Stable - - 1.0 - - * - :ref:`sensor_api` - - Stable - - 1.2 - - * - :ref:`settings_api` - - Stable - - 1.12 - - * - :ref:`shell_api` - - Stable - - 1.14 - - * - :ref:`stream_flash` - - Experimental - - 2.3 - - * - :ref:`sdhc_api` - - Experimental - - 3.1 - - * - :ref:`task_wdt_api` - - Unstable - - 2.5 - - * - :ref:`tcpc_api` - - Experimental - - 3.1 - - * - :ref:`tgpio_api` - - Experimental - - 3.5 - - * - :ref:`uart_api` - - Stable - - 1.0 - - * - :ref:`UART async ` - - Unstable - - 1.14 - - * - :ref:`usb_api` - - Stable - - 1.5 - - * - :ref:`usbc_api` - - Experimental - - 3.3 - - * - :ref:`usermode_api` - - Stable - - 1.11 - - * - :ref:`usbc_vbus_api` - - Experimental - - 3.3 - - * - :ref:`util_api` - - Experimental - - 2.4 - - * - :ref:`video_api` - - Stable - - 2.1 - - * - :ref:`w1_api` - - Experimental - - 3.2 - - * - :ref:`watchdog_api` - - Stable - - 1.0 - - * - :ref:`zdsp_api` - - Experimental - - 3.3 +.. api-overview-table::