From de5fb4f71fa587293a1cea460efe55181976089b Mon Sep 17 00:00:00 2001 From: Andrew Scheller Date: Fri, 25 Jul 2025 15:13:40 +0100 Subject: [PATCH] Doxygen "group" fixes (#2591) - Add missing libraries to docs/index.h - Remove duplicate libraries from docs/index.h - Add new script to match up doxygen groups with the PICO_CONFIG groups --- docs/index.h | 8 +-- tools/check_all_groups.py | 136 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 5 deletions(-) create mode 100755 tools/check_all_groups.py diff --git a/docs/index.h b/docs/index.h index be52f6a0..5506b3a3 100644 --- a/docs/index.h +++ b/docs/index.h @@ -15,7 +15,7 @@ * @{ * \cond hardware_adc \defgroup hardware_adc hardware_adc \endcond * \cond hardware_base \defgroup hardware_base hardware_base \endcond - * \cond hardware_bootlock \defgroup hardware_bootlock hardware_bootlock \endcond + * \cond hardware_boot_lock \defgroup hardware_boot_lock hardware_boot_lock \endcond * \cond hardware_claim \defgroup hardware_claim hardware_claim \endcond * \cond hardware_clocks \defgroup hardware_clocks hardware_clocks \endcond * \cond hardware_divider \defgroup hardware_divider hardware_divider \endcond @@ -32,7 +32,6 @@ * \cond hardware_pll \defgroup hardware_pll hardware_pll \endcond * \cond hardware_powman \defgroup hardware_powman hardware_powman \endcond * \cond hardware_pwm \defgroup hardware_pwm hardware_pwm \endcond - * \cond hardware_pwm \defgroup hardware_pwm hardware_pwm \endcond * \cond hardware_resets \defgroup hardware_resets hardware_resets \endcond * \cond hardware_riscv \defgroup hardware_riscv hardware_riscv \endcond * \cond hardware_riscv_platform_timer \defgroup hardware_riscv_platform_timer hardware_riscv_platform_timer \endcond @@ -44,13 +43,11 @@ * \cond hardware_ticks \defgroup hardware_ticks hardware_ticks \endcond * \cond hardware_timer \defgroup hardware_timer hardware_timer \endcond * \cond hardware_uart \defgroup hardware_uart hardware_uart \endcond + * \cond hardware_usb \defgroup hardware_usb hardware_usb \endcond * \cond hardware_vreg \defgroup hardware_vreg hardware_vreg \endcond * \cond hardware_watchdog \defgroup hardware_watchdog hardware_watchdog \endcond * \cond hardware_xip_cache \defgroup hardware_xip_cache hardware_xip_cache \endcond * \cond hardware_xosc \defgroup hardware_xosc hardware_xosc \endcond - * \cond hardware_powman \defgroup hardware_powman hardware_powman \endcond - * \cond hardware_hazard3 \defgroup hardware_hazard3 hardware_hazard3 \endcond - * \cond hardware_riscv \defgroup hardware_riscv hardware_riscv \endcond * @} * @@ -61,6 +58,7 @@ * \cond pico_aon_timer \defgroup pico_aon_timer pico_aon_timer \endcond * \cond pico_async_context \defgroup pico_async_context pico_async_context \endcond * \cond pico_bootsel_via_double_reset \defgroup pico_bootsel_via_double_reset pico_bootsel_via_double_reset \endcond + * \cond pico_fix \defgroup pico_fix pico_fix \endcond * \cond pico_flash \defgroup pico_flash pico_flash \endcond * \cond pico_i2c_slave \defgroup pico_i2c_slave pico_i2c_slave \endcond * \cond pico_multicore \defgroup pico_multicore pico_multicore \endcond diff --git a/tools/check_all_groups.py b/tools/check_all_groups.py new file mode 100755 index 00000000..3a0d0bf6 --- /dev/null +++ b/tools/check_all_groups.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2025 Raspberry Pi Ltd +# +# SPDX-License-Identifier: BSD-3-Clause +# +# +# Script to check that the "groups" in various contexts all match up. (like an enhanced version of check_doxygen_groups.py) +# Note that it only reports the *first* instance of a missing group, not all occurrences. +# +# Usage: +# +# tools/check_all_groups.py +# (you'll probably want to pipe the output through ` | grep -v cmsis` ) + +import re +import sys +import os + +scandir = sys.argv[1] + +DEFGROUP_NAME = r'\defgroup' +DEFGROUP_RE = re.compile(r'{}\s+(\w+)'.format(re.escape(DEFGROUP_NAME))) +INGROUP_NAME = r'\ingroup' +INGROUP_RE = re.compile(r'{}\s+(\w+)'.format(re.escape(INGROUP_NAME))) +DOCS_INDEX_HEADER = 'docs/index.h' + +BASE_CONFIG_NAME = 'PICO_CONFIG' +CONFIG_RE = re.compile(r'//\s+{}:\s+(\w+),\s+([^,]+)(?:,\s+(.*))?$'.format(BASE_CONFIG_NAME)) +BASE_CMAKE_CONFIG_NAME = 'PICO_CMAKE_CONFIG' +CMAKE_CONFIG_RE = re.compile(r'#\s+{}:\s+([\w-]+),\s+([^,]+)(?:,\s+(.*))?$'.format(BASE_CMAKE_CONFIG_NAME)) +BASE_BUILD_DEFINE_NAME = 'PICO_BUILD_DEFINE' +BUILD_DEFINE_RE = re.compile(r'#\s+{}:\s+(\w+),\s+([^,]+)(?:,\s+(.*))?$'.format(BASE_BUILD_DEFINE_NAME)) +BASE_GROUP_NAME = 'group=' +GROUP_RE = re.compile(r'\b{}(\w+)\b'.format(BASE_GROUP_NAME)) + +def_groups = {} +in_groups = {} +doc_groups = {} +config_groups = {} +cmake_config_groups = {} +build_define_groups = {} +any_errors = False + +def get_group_from_config_attrs(attr_str): + m = GROUP_RE.search(attr_str) + if m: + return m.group(1) + +# Scan all .c and .h and .S and .cmake and CMakeLists.txt files in the specific path, recursively. + +for dirpath, dirnames, filenames in os.walk(scandir): + for filename in filenames: + file_ext = os.path.splitext(filename)[1] + if filename == 'CMakeLists.txt' or file_ext in ('.c', '.h', '.S', '.cmake'): + file_path = os.path.join(dirpath, filename) + with open(file_path) as fh: + for line in fh.readlines(): + m = DEFGROUP_RE.search(line) + if m: + group = m.group(1) + if file_path.endswith(DOCS_INDEX_HEADER): + if group in doc_groups: + any_errors = True + print("{} uses {} {} but so does {}".format(doc_groups[group], DEFGROUP_NAME, group, file_path)) + else: + doc_groups[group] = file_path + else: + if group in def_groups: + any_errors = True + print("{} uses {} {} but so does {}".format(def_groups[group], DEFGROUP_NAME, group, file_path)) + else: + def_groups[group] = file_path + else: + m = INGROUP_RE.search(line) + if m: + group = m.group(1) + if group not in in_groups: + in_groups[group] = file_path + else: + m = CONFIG_RE.search(line) + if m: + group = get_group_from_config_attrs(m.group(3)) + if group not in config_groups: + config_groups[group] = file_path + else: + m = CMAKE_CONFIG_RE.search(line) + if m: + group = get_group_from_config_attrs(m.group(3)) + if group not in cmake_config_groups: + cmake_config_groups[group] = file_path + else: + m = BUILD_DEFINE_RE.search(line) + if m: + group = get_group_from_config_attrs(m.group(3)) + if group not in build_define_groups: + build_define_groups[group] = file_path + +seen_groups = set() +for group, file_path in in_groups.items(): + seen_groups.add(group) + if group not in def_groups and group not in doc_groups: + any_errors = True + print("{} uses {} {} which was never defined".format(file_path, INGROUP_NAME, group)) +for group, file_path in config_groups.items(): + seen_groups.add(group) + if group not in def_groups and group not in doc_groups: + any_errors = True + print("{} uses {} {}{} which was never defined".format(file_path, BASE_CONFIG_NAME, BASE_GROUP_NAME, group)) +for group, file_path in cmake_config_groups.items(): + seen_groups.add(group) + if group == 'build': # dummy group + continue + if group not in def_groups and group not in doc_groups: + any_errors = True + print("{} uses {} {}{} which was never defined".format(file_path, BASE_CMAKE_CONFIG_NAME, BASE_GROUP_NAME, group)) +for group, file_path in build_define_groups.items(): + seen_groups.add(group) + if group == 'build': # dummy group + continue + if group not in def_groups and group not in doc_groups: + any_errors = True + print("{} uses {} {}{} which was never defined".format(file_path, BASE_BUILD_DEFINE_NAME, BASE_GROUP_NAME, group)) + +for group in doc_groups.keys(): + seen_groups.add(group) + if group not in seen_groups and group not in def_groups: + any_errors = True + print("{} uses {} {} which doesn't appear anywhere else".format(DOCS_INDEX_HEADER, DEFGROUP_NAME, group)) + +unused_groups = set(def_groups.keys()) - seen_groups +if unused_groups: + any_errors = True + print("The following groups were defined with {} but never referenced:\n{}".format(DEFGROUP_NAME, sorted(unused_groups))) + +sys.exit(any_errors)