From d1beb70e5b7ec8491c56bad0bda30f9f01b1ef94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 6 Feb 2025 19:09:24 +0100 Subject: [PATCH 1/4] twister: allowlist build_info.yml and zephyr.dts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These files can be useful to troubleshoot a test that's failing, and they're really small, too. Signed-off-by: Benjamin Cabé --- scripts/pylib/twister/twisterlib/runner.py | 2 ++ scripts/tests/twister_blackbox/test_outfile.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/pylib/twister/twisterlib/runner.py b/scripts/pylib/twister/twisterlib/runner.py index 0593cbc7fc3a..2e402b66356c 100644 --- a/scripts/pylib/twister/twisterlib/runner.py +++ b/scripts/pylib/twister/twisterlib/runner.py @@ -1313,6 +1313,8 @@ def cleanup_artifacts(self, additional_keep: list[str] = None): 'recording.csv', 'rom.json', 'ram.json', + 'build_info.yml', + 'zephyr/zephyr.dts', # below ones are needed to make --test-only work as well 'Makefile', 'CMakeCache.txt', diff --git a/scripts/tests/twister_blackbox/test_outfile.py b/scripts/tests/twister_blackbox/test_outfile.py index 57a874041cbf..590e0c8f58a7 100644 --- a/scripts/tests/twister_blackbox/test_outfile.py +++ b/scripts/tests/twister_blackbox/test_outfile.py @@ -102,8 +102,8 @@ def test_runtime_artifact_cleanup(self, out_path): zephyr_listdir = os.listdir(os.path.join(sample_path, 'zephyr')) expected_contents = ['CMakeFiles', 'handler.log', 'build.ninja', 'CMakeCache.txt', - 'zephyr', 'build.log'] - expected_zephyr_contents = ['.config'] + 'zephyr', 'build.log', 'build_info.yml'] + expected_zephyr_contents = ['.config', 'zephyr.dts'] assert all([content in expected_zephyr_contents for content in zephyr_listdir]), \ 'Cleaned zephyr directory has unexpected files.' From d76911a7807723e78cdc289cd1f2f28afbc9ab14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 6 Dec 2024 14:09:59 +0100 Subject: [PATCH 2/4] samples: hello_world: set min RAM to 2KB for Hello World test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible to fit the Hello World in 2K of RAM so update the sample.yaml file accordingly. It might not *run*, but then the console harness will catch that. Signed-off-by: Benjamin Cabé --- samples/hello_world/sample.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/hello_world/sample.yaml b/samples/hello_world/sample.yaml index 1bcb7db62f25..1de267bbe3d8 100644 --- a/samples/hello_world/sample.yaml +++ b/samples/hello_world/sample.yaml @@ -3,6 +3,7 @@ sample: application name: hello world common: + min_ram: 2 tags: introduction integration_platforms: - native_sim From dcef6facf2df290503a6ca5d4e624b4d674e7f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 5 Feb 2025 18:08:44 +0100 Subject: [PATCH 3/4] ci: doc: add action-zephyr-setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setup a fully working Zephyr workspace so that documentation steps that require e.g. building Zephyr apps have the ability to do so. The "turbo" builds running on pull requests get a minimal setup, with no toolchain being installed. Signed-off-by: Benjamin Cabé --- .github/workflows/doc-build.yml | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index d263c1c80b6e..fa6d79766c2f 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -84,6 +84,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 + path: zephyr - name: Rebase if: github.event_name == 'pull_request' @@ -91,6 +92,7 @@ jobs: env: BASE_REF: ${{ github.base_ref }} PR_HEAD: ${{ github.event.pull_request.head.sha }} + working-directory: zephyr run: | git config --global user.email "actions@zephyrproject.org" git config --global user.name "Github Actions" @@ -100,25 +102,21 @@ jobs: git clean -f -d git log --graph --oneline HEAD...${PR_HEAD} - - name: cache-pip - uses: actions/cache@v4 + - name: Setup Zephyr project + uses: zephyrproject-rtos/action-zephyr-setup@v1 with: - path: ~/.cache/pip - key: pip-${{ hashFiles('doc/requirements.txt') }} + app-path: zephyr + toolchains: 'all' - name: install-pip + working-directory: zephyr run: | pip install -r doc/requirements.txt - pip install west==${WEST_VERSION} - pip install cmake==${CMAKE_VERSION} pip install coverxygen - - name: west setup - run: | - west init -l . - - name: build-docs shell: bash + working-directory: zephyr run: | if [[ "$GITHUB_REF" =~ "refs/tags/v" ]]; then DOC_TAG="release" @@ -144,6 +142,7 @@ jobs: genhtml --no-function-coverage --no-branch-coverage new.info -o coverage-report - name: compress-docs + working-directory: zephyr run: | tar --use-compress-program="xz -T0" -cf html-output.tar.xz --exclude html/_sources --exclude html/doxygen/xml --directory=doc/_build html tar --use-compress-program="xz -T0" -cf api-output.tar.xz --directory=doc/_build html/doxygen/html @@ -153,7 +152,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: html-output - path: html-output.tar.xz + path: zephyr/html-output.tar.xz - name: upload-api-coverage uses: actions/upload-artifact@v4 From 632aeb3c48d5b57936eca0d14e4e789f538404ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Sat, 12 Oct 2024 21:58:18 +0200 Subject: [PATCH 4/4] doc: extensions: boards: Add hardware features filter to board catalog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new hardware features filter to the board catalog that allows users to filter boards based on their supported hardware capabilities. The features are extracted from the devicetree files and organized by binding type. Key changes: - Extract hardware feature descriptions from devicetree bindings - Add HW_FEATURES_TURBO_MODE option to skip feature generation for faster builds (similar to DT_TURBO_MODE) - Add tag-based UI for filtering boards by hardware features The feature can be disabled for faster documentation builds using -DHW_FEATURES_TURBO_MODE=1 or by using 'make html-fast'. Signed-off-by: Benjamin Cabé --- boards/index.rst | 4 + doc/CMakeLists.txt | 19 ++- doc/Makefile | 8 +- doc/_extensions/zephyr/domain/__init__.py | 6 +- .../domain/static/css/board-catalog.css | 45 +++++- .../zephyr/domain/static/js/board-catalog.js | 120 ++++++++++++++- .../zephyr/domain/templates/board-card.html | 4 +- .../domain/templates/board-catalog.html | 16 +- doc/_scripts/gen_boards_catalog.py | 145 +++++++++++++++++- doc/_static/css/custom.css | 1 + doc/conf.py | 1 + doc/contribute/documentation/generation.rst | 12 +- 12 files changed, 356 insertions(+), 25 deletions(-) diff --git a/boards/index.rst b/boards/index.rst index 0643a355e590..9b83276abbfe 100644 --- a/boards/index.rst +++ b/boards/index.rst @@ -24,6 +24,10 @@ this page `. single field, selecting multiple options (such as two architectures) will show boards matching **either** option. + * The list of supported hardware features for each board is automatically generated using + information from the Devicetree. It may not be reflecting the full list of supported features + since some of them may not be enabled by default. + * Can't find your exact board? Don't worry! If a similar board with the same or a closely related MCU exists, you can use it as a :ref:`starting point ` for adding support for your own board. diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index bcabb520a79a..83b45a3a2a75 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -16,6 +16,7 @@ set(SPHINXOPTS "-j auto -W --keep-going -T" CACHE STRING "Default Sphinx Options set(SPHINXOPTS_EXTRA "" CACHE STRING "Extra Sphinx Options (added to defaults)") set(LATEXMKOPTS "-halt-on-error -no-shell-escape" CACHE STRING "Default latexmk options") set(DT_TURBO_MODE OFF CACHE BOOL "Enable DT turbo mode") +set(HW_FEATURES_TURBO_MODE OFF CACHE BOOL "Enable HW features turbo mode") set(DOC_TAG "development" CACHE STRING "Documentation tag") set(DTS_ROOTS "${ZEPHYR_BASE}" CACHE STRING "DT bindings root folders") @@ -149,6 +150,16 @@ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${GEN_DEVICETREE_ #------------------------------------------------------------------------------- # html +set(SPHINX_TAGS "${DOC_TAG}") +if(HW_FEATURES_TURBO_MODE) + list(APPEND SPHINX_TAGS "hw_features_turbo") +endif() + +set(SPHINX_TAGS_ARGS "") +foreach(tag ${SPHINX_TAGS}) + list(APPEND SPHINX_TAGS_ARGS "-t" "${tag}") +endforeach() + add_doc_target( html COMMAND ${CMAKE_COMMAND} -E env ${SPHINX_ENV} OUTPUT_DIR=${DOCS_HTML_DIR} @@ -157,7 +168,7 @@ add_doc_target( -c ${DOCS_CFG_DIR} -d ${DOCS_DOCTREE_DIR} -w ${DOCS_BUILD_DIR}/html.log - -t ${DOC_TAG} + ${SPHINX_TAGS_ARGS} ${SPHINXOPTS} ${SPHINXOPTS_EXTRA} ${DOCS_SRC_DIR} @@ -187,7 +198,7 @@ add_doc_target( -c ${DOCS_CFG_DIR} -d ${DOCS_DOCTREE_DIR} -w ${DOCS_BUILD_DIR}/html.log - -t ${DOC_TAG} + ${SPHINX_TAGS_ARGS} ${SPHINXOPTS} ${SPHINXOPTS_EXTRA} ${DOCS_SRC_DIR} @@ -214,7 +225,7 @@ add_doc_target( -c ${DOCS_CFG_DIR} -d ${DOCS_DOCTREE_DIR} -w ${DOCS_BUILD_DIR}/latex.log - -t ${DOC_TAG} + ${SPHINX_TAGS_ARGS} -t convertimages ${SPHINXOPTS} ${SPHINXOPTS_EXTRA} @@ -266,7 +277,7 @@ add_doc_target( -c ${DOCS_CFG_DIR} -d ${DOCS_DOCTREE_DIR} -w ${DOCS_BUILD_DIR}/linkcheck.log - -t ${DOC_TAG} + ${SPHINX_TAGS_ARGS} ${SPHINXOPTS} ${SPHINXOPTS_EXTRA} ${DOCS_SRC_DIR} diff --git a/doc/Makefile b/doc/Makefile index e1dd82d59cb6..4ce22f280f43 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -8,6 +8,7 @@ SPHINXOPTS ?= -j auto -W --keep-going -T SPHINXOPTS_EXTRA ?= LATEXMKOPTS ?= -halt-on-error -no-shell-escape DT_TURBO_MODE ?= 0 +HW_FEATURES_TURBO_MODE ?= 0 # ------------------------------------------------------------------------------ # Documentation targets @@ -15,10 +16,10 @@ DT_TURBO_MODE ?= 0 .PHONY: configure clean html html-fast html-live html-live-fast latex pdf doxygen html-fast: - ${MAKE} html DT_TURBO_MODE=1 + ${MAKE} html DT_TURBO_MODE=1 HW_FEATURES_TURBO_MODE=1 html-live-fast: - ${MAKE} html-live DT_TURBO_MODE=1 + ${MAKE} html-live DT_TURBO_MODE=1 HW_FEATURES_TURBO_MODE=1 html html-live latex pdf linkcheck doxygen: configure cmake --build ${BUILDDIR} --target $@ @@ -32,7 +33,8 @@ configure: -DSPHINXOPTS="${SPHINXOPTS}" \ -DSPHINXOPTS_EXTRA="${SPHINXOPTS_EXTRA}" \ -DLATEXMKOPTS="${LATEXMKOPTS}" \ - -DDT_TURBO_MODE=${DT_TURBO_MODE} + -DDT_TURBO_MODE=${DT_TURBO_MODE} \ + -DHW_FEATURES_TURBO_MODE=${HW_FEATURES_TURBO_MODE} clean: cmake --build ${BUILDDIR} --target clean diff --git a/doc/_extensions/zephyr/domain/__init__.py b/doc/_extensions/zephyr/domain/__init__.py index 5cdb7213ca93..bc8957de01e4 100644 --- a/doc/_extensions/zephyr/domain/__init__.py +++ b/doc/_extensions/zephyr/domain/__init__.py @@ -2,7 +2,7 @@ Zephyr Extension ################ -Copyright (c) 2023 The Linux Foundation +Copyright (c) 2023-2025 The Linux Foundation SPDX-License-Identifier: Apache-2.0 This extension adds a new ``zephyr`` domain for handling the documentation of various entities @@ -708,6 +708,7 @@ def run(self): "boards": domain_data["boards"], "vendors": domain_data["vendors"], "socs": domain_data["socs"], + "hw_features_present": self.env.app.config.zephyr_generate_hw_features, }, ) return [nodes.raw("", rendered, format="html")] @@ -954,7 +955,7 @@ def install_static_assets_as_needed( def load_board_catalog_into_domain(app: Sphinx) -> None: - board_catalog = get_catalog() + board_catalog = get_catalog(generate_hw_features=app.config.zephyr_generate_hw_features) app.env.domaindata["zephyr"]["boards"] = board_catalog["boards"] app.env.domaindata["zephyr"]["vendors"] = board_catalog["vendors"] app.env.domaindata["zephyr"]["socs"] = board_catalog["socs"] @@ -962,6 +963,7 @@ def load_board_catalog_into_domain(app: Sphinx) -> None: def setup(app): app.add_config_value("zephyr_breathe_insert_related_samples", False, "env") + app.add_config_value("zephyr_generate_hw_features", False, "env") app.add_domain(ZephyrDomain) diff --git a/doc/_extensions/zephyr/domain/static/css/board-catalog.css b/doc/_extensions/zephyr/domain/static/css/board-catalog.css index 9e5e33f7e7f7..36768a1be4ef 100644 --- a/doc/_extensions/zephyr/domain/static/css/board-catalog.css +++ b/doc/_extensions/zephyr/domain/static/css/board-catalog.css @@ -1,5 +1,5 @@ /** - * Copyright (c) 2024, The Linux Foundation. + * Copyright (c) 2024-2025, The Linux Foundation. * SPDX-License-Identifier: Apache-2.0 */ @@ -82,6 +82,49 @@ white-space: nowrap; } +.tag-container { + display: flex; + flex-wrap: wrap; + border: 1px solid #ccc; + border-radius: 50px; + padding: 5px 18px; +} + +.tag-container:focus-within { + border-color: var(--input-focus-border-color); +} + +.tag { + background-color: var(--admonition-note-background-color); + color: var(--admonition-note-color); + padding: 2px 12px 4px 16px; + border-radius: 30px; + display: inline-flex; + align-items: center; + cursor: pointer; + font-size: 14px; + margin-right: 8px; +} + +.tag:hover { + background-color: #0056b3; +} + +.tag::after { + content: '\00D7'; /* multiplication sign */ + margin-left: 8px; + font-size: 12px; + cursor: pointer; +} + +.filter-form input.tag-input { + flex: 1; + border: none; + padding: 5px; + outline: none; + background-color: transparent; +} + #catalog { display: flex; flex-wrap: wrap; diff --git a/doc/_extensions/zephyr/domain/static/js/board-catalog.js b/doc/_extensions/zephyr/domain/static/js/board-catalog.js index 6ac7ae946b8f..0e8c2767c7fc 100644 --- a/doc/_extensions/zephyr/domain/static/js/board-catalog.js +++ b/doc/_extensions/zephyr/domain/static/js/board-catalog.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2024, The Linux Foundation. + * Copyright (c) 2024-2025, The Linux Foundation. * SPDX-License-Identifier: Apache-2.0 */ @@ -29,6 +29,29 @@ function populateFormFromURL() { } }); + // Restore supported features from URL + if (hashParams.has("features")) { + const features = hashParams.get("features").split(","); + setTimeout(() => { + features.forEach(feature => { + const tagContainer = document.getElementById('tag-container'); + const tagInput = document.getElementById('tag-input'); + + const tagElement = document.createElement('span'); + tagElement.classList.add('tag'); + tagElement.textContent = feature; + tagElement.onclick = () => { + const selectedTags = [...document.querySelectorAll('.tag')].map(tag => tag.textContent); + selectedTags.splice(selectedTags.indexOf(feature), 1); + tagElement.remove(); + filterBoards(); + }; + tagContainer.insertBefore(tagElement, tagInput); + }); + filterBoards(); + }, 0); + } + filterBoards(); } @@ -47,6 +70,10 @@ function updateURL() { } }); + // Add supported features to URL + const selectedTags = [...document.querySelectorAll('.tag')].map(tag => tag.textContent); + selectedTags.length ? hashParams.set("features", selectedTags.join(",")) : hashParams.delete("features"); + window.history.replaceState({}, "", `#${hashParams.toString()}`); } @@ -84,6 +111,81 @@ function fillSocSocSelect(families, series = undefined, selectOnFill = false) { }); } +function setupHWCapabilitiesField() { + let selectedTags = []; + + const tagContainer = document.getElementById('tag-container'); + const tagInput = document.getElementById('tag-input'); + const datalist = document.getElementById('tag-list'); + + const tagCounts = Array.from(document.querySelectorAll('.board-card')).reduce((acc, board) => { + board.getAttribute('data-supported-features').split(' ').forEach(tag => { + acc[tag] = (acc[tag] || 0) + 1; + }); + return acc; + }, {}); + + const allTags = Object.keys(tagCounts).sort(); + + function addTag(tag) { + if (selectedTags.includes(tag) || tag === "" || !allTags.includes(tag)) return; + selectedTags.push(tag); + + const tagElement = document.createElement('span'); + tagElement.classList.add('tag'); + tagElement.textContent = tag; + tagElement.onclick = () => removeTag(tag); + tagContainer.insertBefore(tagElement, tagInput); + + tagInput.value = ''; + updateDatalist(); + } + + function removeTag(tag) { + selectedTags = selectedTags.filter(t => t !== tag); + document.querySelectorAll('.tag').forEach(el => { + if (el.textContent.includes(tag)) el.remove(); + }); + updateDatalist(); + } + + function updateDatalist() { + datalist.innerHTML = ''; + const filteredTags = allTags.filter(tag => !selectedTags.includes(tag)); + + filteredTags.forEach(tag => { + const option = document.createElement('option'); + option.value = tag; + datalist.appendChild(option); + }); + + filterBoards(); + } + + tagInput.addEventListener('input', () => { + if (allTags.includes(tagInput.value)) { + addTag(tagInput.value); + } + }); + + // Add tag when pressing the Enter key + tagInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && allTags.includes(tagInput.value)) { + addTag(tagInput.value); + e.preventDefault(); + } + }); + + // Delete tag when pressing the Backspace key + tagInput.addEventListener('keydown', (e) => { + if (e.key === 'Backspace' && tagInput.value === '' && selectedTags.length > 0) { + removeTag(selectedTags[selectedTags.length - 1]); + } + }); + + updateDatalist(); +} + document.addEventListener("DOMContentLoaded", function () { const form = document.querySelector(".filter-form"); @@ -101,9 +203,10 @@ document.addEventListener("DOMContentLoaded", function () { fillSocFamilySelect(); fillSocSeriesSelect(); fillSocSocSelect(); - populateFormFromURL(); + setupHWCapabilitiesField(); + socFamilySelect = document.getElementById("family"); socFamilySelect.addEventListener("change", () => { const selectedFamilies = [...socFamilySelect.selectedOptions].map(({ value }) => value); @@ -142,6 +245,11 @@ function resetForm() { fillSocFamilySelect(); fillSocSeriesSelect(); fillSocSocSelect(); + + // Clear supported features + document.querySelectorAll('.tag').forEach(tag => tag.remove()); + document.getElementById('tag-input').value = ''; + filterBoards(); } @@ -160,8 +268,10 @@ function filterBoards() { const vendorSelect = document.getElementById("vendor").value; const socSocSelect = document.getElementById("soc"); + const selectedTags = [...document.querySelectorAll('.tag')].map(tag => tag.textContent); + const resetFiltersBtn = document.getElementById("reset-filters"); - if (nameInput || archSelect || vendorSelect || socSocSelect.selectedOptions.length) { + if (nameInput || archSelect || vendorSelect || socSocSelect.selectedOptions.length || selectedTags.length) { resetFiltersBtn.classList.remove("btn-disabled"); } else { resetFiltersBtn.classList.add("btn-disabled"); @@ -174,6 +284,7 @@ function filterBoards() { const boardArchs = board.getAttribute("data-arch").split(" "); const boardVendor = board.getAttribute("data-vendor"); const boardSocs = board.getAttribute("data-socs").split(" "); + const boardSupportedFeatures = board.getAttribute("data-supported-features").split(" "); let matches = true; @@ -183,7 +294,8 @@ function filterBoards() { !(nameInput && !boardName.includes(nameInput)) && !(archSelect && !boardArchs.includes(archSelect)) && !(vendorSelect && boardVendor !== vendorSelect) && - (selectedSocs.length === 0 || selectedSocs.some((soc) => boardSocs.includes(soc))); + (selectedSocs.length === 0 || selectedSocs.some((soc) => boardSocs.includes(soc))) && + (selectedTags.length === 0 || selectedTags.every((tag) => boardSupportedFeatures.includes(tag))); board.classList.toggle("hidden", !matches); }); diff --git a/doc/_extensions/zephyr/domain/templates/board-card.html b/doc/_extensions/zephyr/domain/templates/board-card.html index 4c16a3c0cc5c..efc83cdcdabe 100644 --- a/doc/_extensions/zephyr/domain/templates/board-card.html +++ b/doc/_extensions/zephyr/domain/templates/board-card.html @@ -1,5 +1,5 @@ {# - Copyright (c) 2024, The Linux Foundation. + Copyright (c) 2024-2025, The Linux Foundation. SPDX-License-Identifier: Apache-2.0 #} @@ -15,7 +15,7 @@ data-arch="{{ board.archs | join(" ") }}" data-vendor="{{ board.vendor }}" data-socs="{{ board.socs | join(" ") }}" - tabindex="0"> + data-supported-features="{{ board.supported_features | join(" ") }}" tabindex="0">
{{ vendors[board.vendor] }}
{% if board.image -%} A picture of the {{ board.full_name }} board +
+ +
+ + +
+
+
diff --git a/doc/_scripts/gen_boards_catalog.py b/doc/_scripts/gen_boards_catalog.py index db17c67314fe..4408343dab5f 100644 --- a/doc/_scripts/gen_boards_catalog.py +++ b/doc/_scripts/gen_boards_catalog.py @@ -1,7 +1,10 @@ -# Copyright (c) 2024 The Linux Foundation +# Copyright (c) 2024-2025 The Linux Foundation # SPDX-License-Identifier: Apache-2.0 import logging +import os +import subprocess +import sys from collections import namedtuple from pathlib import Path @@ -9,6 +12,7 @@ import list_hardware import yaml import zephyr_module +from devicetree import edtlib from gen_devicetree_rest import VndLookup ZEPHYR_BASE = Path(__file__).parents[2] @@ -38,6 +42,7 @@ def guess_image(board_or_shield): return (img_file.relative_to(ZEPHYR_BASE)).as_posix() if img_file else None + def guess_doc_page(board_or_shield): patterns = [ "doc/index.{ext}", @@ -51,7 +56,98 @@ def guess_doc_page(board_or_shield): return doc_file -def get_catalog(): +def gather_board_devicetrees(twister_out_dir): + """Gather EDT objects for each board from twister output directory. + + Args: + twister_out_dir: Path object pointing to twister output directory + + Returns: + A dictionary mapping board names to a dictionary of board targets and their EDT objects. + The structure is: {board_name: {board_target: edt_object}} + """ + board_devicetrees = {} + + if not twister_out_dir.exists(): + return board_devicetrees + + # Find all build_info.yml files in twister-out + build_info_files = list(twister_out_dir.glob("*/**/build_info.yml")) + + for build_info_file in build_info_files: + # Look for corresponding zephyr.dts + dts_file = build_info_file.parent / "zephyr/zephyr.dts" + if not dts_file.exists(): + continue + + try: + with open(build_info_file) as f: + build_info = yaml.safe_load(f) + board_info = build_info.get('cmake', {}).get('board', {}) + board_name = board_info.get('name') + qualifier = board_info.get('qualifiers', '') + revision = board_info.get('revision', '') + + board_target = board_name + if qualifier: + board_target = f"{board_name}/{qualifier}" + if revision: + board_target = f"{board_target}@{revision}" + + # Create EDT object for this board's devicetree + try: + edt = edtlib.EDT( + str(dts_file), + [str(ZEPHYR_BASE / "dts/bindings")], + warn_reg_unit_address_mismatch=False + ) + board_devicetrees.setdefault(board_name, {})[board_target] = edt + except Exception as e: + logger.error(f"Error creating EDT for {dts_file}: {e}") + + except Exception as e: + logger.error(f"Error processing build info file {build_info_file}: {e}") + + return board_devicetrees + + +def run_twister_cmake_only(outdir): + """Run twister in cmake-only mode to generate build info files. + + Args: + outdir: Directory where twister should output its files + """ + twister_cmd = [ + sys.executable, + f"{ZEPHYR_BASE}/scripts/twister", + "-T", "samples/hello_world/", + "--all", + "-M", + "--cmake-only", + "--outdir", str(outdir), + ] + + minimal_env = { + 'PATH': os.environ.get('PATH', ''), + 'ZEPHYR_BASE': str(ZEPHYR_BASE), + 'HOME': os.environ.get('HOME', ''), + 'PYTHONPATH': os.environ.get('PYTHONPATH', '') + } + + try: + subprocess.run(twister_cmd, check=True, cwd=ZEPHYR_BASE, env=minimal_env) + except subprocess.CalledProcessError as e: + logger.warning(f"Failed to run Twister, list of hw features might be incomplete.\n{e}") + + +def get_catalog(generate_hw_features=False): + """Get the board catalog. + + Args: + generate_hw_features: If True, run twister to generate hardware features information. + """ + import tempfile + vnd_lookup = VndLookup(ZEPHYR_BASE / "dts/bindings/vendor-prefixes.txt", []) module_settings = { @@ -78,6 +174,15 @@ def get_catalog(): boards = list_boards.find_v2_boards(args_find_boards) systems = list_hardware.find_v2_systems(args_find_boards) board_catalog = {} + board_devicetrees = {} + + if generate_hw_features: + logger.info("Running twister in cmake-only mode to get Devicetree files for all boards") + with tempfile.TemporaryDirectory() as tmp_dir: + run_twister_cmake_only(tmp_dir) + board_devicetrees = gather_board_devicetrees(Path(tmp_dir)) + else: + logger.info("Skipping generation of supported hardware features.") for board in boards.values(): # We could use board.vendor but it is often incorrect. Instead, deduce vendor from @@ -91,6 +196,36 @@ def get_catalog(): vendor = folder.name break + socs = {soc.name for soc in board.socs} + full_name = board.full_name or board.name + doc_page = guess_doc_page(board) + + supported_features = {} + targets = set() + + # Use pre-gathered build info and DTS files + if board.name in board_devicetrees: + for board_target, edt in board_devicetrees[board.name].items(): + targets.add(board_target) + + okay_nodes = [ + node + for node in edt.nodes + if node.status == "okay" and node.matching_compat is not None + ] + + target_features = {} + for node in okay_nodes: + binding_path = Path(node.binding_path) + binding_type = binding_path.relative_to(ZEPHYR_BASE / "dts/bindings").parts[0] + target_features.setdefault(binding_type, set()).add(node.matching_compat) + + + # for now we do the union of all supported features for all of board's targets but + # in the future it's likely the catalog will be organized so that the list of + # supported features is also available per target. + supported_features.update(target_features) + # Grab all the twister files for this board and use them to figure out all the archs it # supports. archs = set() @@ -103,10 +238,6 @@ def get_catalog(): except Exception as e: logger.error(f"Error parsing twister file {twister_file}: {e}") - socs = {soc.name for soc in board.socs} - full_name = board.full_name or board.name - doc_page = guess_doc_page(board) - board_catalog[board.name] = { "name": board.name, "full_name": full_name, @@ -114,6 +245,8 @@ def get_catalog(): "vendor": vendor, "archs": list(archs), "socs": list(socs), + "supported_features": supported_features, + "targets": list(targets), "image": guess_image(board), } diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css index 586450fc6496..5671d6a05205 100644 --- a/doc/_static/css/custom.css +++ b/doc/_static/css/custom.css @@ -1,6 +1,7 @@ /** * Copyright (c) 2019-2020, Juan Linietsky, Ariel Manzur and the Godot community * Copyright (c) 2021, Teslabs Engineering S.L. + * Copyright (c) 2023-2025, The Linux Foundation. * SPDX-License-Identifier: CC-BY-3.0 * * Various tweaks to the Read the Docs theme to better conform with Zephyr's diff --git a/doc/conf.py b/doc/conf.py index a85028725629..43a228916b17 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -324,6 +324,7 @@ # -- Options for zephyr.domain -------------------------------------------- zephyr_breathe_insert_related_samples = True +zephyr_generate_hw_features = not tags.has("hw_features_turbo") # pylint: disable=undefined-variable # noqa: F821 # -- Options for sphinx.ext.graphviz -------------------------------------- diff --git a/doc/contribute/documentation/generation.rst b/doc/contribute/documentation/generation.rst index 7c88ccd1beb2..cad9201b113c 100644 --- a/doc/contribute/documentation/generation.rst +++ b/doc/contribute/documentation/generation.rst @@ -253,11 +253,19 @@ To enable this mode, set the following option when invoking cmake:: -DDT_TURBO_MODE=1 -or invoke make with the following target:: +Another step that typically takes a long time is the generation of the list of +supported features for each board. This can be disabled by setting the following +option when invoking cmake:: + + -DHW_FEATURES_TURBO_MODE=1 + +Invoking :command:`make` with the following target will build the documentation +without either of the aforementioned features:: cd ~/zephyrproject/zephyr/doc - # To generate HTML output without detailed Kconfig + # To generate HTML output without detailed Devicetree bindings documentation + # and supported features index make html-fast Viewing generated documentation locally