mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
268 lines
11 KiB
Python
268 lines
11 KiB
Python
#!/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:`<path>` 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-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',
|
|
# TODO: remove once the checker can also account for the ESP-USB programming guide.
|
|
'peripherals/usb/device/cherryusb_serial_device',
|
|
'peripherals/usb/device/tusb_composite_msc_serialdevice',
|
|
'peripherals/usb/device/tusb_console',
|
|
'peripherals/usb/device/tusb_hid',
|
|
'peripherals/usb/device/tusb_midi',
|
|
'peripherals/usb/device/tusb_msc',
|
|
'peripherals/usb/device/tusb_ncm',
|
|
'peripherals/usb/device/tusb_serial_device',
|
|
'peripherals/usb/host/cdc',
|
|
'peripherals/usb/host/cherryusb_host',
|
|
'peripherals/usb/host/hid',
|
|
'peripherals/usb/host/msc',
|
|
'peripherals/usb/host/usb_host_lib',
|
|
'peripherals/usb/host/uvc',
|
|
# 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 <path>`
|
|
_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:`<path>` 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())
|