diff --git a/.gitlab/ci/pre_check.yml b/.gitlab/ci/pre_check.yml index dcf8a2533c..df14ff6080 100644 --- a/.gitlab/ci/pre_check.yml +++ b/.gitlab/ci/pre_check.yml @@ -38,6 +38,11 @@ check_wifi_remote_api: - python generate_and_check.py - git diff --exit-code || { echo 'Differences found. Please run generate_and_check.py and commit the changes.'; exit 1; } +check_examples_documented: + extends: .pre_check_template + script: + - python3 tools/ci/check_examples_documented.py + check_blobs: extends: - .pre_check_template diff --git a/docs/en/api-guides/build-system-v2.rst b/docs/en/api-guides/build-system-v2.rst index 747474b769..1c5be6a7ef 100644 --- a/docs/en/api-guides/build-system-v2.rst +++ b/docs/en/api-guides/build-system-v2.rst @@ -7,7 +7,7 @@ Build System v2 ESP-IDF CMake-based build system v2, referred to in this documentation simply as v2 or build system, is a successor to the original CMake-based :doc:`/api-guides/build-system`, referred to as v1. The v2 addresses limitations introduced in the previous version while trying to maintain backward compatibility for components written for v1. The most significant changes include the ability to use Kconfig variables to specify component dependencies, the removal of early component evaluation using CMake script mode, and support for writing components using the native CMake approach. While v2 aims to be as backward compatible with v1 as possible, meaning most components written for v1 should work without modification with v2, there are design differences between v1 and v2 that may require changes in v1 components to work with v2. The incompatibilities are described in :ref:`cmakev2-breaking-changes`. -Example applications for Build System v2 are described in the :idf_file:`Build System v2 examples README `. +Example applications for Build System v2 are described in the :example:`Build System v2 examples README `. Creating a New Project ====================== diff --git a/docs/en/api-reference/system/sleep_modes.rst b/docs/en/api-reference/system/sleep_modes.rst index a141c153ba..40df171e94 100644 --- a/docs/en/api-reference/system/sleep_modes.rst +++ b/docs/en/api-reference/system/sleep_modes.rst @@ -527,6 +527,7 @@ Application Examples :esp32h2: - :example:`system/deep_sleep` demonstrates the usage of Deep-sleep wakeup triggered by various sources, such as the RTC timer, EXT0, EXT1, supported by ESP32-H2. - :example:`system/light_sleep` demonstrates the usage of Light-sleep wakeup triggered by various sources, such as the timer, GPIOs, supported by {IDF_TARGET_NAME}. :SOC_TOUCH_SENSOR_SUPPORTED and SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP: - :example:`peripherals/touch_sensor/touch_sens_sleep` demonstrates the usage of Light-sleep and Deep-sleep wakeup triggered by the touch sensor. + :SOC_VBAT_SUPPORTED: - :example:`lowpower/vbat` demonstrates the use of backup battery power (VBAT) during Deep-sleep, allowing the RTC timer to keep running after the main power is removed. API Reference ------------- diff --git a/docs/zh_CN/api-reference/system/sleep_modes.rst b/docs/zh_CN/api-reference/system/sleep_modes.rst index 4e414c7477..1bc91a249a 100644 --- a/docs/zh_CN/api-reference/system/sleep_modes.rst +++ b/docs/zh_CN/api-reference/system/sleep_modes.rst @@ -527,6 +527,7 @@ UART 输出处理 :esp32h2: - :example:`system/deep_sleep` 演示如何通过 ESP32-H2 的唤醒源,如 RTC 定时器、EXT0、EXT1 等,触发 Deep-sleep 唤醒。 - :example:`system/light_sleep` 演示如何使用 {IDF_TARGET_NAME} 的唤醒源,如定时器,GPIO 等,触发 Light-sleep 唤醒。 :SOC_TOUCH_SENSOR_SUPPORTED and SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP: - :example:`peripherals/touch_sensor/touch_sens_sleep` 演示如何使用触摸传感器唤醒 Light-sleep 或 Deep-sleep。 + :SOC_VBAT_SUPPORTED: - :example:`lowpower/vbat` 演示如何在 Deep-sleep 期间使用备用电池电源(VBAT),使 RTC 定时器在主电源断开后继续运行。 API 参考 ------------- diff --git a/tools/ci/check_examples_documented.py b/tools/ci/check_examples_documented.py new file mode 100644 index 0000000000..4f2ada41a0 --- /dev/null +++ b/tools/ci/check_examples_documented.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python3 +# +# Checks that every IDF example is referenced in the programming guide (docs/en/). +# Each example must be referenced exactly by its own path; parent-directory references do not count. +# +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import os +import re +import sys +from pathlib import Path + +# Examples that are not yet referenced in the programming guide. +# These must be fixed: add a :example:`` reference in docs/en/ and remove from here. +KNOWN_MISSING = { + # TODO IDF-15374: add :example: references for ble_get_started examples + 'bluetooth/ble_get_started/bluedroid/Bluedroid_Beacon', + 'bluetooth/ble_get_started/bluedroid/Bluedroid_Connection', + 'bluetooth/ble_get_started/bluedroid/Bluedroid_GATT_Server', + 'bluetooth/ble_get_started/nimble/NimBLE_Security', + # TODO IDF-15375: add :example: references for zigbee and ieee802154 examples + 'ieee802154/ieee802154_cli', + 'zigbee/esp_zigbee_gateway', + 'zigbee/light_sample/HA_on_off_light', + 'zigbee/light_sample/HA_on_off_switch', + # TODO IDF-15376: add :example: reference for security_features_app + 'security/security_features_app', + # TODO IDF-15380: add :example: references for bluetooth examples + 'bluetooth/bluedroid/ble/ble_acl_latency/cent', + 'bluetooth/bluedroid/ble/ble_acl_latency/periph', + 'bluetooth/bluedroid/ble/ble_ancs', + 'bluetooth/bluedroid/ble/ble_compatibility_test', + 'bluetooth/bluedroid/ble/ble_eddystone_receiver', + 'bluetooth/bluedroid/ble/ble_eddystone_sender', + 'bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent', + 'bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph', + 'bluetooth/bluedroid/ble/ble_hid_device_demo', + 'bluetooth/bluedroid/ble/ble_ibeacon', + 'bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent', + 'bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph', + 'bluetooth/bluedroid/ble/ble_spp_client', + 'bluetooth/bluedroid/ble/ble_spp_server', + 'bluetooth/bluedroid/ble/ble_throughput/throughput_client', + 'bluetooth/bluedroid/ble/ble_throughput/throughput_server', + 'bluetooth/bluedroid/ble_50/ble50_security_client', + 'bluetooth/bluedroid/ble_50/ble50_security_server', + 'bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client', + 'bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server', + 'bluetooth/bluedroid/ble_50/ble_conn_subrating_central', + 'bluetooth/bluedroid/ble_50/ble_conn_subrating_peripheral', + 'bluetooth/bluedroid/ble_50/ble_connection_central_with_cte', + 'bluetooth/bluedroid/ble_50/ble_connection_peripheral_with_cte', + 'bluetooth/bluedroid/ble_50/ble_pawr_advertiser', + 'bluetooth/bluedroid/ble_50/ble_pawr_advertiser_conn', + 'bluetooth/bluedroid/ble_50/ble_pawr_synchronizer', + 'bluetooth/bluedroid/ble_50/ble_periodic_adv_with_cte', + 'bluetooth/bluedroid/ble_50/ble_periodic_sync_with_cte', + 'bluetooth/bluedroid/ble_50/ble_power_control_central', + 'bluetooth/bluedroid/ble_50/ble_power_control_peripheral', + 'bluetooth/bluedroid/ble_50/multi-adv', + 'bluetooth/bluedroid/ble_50/periodic_adv', + 'bluetooth/bluedroid/ble_50/periodic_sync', + 'bluetooth/bluedroid/bluedroid_host_only/bluedroid_host_only_uart', + 'bluetooth/esp_ble_mesh/aligenie_demo', + 'bluetooth/esp_ble_mesh/directed_forwarding/df_client', + 'bluetooth/esp_ble_mesh/directed_forwarding/df_server', + 'bluetooth/nimble/ble_ancs', + 'bluetooth/nimble/ble_chan_sound_initiator', + 'bluetooth/nimble/ble_chan_sound_reflector', + 'bluetooth/nimble/ble_cte/ble_periodic_adv_with_cte', + 'bluetooth/nimble/ble_cte/ble_periodic_sync_with_cte', + 'bluetooth/nimble/ble_cts/cts_cent', + 'bluetooth/nimble/ble_cts/cts_prph', + 'bluetooth/nimble/ble_dynamic_service', + 'bluetooth/nimble/ble_enc_adv_data/enc_adv_data_cent', + 'bluetooth/nimble/ble_enc_adv_data/enc_adv_data_prph', + 'bluetooth/nimble/ble_gattc_gatts_coex', + 'bluetooth/nimble/ble_htp/htp_cent', + 'bluetooth/nimble/ble_htp/htp_prph', + 'bluetooth/nimble/ble_l2cap_coc/coc_blecent', + 'bluetooth/nimble/ble_l2cap_coc/coc_bleprph', + 'bluetooth/nimble/ble_multi_adv', + 'bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent', + 'bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph', + 'bluetooth/nimble/ble_pawr_adv/ble_pawr_adv', + 'bluetooth/nimble/ble_pawr_adv/ble_pawr_sync', + 'bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn', + 'bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn', + 'bluetooth/nimble/ble_periodic_adv', + 'bluetooth/nimble/ble_periodic_sync', + 'bluetooth/nimble/ble_phy/phy_cent', + 'bluetooth/nimble/ble_phy/phy_prph', + 'bluetooth/nimble/ble_proximity_sensor/proximity_sensor_cent', + 'bluetooth/nimble/ble_proximity_sensor/proximity_sensor_prph', + 'bluetooth/nimble/ble_spp/spp_client', + 'bluetooth/nimble/ble_spp/spp_server', + 'bluetooth/nimble/blecent', + 'bluetooth/nimble/blecsc', + 'bluetooth/nimble/blehr', + 'bluetooth/nimble/blemesh', + 'bluetooth/nimble/bleprph', + 'bluetooth/nimble/bleprph_host_only', + 'bluetooth/nimble/bleprph_wifi_coex', + 'bluetooth/nimble/hci', + 'bluetooth/nimble/throughput_app/blecent_throughput', + 'bluetooth/nimble/throughput_app/bleprph_throughput', + # TODO IDF-15385: add :example: references for build_system examples + 'build_system/cmake/import_prebuilt/prebuilt', + 'build_system/cmakev2/features/component_manager', + 'build_system/cmakev2/features/conditional_component', + 'build_system/cmakev2/features/idf_as_lib', + 'build_system/cmakev2/features/import_lib', + 'build_system/cmakev2/features/import_lib_direct', + 'build_system/cmakev2/features/import_prebuilt', + 'build_system/cmakev2/features/import_prebuilt/prebuilt', + 'build_system/cmakev2/features/multi_binary', + 'build_system/cmakev2/features/multi_config', + 'build_system/cmakev2/features/plugins', + 'build_system/cmakev2/get-started/hello_world', + # TODO IDF-15381: add :example: references for system and custom_bootloader examples + 'custom_bootloader/bootloader_multiboot', + 'system/gcov', + 'system/gdbstub', + 'system/rt_mqueue', + 'system/sysview_tracing', + 'system/sysview_tracing_heap_log', + 'system/ulp/lp_core/build_system', + 'system/ulp/lp_core/build_system/main/ulp', + 'system/ulp/lp_core/debugging/main/ulp', + 'system/ulp/lp_core/gpio_wakeup', + 'system/ulp/lp_core/inter_cpu_critical_section', + 'system/ulp/lp_core/lp_adc', + 'system/ulp/lp_core/lp_spi', + 'system/ulp/lp_core/lp_timer_interrupt', + 'system/ulp/lp_core/lp_touch', + 'system/ulp/ulp_riscv/gpio_pulse_counter', + 'system/unit_test/test', + # TODO IDF-15382: add :example: references for peripherals examples + 'peripherals/camera/dvp_dsi', + 'peripherals/camera/dvp_spi_lcd', + 'peripherals/h264', + 'peripherals/i2s/i2s_advance/i2s_usb', + 'peripherals/spi_slave/receiver', + 'peripherals/spi_slave/sender', + 'peripherals/spi_slave_hd/append_mode/master', + 'peripherals/spi_slave_hd/append_mode/slave', + 'peripherals/spi_slave_hd/segment_mode/seg_master', + 'peripherals/spi_slave_hd/segment_mode/seg_slave', + 'peripherals/twai/twai_network/twai_listen_only', + 'peripherals/twai/twai_network/twai_sender', + 'peripherals/usb/device/cherryusb_serial_device', + 'peripherals/usb/host/cherryusb_host', + # TODO IDF-15383: add :example: references for protocols examples + 'protocols/dns_over_https', + 'protocols/http_request', + 'protocols/icmp/pmtu_probe', + 'protocols/mqtt', + 'protocols/mqtt5', + 'protocols/smtp_client', + 'protocols/sockets/tcp_transport_client', + # TODO IDF-15384: add :example: references for wifi examples + 'wifi/ftm', + 'wifi/wifi_nvs_config', +} + +# Matches :example:`path` and :example:`label ` +_EXAMPLE_ROLE_RE = re.compile(r':example:`(?:[^<`]*<([^>]+)>|([^`]+))`') + + +def find_leaf_examples(examples_dir: Path) -> list[str]: + """Return relative paths of all leaf examples (those containing a project() call).""" + results = [] + for cmake_file in examples_dir.rglob('CMakeLists.txt'): + # Skip managed_components and build dirs + parts = cmake_file.parts + if 'managed_components' in parts or 'build' in parts: + continue + if cmake_file.read_text().startswith('project(') or '\nproject(' in cmake_file.read_text(): + rel = cmake_file.parent.relative_to(examples_dir) + results.append(str(rel)) + return sorted(results) + + +def collect_doc_example_refs(docs_dir: Path) -> set[str]: + """Return the set of example paths referenced in .rst files under docs_dir.""" + refs: set[str] = set() + for rst_file in docs_dir.rglob('*.rst'): + text = rst_file.read_text(errors='replace') + for m in _EXAMPLE_ROLE_RE.finditer(text): + path = (m.group(1) or m.group(2)).strip() + refs.add(path) + return refs + + +def is_covered(example: str, refs: set[str]) -> bool: + """An example is covered only if it is referenced exactly by its own path.""" + return example in refs + + +def main() -> int: + parser = argparse.ArgumentParser(description='Check that all IDF examples are referenced in the programming guide.') + parser.add_argument( + '--idf-path', + default=os.environ.get('IDF_PATH', '.'), + help='Path to the IDF repository root (default: $IDF_PATH or .)', + ) + args = parser.parse_args() + + idf_path = Path(args.idf_path) + examples_dir = idf_path / 'examples' + docs_dir = idf_path / 'docs' / 'en' + + if not examples_dir.is_dir(): + print(f'ERROR: examples directory not found: {examples_dir}', file=sys.stderr) + return 1 + if not docs_dir.is_dir(): + print(f'ERROR: docs/en directory not found: {docs_dir}', file=sys.stderr) + return 1 + + examples = find_leaf_examples(examples_dir) + refs = collect_doc_example_refs(docs_dir) + + newly_missing = [] + fixed = [] + + for ex in examples: + covered = is_covered(ex, refs) + if not covered and ex not in KNOWN_MISSING: + newly_missing.append(ex) + elif covered and ex in KNOWN_MISSING: + fixed.append(ex) + + if fixed: + print('The following examples were in KNOWN_MISSING but are now referenced in docs.') + print('Please remove them from KNOWN_MISSING in tools/ci/check_examples_documented.py:') + for ex in fixed: + print(f' {ex}') + print() + + if newly_missing: + print('ERROR: The following examples are not referenced anywhere in the programming guide (docs/en/).') + print('Add a :example:`` role to an appropriate .rst file in docs/en/, then run this check again.') + print() + for ex in newly_missing: + print(f' {ex}') + return 1 + + if not fixed: + print(f'OK: all {len(examples)} examples are referenced in the programming guide.') + return 1 if fixed else 0 + + +if __name__ == '__main__': + sys.exit(main())