From d670774f5c9e7ae476cfb6921aa5b359cf3c99f7 Mon Sep 17 00:00:00 2001 From: Guillaume Souchere Date: Mon, 2 Mar 2026 12:18:44 +0100 Subject: [PATCH 1/2] feat(esp_common): implement composable error code registration via link-time arrays Refactor the esp_err_to_name() system to decouple esp_common from higher-level components. Instead of a monolithic generated table, each component registers its error codes into a dedicated linker section (.esp_err_msg_table) via idf_define_esp_err_codes() in its CMakeLists.txt. New files: - tools/err_codes_extract.py: extract ESP_ERR_* defines from headers to CSV - tools/err_codes_to_c.py: generate C source placing entries into linker section - tools/err_codes_to_rst.py: generate RST documentation from error codes - tools/cmake/err_codes.cmake: CMake module providing idf_define_esp_err_codes() - components/esp_common/include/esp_err_codes.h: esp_err_msg_t typedef - components/esp_common/src/esp_err_to_name_new.c: new lookup using link-time array - tools/test_apps/build_system/err_codes_check/: CI test app Changes: - Remove all optional component dependencies from esp_common/CMakeLists.txt - Add .esp_err_msg_table section to all 5 linker scripts - Register error codes in 18 components via idf_define_esp_err_codes() - Add new scripts to .gitlab/ci/rules.yml build_check patterns - use new scripts to generate doc and add CI validation - Update esp_err.rst to add description of composable code registration --- .gitlab/ci/pre_check.yml | 14 +- .gitlab/ci/rules.yml | 3 + components/app_update/CMakeLists.txt | 2 + components/bootloader_support/CMakeLists.txt | 2 + components/efuse/CMakeLists.txt | 1 + components/esp-tls/CMakeLists.txt | 1 + components/esp_common/CMakeLists.txt | 40 +- components/esp_common/include/esp_err.h | 24 +- components/esp_common/include/esp_err_msg.h | 29 + components/esp_common/linker.lf | 13 + components/esp_common/linux/sections.ld | 16 + components/esp_common/src/esp_err_to_name.c | 910 +----------------- .../esp_common/src/esp_err_to_name.c.in | 71 -- .../test_apps/esp_common/main/CMakeLists.txt | 1 + .../esp_common/main/test_esp_err_to_name.c | 81 ++ components/esp_hal_mspi/CMakeLists.txt | 2 + components/esp_http_client/CMakeLists.txt | 2 + components/esp_http_server/CMakeLists.txt | 2 + components/esp_https_ota/CMakeLists.txt | 2 + components/esp_hw_support/CMakeLists.txt | 2 + components/esp_netif/CMakeLists.txt | 2 + components/esp_security/CMakeLists.txt | 2 + components/esp_wifi/CMakeLists.txt | 2 + components/nvs_flash/CMakeLists.txt | 2 + components/nvs_sec_provider/CMakeLists.txt | 2 + components/spi_flash/CMakeLists.txt | 2 + components/tcp_transport/CMakeLists.txt | 2 + components/ulp/CMakeLists.txt | 2 + components/wpa_supplicant/CMakeLists.txt | 2 + docs/en/api-reference/system/esp_err.rst | 70 ++ tools/ci/mypy_ignore_list.txt | 1 - tools/cmake/err_codes.cmake | 153 +++ tools/cmake/idf.cmake | 1 + tools/cmake/project.cmake | 28 +- tools/cmakev2/idf.cmake | 5 + tools/err_codes_extract.py | 377 ++++++++ tools/err_codes_to_c.py | 142 +++ tools/err_codes_to_rst.py | 98 ++ tools/gen_esp_err_to_name.py | 386 +------- .../build_system/.build-test-rules.yml | 4 + .../err_codes_check/CMakeLists.txt | 14 + .../build_system/err_codes_check/README.md | 25 + .../err_codes_check/gen_err_codes_test.py | 78 ++ .../err_codes_check/main/CMakeLists.txt | 33 + .../err_codes_check/main/test_main.c | 24 + .../err_codes_check/pytest_err_codes_check.py | 25 + 46 files changed, 1333 insertions(+), 1367 deletions(-) create mode 100644 components/esp_common/include/esp_err_msg.h create mode 100644 components/esp_common/linker.lf create mode 100644 components/esp_common/linux/sections.ld delete mode 100644 components/esp_common/src/esp_err_to_name.c.in create mode 100644 components/esp_common/test_apps/esp_common/main/test_esp_err_to_name.c create mode 100644 tools/cmake/err_codes.cmake create mode 100644 tools/err_codes_extract.py create mode 100644 tools/err_codes_to_c.py create mode 100644 tools/err_codes_to_rst.py create mode 100644 tools/test_apps/build_system/err_codes_check/CMakeLists.txt create mode 100644 tools/test_apps/build_system/err_codes_check/README.md create mode 100644 tools/test_apps/build_system/err_codes_check/gen_err_codes_test.py create mode 100644 tools/test_apps/build_system/err_codes_check/main/CMakeLists.txt create mode 100644 tools/test_apps/build_system/err_codes_check/main/test_main.c create mode 100644 tools/test_apps/build_system/err_codes_check/pytest_err_codes_check.py diff --git a/.gitlab/ci/pre_check.yml b/.gitlab/ci/pre_check.yml index eae469c5b80..884d32c9b6e 100644 --- a/.gitlab/ci/pre_check.yml +++ b/.gitlab/ci/pre_check.yml @@ -112,19 +112,15 @@ check_chip_support_components: - find ${IDF_PATH}/components/soc \( -path "*/include/soc/*_struct.h" -o -path "*/register/soc/*_struct.h" -o -path "*/register/*/soc/*_struct.h" \) -print0 | xargs -0 -n1 ./tools/ci/check_soc_struct_headers.py - tools/ci/check_esp_memory_utils_headers.sh -check_esp_err_to_name: +check_esp_err_codes: extends: - .pre_check_template - .rules:build:check - artifacts: - when: on_failure - paths: - - components/esp_common/esp_err_to_name.c - expire_in: 1 week script: - - cd ${IDF_PATH}/tools/ - - ./gen_esp_err_to_name.py - - git diff --exit-code -- ../components/esp_common/src/esp_err_to_name.c || { echo 'Differences found. Please run gen_esp_err_to_name.py and commit the changes.'; exit 1; } + - cd ${IDF_PATH} + - python tools/err_codes_extract.py --search-dirs components/ --output /tmp/err_codes.csv --validate + - python tools/err_codes_to_rst.py --csv /tmp/err_codes.csv --output /tmp/esp_err_defs.inc + - echo "Extracted $(wc -l < /tmp/err_codes.csv) error codes" check_esp_system: extends: diff --git a/.gitlab/ci/rules.yml b/.gitlab/ci/rules.yml index 16c0a9a7ff9..b709aa4b301 100644 --- a/.gitlab/ci/rules.yml +++ b/.gitlab/ci/rules.yml @@ -66,6 +66,9 @@ - "tools/test_build_system/**/*" - "tools/ci/test_configure_ci_environment.sh" - "tools/gen_esp_err_to_name.py" + - "tools/err_codes_extract.py" + - "tools/err_codes_to_c.py" + - "tools/err_codes_to_rst.py" - "tools/ci/check_soc_headers_leak.py" - "tools/ci/check_esp_memory_utils_headers.sh" - "tools/ci/check_blobs.sh" diff --git a/components/app_update/CMakeLists.txt b/components/app_update/CMakeLists.txt index d1be3bdab97..c853ba64640 100644 --- a/components/app_update/CMakeLists.txt +++ b/components/app_update/CMakeLists.txt @@ -13,6 +13,8 @@ if(CONFIG_SECURE_SIGNED_DATA_PARTITION) idf_component_optional_requires(PRIVATE mbedtls) endif() +idf_define_esp_err_codes(HEADERS include/esp_ota_ops.h) + if(NOT BOOTLOADER_BUILD) partition_table_get_partition_info(otadata_offset "--partition-type data --partition-subtype ota" "offset") partition_table_get_partition_info(otadata_size "--partition-type data --partition-subtype ota" "size") diff --git a/components/bootloader_support/CMakeLists.txt b/components/bootloader_support/CMakeLists.txt index 9a91d1c10e9..eb274e46175 100644 --- a/components/bootloader_support/CMakeLists.txt +++ b/components/bootloader_support/CMakeLists.txt @@ -162,6 +162,8 @@ idf_component_register(SRCS "${srcs}" REQUIRES "${requires}" PRIV_REQUIRES "${priv_requires}") +idf_define_esp_err_codes(HEADERS include/esp_image_format.h) + if(NOT BOOTLOADER_BUILD) if(CONFIG_SECURE_SIGNED_ON_UPDATE) if(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME OR CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME OR diff --git a/components/efuse/CMakeLists.txt b/components/efuse/CMakeLists.txt index 66413755b91..28265fa6666 100644 --- a/components/efuse/CMakeLists.txt +++ b/components/efuse/CMakeLists.txt @@ -45,6 +45,7 @@ else() PRIV_REQUIRES "${priv_requires}" INCLUDE_DIRS "${include_dirs}" PRIV_INCLUDE_DIRS "${private_include}") + idf_define_esp_err_codes(HEADERS include/esp_efuse.h) if(NOT ${target} STREQUAL "linux") # Forces the linker to include esp_efuse_startup.c target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_efuse_startup_include_func") diff --git a/components/esp-tls/CMakeLists.txt b/components/esp-tls/CMakeLists.txt index 14a8503cd5c..f6caf1b5137 100644 --- a/components/esp-tls/CMakeLists.txt +++ b/components/esp-tls/CMakeLists.txt @@ -25,6 +25,7 @@ idf_component_register(SRCS "${srcs}" REQUIRES mbedtls PRIV_REQUIRES ${priv_req}) +idf_define_esp_err_codes(HEADERS esp_tls_errors.h) if(NOT ${IDF_TARGET} STREQUAL "linux") idf_component_get_property(lwip lwip COMPONENT_LIB) diff --git a/components/esp_common/CMakeLists.txt b/components/esp_common/CMakeLists.txt index c16d2e772d6..44cc0a62bce 100644 --- a/components/esp_common/CMakeLists.txt +++ b/components/esp_common/CMakeLists.txt @@ -3,7 +3,7 @@ idf_build_get_property(target IDF_TARGET) if(${target} STREQUAL "linux") set(ldfragments) else() - set(ldfragments common.lf soc.lf) + set(ldfragments common.lf soc.lf linker.lf) endif() set(srcs "src/esp_err_to_name.c") @@ -21,34 +21,12 @@ idf_component_register(SRCS "${srcs}" # multiplicity property on this universal dependency is the established fix. set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY LINK_INTERFACE_MULTIPLICITY 4) -# List of components needed for the error codes list -set(optional_reqs ulp - efuse - esp_http_client - esp_http_server - bootloader_support - nvs_flash - esp_wifi - app_update - lwip - spi_flash - wpa_supplicant - esp_serial_slave_link - esp_netif - soc - esp-tls - esp_https_ota - esp_hal_mspi - esp_hw_support) - -if(IDF_BUILD_V2) - idf_component_optional_requires(PRIVATE ${optional_reqs}) -else() - idf_build_get_property(build_components BUILD_COMPONENTS) - foreach(req ${optional_reqs}) - if(req IN_LIST build_components) - idf_component_get_property(req_lib ${req} COMPONENT_LIB) - target_link_libraries(${COMPONENT_LIB} PRIVATE ${req_lib}) - endif() - endforeach() +if(CONFIG_IDF_TARGET_LINUX AND NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + # On Linux, provide a linker script that collects specific sections entries (e.g., .esp_err_msg_tbl) + # and place them in the final binary, since the linker script used for Linux build doesn't have the usual + # memory layout and section placement as in embedded targets. + target_link_options(${COMPONENT_LIB} INTERFACE "-Wl,-T,${CMAKE_CURRENT_SOURCE_DIR}/linux/sections.ld") endif() + +# Register error codes defined in esp_common's own headers +idf_define_esp_err_codes(HEADERS include/esp_err.h) diff --git a/components/esp_common/include/esp_err.h b/components/esp_common/include/esp_err.h index 58026d02e69..cfbacfadb00 100644 --- a/components/esp_common/include/esp_err.h +++ b/components/esp_common/include/esp_err.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -45,12 +45,13 @@ typedef int esp_err_t; /** * @brief Returns string for esp_err_t error codes * - * This function finds the error code in a pre-generated lookup-table and - * returns its string representation. + * This function finds the error code in a lookup-table populated at link time + * and returns its string representation. * - * The function is generated by the Python script - * tools/gen_esp_err_to_name.py which should be run each time an esp_err_t - * error is modified, created or removed from the IDF project. + * Components register their error codes by calling idf_define_esp_err_codes() + * in their CMakeLists.txt. The build system extracts error codes from the + * specified headers and generates entries placed in the .esp_err_msg_tbl + * linker section. * * @param code esp_err_t error code * @return string error message @@ -60,13 +61,12 @@ const char *esp_err_to_name(esp_err_t code); /** * @brief Returns string for esp_err_t and system error codes * - * This function finds the error code in a pre-generated lookup-table of - * esp_err_t errors and returns its string representation. If the error code - * is not found then it is attempted to be found among system errors. + * This function finds the error code in a lookup-table populated at link time + * and returns its string representation. If the error code is not found then + * it is attempted to be found among system errors. * - * The function is generated by the Python script - * tools/gen_esp_err_to_name.py which should be run each time an esp_err_t - * error is modified, created or removed from the IDF project. + * Components register their error codes by calling idf_define_esp_err_codes() + * in their CMakeLists.txt. * * @param code esp_err_t error code * @param[out] buf buffer where the error message should be written diff --git a/components/esp_common/include/esp_err_msg.h b/components/esp_common/include/esp_err_msg.h new file mode 100644 index 00000000000..e651d2f832f --- /dev/null +++ b/components/esp_common/include/esp_err_msg.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Structure describing an error code entry for the link-time error table. + * + * Components register their error codes by placing arrays of this structure + * into the ".esp_err_msg_tbl" linker section. The esp_err_to_name() function + * iterates over all such entries at runtime. + */ +typedef struct { + esp_err_t code; /*!< Error code value */ + const char *msg; /*!< String representation of the error code name */ +} esp_err_msg_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_common/linker.lf b/components/esp_common/linker.lf new file mode 100644 index 00000000000..be11e070d39 --- /dev/null +++ b/components/esp_common/linker.lf @@ -0,0 +1,13 @@ +[sections:esp_err_msg_tbl] +entries: + .esp_err_msg_tbl+ + +[scheme:esp_err_msg_tbl_default] +entries: + esp_err_msg_tbl -> flash_rodata + +[mapping:esp_err_msg_tbl] +archive: * +entries: + * (esp_err_msg_tbl_default); + esp_err_msg_tbl -> flash_rodata KEEP() SORT(name) SURROUND(esp_err_msg_tbl) diff --git a/components/esp_common/linux/sections.ld b/components/esp_common/linux/sections.ld new file mode 100644 index 00000000000..0497600f9ed --- /dev/null +++ b/components/esp_common/linux/sections.ld @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +SECTIONS +{ + .esp_err_msg_tbl : + { + PROVIDE(_esp_err_msg_tbl_start = .); + KEEP(*(SORT(.esp_err_msg_tbl*))) + PROVIDE(_esp_err_msg_tbl_end = .); + } +} +INSERT AFTER .rodata; diff --git a/components/esp_common/src/esp_err_to_name.c b/components/esp_common/src/esp_err_to_name.c index 9e613764b5b..4e22244d156 100644 --- a/components/esp_common/src/esp_err_to_name.c +++ b/components/esp_common/src/esp_err_to_name.c @@ -1,891 +1,27 @@ /* - * SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -//Do not edit this file because it is autogenerated by gen_esp_err_to_name.py +/* + * This file implements esp_err_to_name() using a link-time array. + * + * Components register their error codes by placing esp_err_msg_t arrays into + * the ".esp_err_msg_tbl" linker section via idf_define_esp_err_codes() in + * their CMakeLists.txt. The linker collects all such entries between the + * _esp_err_msg_tbl_start and _esp_err_msg_tbl_end symbols. + */ #include +#include #include "esp_err.h" -#if __has_include("soc/soc.h") -#include "soc/soc.h" -#endif -#if __has_include("esp_dpp.h") -#include "esp_dpp.h" -#endif -#if __has_include("esp_ds_err.h") -#include "esp_ds_err.h" -#endif -#if __has_include("esp_efuse.h") -#include "esp_efuse.h" -#endif -#if __has_include("esp_http_client.h") -#include "esp_http_client.h" -#endif -#if __has_include("esp_http_server.h") -#include "esp_http_server.h" -#endif -#if __has_include("esp_https_ota.h") -#include "esp_https_ota.h" -#endif -#if __has_include("esp_image_format.h") -#include "esp_image_format.h" -#endif -#if __has_include("esp_memprot_err.h") -#include "esp_memprot_err.h" -#endif -#if __has_include("esp_mesh.h") -#include "esp_mesh.h" -#endif -#if __has_include("esp_netif_types.h") -#include "esp_netif_types.h" -#endif -#if __has_include("esp_now.h") -#include "esp_now.h" -#endif -#if __has_include("esp_ota_ops.h") -#include "esp_ota_ops.h" -#endif -#if __has_include("esp_tls_errors.h") -#include "esp_tls_errors.h" -#endif -#if __has_include("esp_transport.h") -#include "esp_transport.h" -#endif -#if __has_include("esp_wifi.h") -#include "esp_wifi.h" -#endif -#if __has_include("esp_wps.h") -#include "esp_wps.h" -#endif -#if __has_include("hal/esp_flash_err.h") -#include "hal/esp_flash_err.h" -#endif -#if __has_include("nvs.h") -#include "nvs.h" -#endif -#if __has_include("nvs_sec_provider.h") -#include "nvs_sec_provider.h" -#endif -#if __has_include("spi_flash_mmap.h") -#include "spi_flash_mmap.h" -#endif -#if __has_include("ulp_fsm_common.h") -#include "ulp_fsm_common.h" -#endif +#include "esp_err_msg.h" +#include "esp_attr.h" #ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP -#define ERR_TBL_IT(err) {err, #err} - -typedef struct { - esp_err_t code; - const char *msg; -} esp_err_msg_t; - -static const esp_err_msg_t esp_err_msg_table[] = { - // components/esp_common/include/esp_err.h -# ifdef ESP_FAIL - ERR_TBL_IT(ESP_FAIL), /* -1 Generic esp_err_t code indicating failure */ -# endif -# ifdef ESP_OK - ERR_TBL_IT(ESP_OK), /* 0 esp_err_t value indicating success (no error) */ -# endif -# ifdef ESP_ERR_NO_MEM - ERR_TBL_IT(ESP_ERR_NO_MEM), /* 257 0x101 Out of memory */ -# endif -# ifdef ESP_ERR_INVALID_ARG - ERR_TBL_IT(ESP_ERR_INVALID_ARG), /* 258 0x102 Invalid argument */ -# endif -# ifdef ESP_ERR_INVALID_STATE - ERR_TBL_IT(ESP_ERR_INVALID_STATE), /* 259 0x103 Invalid state */ -# endif -# ifdef ESP_ERR_INVALID_SIZE - ERR_TBL_IT(ESP_ERR_INVALID_SIZE), /* 260 0x104 Invalid size */ -# endif -# ifdef ESP_ERR_NOT_FOUND - ERR_TBL_IT(ESP_ERR_NOT_FOUND), /* 261 0x105 Requested resource not found */ -# endif -# ifdef ESP_ERR_NOT_SUPPORTED - ERR_TBL_IT(ESP_ERR_NOT_SUPPORTED), /* 262 0x106 Operation or feature not supported */ -# endif -# ifdef ESP_ERR_TIMEOUT - ERR_TBL_IT(ESP_ERR_TIMEOUT), /* 263 0x107 Operation timed out */ -# endif -# ifdef ESP_ERR_INVALID_RESPONSE - ERR_TBL_IT(ESP_ERR_INVALID_RESPONSE), /* 264 0x108 Received response was invalid */ -# endif -# ifdef ESP_ERR_INVALID_CRC - ERR_TBL_IT(ESP_ERR_INVALID_CRC), /* 265 0x109 CRC or checksum was invalid */ -# endif -# ifdef ESP_ERR_INVALID_VERSION - ERR_TBL_IT(ESP_ERR_INVALID_VERSION), /* 266 0x10a Version was invalid */ -# endif -# ifdef ESP_ERR_INVALID_MAC - ERR_TBL_IT(ESP_ERR_INVALID_MAC), /* 267 0x10b MAC address was invalid */ -# endif -# ifdef ESP_ERR_NOT_FINISHED - ERR_TBL_IT(ESP_ERR_NOT_FINISHED), /* 268 0x10c Operation has not fully completed */ -# endif -# ifdef ESP_ERR_NOT_ALLOWED - ERR_TBL_IT(ESP_ERR_NOT_ALLOWED), /* 269 0x10d Operation is not allowed */ -# endif - // components/nvs_flash/include/nvs.h -# ifdef ESP_ERR_NVS_BASE - ERR_TBL_IT(ESP_ERR_NVS_BASE), /* 4352 0x1100 Starting number of error codes */ -# endif -# ifdef ESP_ERR_NVS_NOT_INITIALIZED - ERR_TBL_IT(ESP_ERR_NVS_NOT_INITIALIZED), /* 4353 0x1101 The storage driver is not initialized */ -# endif -# ifdef ESP_ERR_NVS_NOT_FOUND - ERR_TBL_IT(ESP_ERR_NVS_NOT_FOUND), /* 4354 0x1102 A requested entry couldn't be found or - namespace doesn’t exist yet and mode is - NVS_READONLY */ -# endif -# ifdef ESP_ERR_NVS_TYPE_MISMATCH - ERR_TBL_IT(ESP_ERR_NVS_TYPE_MISMATCH), /* 4355 0x1103 The type of set or get operation doesn't - match the type of value stored in NVS */ -# endif -# ifdef ESP_ERR_NVS_READ_ONLY - ERR_TBL_IT(ESP_ERR_NVS_READ_ONLY), /* 4356 0x1104 Storage handle was opened as read only */ -# endif -# ifdef ESP_ERR_NVS_NOT_ENOUGH_SPACE - ERR_TBL_IT(ESP_ERR_NVS_NOT_ENOUGH_SPACE), /* 4357 0x1105 There is not enough space in the - underlying storage to save the value */ -# endif -# ifdef ESP_ERR_NVS_INVALID_NAME - ERR_TBL_IT(ESP_ERR_NVS_INVALID_NAME), /* 4358 0x1106 Namespace name doesn’t satisfy constraints */ -# endif -# ifdef ESP_ERR_NVS_INVALID_HANDLE - ERR_TBL_IT(ESP_ERR_NVS_INVALID_HANDLE), /* 4359 0x1107 Handle has been closed or is NULL */ -# endif -# ifdef ESP_ERR_NVS_REMOVE_FAILED - ERR_TBL_IT(ESP_ERR_NVS_REMOVE_FAILED), /* 4360 0x1108 The value wasn’t updated because flash - write operation has failed. The value - was written however, and update will be - finished after re-initialization of nvs, - provided that flash operation doesn’t - fail again. */ -# endif -# ifdef ESP_ERR_NVS_KEY_TOO_LONG - ERR_TBL_IT(ESP_ERR_NVS_KEY_TOO_LONG), /* 4361 0x1109 Key name is too long */ -# endif -# ifdef ESP_ERR_NVS_PAGE_FULL - ERR_TBL_IT(ESP_ERR_NVS_PAGE_FULL), /* 4362 0x110a Internal error; never returned by nvs - API functions */ -# endif -# ifdef ESP_ERR_NVS_INVALID_STATE - ERR_TBL_IT(ESP_ERR_NVS_INVALID_STATE), /* 4363 0x110b NVS is in an inconsistent state due to a - previous error. Call nvs_flash_init and - nvs_open again, then retry. */ -# endif -# ifdef ESP_ERR_NVS_INVALID_LENGTH - ERR_TBL_IT(ESP_ERR_NVS_INVALID_LENGTH), /* 4364 0x110c String or blob length is not sufficient - to store data */ -# endif -# ifdef ESP_ERR_NVS_NO_FREE_PAGES - ERR_TBL_IT(ESP_ERR_NVS_NO_FREE_PAGES), /* 4365 0x110d NVS partition doesn't contain any empty - pages. This may happen if NVS partition - was truncated. Erase the whole partition - and call nvs_flash_init again. */ -# endif -# ifdef ESP_ERR_NVS_VALUE_TOO_LONG - ERR_TBL_IT(ESP_ERR_NVS_VALUE_TOO_LONG), /* 4366 0x110e Value doesn't fit into the entry or - string or blob length is longer than - supported by the implementation */ -# endif -# ifdef ESP_ERR_NVS_PART_NOT_FOUND - ERR_TBL_IT(ESP_ERR_NVS_PART_NOT_FOUND), /* 4367 0x110f Partition with specified name is not - found in the partition table */ -# endif -# ifdef ESP_ERR_NVS_NEW_VERSION_FOUND - ERR_TBL_IT(ESP_ERR_NVS_NEW_VERSION_FOUND), /* 4368 0x1110 NVS partition contains data in new - format and cannot be recognized by this - version of code */ -# endif -# ifdef ESP_ERR_NVS_XTS_ENCR_FAILED - ERR_TBL_IT(ESP_ERR_NVS_XTS_ENCR_FAILED), /* 4369 0x1111 XTS encryption failed while writing NVS entry */ -# endif -# ifdef ESP_ERR_NVS_XTS_DECR_FAILED - ERR_TBL_IT(ESP_ERR_NVS_XTS_DECR_FAILED), /* 4370 0x1112 XTS decryption failed while reading NVS entry */ -# endif -# ifdef ESP_ERR_NVS_XTS_CFG_FAILED - ERR_TBL_IT(ESP_ERR_NVS_XTS_CFG_FAILED), /* 4371 0x1113 XTS configuration setting failed */ -# endif -# ifdef ESP_ERR_NVS_XTS_CFG_NOT_FOUND - ERR_TBL_IT(ESP_ERR_NVS_XTS_CFG_NOT_FOUND), /* 4372 0x1114 XTS configuration not found */ -# endif -# ifdef ESP_ERR_NVS_ENCR_NOT_SUPPORTED - ERR_TBL_IT(ESP_ERR_NVS_ENCR_NOT_SUPPORTED), /* 4373 0x1115 NVS encryption is not supported in this version */ -# endif -# ifdef ESP_ERR_NVS_KEYS_NOT_INITIALIZED - ERR_TBL_IT(ESP_ERR_NVS_KEYS_NOT_INITIALIZED), /* 4374 0x1116 NVS key partition is uninitialized */ -# endif -# ifdef ESP_ERR_NVS_CORRUPT_KEY_PART - ERR_TBL_IT(ESP_ERR_NVS_CORRUPT_KEY_PART), /* 4375 0x1117 NVS key partition is corrupt */ -# endif -# ifdef ESP_ERR_NVS_CONTENT_DIFFERS - ERR_TBL_IT(ESP_ERR_NVS_CONTENT_DIFFERS), /* 4376 0x1118 Internal error; never returned by nvs - API functions. NVS key is different in - comparison */ -# endif -# ifdef ESP_ERR_NVS_WRONG_ENCRYPTION - ERR_TBL_IT(ESP_ERR_NVS_WRONG_ENCRYPTION), /* 4377 0x1119 NVS partition is marked as encrypted - with generic flash encryption. This is - forbidden since the NVS encryption works - differently. */ -# endif - // components/ulp/ulp_fsm/include/ulp_fsm_common.h -# ifdef ESP_ERR_ULP_BASE - ERR_TBL_IT(ESP_ERR_ULP_BASE), /* 4608 0x1200 Offset for ULP-related error codes */ -# endif -# ifdef ESP_ERR_ULP_SIZE_TOO_BIG - ERR_TBL_IT(ESP_ERR_ULP_SIZE_TOO_BIG), /* 4609 0x1201 Program doesn't fit into RTC memory - reserved for the ULP */ -# endif -# ifdef ESP_ERR_ULP_INVALID_LOAD_ADDR - ERR_TBL_IT(ESP_ERR_ULP_INVALID_LOAD_ADDR), /* 4610 0x1202 Load address is outside of RTC memory - reserved for the ULP */ -# endif -# ifdef ESP_ERR_ULP_DUPLICATE_LABEL - ERR_TBL_IT(ESP_ERR_ULP_DUPLICATE_LABEL), /* 4611 0x1203 More than one label with the same number - was defined */ -# endif -# ifdef ESP_ERR_ULP_UNDEFINED_LABEL - ERR_TBL_IT(ESP_ERR_ULP_UNDEFINED_LABEL), /* 4612 0x1204 Branch instructions references an undefined label */ -# endif -# ifdef ESP_ERR_ULP_BRANCH_OUT_OF_RANGE - ERR_TBL_IT(ESP_ERR_ULP_BRANCH_OUT_OF_RANGE), /* 4613 0x1205 Branch target is out of range of B - instruction (try replacing with BX) */ -# endif - // components/app_update/include/esp_ota_ops.h -# ifdef ESP_ERR_OTA_BASE - ERR_TBL_IT(ESP_ERR_OTA_BASE), /* 5376 0x1500 Base error code for ota_ops api */ -# endif -# ifdef ESP_ERR_OTA_PARTITION_CONFLICT - ERR_TBL_IT(ESP_ERR_OTA_PARTITION_CONFLICT), /* 5377 0x1501 Error if request was to write or erase - the current running partition */ -# endif -# ifdef ESP_ERR_OTA_SELECT_INFO_INVALID - ERR_TBL_IT(ESP_ERR_OTA_SELECT_INFO_INVALID), /* 5378 0x1502 Error if OTA data partition contains - invalid content */ -# endif -# ifdef ESP_ERR_OTA_VALIDATE_FAILED - ERR_TBL_IT(ESP_ERR_OTA_VALIDATE_FAILED), /* 5379 0x1503 Error if OTA app image is invalid */ -# endif -# ifdef ESP_ERR_OTA_SMALL_SEC_VER - ERR_TBL_IT(ESP_ERR_OTA_SMALL_SEC_VER), /* 5380 0x1504 Error if the firmware has a secure - version less than the running firmware. */ -# endif -# ifdef ESP_ERR_OTA_ROLLBACK_FAILED - ERR_TBL_IT(ESP_ERR_OTA_ROLLBACK_FAILED), /* 5381 0x1505 Error if flash does not have valid - firmware in passive partition and hence - rollback is not possible */ -# endif -# ifdef ESP_ERR_OTA_ROLLBACK_INVALID_STATE - ERR_TBL_IT(ESP_ERR_OTA_ROLLBACK_INVALID_STATE), /* 5382 0x1506 Error if current active firmware is - still marked in pending validation state - (ESP_OTA_IMG_PENDING_VERIFY), - essentially first boot of firmware image - post upgrade and hence firmware upgrade - is not possible */ -# endif -# ifdef ESP_ERR_OTA_ALREADY_IN_PROGRESS - ERR_TBL_IT(ESP_ERR_OTA_ALREADY_IN_PROGRESS), /* 5383 0x1507 Error if another OTA operation is - already in progress on the same - partition */ -# endif -# ifdef ESP_ERR_OTA_SPI_MODE_MISMATCH - ERR_TBL_IT(ESP_ERR_OTA_SPI_MODE_MISMATCH), /* 5384 0x1508 Error if the firmware's SPI flash mode - doesn't match the running firmware */ -# endif - // components/efuse/include/esp_efuse.h -# ifdef ESP_ERR_EFUSE - ERR_TBL_IT(ESP_ERR_EFUSE), /* 5632 0x1600 Base error code for efuse api. */ -# endif -# ifdef ESP_OK_EFUSE_CNT - ERR_TBL_IT(ESP_OK_EFUSE_CNT), /* 5633 0x1601 OK the required number of bits is set. */ -# endif -# ifdef ESP_ERR_EFUSE_CNT_IS_FULL - ERR_TBL_IT(ESP_ERR_EFUSE_CNT_IS_FULL), /* 5634 0x1602 Error field is full. */ -# endif -# ifdef ESP_ERR_EFUSE_REPEATED_PROG - ERR_TBL_IT(ESP_ERR_EFUSE_REPEATED_PROG), /* 5635 0x1603 Error repeated programming of programmed - bits is strictly forbidden. */ -# endif -# ifdef ESP_ERR_CODING - ERR_TBL_IT(ESP_ERR_CODING), /* 5636 0x1604 Error while a encoding operation. */ -# endif -# ifdef ESP_ERR_NOT_ENOUGH_UNUSED_KEY_BLOCKS - ERR_TBL_IT(ESP_ERR_NOT_ENOUGH_UNUSED_KEY_BLOCKS), /* 5637 0x1605 Error not enough unused key blocks available */ -# endif -# ifdef ESP_ERR_DAMAGED_READING - ERR_TBL_IT(ESP_ERR_DAMAGED_READING), /* 5638 0x1606 Error. Burn or reset was done during a - reading operation leads to damage read - data. This error is internal to the - efuse component and not returned by any - public API. */ -# endif -# ifdef ESP_ERR_BURN_WR_DIS - ERR_TBL_IT(ESP_ERR_BURN_WR_DIS), /* 5639 0x1607 Error to burn WR_DIS field. */ -# endif - // components/bootloader_support/include/esp_image_format.h -# ifdef ESP_ERR_IMAGE_BASE - ERR_TBL_IT(ESP_ERR_IMAGE_BASE), /* 8192 0x2000 */ -# endif -# ifdef ESP_ERR_IMAGE_FLASH_FAIL - ERR_TBL_IT(ESP_ERR_IMAGE_FLASH_FAIL), /* 8193 0x2001 */ -# endif -# ifdef ESP_ERR_IMAGE_INVALID - ERR_TBL_IT(ESP_ERR_IMAGE_INVALID), /* 8194 0x2002 */ -# endif - // components/esp_common/include/esp_err.h -# ifdef ESP_ERR_WIFI_BASE - ERR_TBL_IT(ESP_ERR_WIFI_BASE), /* 12288 0x3000 Starting number of WiFi error codes */ -# endif - // components/esp_wifi/include/esp_wifi.h -# ifdef ESP_ERR_WIFI_NOT_INIT - ERR_TBL_IT(ESP_ERR_WIFI_NOT_INIT), /* 12289 0x3001 WiFi driver was not installed by esp_wifi_init */ -# endif -# ifdef ESP_ERR_WIFI_NOT_STARTED - ERR_TBL_IT(ESP_ERR_WIFI_NOT_STARTED), /* 12290 0x3002 WiFi driver was not started by esp_wifi_start */ -# endif -# ifdef ESP_ERR_WIFI_NOT_STOPPED - ERR_TBL_IT(ESP_ERR_WIFI_NOT_STOPPED), /* 12291 0x3003 WiFi driver was not stopped by esp_wifi_stop */ -# endif -# ifdef ESP_ERR_WIFI_IF - ERR_TBL_IT(ESP_ERR_WIFI_IF), /* 12292 0x3004 WiFi interface error */ -# endif -# ifdef ESP_ERR_WIFI_MODE - ERR_TBL_IT(ESP_ERR_WIFI_MODE), /* 12293 0x3005 WiFi mode error */ -# endif -# ifdef ESP_ERR_WIFI_STATE - ERR_TBL_IT(ESP_ERR_WIFI_STATE), /* 12294 0x3006 WiFi internal state error */ -# endif -# ifdef ESP_ERR_WIFI_CONN - ERR_TBL_IT(ESP_ERR_WIFI_CONN), /* 12295 0x3007 WiFi internal control block of station - or soft-AP error */ -# endif -# ifdef ESP_ERR_WIFI_NVS - ERR_TBL_IT(ESP_ERR_WIFI_NVS), /* 12296 0x3008 WiFi internal NVS module error */ -# endif -# ifdef ESP_ERR_WIFI_MAC - ERR_TBL_IT(ESP_ERR_WIFI_MAC), /* 12297 0x3009 MAC address is invalid */ -# endif -# ifdef ESP_ERR_WIFI_SSID - ERR_TBL_IT(ESP_ERR_WIFI_SSID), /* 12298 0x300a SSID is invalid */ -# endif -# ifdef ESP_ERR_WIFI_PASSWORD - ERR_TBL_IT(ESP_ERR_WIFI_PASSWORD), /* 12299 0x300b Password is invalid */ -# endif -# ifdef ESP_ERR_WIFI_TIMEOUT - ERR_TBL_IT(ESP_ERR_WIFI_TIMEOUT), /* 12300 0x300c Timeout error */ -# endif -# ifdef ESP_ERR_WIFI_WAKE_FAIL - ERR_TBL_IT(ESP_ERR_WIFI_WAKE_FAIL), /* 12301 0x300d WiFi is in sleep state(RF closed) and wakeup fail */ -# endif -# ifdef ESP_ERR_WIFI_WOULD_BLOCK - ERR_TBL_IT(ESP_ERR_WIFI_WOULD_BLOCK), /* 12302 0x300e The caller would block */ -# endif -# ifdef ESP_ERR_WIFI_NOT_CONNECT - ERR_TBL_IT(ESP_ERR_WIFI_NOT_CONNECT), /* 12303 0x300f Station still in disconnect status */ -# endif -# ifdef ESP_ERR_WIFI_POST - ERR_TBL_IT(ESP_ERR_WIFI_POST), /* 12306 0x3012 Failed to post the event to WiFi task */ -# endif -# ifdef ESP_ERR_WIFI_INIT_STATE - ERR_TBL_IT(ESP_ERR_WIFI_INIT_STATE), /* 12307 0x3013 Invalid WiFi state when init/deinit is called */ -# endif -# ifdef ESP_ERR_WIFI_STOP_STATE - ERR_TBL_IT(ESP_ERR_WIFI_STOP_STATE), /* 12308 0x3014 Returned when WiFi is stopping */ -# endif -# ifdef ESP_ERR_WIFI_NOT_ASSOC - ERR_TBL_IT(ESP_ERR_WIFI_NOT_ASSOC), /* 12309 0x3015 The WiFi connection is not associated */ -# endif -# ifdef ESP_ERR_WIFI_TX_DISALLOW - ERR_TBL_IT(ESP_ERR_WIFI_TX_DISALLOW), /* 12310 0x3016 The WiFi TX is disallowed */ -# endif -# ifdef ESP_ERR_WIFI_TWT_FULL - ERR_TBL_IT(ESP_ERR_WIFI_TWT_FULL), /* 12311 0x3017 no available flow id */ -# endif -# ifdef ESP_ERR_WIFI_TWT_SETUP_TIMEOUT - ERR_TBL_IT(ESP_ERR_WIFI_TWT_SETUP_TIMEOUT), /* 12312 0x3018 Timeout of receiving twt setup response - frame, timeout times can be set during - twt setup */ -# endif -# ifdef ESP_ERR_WIFI_TWT_SETUP_TXFAIL - ERR_TBL_IT(ESP_ERR_WIFI_TWT_SETUP_TXFAIL), /* 12313 0x3019 TWT setup frame tx failed */ -# endif -# ifdef ESP_ERR_WIFI_TWT_SETUP_REJECT - ERR_TBL_IT(ESP_ERR_WIFI_TWT_SETUP_REJECT), /* 12314 0x301a The twt setup request was rejected by the AP */ -# endif -# ifdef ESP_ERR_WIFI_DISCARD - ERR_TBL_IT(ESP_ERR_WIFI_DISCARD), /* 12315 0x301b Discard frame */ -# endif -# ifdef ESP_ERR_WIFI_ROC_IN_PROGRESS - ERR_TBL_IT(ESP_ERR_WIFI_ROC_IN_PROGRESS), /* 12316 0x301c ROC op is in progress */ -# endif - // components/wpa_supplicant/esp_supplicant/include/esp_wps.h -# ifdef ESP_ERR_WIFI_REGISTRAR - ERR_TBL_IT(ESP_ERR_WIFI_REGISTRAR), /* 12339 0x3033 WPS registrar is not supported */ -# endif -# ifdef ESP_ERR_WIFI_WPS_TYPE - ERR_TBL_IT(ESP_ERR_WIFI_WPS_TYPE), /* 12340 0x3034 WPS type error */ -# endif -# ifdef ESP_ERR_WIFI_WPS_SM - ERR_TBL_IT(ESP_ERR_WIFI_WPS_SM), /* 12341 0x3035 WPS state machine is not initialized */ -# endif - // components/esp_wifi/include/esp_now.h -# ifdef ESP_ERR_ESPNOW_BASE - ERR_TBL_IT(ESP_ERR_ESPNOW_BASE), /* 12388 0x3064 ESPNOW error number base. */ -# endif -# ifdef ESP_ERR_ESPNOW_NOT_INIT - ERR_TBL_IT(ESP_ERR_ESPNOW_NOT_INIT), /* 12389 0x3065 ESPNOW is not initialized. */ -# endif -# ifdef ESP_ERR_ESPNOW_ARG - ERR_TBL_IT(ESP_ERR_ESPNOW_ARG), /* 12390 0x3066 Invalid argument */ -# endif -# ifdef ESP_ERR_ESPNOW_NO_MEM - ERR_TBL_IT(ESP_ERR_ESPNOW_NO_MEM), /* 12391 0x3067 Out of memory */ -# endif -# ifdef ESP_ERR_ESPNOW_FULL - ERR_TBL_IT(ESP_ERR_ESPNOW_FULL), /* 12392 0x3068 ESPNOW peer list is full */ -# endif -# ifdef ESP_ERR_ESPNOW_NOT_FOUND - ERR_TBL_IT(ESP_ERR_ESPNOW_NOT_FOUND), /* 12393 0x3069 ESPNOW peer is not found */ -# endif -# ifdef ESP_ERR_ESPNOW_INTERNAL - ERR_TBL_IT(ESP_ERR_ESPNOW_INTERNAL), /* 12394 0x306a Internal error */ -# endif -# ifdef ESP_ERR_ESPNOW_EXIST - ERR_TBL_IT(ESP_ERR_ESPNOW_EXIST), /* 12395 0x306b ESPNOW peer has existed */ -# endif -# ifdef ESP_ERR_ESPNOW_IF - ERR_TBL_IT(ESP_ERR_ESPNOW_IF), /* 12396 0x306c Interface error */ -# endif -# ifdef ESP_ERR_ESPNOW_CHAN - ERR_TBL_IT(ESP_ERR_ESPNOW_CHAN), /* 12397 0x306d Channel error */ -# endif - // components/wpa_supplicant/esp_supplicant/include/esp_dpp.h -# ifdef ESP_ERR_DPP_FAILURE - ERR_TBL_IT(ESP_ERR_DPP_FAILURE), /* 12439 0x3097 Generic failure during DPP Operation */ -# endif -# ifdef ESP_ERR_DPP_TX_FAILURE - ERR_TBL_IT(ESP_ERR_DPP_TX_FAILURE), /* 12440 0x3098 DPP Frame Tx failed OR not Acked */ -# endif -# ifdef ESP_ERR_DPP_INVALID_ATTR - ERR_TBL_IT(ESP_ERR_DPP_INVALID_ATTR), /* 12441 0x3099 Encountered invalid DPP Attribute */ -# endif -# ifdef ESP_ERR_DPP_AUTH_TIMEOUT - ERR_TBL_IT(ESP_ERR_DPP_AUTH_TIMEOUT), /* 12442 0x309a DPP Auth response was not received in time */ -# endif -# ifdef ESP_ERR_DPP_INVALID_LIST - ERR_TBL_IT(ESP_ERR_DPP_INVALID_LIST), /* 12443 0x309b Channel list given in - esp_supp_dpp_bootstrap_gen() is not - valid or too big */ -# endif -# ifdef ESP_ERR_DPP_CONF_TIMEOUT - ERR_TBL_IT(ESP_ERR_DPP_CONF_TIMEOUT), /* 12444 0x309c DPP Configuration was not received in time */ -# endif - // components/esp_common/include/esp_err.h -# ifdef ESP_ERR_MESH_BASE - ERR_TBL_IT(ESP_ERR_MESH_BASE), /* 16384 0x4000 Starting number of MESH error codes */ -# endif - // components/esp_wifi/include/esp_mesh.h -# ifdef ESP_ERR_MESH_WIFI_NOT_START - ERR_TBL_IT(ESP_ERR_MESH_WIFI_NOT_START), /* 16385 0x4001 */ -# endif -# ifdef ESP_ERR_MESH_NOT_INIT - ERR_TBL_IT(ESP_ERR_MESH_NOT_INIT), /* 16386 0x4002 */ -# endif -# ifdef ESP_ERR_MESH_NOT_CONFIG - ERR_TBL_IT(ESP_ERR_MESH_NOT_CONFIG), /* 16387 0x4003 */ -# endif -# ifdef ESP_ERR_MESH_NOT_START - ERR_TBL_IT(ESP_ERR_MESH_NOT_START), /* 16388 0x4004 */ -# endif -# ifdef ESP_ERR_MESH_NOT_SUPPORT - ERR_TBL_IT(ESP_ERR_MESH_NOT_SUPPORT), /* 16389 0x4005 */ -# endif -# ifdef ESP_ERR_MESH_NOT_ALLOWED - ERR_TBL_IT(ESP_ERR_MESH_NOT_ALLOWED), /* 16390 0x4006 */ -# endif -# ifdef ESP_ERR_MESH_NO_MEMORY - ERR_TBL_IT(ESP_ERR_MESH_NO_MEMORY), /* 16391 0x4007 */ -# endif -# ifdef ESP_ERR_MESH_ARGUMENT - ERR_TBL_IT(ESP_ERR_MESH_ARGUMENT), /* 16392 0x4008 */ -# endif -# ifdef ESP_ERR_MESH_EXCEED_MTU - ERR_TBL_IT(ESP_ERR_MESH_EXCEED_MTU), /* 16393 0x4009 */ -# endif -# ifdef ESP_ERR_MESH_TIMEOUT - ERR_TBL_IT(ESP_ERR_MESH_TIMEOUT), /* 16394 0x400a */ -# endif -# ifdef ESP_ERR_MESH_DISCONNECTED - ERR_TBL_IT(ESP_ERR_MESH_DISCONNECTED), /* 16395 0x400b */ -# endif -# ifdef ESP_ERR_MESH_QUEUE_FAIL - ERR_TBL_IT(ESP_ERR_MESH_QUEUE_FAIL), /* 16396 0x400c */ -# endif -# ifdef ESP_ERR_MESH_QUEUE_FULL - ERR_TBL_IT(ESP_ERR_MESH_QUEUE_FULL), /* 16397 0x400d */ -# endif -# ifdef ESP_ERR_MESH_NO_PARENT_FOUND - ERR_TBL_IT(ESP_ERR_MESH_NO_PARENT_FOUND), /* 16398 0x400e */ -# endif -# ifdef ESP_ERR_MESH_NO_ROUTE_FOUND - ERR_TBL_IT(ESP_ERR_MESH_NO_ROUTE_FOUND), /* 16399 0x400f */ -# endif -# ifdef ESP_ERR_MESH_OPTION_NULL - ERR_TBL_IT(ESP_ERR_MESH_OPTION_NULL), /* 16400 0x4010 */ -# endif -# ifdef ESP_ERR_MESH_OPTION_UNKNOWN - ERR_TBL_IT(ESP_ERR_MESH_OPTION_UNKNOWN), /* 16401 0x4011 */ -# endif -# ifdef ESP_ERR_MESH_XON_NO_WINDOW - ERR_TBL_IT(ESP_ERR_MESH_XON_NO_WINDOW), /* 16402 0x4012 */ -# endif -# ifdef ESP_ERR_MESH_INTERFACE - ERR_TBL_IT(ESP_ERR_MESH_INTERFACE), /* 16403 0x4013 */ -# endif -# ifdef ESP_ERR_MESH_DISCARD_DUPLICATE - ERR_TBL_IT(ESP_ERR_MESH_DISCARD_DUPLICATE), /* 16404 0x4014 */ -# endif -# ifdef ESP_ERR_MESH_DISCARD - ERR_TBL_IT(ESP_ERR_MESH_DISCARD), /* 16405 0x4015 */ -# endif -# ifdef ESP_ERR_MESH_VOTING - ERR_TBL_IT(ESP_ERR_MESH_VOTING), /* 16406 0x4016 */ -# endif -# ifdef ESP_ERR_MESH_XMIT - ERR_TBL_IT(ESP_ERR_MESH_XMIT), /* 16407 0x4017 */ -# endif -# ifdef ESP_ERR_MESH_QUEUE_READ - ERR_TBL_IT(ESP_ERR_MESH_QUEUE_READ), /* 16408 0x4018 */ -# endif -# ifdef ESP_ERR_MESH_PS - ERR_TBL_IT(ESP_ERR_MESH_PS), /* 16409 0x4019 */ -# endif -# ifdef ESP_ERR_MESH_RECV_RELEASE - ERR_TBL_IT(ESP_ERR_MESH_RECV_RELEASE), /* 16410 0x401a */ -# endif - // components/esp_netif/include/esp_netif_types.h -# ifdef ESP_ERR_ESP_NETIF_BASE - ERR_TBL_IT(ESP_ERR_ESP_NETIF_BASE), /* 20480 0x5000 */ -# endif -# ifdef ESP_ERR_ESP_NETIF_INVALID_PARAMS - ERR_TBL_IT(ESP_ERR_ESP_NETIF_INVALID_PARAMS), /* 20481 0x5001 */ -# endif -# ifdef ESP_ERR_ESP_NETIF_IF_NOT_READY - ERR_TBL_IT(ESP_ERR_ESP_NETIF_IF_NOT_READY), /* 20482 0x5002 */ -# endif -# ifdef ESP_ERR_ESP_NETIF_DHCPC_START_FAILED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_DHCPC_START_FAILED), /* 20483 0x5003 */ -# endif -# ifdef ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED), /* 20484 0x5004 */ -# endif -# ifdef ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED), /* 20485 0x5005 */ -# endif -# ifdef ESP_ERR_ESP_NETIF_NO_MEM - ERR_TBL_IT(ESP_ERR_ESP_NETIF_NO_MEM), /* 20486 0x5006 */ -# endif -# ifdef ESP_ERR_ESP_NETIF_DHCP_NOT_STOPPED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_DHCP_NOT_STOPPED), /* 20487 0x5007 */ -# endif -# ifdef ESP_ERR_ESP_NETIF_DRIVER_ATTACH_FAILED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_DRIVER_ATTACH_FAILED), /* 20488 0x5008 */ -# endif -# ifdef ESP_ERR_ESP_NETIF_INIT_FAILED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_INIT_FAILED), /* 20489 0x5009 */ -# endif -# ifdef ESP_ERR_ESP_NETIF_DNS_NOT_CONFIGURED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_DNS_NOT_CONFIGURED), /* 20490 0x500a */ -# endif -# ifdef ESP_ERR_ESP_NETIF_MLD6_FAILED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_MLD6_FAILED), /* 20491 0x500b */ -# endif -# ifdef ESP_ERR_ESP_NETIF_IP6_ADDR_FAILED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_IP6_ADDR_FAILED), /* 20492 0x500c */ -# endif -# ifdef ESP_ERR_ESP_NETIF_DHCPS_START_FAILED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_DHCPS_START_FAILED), /* 20493 0x500d */ -# endif -# ifdef ESP_ERR_ESP_NETIF_TX_FAILED - ERR_TBL_IT(ESP_ERR_ESP_NETIF_TX_FAILED), /* 20494 0x500e */ -# endif - // components/esp_common/include/esp_err.h -# ifdef ESP_ERR_FLASH_BASE - ERR_TBL_IT(ESP_ERR_FLASH_BASE), /* 24576 0x6000 Starting number of flash error codes */ -# endif - // components/spi_flash/include/spi_flash_mmap.h -# ifdef ESP_ERR_FLASH_OP_FAIL - ERR_TBL_IT(ESP_ERR_FLASH_OP_FAIL), /* 24577 0x6001 */ -# endif -# ifdef ESP_ERR_FLASH_OP_TIMEOUT - ERR_TBL_IT(ESP_ERR_FLASH_OP_TIMEOUT), /* 24578 0x6002 */ -# endif - // components/esp_hal_mspi/include/hal/esp_flash_err.h -# ifdef ESP_ERR_FLASH_NOT_INITIALISED - ERR_TBL_IT(ESP_ERR_FLASH_NOT_INITIALISED), /* 24579 0x6003 */ -# endif -# ifdef ESP_ERR_FLASH_UNSUPPORTED_HOST - ERR_TBL_IT(ESP_ERR_FLASH_UNSUPPORTED_HOST), /* 24580 0x6004 */ -# endif -# ifdef ESP_ERR_FLASH_UNSUPPORTED_CHIP - ERR_TBL_IT(ESP_ERR_FLASH_UNSUPPORTED_CHIP), /* 24581 0x6005 */ -# endif -# ifdef ESP_ERR_FLASH_PROTECTED - ERR_TBL_IT(ESP_ERR_FLASH_PROTECTED), /* 24582 0x6006 */ -# endif - // components/esp_http_client/include/esp_http_client.h -# ifdef ESP_ERR_HTTP_BASE - ERR_TBL_IT(ESP_ERR_HTTP_BASE), /* 28672 0x7000 Starting number of HTTP error codes */ -# endif -# ifdef ESP_ERR_HTTP_MAX_REDIRECT - ERR_TBL_IT(ESP_ERR_HTTP_MAX_REDIRECT), /* 28673 0x7001 The error exceeds the number of HTTP redirects */ -# endif -# ifdef ESP_ERR_HTTP_CONNECT - ERR_TBL_IT(ESP_ERR_HTTP_CONNECT), /* 28674 0x7002 Error open the HTTP connection */ -# endif -# ifdef ESP_ERR_HTTP_WRITE_DATA - ERR_TBL_IT(ESP_ERR_HTTP_WRITE_DATA), /* 28675 0x7003 Error write HTTP data */ -# endif -# ifdef ESP_ERR_HTTP_FETCH_HEADER - ERR_TBL_IT(ESP_ERR_HTTP_FETCH_HEADER), /* 28676 0x7004 Error read HTTP header from server */ -# endif -# ifdef ESP_ERR_HTTP_INVALID_TRANSPORT - ERR_TBL_IT(ESP_ERR_HTTP_INVALID_TRANSPORT), /* 28677 0x7005 There are no transport support for the - input scheme */ -# endif -# ifdef ESP_ERR_HTTP_CONNECTING - ERR_TBL_IT(ESP_ERR_HTTP_CONNECTING), /* 28678 0x7006 HTTP connection hasn't been established yet */ -# endif -# ifdef ESP_ERR_HTTP_EAGAIN - ERR_TBL_IT(ESP_ERR_HTTP_EAGAIN), /* 28679 0x7007 Mapping of errno EAGAIN to esp_err_t */ -# endif -# ifdef ESP_ERR_HTTP_CONNECTION_CLOSED - ERR_TBL_IT(ESP_ERR_HTTP_CONNECTION_CLOSED), /* 28680 0x7008 Read FIN from peer and the connection closed */ -# endif -# ifdef ESP_ERR_HTTP_NOT_MODIFIED - ERR_TBL_IT(ESP_ERR_HTTP_NOT_MODIFIED), /* 28681 0x7009 HTTP 304 Not Modified, no update available */ -# endif -# ifdef ESP_ERR_HTTP_RANGE_NOT_SATISFIABLE - ERR_TBL_IT(ESP_ERR_HTTP_RANGE_NOT_SATISFIABLE), /* 28682 0x700a HTTP 416 Range Not Satisfiable, - requested range in header is incorrect */ -# endif -# ifdef ESP_ERR_HTTP_READ_TIMEOUT - ERR_TBL_IT(ESP_ERR_HTTP_READ_TIMEOUT), /* 28683 0x700b HTTP data read timeout */ -# endif -# ifdef ESP_ERR_HTTP_INCOMPLETE_DATA - ERR_TBL_IT(ESP_ERR_HTTP_INCOMPLETE_DATA), /* 28684 0x700c Incomplete data received, less than - Content-Length or last chunk */ -# endif -# ifdef ESP_ERR_HTTP_REDIRECT_DOWNGRADE - ERR_TBL_IT(ESP_ERR_HTTP_REDIRECT_DOWNGRADE), /* 28685 0x700d HTTPS origin redirected to a non-HTTPS - scheme (downgrade blocked) */ -# endif - // components/esp-tls/esp_tls_errors.h -# ifdef ESP_ERR_ESP_TLS_BASE - ERR_TBL_IT(ESP_ERR_ESP_TLS_BASE), /* 32768 0x8000 Starting number of ESP-TLS error codes */ -# endif -# ifdef ESP_ERR_ESP_TLS_CANNOT_RESOLVE_HOSTNAME - ERR_TBL_IT(ESP_ERR_ESP_TLS_CANNOT_RESOLVE_HOSTNAME), /* 32769 0x8001 Error if hostname couldn't be resolved - upon tls connection */ -# endif -# ifdef ESP_ERR_ESP_TLS_CANNOT_CREATE_SOCKET - ERR_TBL_IT(ESP_ERR_ESP_TLS_CANNOT_CREATE_SOCKET), /* 32770 0x8002 Failed to create socket */ -# endif -# ifdef ESP_ERR_ESP_TLS_UNSUPPORTED_PROTOCOL_FAMILY - ERR_TBL_IT(ESP_ERR_ESP_TLS_UNSUPPORTED_PROTOCOL_FAMILY), /* 32771 0x8003 Unsupported protocol family */ -# endif -# ifdef ESP_ERR_ESP_TLS_FAILED_CONNECT_TO_HOST - ERR_TBL_IT(ESP_ERR_ESP_TLS_FAILED_CONNECT_TO_HOST), /* 32772 0x8004 Failed to connect to host */ -# endif -# ifdef ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED - ERR_TBL_IT(ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED), /* 32773 0x8005 failed to set/get socket option */ -# endif -# ifdef ESP_ERR_ESP_TLS_CONNECTION_TIMEOUT - ERR_TBL_IT(ESP_ERR_ESP_TLS_CONNECTION_TIMEOUT), /* 32774 0x8006 new connection in esp_tls_low_level_conn - connection timeouted */ -# endif -# ifdef ESP_ERR_ESP_TLS_SE_FAILED - ERR_TBL_IT(ESP_ERR_ESP_TLS_SE_FAILED), /* 32775 0x8007 */ -# endif -# ifdef ESP_ERR_ESP_TLS_TCP_CLOSED_FIN - ERR_TBL_IT(ESP_ERR_ESP_TLS_TCP_CLOSED_FIN), /* 32776 0x8008 */ -# endif -# ifdef ESP_ERR_ESP_TLS_SERVER_HANDSHAKE_TIMEOUT - ERR_TBL_IT(ESP_ERR_ESP_TLS_SERVER_HANDSHAKE_TIMEOUT), /* 32777 0x8009 TLS handshake timeout */ -# endif -# ifdef ESP_ERR_MBEDTLS_CERT_PARTLY_OK - ERR_TBL_IT(ESP_ERR_MBEDTLS_CERT_PARTLY_OK), /* 32784 0x8010 mbedtls parse certificates was partly successful */ -# endif -# ifdef ESP_ERR_MBEDTLS_CTR_DRBG_SEED_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_CTR_DRBG_SEED_FAILED), /* 32785 0x8011 mbedtls api returned error */ -# endif -# ifdef ESP_ERR_MBEDTLS_SSL_SET_HOSTNAME_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_SET_HOSTNAME_FAILED), /* 32786 0x8012 mbedtls api returned error */ -# endif -# ifdef ESP_ERR_MBEDTLS_SSL_CONFIG_DEFAULTS_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_CONFIG_DEFAULTS_FAILED), /* 32787 0x8013 mbedtls api returned error */ -# endif -# ifdef ESP_ERR_MBEDTLS_SSL_CONF_ALPN_PROTOCOLS_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_CONF_ALPN_PROTOCOLS_FAILED), /* 32788 0x8014 mbedtls api returned error */ -# endif -# ifdef ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED), /* 32789 0x8015 mbedtls api returned error */ -# endif -# ifdef ESP_ERR_MBEDTLS_SSL_CONF_OWN_CERT_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_CONF_OWN_CERT_FAILED), /* 32790 0x8016 mbedtls api returned error */ -# endif -# ifdef ESP_ERR_MBEDTLS_SSL_SETUP_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_SETUP_FAILED), /* 32791 0x8017 mbedtls api returned error */ -# endif -# ifdef ESP_ERR_MBEDTLS_SSL_WRITE_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_WRITE_FAILED), /* 32792 0x8018 mbedtls api returned error */ -# endif -# ifdef ESP_ERR_MBEDTLS_PK_PARSE_KEY_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_PK_PARSE_KEY_FAILED), /* 32793 0x8019 mbedtls api returned failed */ -# endif -# ifdef ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED), /* 32794 0x801a mbedtls api returned failed */ -# endif -# ifdef ESP_ERR_MBEDTLS_SSL_CONF_PSK_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_CONF_PSK_FAILED), /* 32795 0x801b mbedtls api returned failed */ -# endif -# ifdef ESP_ERR_MBEDTLS_SSL_TICKET_SETUP_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_TICKET_SETUP_FAILED), /* 32796 0x801c mbedtls api returned failed */ -# endif -# ifdef ESP_ERR_MBEDTLS_SSL_READ_FAILED - ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_READ_FAILED), /* 32797 0x801d mbedtls api returned failed */ -# endif - // components/esp_https_ota/include/esp_https_ota.h -# ifdef ESP_ERR_HTTPS_OTA_BASE - ERR_TBL_IT(ESP_ERR_HTTPS_OTA_BASE), /* 36864 0x9000 */ -# endif -# ifdef ESP_ERR_HTTPS_OTA_IN_PROGRESS - ERR_TBL_IT(ESP_ERR_HTTPS_OTA_IN_PROGRESS), /* 36865 0x9001 */ -# endif - // components/esp_http_server/include/esp_http_server.h -# ifdef ESP_ERR_HTTPD_BASE - ERR_TBL_IT(ESP_ERR_HTTPD_BASE), /* 45056 0xb000 Starting number of HTTPD error codes */ -# endif -# ifdef ESP_ERR_HTTPD_HANDLERS_FULL - ERR_TBL_IT(ESP_ERR_HTTPD_HANDLERS_FULL), /* 45057 0xb001 All slots for registering URI handlers - have been consumed */ -# endif -# ifdef ESP_ERR_HTTPD_HANDLER_EXISTS - ERR_TBL_IT(ESP_ERR_HTTPD_HANDLER_EXISTS), /* 45058 0xb002 URI handler with same method and target - URI already registered */ -# endif -# ifdef ESP_ERR_HTTPD_INVALID_REQ - ERR_TBL_IT(ESP_ERR_HTTPD_INVALID_REQ), /* 45059 0xb003 Invalid request pointer */ -# endif -# ifdef ESP_ERR_HTTPD_RESULT_TRUNC - ERR_TBL_IT(ESP_ERR_HTTPD_RESULT_TRUNC), /* 45060 0xb004 Result string truncated */ -# endif -# ifdef ESP_ERR_HTTPD_RESP_HDR - ERR_TBL_IT(ESP_ERR_HTTPD_RESP_HDR), /* 45061 0xb005 Response header field larger than supported */ -# endif -# ifdef ESP_ERR_HTTPD_RESP_SEND - ERR_TBL_IT(ESP_ERR_HTTPD_RESP_SEND), /* 45062 0xb006 Error occurred while sending response packet */ -# endif -# ifdef ESP_ERR_HTTPD_ALLOC_MEM - ERR_TBL_IT(ESP_ERR_HTTPD_ALLOC_MEM), /* 45063 0xb007 Failed to dynamically allocate memory - for resource */ -# endif -# ifdef ESP_ERR_HTTPD_TASK - ERR_TBL_IT(ESP_ERR_HTTPD_TASK), /* 45064 0xb008 Failed to launch server task/thread */ -# endif - // components/esp_common/include/esp_err.h -# ifdef ESP_ERR_HW_CRYPTO_BASE - ERR_TBL_IT(ESP_ERR_HW_CRYPTO_BASE), /* 49152 0xc000 Starting number of HW cryptography - module error codes */ -# endif - // components/esp_security/include/esp_ds_err.h -# ifdef ESP_ERR_HW_CRYPTO_DS_HMAC_FAIL - ERR_TBL_IT(ESP_ERR_HW_CRYPTO_DS_HMAC_FAIL), /* 49153 0xc001 HMAC peripheral problem */ -# endif -# ifdef ESP_ERR_HW_CRYPTO_DS_INVALID_KEY - ERR_TBL_IT(ESP_ERR_HW_CRYPTO_DS_INVALID_KEY), /* 49154 0xc002 */ -# endif -# ifdef ESP_ERR_HW_CRYPTO_DS_INVALID_DIGEST - ERR_TBL_IT(ESP_ERR_HW_CRYPTO_DS_INVALID_DIGEST), /* 49156 0xc004 */ -# endif -# ifdef ESP_ERR_HW_CRYPTO_DS_INVALID_PADDING - ERR_TBL_IT(ESP_ERR_HW_CRYPTO_DS_INVALID_PADDING), /* 49157 0xc005 */ -# endif - // components/esp_common/include/esp_err.h -# ifdef ESP_ERR_MEMPROT_BASE - ERR_TBL_IT(ESP_ERR_MEMPROT_BASE), /* 53248 0xd000 Starting number of Memory Protection API - error codes */ -# endif - // components/esp_hw_support/include/esp_memprot_err.h -# ifdef ESP_ERR_MEMPROT_MEMORY_TYPE_INVALID - ERR_TBL_IT(ESP_ERR_MEMPROT_MEMORY_TYPE_INVALID), /* 53249 0xd001 */ -# endif -# ifdef ESP_ERR_MEMPROT_SPLIT_ADDR_INVALID - ERR_TBL_IT(ESP_ERR_MEMPROT_SPLIT_ADDR_INVALID), /* 53250 0xd002 */ -# endif -# ifdef ESP_ERR_MEMPROT_SPLIT_ADDR_OUT_OF_RANGE - ERR_TBL_IT(ESP_ERR_MEMPROT_SPLIT_ADDR_OUT_OF_RANGE), /* 53251 0xd003 */ -# endif -# ifdef ESP_ERR_MEMPROT_SPLIT_ADDR_UNALIGNED - ERR_TBL_IT(ESP_ERR_MEMPROT_SPLIT_ADDR_UNALIGNED), /* 53252 0xd004 */ -# endif -# ifdef ESP_ERR_MEMPROT_UNIMGMT_BLOCK_INVALID - ERR_TBL_IT(ESP_ERR_MEMPROT_UNIMGMT_BLOCK_INVALID), /* 53253 0xd005 */ -# endif -# ifdef ESP_ERR_MEMPROT_WORLD_INVALID - ERR_TBL_IT(ESP_ERR_MEMPROT_WORLD_INVALID), /* 53254 0xd006 */ -# endif -# ifdef ESP_ERR_MEMPROT_AREA_INVALID - ERR_TBL_IT(ESP_ERR_MEMPROT_AREA_INVALID), /* 53255 0xd007 */ -# endif -# ifdef ESP_ERR_MEMPROT_CPUID_INVALID - ERR_TBL_IT(ESP_ERR_MEMPROT_CPUID_INVALID), /* 53256 0xd008 */ -# endif - // components/tcp_transport/include/esp_transport.h -# ifdef ESP_ERR_TCP_TRANSPORT_BASE - ERR_TBL_IT(ESP_ERR_TCP_TRANSPORT_BASE), /* 57344 0xe000 Starting number of TCP Transport error codes */ -# endif -# ifdef ESP_ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT - ERR_TBL_IT(ESP_ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT), /* 57345 0xe001 Connection has timed out */ -# endif -# ifdef ESP_ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN - ERR_TBL_IT(ESP_ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN), /* 57346 0xe002 Read FIN from peer and the connection - has closed (in a clean way) */ -# endif -# ifdef ESP_ERR_TCP_TRANSPORT_CONNECTION_FAILED - ERR_TBL_IT(ESP_ERR_TCP_TRANSPORT_CONNECTION_FAILED), /* 57347 0xe003 Failed to connect to the peer */ -# endif -# ifdef ESP_ERR_TCP_TRANSPORT_NO_MEM - ERR_TBL_IT(ESP_ERR_TCP_TRANSPORT_NO_MEM), /* 57348 0xe004 Memory allocation failed */ -# endif - // components/nvs_sec_provider/include/nvs_sec_provider.h -# ifdef ESP_ERR_NVS_SEC_BASE - ERR_TBL_IT(ESP_ERR_NVS_SEC_BASE), /* 61440 0xf000 Starting number of error codes */ -# endif -# ifdef ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND - ERR_TBL_IT(ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND), /* 61441 0xf001 HMAC Key required to generate the NVS - encryption keys not found */ -# endif -# ifdef ESP_ERR_NVS_SEC_HMAC_KEY_BLK_ALREADY_USED - ERR_TBL_IT(ESP_ERR_NVS_SEC_HMAC_KEY_BLK_ALREADY_USED), /* 61442 0xf002 Provided eFuse block for HMAC key - generation is already in use */ -# endif -# ifdef ESP_ERR_NVS_SEC_HMAC_KEY_GENERATION_FAILED - ERR_TBL_IT(ESP_ERR_NVS_SEC_HMAC_KEY_GENERATION_FAILED), /* 61443 0xf003 Failed to generate/write the HMAC key to eFuse */ -# endif -# ifdef ESP_ERR_NVS_SEC_HMAC_XTS_KEYS_DERIV_FAILED - ERR_TBL_IT(ESP_ERR_NVS_SEC_HMAC_XTS_KEYS_DERIV_FAILED), /* 61444 0xf004 Failed to derive the NVS encryption keys - based on the HMAC-based scheme */ -# endif -}; -#endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP +_SECTION_ATTR_SYMBOL_DECL_GENERIC(esp_err_msg_t, esp_err_msg_tbl) +#endif /* CONFIG_ESP_ERR_TO_NAME_LOOKUP */ static const char esp_unknown_msg[] = #ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP @@ -897,11 +33,10 @@ static const char esp_unknown_msg[] = const char *esp_err_to_name(esp_err_t code) { #ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP - size_t i; - - for (i = 0; i < sizeof(esp_err_msg_table) / sizeof(esp_err_msg_table[0]); ++i) { - if (esp_err_msg_table[i].code == code) { - return esp_err_msg_table[i].msg; + const esp_err_msg_t *p; + for (p = _SECTION_START(esp_err_msg_tbl); p < _SECTION_END(esp_err_msg_tbl); ++p) { + if (p->code == code) { + return p->msg; } } #endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP @@ -912,11 +47,10 @@ const char *esp_err_to_name(esp_err_t code) const char *esp_err_to_name_r(esp_err_t code, char *buf, size_t buflen) { #ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP - size_t i; - - for (i = 0; i < sizeof(esp_err_msg_table) / sizeof(esp_err_msg_table[0]); ++i) { - if (esp_err_msg_table[i].code == code) { - strlcpy(buf, esp_err_msg_table[i].msg, buflen); + const esp_err_msg_t *p; + for (p = _SECTION_START(esp_err_msg_tbl); p < _SECTION_END(esp_err_msg_tbl); ++p) { + if (p->code == code) { + strlcpy(buf, p->msg, buflen); return buf; } } diff --git a/components/esp_common/src/esp_err_to_name.c.in b/components/esp_common/src/esp_err_to_name.c.in deleted file mode 100644 index d9c48c270ce..00000000000 --- a/components/esp_common/src/esp_err_to_name.c.in +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -@COMMENT@ - -#include -#include "esp_err.h" -#if __has_include("soc/soc.h") -#include "soc/soc.h" -#endif -@HEADERS@ - -#ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP -#define ERR_TBL_IT(err) {err, #err} - -typedef struct { - esp_err_t code; - const char *msg; -} esp_err_msg_t; - -static const esp_err_msg_t esp_err_msg_table[] = { -@ERROR_ITEMS@ -}; -#endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP - -static const char esp_unknown_msg[] = -#ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP - "ERROR"; -#else - "UNKNOWN ERROR"; -#endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP - -const char *esp_err_to_name(esp_err_t code) -{ -#ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP - size_t i; - - for (i = 0; i < sizeof(esp_err_msg_table) / sizeof(esp_err_msg_table[0]); ++i) { - if (esp_err_msg_table[i].code == code) { - return esp_err_msg_table[i].msg; - } - } -#endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP - - return esp_unknown_msg; -} - -const char *esp_err_to_name_r(esp_err_t code, char *buf, size_t buflen) -{ -#ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP - size_t i; - - for (i = 0; i < sizeof(esp_err_msg_table) / sizeof(esp_err_msg_table[0]); ++i) { - if (esp_err_msg_table[i].code == code) { - strlcpy(buf, esp_err_msg_table[i].msg, buflen); - return buf; - } - } -#endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP - - if (strerror_r(code, buf, buflen) == 0) { - return buf; - } - - snprintf(buf, buflen, "%s 0x%x(%d)", esp_unknown_msg, code, code); - - return buf; -} diff --git a/components/esp_common/test_apps/esp_common/main/CMakeLists.txt b/components/esp_common/test_apps/esp_common/main/CMakeLists.txt index defaf9f7073..41a7666f672 100644 --- a/components/esp_common/test_apps/esp_common/main/CMakeLists.txt +++ b/components/esp_common/test_apps/esp_common/main/CMakeLists.txt @@ -1,6 +1,7 @@ idf_component_register(SRCS "test_app_main.c" "test_attr.c" "test_esp_check.c" + "test_esp_err_to_name.c" "test_esp_macro.c" INCLUDE_DIRS "." PRIV_REQUIRES unity esp_mm esp_psram diff --git a/components/esp_common/test_apps/esp_common/main/test_esp_err_to_name.c b/components/esp_common/test_apps/esp_common/main/test_esp_err_to_name.c new file mode 100644 index 00000000000..0e12b45c0bb --- /dev/null +++ b/components/esp_common/test_apps/esp_common/main/test_esp_err_to_name.c @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include "esp_err.h" +#include "unity.h" + +/** + * Tests for esp_err_to_name() and esp_err_to_name_r(). + * + * These verify that the link-time error code table is populated correctly + * and that lookups work for well-known codes defined in esp_err.h. + */ + +TEST_CASE("esp_err_to_name returns correct string for ESP_OK", "[esp_err]") +{ + const char *name = esp_err_to_name(ESP_OK); + TEST_ASSERT_NOT_NULL(name); + TEST_ASSERT_EQUAL_STRING("ESP_OK", name); +} + +TEST_CASE("esp_err_to_name returns correct string for ESP_FAIL", "[esp_err]") +{ + const char *name = esp_err_to_name(ESP_FAIL); + TEST_ASSERT_NOT_NULL(name); + TEST_ASSERT_EQUAL_STRING("ESP_FAIL", name); +} + +TEST_CASE("esp_err_to_name returns correct strings for common error codes", "[esp_err]") +{ + TEST_ASSERT_EQUAL_STRING("ESP_ERR_NO_MEM", esp_err_to_name(ESP_ERR_NO_MEM)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_INVALID_ARG", esp_err_to_name(ESP_ERR_INVALID_ARG)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_INVALID_STATE", esp_err_to_name(ESP_ERR_INVALID_STATE)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_INVALID_SIZE", esp_err_to_name(ESP_ERR_INVALID_SIZE)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_NOT_FOUND", esp_err_to_name(ESP_ERR_NOT_FOUND)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_NOT_SUPPORTED", esp_err_to_name(ESP_ERR_NOT_SUPPORTED)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_TIMEOUT", esp_err_to_name(ESP_ERR_TIMEOUT)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_INVALID_RESPONSE", esp_err_to_name(ESP_ERR_INVALID_RESPONSE)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_INVALID_CRC", esp_err_to_name(ESP_ERR_INVALID_CRC)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_INVALID_VERSION", esp_err_to_name(ESP_ERR_INVALID_VERSION)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_INVALID_MAC", esp_err_to_name(ESP_ERR_INVALID_MAC)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_NOT_FINISHED", esp_err_to_name(ESP_ERR_NOT_FINISHED)); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_NOT_ALLOWED", esp_err_to_name(ESP_ERR_NOT_ALLOWED)); +} + +TEST_CASE("esp_err_to_name returns fallback for unknown code", "[esp_err]") +{ + /* Use a code that should never be defined */ + const char *name = esp_err_to_name(0xFFFF); + TEST_ASSERT_NOT_NULL(name); + /* Should return "ERROR" when lookup is enabled, "UNKNOWN ERROR" otherwise */ + TEST_ASSERT_TRUE(strcmp(name, "ERROR") == 0 || strcmp(name, "UNKNOWN ERROR") == 0); +} + +TEST_CASE("esp_err_to_name_r writes correct string to buffer", "[esp_err]") +{ + char buf[64]; + const char *result = esp_err_to_name_r(ESP_ERR_NO_MEM, buf, sizeof(buf)); + TEST_ASSERT_EQUAL_PTR(buf, result); + TEST_ASSERT_EQUAL_STRING("ESP_ERR_NO_MEM", buf); +} + +TEST_CASE("esp_err_to_name_r truncates to buffer size", "[esp_err]") +{ + char buf[8]; /* Too small for "ESP_ERR_NO_MEM" (14 chars + null) */ + const char *result = esp_err_to_name_r(ESP_ERR_NO_MEM, buf, sizeof(buf)); + TEST_ASSERT_EQUAL_PTR(buf, result); + /* strlcpy should truncate and null-terminate */ + TEST_ASSERT_EQUAL_STRING("ESP_ERR", buf); +} + +TEST_CASE("esp_err_to_name_r formats unknown code with hex value", "[esp_err]") +{ + char buf[64]; + const char *result = esp_err_to_name_r(0xFFFF, buf, sizeof(buf)); + TEST_ASSERT_EQUAL_PTR(buf, result); + /* Should contain the hex value */ + TEST_ASSERT_NOT_NULL(strstr(buf, "0xffff")); +} diff --git a/components/esp_hal_mspi/CMakeLists.txt b/components/esp_hal_mspi/CMakeLists.txt index afe56677597..08f052c88e8 100644 --- a/components/esp_hal_mspi/CMakeLists.txt +++ b/components/esp_hal_mspi/CMakeLists.txt @@ -38,3 +38,5 @@ idf_component_register( REQUIRES soc hal esp_hal_gpspi esp_hal_clock # TODO: IDF-15106 remove esp_hal_clock dependency PRIV_REQUIRES esp_hal_gpio ) + +idf_define_esp_err_codes(HEADERS include/hal/esp_flash_err.h) diff --git a/components/esp_http_client/CMakeLists.txt b/components/esp_http_client/CMakeLists.txt index 4a353e33ca0..44708599090 100644 --- a/components/esp_http_client/CMakeLists.txt +++ b/components/esp_http_client/CMakeLists.txt @@ -13,3 +13,5 @@ idf_component_register(SRCS "esp_http_client.c" # lwip is a public requirement because esp_http_client.h includes sys/socket.h REQUIRES ${req} PRIV_REQUIRES tcp_transport http_parser) + +idf_define_esp_err_codes(HEADERS include/esp_http_client.h) diff --git a/components/esp_http_server/CMakeLists.txt b/components/esp_http_server/CMakeLists.txt index 25bc836276b..47952035995 100644 --- a/components/esp_http_server/CMakeLists.txt +++ b/components/esp_http_server/CMakeLists.txt @@ -14,3 +14,5 @@ idf_component_register(SRCS "src/httpd_main.c" PRIV_INCLUDE_DIRS ${priv_inc_dir} REQUIRES ${requires} PRIV_REQUIRES ${priv_req}) + +idf_define_esp_err_codes(HEADERS include/esp_http_server.h) diff --git a/components/esp_https_ota/CMakeLists.txt b/components/esp_https_ota/CMakeLists.txt index 95b5bbbde1d..b65b8f7963d 100644 --- a/components/esp_https_ota/CMakeLists.txt +++ b/components/esp_https_ota/CMakeLists.txt @@ -9,3 +9,5 @@ idf_component_register(SRCS "src/esp_https_ota.c" REQUIRES esp_http_client esp_bootloader_format esp_app_format esp_event esp_partition PRIV_REQUIRES log app_update efuse) + +idf_define_esp_err_codes(HEADERS include/esp_https_ota.h) diff --git a/components/esp_hw_support/CMakeLists.txt b/components/esp_hw_support/CMakeLists.txt index 9ff7582e140..728b91040d0 100644 --- a/components/esp_hw_support/CMakeLists.txt +++ b/components/esp_hw_support/CMakeLists.txt @@ -200,6 +200,8 @@ idf_component_register(SRCS ${srcs} PRIV_REQUIRES "${priv_requires}" LDFRAGMENTS linker.lf ldo/linker.lf mspi/linker.lf) +idf_define_esp_err_codes(HEADERS include/esp_memprot_err.h) + idf_build_get_property(target IDF_TARGET) add_subdirectory(port/${target}) if(CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING) diff --git a/components/esp_netif/CMakeLists.txt b/components/esp_netif/CMakeLists.txt index d1aa21974b8..dfc4fbdcb4d 100644 --- a/components/esp_netif/CMakeLists.txt +++ b/components/esp_netif/CMakeLists.txt @@ -42,6 +42,8 @@ idf_component_register(SRCS "${srcs}" PRIV_REQUIRES esp_netif_stack LDFRAGMENTS linker.lf) +idf_define_esp_err_codes(HEADERS include/esp_netif_types.h) + if(CONFIG_ESP_NETIF_L2_TAP OR CONFIG_ESP_NETIF_BRIDGE_EN) set(optional_requires "") if(CONFIG_ESP_NETIF_L2_TAP) diff --git a/components/esp_security/CMakeLists.txt b/components/esp_security/CMakeLists.txt index fe08fb3e07a..d9a44aa698b 100644 --- a/components/esp_security/CMakeLists.txt +++ b/components/esp_security/CMakeLists.txt @@ -57,6 +57,8 @@ idf_component_register(SRCS ${srcs} REQUIRES ${requires} PRIV_REQUIRES ${priv_requires}) +idf_define_esp_err_codes(HEADERS include/esp_ds_err.h) + if(NOT non_os_build) target_link_libraries(${COMPONENT_LIB} PRIVATE "-u esp_security_init_include_impl") elseif(esp_tee_build) diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index a0c6f614167..29e9b57d131 100644 --- a/components/esp_wifi/CMakeLists.txt +++ b/components/esp_wifi/CMakeLists.txt @@ -85,6 +85,8 @@ idf_component_register(SRCS "${srcs}" wifi_apps/roaming_app/include wifi_apps/roaming_app/src LDFRAGMENTS "${ldfragments}") +idf_define_esp_err_codes(HEADERS include/esp_wifi.h include/esp_now.h include/esp_mesh.h) + if(CONFIG_ESP_WIFI_ENABLED OR CONFIG_ESP_HOST_WIFI_ENABLED) idf_build_get_property(build_dir BUILD_DIR) diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index d657ad7d3ff..70aad776d9f 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -72,6 +72,8 @@ else() INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS "private_include") + idf_define_esp_err_codes(HEADERS include/nvs.h) + # If we use the linux target, we need to redirect the crc functions to the linux if(${target} STREQUAL "linux") if(CONFIG_NVS_ENCRYPTION) diff --git a/components/nvs_sec_provider/CMakeLists.txt b/components/nvs_sec_provider/CMakeLists.txt index 2f45f352217..41b7eae34d4 100644 --- a/components/nvs_sec_provider/CMakeLists.txt +++ b/components/nvs_sec_provider/CMakeLists.txt @@ -17,6 +17,8 @@ idf_component_register(SRCS ${srcs} PRIV_REQUIRES bootloader_support efuse esp_partition nvs_flash REQUIRES esp_security) +idf_define_esp_err_codes(HEADERS include/nvs_sec_provider.h) + # NOTE: In a case where only the default NVS partition is to be encrypted # and no custom NVS partitions exist, `nvs_flash_init` is the only API that # needs to be called. No API from the `nvs_sec_provider` component is called diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index 9f3e8ff644b..7942dde494d 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -72,6 +72,8 @@ idf_component_register(SRCS "${srcs}" INCLUDE_DIRS include LDFRAGMENTS linker.lf) +idf_define_esp_err_codes(HEADERS include/spi_flash_mmap.h) + # Avoid cache miss by unexpected inlineing when built by -Os set_source_files_properties(${cache_srcs} PROPERTIES COMPILE_FLAGS "-fno-inline-functions") if(CMAKE_C_COMPILER_ID MATCHES "GNU") diff --git a/components/tcp_transport/CMakeLists.txt b/components/tcp_transport/CMakeLists.txt index 9c4fce482df..fe07e942939 100644 --- a/components/tcp_transport/CMakeLists.txt +++ b/components/tcp_transport/CMakeLists.txt @@ -30,6 +30,8 @@ idf_component_register(SRCS "${srcs}" PRIV_INCLUDE_DIRS "private_include" REQUIRES ${req}) +idf_define_esp_err_codes(HEADERS include/esp_transport.h) + if(${IDF_TARGET} STREQUAL "linux" AND NOT IDF_BUILD_V2) # Check if LWIP in the build for linux target to add esp_timer to the dependencies # since socks_proxy transport needs it and lwip & linux build could use it diff --git a/components/ulp/CMakeLists.txt b/components/ulp/CMakeLists.txt index f3ba3d86646..b55067b5801 100644 --- a/components/ulp/CMakeLists.txt +++ b/components/ulp/CMakeLists.txt @@ -102,3 +102,5 @@ idf_component_register( esp_hal_i2c esp_hal_touch_sens esp_hal_gpspi esp_hal_pmu esp_hal_rtc_timer esp_hal_clock PRIV_REQUIRES bootloader_support esp_mm ) + +idf_define_esp_err_codes(HEADERS ulp_fsm/include/ulp_fsm_common.h) diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 8a9b5829334..e89cf707098 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -279,6 +279,8 @@ idf_component_register(SRCS "${srcs}" "${esp_srcs}" "${tls_src}" "${roaming_src} LDFRAGMENTS ${linker_fragments} PRIV_REQUIRES mbedtls esp_timer esp_wifi) +idf_define_esp_err_codes(HEADERS esp_supplicant/include/esp_dpp.h esp_supplicant/include/esp_wps.h) + target_compile_definitions(${COMPONENT_LIB} PRIVATE __ets__ ESP_SUPPLICANT diff --git a/docs/en/api-reference/system/esp_err.rst b/docs/en/api-reference/system/esp_err.rst index 62944412431..bb7271d5354 100644 --- a/docs/en/api-reference/system/esp_err.rst +++ b/docs/en/api-reference/system/esp_err.rst @@ -9,6 +9,76 @@ For general information about error codes in ESP-IDF, see :doc:`Error Handling < For the full list of error codes defined in ESP-IDF, see :doc:`Error Codes Reference <../error-codes>`. +.. _registering-error-codes: + +Registering Error Codes +------------------------ + +ESP-IDF uses a composable error code registration system that automatically collects error code definitions from all components at build time. This allows :cpp:func:`esp_err_to_name` and :cpp:func:`esp_err_to_name_r` to look up error codes defined across the entire project without requiring manual maintenance of a central registry. + +How It Works +^^^^^^^^^^^^ + +Error codes are registered at link time using a linker section called ``.esp_err_msg_tbl``. When you define error codes in your component's header files, you need to register those headers with the build system, which will: + +1. Extract the error code definitions during the build +2. Generate C code that places them into the ``.esp_err_msg_tbl`` section +3. Link them into your application automatically + +The linker collects all error code entries from all components into a single array that :cpp:func:`esp_err_to_name` searches at runtime. + +Registering Your Component's Error Codes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To register error codes from your component, add the following line to your component's ``CMakeLists.txt``: + +.. code-block:: cmake + + idf_define_esp_err_codes(HEADERS include/my_component.h) + +Replace ``include/my_component.h`` with the path to your header file(s) that contain error code definitions. You can specify multiple headers: + +.. code-block:: cmake + + idf_define_esp_err_codes(HEADERS + include/my_api.h + include/my_driver.h + ) + +The build system will automatically process these headers and register any ``#define`` directives that match the error code pattern (typically starting with ``ESP_ERR_`` or similar). + +Example +^^^^^^^ + +In your component's header file ``include/my_component.h``: + +.. code-block:: c + + #pragma once + + #include "esp_err.h" + + #define ESP_ERR_MY_COMPONENT_BASE 0x7000 + #define ESP_ERR_MY_COMPONENT_INIT (ESP_ERR_MY_COMPONENT_BASE + 1) /*!< Component initialization failed */ + #define ESP_ERR_MY_COMPONENT_BUSY (ESP_ERR_MY_COMPONENT_BASE + 2) /*!< Component is busy */ + +In your component's ``CMakeLists.txt``: + +.. code-block:: cmake + + idf_component_register(SRCS "my_component.c" + INCLUDE_DIRS "include" + PRIV_REQUIRES esp_common) + + # Register error codes + idf_define_esp_err_codes(HEADERS include/my_component.h) + +After building your project, calls to ``esp_err_to_name(ESP_ERR_MY_COMPONENT_INIT)`` will return the string ``"ESP_ERR_MY_COMPONENT_INIT"``. + +.. note:: + + Most ESP-IDF components already have their error codes registered. You only need to add ``idf_define_esp_err_codes()`` for your own custom components or when adding new error codes to existing components. + .. _esp-check-api-ref: API Reference diff --git a/tools/ci/mypy_ignore_list.txt b/tools/ci/mypy_ignore_list.txt index 06fed1303ed..650a100ca2a 100644 --- a/tools/ci/mypy_ignore_list.txt +++ b/tools/ci/mypy_ignore_list.txt @@ -41,7 +41,6 @@ tools/esp_app_trace/espytrace/apptrace.py tools/esp_app_trace/espytrace/sysview.py tools/esp_app_trace/logtrace_proc.py tools/esp_app_trace/sysviewtrace_proc.py -tools/gen_esp_err_to_name.py tools/gen_soc_caps_kconfig/test/test_gen_soc_caps_kconfig.py tools/ldgen/ldgen.py tools/ldgen/ldgen/entity.py diff --git a/tools/cmake/err_codes.cmake b/tools/cmake/err_codes.cmake new file mode 100644 index 00000000000..0c3682f1341 --- /dev/null +++ b/tools/cmake/err_codes.cmake @@ -0,0 +1,153 @@ +# SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# +# CMake module for composable error code registration. +# +# Provides the idf_define_esp_err_codes() function that components call +# to register their error codes into a link-time array. +# + +# +# idf_define_esp_err_codes +# +# Register error codes defined in the specified header files. +# This function: +# 1. Runs err_codes_extract.py to extract error codes from headers into a CSV +# 2. Runs err_codes_to_c.py to generate a C source file placing entries into +# the .esp_err_msg_tbl linker section +# 3. Adds the generated C source file to the current component +# 4. Adds -Wl,--undefined to force-link the error code symbols +# +# Usage (must be called AFTER idf_component_register): +# idf_define_esp_err_codes(HEADERS include/my_errors.h include/other.h) +# +# Note: This function relies on COMPONENT_LIB and COMPONENT_NAME which are +# only available after idf_component_register() has been called. +# +function(idf_define_esp_err_codes) + # If error-to-name lookup is disabled, do nothing + if(NOT CONFIG_ESP_ERR_TO_NAME_LOOKUP) + return() + endif() + + set(options "") + set(single_value "") + set(multi_value HEADERS) + cmake_parse_arguments(ARG "${options}" "${single_value}" "${multi_value}" ${ARGN}) + + idf_build_get_property(idf_path IDF_PATH) + idf_build_get_property(build_dir BUILD_DIR) + idf_build_get_property(python PYTHON) + + # Determine component name + if(NOT COMPONENT_NAME) + get_filename_component(COMPONENT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) + endif() + + # Collect header files + set(header_files "") + + # Process explicit headers (relative to component dir) + foreach(header ${ARG_HEADERS}) + if(IS_ABSOLUTE ${header}) + list(APPEND header_files "${header}") + else() + list(APPEND header_files "${CMAKE_CURRENT_LIST_DIR}/${header}") + endif() + endforeach() + + if(NOT header_files) + message(FATAL_ERROR "idf_define_esp_err_codes: No header files found for component ${COMPONENT_NAME}") + endif() + + # Output paths + set(csv_output "${build_dir}/esp_err_codes/${COMPONENT_NAME}_err_codes.csv") + set(c_output "${build_dir}/esp_err_codes/${COMPONENT_NAME}_esp_err_codes.c") + + # Ensure output directory exists + file(MAKE_DIRECTORY "${build_dir}/esp_err_codes") + + # Build the header arguments for the extract script + # Always provide esp_err.h as a context header for ESP_ERR_*_BASE definitions + set(header_args "") + set(esp_err_h "${idf_path}/components/esp_common/include/esp_err.h") + set(context_args "") + set(need_context TRUE) + foreach(h ${header_files}) + list(APPEND header_args "${h}") + if("${h}" STREQUAL "${esp_err_h}") + set(need_context FALSE) + endif() + endforeach() + if(need_context) + set(context_args "--context-headers" "${esp_err_h}") + endif() + + # Step 1: Extract error codes from headers to CSV (build time) + set(extract_script "${idf_path}/tools/err_codes_extract.py") + + add_custom_command( + OUTPUT "${csv_output}" + COMMAND ${python} "${extract_script}" + --headers ${header_args} + ${context_args} + --base-path "${idf_path}" + --output "${csv_output}" + DEPENDS ${header_files} ${esp_err_h} "${extract_script}" + COMMENT "Extracting error codes for ${COMPONENT_NAME}" + VERBATIM + ) + + # Compute include-form headers for the generated C file + set(include_headers "") + foreach(h ${header_files}) + # Convert full path to include-relative path + get_filename_component(h_name "${h}" NAME) + # Try to find 'include' in the path + string(FIND "${h}" "/include/" include_pos) + if(include_pos GREATER -1) + math(EXPR include_start "${include_pos} + 9") # length of "/include/" + string(SUBSTRING "${h}" ${include_start} -1 include_rel) + list(APPEND include_headers "${include_rel}") + else() + list(APPEND include_headers "${h_name}") + endif() + endforeach() + + # Step 2: Generate C source from CSV (build time) + set(gen_script "${idf_path}/tools/err_codes_to_c.py") + set(header_flags "") + foreach(inc ${include_headers}) + list(APPEND header_flags "--headers" "${inc}") + endforeach() + + add_custom_command( + OUTPUT "${c_output}" + COMMAND ${python} "${gen_script}" + --csv "${csv_output}" + --output "${c_output}" + --component "${COMPONENT_NAME}" + ${header_flags} + DEPENDS "${csv_output}" "${gen_script}" + COMMENT "Generating error code table for ${COMPONENT_NAME}" + VERBATIM + ) + + # Step 3: Add generated C source to the component + target_sources(${COMPONENT_LIB} PRIVATE "${c_output}") + + # Step 4: Force-link the error codes symbol so it's not discarded + string(REPLACE "-" "_" safe_component "${COMPONENT_NAME}") + string(REPLACE "." "_" safe_component "${safe_component}") + # On macOS (Mach-O) the linker sees C symbols with a leading '_' prepended + # by the compiler. Pass the mangled name so -u resolves correctly. + # On Linux (ELF) no prefix is added, so use the C identifier as-is. + if(APPLE) + set(force_link_symbol "_esp_err_msg_${safe_component}_include") + else() + set(force_link_symbol "esp_err_msg_${safe_component}_include") + endif() + + target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ${force_link_symbol}") + +endfunction() diff --git a/tools/cmake/idf.cmake b/tools/cmake/idf.cmake index 06600490799..70a24bbccef 100644 --- a/tools/cmake/idf.cmake +++ b/tools/cmake/idf.cmake @@ -51,6 +51,7 @@ if(NOT __idf_env_set) include(gdbinit) include(prefix_map) include(openocd) + include(err_codes) # ESP-IDF extra dependencies defined in tools/idf_extra_components.yml if(WIN32) diff --git a/tools/cmake/project.cmake b/tools/cmake/project.cmake index aecb4dc6384..04dc1da4652 100644 --- a/tools/cmake/project.cmake +++ b/tools/cmake/project.cmake @@ -831,13 +831,29 @@ macro(project project_name) COMMAND ${CMAKE_COMMAND} -E touch ${project_elf_src} VERBATIM) add_custom_target(_project_elf_src DEPENDS "${project_elf_src}") + + # On the Linux (host) target the standard GNU ld processes static archives + # in a single left-to-right pass, which fails when component libraries (or + # their transitive dependencies such as the mbedtls sub-libraries) have + # circular symbol references. Wrap all archives in --start-group / + # --end-group so the linker re-scans until every symbol is resolved. + if(CONFIG_IDF_TARGET_LINUX AND NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + string(CONCAT _link_exe_template + " " + " -o " + " -Wl,--start-group -Wl,--end-group") + set(CMAKE_C_LINK_EXECUTABLE "${_link_exe_template}") + string(REPLACE "" "" + _link_exe_template "${_link_exe_template}") + string(REPLACE "" "" + _link_exe_template "${_link_exe_template}") + set(CMAKE_CXX_LINK_EXECUTABLE "${_link_exe_template}") + unset(_link_exe_template) + endif() + add_executable(${project_elf} "${project_elf_src}") add_dependencies(${project_elf} _project_elf_src) - if(__PROJECT_GROUP_LINK_COMPONENTS) - target_link_libraries(${project_elf} PRIVATE "-Wl,--start-group") - endif() - if(CONFIG_IDF_TARGET_LINUX AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") # Compiling for the host, and the host is macOS, so the linker is Darwin LD. # Note, when adding support for Clang and LLD based toolchain this check will @@ -847,6 +863,10 @@ macro(project project_name) set(linker_type "GNU") endif() + if(__PROJECT_GROUP_LINK_COMPONENTS) + target_link_libraries(${project_elf} PRIVATE "-Wl,--start-group") + endif() + if(test_components) if(linker_type STREQUAL "GNU") target_link_libraries(${project_elf} PRIVATE "-Wl,--whole-archive") diff --git a/tools/cmakev2/idf.cmake b/tools/cmakev2/idf.cmake index 4472dec7e25..03b45abb57f 100644 --- a/tools/cmakev2/idf.cmake +++ b/tools/cmakev2/idf.cmake @@ -32,6 +32,10 @@ include(${CMAKE_CURRENT_LIST_DIR}/../cmake/openocd.cmake) # idf_build_generate_depgraph function. include(${CMAKE_CURRENT_LIST_DIR}/../cmake/depgraph.cmake) +# The err_codes.cmake file from cmakev1 provides idf_define_esp_err_codes(), +# which generates per-component error code tables placed in a link-time array. +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/err_codes.cmake) + include(component) include(build) include(kconfig) @@ -42,6 +46,7 @@ include(ldgen) include(dfu) include(uf2) include(size) + include(GetGitRevisionDescription) # For backward compatibility, since externalproject_add is used by # project_include.cmake in the bootloader component. The ExternalProject diff --git a/tools/err_codes_extract.py b/tools/err_codes_extract.py new file mode 100644 index 00000000000..88df8654f4e --- /dev/null +++ b/tools/err_codes_extract.py @@ -0,0 +1,377 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# +# Extract ESP_ERR_* error code definitions from C header files and output them as CSV. +# This script is used by the composable error code registration system. +# +# Usage: +# python err_codes_extract.py --headers file1.h file2.h --output errors.csv +# python err_codes_extract.py --search-dirs components/ --output errors.csv +# + +import argparse +import csv +import fnmatch +import os +import re +import sys + +# Directories to exclude from recursive search +EXCLUDE_SEARCH_DIRS = {'test_apps', 'unit-test-app', 'CMock', 'test'} + +# Files to exclude +EXCLUDE_FILES: list[str] = [] + + +class ErrCode: + """Represents a single extracted error code definition.""" + + def __init__( + self, + name: str, + file: str, + value: int | None = None, + base_name: str = '', + base_offset: int = 0, + comment: str = '', + ) -> None: + self.name = name + self.file = file + self.value = value # Resolved integer value, or None if unresolved + self.base_name = base_name # Name of base error if this is BASE + offset + self.base_offset = base_offset + self.comment = comment + + def __repr__(self) -> str: + if self.value is not None: + return f'{self.name} = {self.value} (0x{self.value & 0xFFFFFFFF:x}) from {self.file}' + return f'{self.name} = {self.base_name}+{self.base_offset} from {self.file}' + + +def _parse_define_value(value_str: str) -> tuple[int | None, str, int]: + """ + Parse the value part of a #define. + + Returns: + (resolved_value, base_name, base_offset) + If the value is a plain number, resolved_value is set and base_name is empty. + If the value references a base, resolved_value is None and base_name/base_offset are set. + """ + # Strip surrounding parentheses + value_str = value_str.strip() + m = re.match(r'^\((.+)\)$', value_str) + if m: + value_str = m.group(1).strip() + + # Check for BASE + offset pattern + m = re.match(r'(\w+)\s*\+\s*(.+)', value_str) + if m: + base_name = m.group(1) + offset_str = m.group(2).strip() + # Try to parse offset as hex or decimal + offset = _parse_number(offset_str) + if offset is not None: + return (None, base_name, offset) + # offset might be another symbol — treat as unresolvable for now + return (None, base_name, 0) + + # Try to parse as a plain number + num = _parse_number(value_str) + if num is not None: + return (num, '', 0) + + # It might be a reference to another symbol (no offset) + m = re.match(r'^(\w+)$', value_str) + if m: + return (None, m.group(1), 0) + + return (None, '', 0) + + +def _parse_number(s: str) -> int | None: + """Parse a hexadecimal or decimal integer from a string.""" + s = s.strip() + m = re.match(r'^0x([0-9A-Fa-f]+)$', s) + if m: + return int(m.group(1), 16) + m = re.match(r'^(-?[0-9]+)$', s) + if m: + return int(m.group(1), 10) + return None + + +def extract_from_file(filepath: str, rel_path: str) -> list[ErrCode]: + """ + Extract ESP_ERR_* defines from a single header file. + + Args: + filepath: Absolute path to the file. + rel_path: Relative path for display/CSV output. + + Returns: + List of ErrCode objects. + """ + results: list[ErrCode] = [] + define_pattern = re.compile( + r'^\s*#\s*define\s+(ESP_ERR_\w+|ESP_OK\w*|ESP_FAIL)\s+(.*?)(?:\s*/\*[*!]? "0x101" + comment_match = re.search(r'\s*/\*[*!]? list[ErrCode]: + """ + Resolve error codes that depend on base codes. + Performs multiple passes to handle transitive dependencies. + + Returns list of resolved ErrCode objects (with .value set). + Unresolvable codes are printed as warnings and excluded. + """ + # Build a lookup from name to resolved value + resolved: dict[str, int] = {} + unresolved: list[ErrCode] = [] + + for ec in err_codes: + if ec.value is not None: + resolved[ec.name] = ec.value + else: + unresolved.append(ec) + + # Iteratively resolve + max_iterations = 10 + for _ in range(max_iterations): + still_unresolved = [] + for ec in unresolved: + if ec.base_name in resolved: + ec.value = resolved[ec.base_name] + ec.base_offset + resolved[ec.name] = ec.value + else: + still_unresolved.append(ec) + if len(still_unresolved) == len(unresolved): + break # No progress + unresolved = still_unresolved + + for ec in unresolved: + print(f'Warning: Cannot resolve {ec.name} (depends on {ec.base_name}) in {ec.file}', file=sys.stderr) + + return [ec for ec in err_codes if ec.value is not None] + + +def search_headers(search_dirs: list[str], base_path: str) -> list[str]: + """ + Recursively find all .h files in the given directories, + excluding test directories. + """ + headers: list[str] = [] + for search_dir in search_dirs: + full_dir = os.path.join(base_path, search_dir) if not os.path.isabs(search_dir) else search_dir + for root, dirnames, filenames in os.walk(full_dir, topdown=True): + # Filter out excluded directories + dirnames[:] = [d for d in dirnames if d not in EXCLUDE_SEARCH_DIRS] + for filename in fnmatch.filter(filenames, '*.h'): + headers.append(os.path.join(root, filename)) + return headers + + +def extract_all(headers: list[str], base_path: str, context_headers: list[str] | None = None) -> list[ErrCode]: + """ + Extract and resolve error codes from a list of header files. + + Args: + headers: Headers whose error codes will be emitted. + base_path: Base path for relative paths. + context_headers: Additional headers scanned only for base definitions + (e.g. ESP_ERR_*_BASE). Their codes are used for + resolution but are excluded from the final output. + """ + context_names: set = set() + all_codes: list[ErrCode] = [] + + # Scan context headers first — their defines help resolve bases + if context_headers: + for header in context_headers: + rel = os.path.relpath(header, base_path) if base_path else header + ctx_codes = extract_from_file(header, rel) + for ec in ctx_codes: + context_names.add(ec.name) + all_codes.extend(ctx_codes) + + # Scan primary headers + for header in headers: + rel = os.path.relpath(header, base_path) if base_path else header + all_codes.extend(extract_from_file(header, rel)) + + resolved = resolve_error_codes(all_codes) + + # Remove context-only codes from the output + if context_names: + resolved = [ec for ec in resolved if ec.name not in context_names] + + return resolved + + +def validate_error_codes(err_codes: list[ErrCode]) -> list[str]: + """ + Validate extracted error codes and return a list of error messages. + Returns an empty list if all checks pass. + + Checks performed: + - No two non-BASE error codes share the same numeric value. + - No duplicate names with conflicting values. + """ + errors: list[str] = [] + + # --- Check 1: duplicate values (skip _BASE codes, they are offset anchors) --- + value_to_codes: dict[int, list[ErrCode]] = {} + for ec in err_codes: + if ec.value is None or ec.name.endswith('_BASE'): + continue + value_to_codes.setdefault(ec.value, []).append(ec) + + for value, codes in sorted(value_to_codes.items()): + # Deduplicate by name — same name from different files is OK + unique_names = {ec.name for ec in codes} + if len(unique_names) > 1: + names_str = ', '.join(f'{ec.name} ({ec.file})' for ec in codes) + errors.append(f'Duplicate value 0x{value & 0xFFFFFFFF:x} ({value}): {names_str}') + + # --- Check 2: same name defined with different values --- + name_to_values: dict[str, list[ErrCode]] = {} + for ec in err_codes: + if ec.value is None: + continue + name_to_values.setdefault(ec.name, []).append(ec) + + for name, codes in sorted(name_to_values.items()): + unique_values = {ec.value for ec in codes} + if len(unique_values) > 1: + defs_str = ', '.join(f'{ec.value} in {ec.file}' for ec in codes) + errors.append(f'Conflicting definitions for {name}: {defs_str}') + + return errors + + +def write_csv(err_codes: list[ErrCode], output_path: str) -> None: + """Write resolved error codes to a CSV file.""" + # Deduplicate: keep the first occurrence of each name + seen = set() + unique_codes: list[ErrCode] = [] + for ec in err_codes: + if ec.name not in seen: + seen.add(ec.name) + unique_codes.append(ec) + + # Sort by value then name + unique_codes.sort(key=lambda e: (e.value if e.value is not None else 0, e.name)) + + with open(output_path, 'w', newline='', encoding='utf-8') as f: + writer = csv.writer(f) + writer.writerow(['name', 'value', 'file', 'comment']) + for ec in unique_codes: + writer.writerow([ec.name, ec.value, ec.file, ec.comment]) + + +def main() -> None: + parser = argparse.ArgumentParser(description='Extract ESP error codes from header files and output as CSV') + parser.add_argument('--headers', nargs='*', default=[], help='Specific header files to process') + parser.add_argument( + '--context-headers', + nargs='*', + default=[], + help='Header files scanned only for base definitions (e.g. ESP_ERR_*_BASE). ' + 'Codes found here are used for resolution but not emitted.', + ) + parser.add_argument( + '--search-dirs', nargs='*', default=[], help='Directories to search recursively for header files' + ) + parser.add_argument( + '--base-path', default=None, help='Base path for computing relative paths (default: IDF_PATH or cwd)' + ) + parser.add_argument('--output', required=True, help='Output CSV file path') + parser.add_argument( + '--validate', + action='store_true', + help='Validate error codes (fail on duplicate values or conflicting definitions)', + ) + + args = parser.parse_args() + + base_path = args.base_path + if base_path is None: + base_path = os.environ.get('IDF_PATH', os.getcwd()) + + headers = list(args.headers) + + if args.search_dirs: + headers.extend(search_headers(args.search_dirs, base_path)) + + if not headers: + print('Error: No header files specified. Use --headers or --search-dirs.', file=sys.stderr) + sys.exit(1) + + context_headers = list(args.context_headers) if args.context_headers else None + + err_codes = extract_all(headers, base_path, context_headers=context_headers) + write_csv(err_codes, args.output) + + if args.validate: + validation_errors = validate_error_codes(err_codes) + if validation_errors: + print(f'\nValidation FAILED with {len(validation_errors)} error(s):', file=sys.stderr) + for err in validation_errors: + print(f' ERROR: {err}', file=sys.stderr) + sys.exit(1) + else: + print('Validation passed: no duplicate values or conflicting definitions.') + + +if __name__ == '__main__': + main() diff --git a/tools/err_codes_to_c.py b/tools/err_codes_to_c.py new file mode 100644 index 00000000000..743eddeb667 --- /dev/null +++ b/tools/err_codes_to_c.py @@ -0,0 +1,142 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# +# Generate a C source file that registers error codes into a designated linker section. +# The generated file creates an array of esp_err_msg_t entries in the ".esp_err_msg_tbl" +# section, which is collected at link time by the linker script. +# +# Usage: +# python err_codes_to_c.py --csv errors.csv --output esp_err_codes_mycomp.c --component mycomp +# + +import argparse +import csv +import datetime +import os + + +def read_csv(csv_path: str) -> list[tuple[str, int, str]]: + """ + Read error codes from CSV file. + Returns list of (name, value, comment) tuples. + """ + entries: list[tuple[str, int, str]] = [] + with open(csv_path, encoding='utf-8') as f: + reader = csv.DictReader(f) + for row in reader: + name = row['name'] + value = int(row['value']) + comment = row.get('comment', '') + entries.append((name, value, comment)) + return entries + + +def generate_c_source(entries: list[tuple[str, int, str]], component: str, headers: list[str]) -> str: + """ + Generate C source code that places error code entries into the + .esp_err_msg_tbl linker section. + """ + lines: list[str] = [] + year = datetime.datetime.now().year + lines.append('/*') + lines.append(f' * SPDX-FileCopyrightText: {year} Espressif Systems (Shanghai) CO LTD') + lines.append(' *') + lines.append(' * SPDX-License-Identifier: Apache-2.0') + lines.append(' */') + lines.append('') + lines.append('// Auto-generated by err_codes_to_c.py — do not edit') + lines.append('') + lines.append('#include "esp_err.h"') + lines.append('#include "esp_err_msg.h"') + lines.append('#include "esp_attr.h"') + lines.append('') + + # Include the headers that define the error codes + # Skip headers already included above + already_included = {'esp_err.h', 'esp_err_msg.h', 'esp_attr.h'} + for header in headers: + if header in already_included: + continue + lines.append(f'#if __has_include("{header}")') + lines.append(f'#include "{header}"') + lines.append('#endif') + lines.append('') + + lines.append('#ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP') + lines.append('') + + # Generate array entries with #ifdef guards + safe_component = component.replace('-', '_').replace('.', '_') + symbol_name = f'esp_err_msg_{safe_component}' + + lines.append(f'// Force-link symbol for {component} error codes') + lines.append(f'const int {symbol_name}_include = 0;') + lines.append('') + + if entries: + lines.append(f'static const esp_err_msg_t {symbol_name}[]') + lines.append(' PLACE_IN_SECTION("esp_err_msg_tbl") = {') + + for name, value, comment in entries: + comment_str = f' /* {comment} */' if comment else '' + lines.append(f'#ifdef {name}') + lines.append(f' {{ {name}, "{name}" }},{comment_str}') + lines.append('#endif') + + lines.append('};') + lines.append('') + lines.append('#endif // CONFIG_ESP_ERR_TO_NAME_LOOKUP') + lines.append('') + + return '\n'.join(lines) + + +def _infer_include_path(file_path: str) -> str: + """ + Convert a file path like 'components/foo/include/bar/baz.h' + into an include path like 'bar/baz.h'. + """ + parts = file_path.replace('\\', '/').split('/') + try: + idx = parts.index('include') + return '/'.join(parts[idx + 1 :]) + except ValueError: + return os.path.basename(file_path) + + +def main() -> None: + parser = argparse.ArgumentParser( + description='Generate C source file with error code registration via linker section' + ) + parser.add_argument('--csv', required=True, help='Input CSV file with error codes (from err_codes_extract.py)') + parser.add_argument('--output', required=True, help='Output C source file path') + parser.add_argument('--component', required=True, help='Component name (used for symbol naming)') + parser.add_argument('--headers', nargs='*', default=[], help='Header files to #include (include-path form)') + + args = parser.parse_args() + + entries = read_csv(args.csv) + + # Deduce headers from the CSV file paths if none specified + headers = list(args.headers) + if not headers: + seen_files = set() + with open(args.csv, encoding='utf-8') as f: + reader = csv.DictReader(f) + for row in reader: + file_path = row.get('file', '') + if file_path and file_path not in seen_files: + seen_files.add(file_path) + inc = _infer_include_path(file_path) + if inc not in headers: + headers.append(inc) + + source = generate_c_source(entries, args.component, headers) + + os.makedirs(os.path.dirname(os.path.abspath(args.output)), exist_ok=True) + with open(args.output, 'w', encoding='utf-8') as f: + f.write(source) + + +if __name__ == '__main__': + main() diff --git a/tools/err_codes_to_rst.py b/tools/err_codes_to_rst.py new file mode 100644 index 00000000000..f75ccba97bb --- /dev/null +++ b/tools/err_codes_to_rst.py @@ -0,0 +1,98 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# +# Generate RST documentation from extracted error codes. +# This script takes a CSV file (produced by err_codes_extract.py) and generates +# an RST file suitable for inclusion in Sphinx documentation. +# +# Usage: +# python err_codes_to_rst.py --csv errors.csv --output error_codes.inc +# python err_codes_to_rst.py --search-dirs components/ --output error_codes.inc +# + +import argparse +import csv +import os +import sys + +# Allow importing err_codes_extract from same directory +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from err_codes_extract import extract_all # noqa: E402 +from err_codes_extract import search_headers # noqa: E402 + + +def read_csv_entries(csv_path: str) -> list[tuple[str, int, str]]: + """Read error codes from a CSV file.""" + entries: list[tuple[str, int, str]] = [] + with open(csv_path, encoding='utf-8') as f: + reader = csv.DictReader(f) + for row in reader: + name = row['name'] + value = int(row['value']) + comment = row.get('comment', '') + entries.append((name, value, comment)) + return entries + + +def generate_rst(entries: list[tuple[str, int, str]]) -> str: + """Generate RST content from error code entries. + + Output format matches the existing esp_err_defs.inc generated by + gen_esp_err_to_name.py so it can be used as a drop-in replacement + in the docs build (included via ``.. include-build-file::``). + """ + lines: list[str] = [] + + # Sort by value: negative values first (ascending), then non-negative (ascending) + sorted_entries = sorted(entries, key=lambda e: (e[1], e[0])) + + for name, value, comment in sorted_entries: + line = f':c:macro:`{name}` ' + if value > 0: + line += f'**(0x{value:x})**' + else: + line += f'({value:d})' + if comment: + line += f': {comment}' + lines.append(line) + lines.append('') + + return '\n'.join(lines) + + +def main() -> None: + parser = argparse.ArgumentParser(description='Generate RST documentation of ESP error codes') + parser.add_argument('--csv', default=None, help='Input CSV file with error codes (from err_codes_extract.py)') + parser.add_argument( + '--search-dirs', nargs='*', default=[], help='Directories to search recursively for header files' + ) + parser.add_argument('--base-path', default=None, help='Base path for computing relative paths') + parser.add_argument('--output', required=True, help='Output RST file path') + + args = parser.parse_args() + + base_path = args.base_path + if base_path is None: + base_path = os.environ.get('IDF_PATH', os.getcwd()) + + if args.csv: + entries = read_csv_entries(args.csv) + elif args.search_dirs: + headers = search_headers(args.search_dirs, base_path) + err_codes = extract_all(headers, base_path) + entries = [(ec.name, ec.value, ec.comment) for ec in err_codes if ec.value is not None] + else: + print('Error: Specify either --csv or --search-dirs.', file=sys.stderr) + sys.exit(1) + + rst_content = generate_rst(entries) + + os.makedirs(os.path.dirname(os.path.abspath(args.output)), exist_ok=True) + with open(args.output, 'w', encoding='utf-8') as f: + f.write(rst_content) + + print(f'Generated {args.output} with {len(entries)} error codes') + + +if __name__ == '__main__': + main() diff --git a/tools/gen_esp_err_to_name.py b/tools/gen_esp_err_to_name.py index 4bbc6ff34ee..9412b9450c6 100755 --- a/tools/gen_esp_err_to_name.py +++ b/tools/gen_esp_err_to_name.py @@ -2,305 +2,49 @@ # # SPDX-FileCopyrightText: 2018-2026 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 +# +# Generate RST documentation of ESP error codes. +# +# Previously this script also generated the C lookup table. +# That path has been replaced by the composable +# error-code registration system (link-time arrays + err_codes_extract.py). +# The RST generation is kept so the esp_docs Sphinx extension +# (esp_docs.idf_extensions.esp_err_definitions) continues to work +# unchanged. import argparse -import builtins -import collections -import fnmatch -import functools import os -import re -import textwrap -from typing import Any +import sys from typing import TextIO -# ignore files and dirs: Please use tools/ci/esp_err_to_name_exceptions.txt to add exceptions -ignore_files: list = [] -ignore_dirs: list = [] - -# macros from here have higher priorities in case of collisions -priority_headers = [os.path.join('components', 'esp_common', 'include', 'esp_err.h')] - -# The following headers won't be included. This is useful if they are permanently included from esp_err_to_name.c.in. -dont_include = [os.path.join('soc', 'soc.h'), os.path.join('esp_err.h')] - -# Don't search the following directories, e.g. test directories -exclude_search_dirs = ['test_apps', 'unit-test-app'] - -err_dict = collections.defaultdict(list) # identified errors are stored here; mapped by the error code -rev_err_dict = dict() # map of error string to error code -unproc_list = list() # errors with unknown codes which depend on other errors +# Allow importing err_codes_extract from the same directory +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from err_codes_extract import extract_all # noqa: E402 +from err_codes_extract import search_headers # noqa: E402 -class ErrItem: +def generate_rst_output(idf_path: str, fout: TextIO) -> None: """ - Contains information about the error: - - name - error string - - file - relative path inside the IDF project to the file which defines this error - - include_as - (optional) overwrites the include determined from file - - comment - (optional) comment for the error - - rel_str - (optional) error string which is a base for the error - - rel_off - (optional) offset in relation to the base error + Generate RST output using the composable error code extraction system + (err_codes_extract.py). Produces the format expected by the + ``.. include-build-file:: inc/esp_err_defs.inc`` directive. """ + components_dir = os.path.join(idf_path, 'components') + headers = search_headers([components_dir], idf_path) + err_codes = extract_all(headers, idf_path) + entries = [(ec.name, ec.value, ec.comment) for ec in err_codes if ec.value is not None] - def __init__( - self, - name: str, - file: str, - include_as: Any | None = None, - comment: str = '', - rel_str: str = '', - rel_off: int = 0, - ) -> None: - self.name = name - self.file = file - self.include_as = include_as - self.comment = comment - self.rel_str = rel_str - self.rel_off = rel_off + # Sort: negative values first (ascending), then non-negative (ascending) + entries.sort(key=lambda e: (e[1], e[0])) - def __str__(self) -> str: - ret = self.name + ' from ' + self.file - if self.rel_str != '': - ret += ' is (' + self.rel_str + ' + ' + str(self.rel_off) + ')' - if self.comment != '': - ret += ' // ' + self.comment - return ret - - def __cmp__(self, other) -> int: - if self.file in priority_headers and other.file not in priority_headers: - return -1 - elif self.file not in priority_headers and other.file in priority_headers: - return 1 - - base = '_BASE' - - if self.file == other.file: - if self.name.endswith(base) and not other.name.endswith(base): - return 1 - elif not self.name.endswith(base) and other.name.endswith(base): - return -1 - - self_key = self.file + self.name - other_key = other.file + other.name - if self_key < other_key: - return -1 - elif self_key > other_key: - return 1 + for name, value, comment in entries: + fout.write(f':c:macro:`{name}` ') + if value > 0: + fout.write(f'**(0x{value:x})**') else: - return 0 - - -class InputError(RuntimeError): - """ - Represents and error on the input - """ - - def __init__(self, p: str, e: str) -> None: - super().__init__(f'{p}: {e}') - - -def process(line: str, idf_path: str, include_as: Any) -> None: - """ - Process a line of text from file idf_path (relative to IDF project). - Fills the global list unproc_list and dictionaries err_dict, rev_err_dict - """ - if idf_path.endswith('.c'): - # We would not try to include a C file - raise InputError(idf_path, f'This line should be in a header file: {line}') - - words = re.split(r' +', line, 2) - # words[1] is the error name - # words[2] is the rest of the line (value, base + value, comment) - if len(words) < 3: - raise InputError(idf_path, f'Error at line {line}') - - line = '' - todo_str = words[2] - - comment = '' - # identify possible comment - m = re.search(r'/\*!<(.+?(?=\*/))', todo_str) - if m: - comment = m.group(1).strip() - todo_str = todo_str[: m.start()].strip() # keep just the part before the comment - - # identify possible parentheses () - m = re.search(r'\((.+)\)', todo_str) - if m: - todo_str = m.group(1) # keep what is inside the parentheses - - # identify BASE error code, e.g. from the form BASE + 0x01 - m = re.search(r'\s*(\w+)\s*\+(.+)', todo_str) - if m: - related = m.group(1) # BASE - todo_str = m.group(2) # keep and process only what is after "BASE +" - - # try to match a hexadecimal number - m = re.search(r'0x([0-9A-Fa-f]+)', todo_str) - if m: - num = int(m.group(1), 16) - else: - # Try to match a decimal number. Negative value is possible for some numbers, e.g. ESP_FAIL - m = re.search(r'(-?[0-9]+)', todo_str) - if m: - num = int(m.group(1), 10) - elif re.match(r'\w+', todo_str): - # It is possible that there is no number, e.g. #define ERROR BASE - related = todo_str # BASE error - num = 0 # (BASE + 0) - else: - raise InputError(idf_path, f'Cannot parse line {line}') - - try: - related - except NameError: - # The value of the error is known at this moment because it do not depends on some other BASE error code - err_dict[num].append(ErrItem(words[1], idf_path, include_as, comment)) - rev_err_dict[words[1]] = num - else: - # Store the information available now and compute the error code later - unproc_list.append(ErrItem(words[1], idf_path, include_as, comment, related, num)) - - -def process_remaining_errors() -> None: - """ - Create errors which could not be processed before because the error code - for the BASE error code wasn't known. - This works for sure only if there is no multiple-time dependency, e.g.: - #define BASE1 0 - #define BASE2 (BASE1 + 10) - #define ERROR (BASE2 + 10) - ERROR will be processed successfully only if it processed later than BASE2 - """ - for item in unproc_list: - if item.rel_str in rev_err_dict: - base_num = rev_err_dict[item.rel_str] - num = base_num + item.rel_off - err_dict[num].append(ErrItem(item.name, item.file, item.include_as, item.comment)) - rev_err_dict[item.name] = num - else: - print(item.rel_str + ' referenced by ' + item.name + ' in ' + item.file + ' is unknown') - - del unproc_list[:] - - -def path_to_include(path: str) -> str: - """ - Process the path (relative to the IDF project) in a form which can be used - to include in a C file. Using just the filename does not work all the - time because some files are deeper in the tree. This approach tries to - find an 'include' parent directory an include its subdirectories, e.g. - "components/XY/include/esp32/file.h" will be transported into "esp32/file.h" - So this solution works only works when the subdirectory or subdirectories - are inside the "include" directory. Other special cases need to be handled - here when the compiler gives an unknown header file error message. - """ - spl_path = path.split(os.sep) - try: - i = spl_path.index('include') - except ValueError: - # no include in the path -> use just the filename - return os.path.basename(path) - else: - return os.sep.join(spl_path[i + 1 :]) # subdirectories and filename in "include" - - -def print_warning(error_list: list, error_code: int) -> None: - """ - Print warning about errors with the same error code - """ - print(f'[WARNING] The following errors have the same code ({error_code}):') - for e in error_list: - print(f' {e}') - - -def max_string_width() -> int: - max_width = 0 - for k in err_dict: - for e in err_dict[k]: - x = len(e.name) - if x > max_width: - max_width = x - return max_width - - -def generate_c_output(fin: TextIO, fout: TextIO) -> None: - """ - Writes the output to fout based on th error dictionary err_dict and - template file fin. - """ - # make includes unique by using a set - includes = set() - for k in err_dict: - for e in err_dict[k]: - if e.include_as: - includes.add(e.include_as) - else: - includes.add(path_to_include(e.file)) - - # The order in a set in non-deterministic therefore it could happen that the - # include order will be different in other machines and false difference - # in the output file could be reported. In order to avoid this, the items - # are sorted in a list. - include_list = list(includes) - include_list.sort() - - max_width = max_string_width() + 17 + 1 # length of " ERR_TBL_IT()," with spaces is 17 - max_decdig = max(len(str(k)) for k in err_dict) - - for line in fin: - if re.match(r'@COMMENT@', line): - fout.write('//Do not edit this file because it is autogenerated by ' + os.path.basename(__file__) + '\n') - - elif re.match(r'@HEADERS@', line): - for i in include_list: - if i not in dont_include: - fout.write('#if __has_include("' + i + '")\n#include "' + i + '"\n#endif\n') - elif re.match(r'@ERROR_ITEMS@', line): - last_file = '' - for k in sorted(err_dict.keys()): - if len(err_dict[k]) > 1: - err_dict[k].sort(key=functools.cmp_to_key(ErrItem.__cmp__)) - print_warning(err_dict[k], k) - for e in err_dict[k]: - if e.file != last_file: - last_file = e.file - fout.write(f' // {last_file}\n') - table_line = ( - (' ERR_TBL_IT(' + e.name + '), ').ljust(max_width) + '/* ' + str(k).rjust(max_decdig) - ) - fout.write(f'# ifdef {e.name}\n') - fout.write(table_line) - hexnum_length = 0 - if k > 0: # negative number and zero should be only ESP_FAIL and ESP_OK - hexnum = f' 0x{k:x}' - hexnum_length = len(hexnum) - fout.write(hexnum) - if e.comment != '': - if len(e.comment) < 50: - fout.write(f' {e.comment}') - else: - indent = ' ' * (len(table_line) + hexnum_length + 1) - w = textwrap.wrap(e.comment, width=120, initial_indent=indent, subsequent_indent=indent) - # this couldn't be done with initial_indent because there is no initial_width option - fout.write(f' {w[0].strip()}') - for i in range(1, len(w)): - fout.write(f'\n{w[i]}') - fout.write(' */\n# endif\n') - else: - fout.write(line) - - -def generate_rst_output(fout: TextIO) -> None: - for k in sorted(err_dict.keys()): - v = err_dict[k][0] - fout.write(f':c:macro:`{v.name}` ') - if k > 0: - fout.write(f'**(0x{k:x})**') - else: - fout.write(f'({k:d})') - if len(v.comment) > 0: - fout.write(f': {v.comment}') + fout.write(f'({value:d})') + if comment: + fout.write(f': {comment}') fout.write('\n\n') @@ -310,72 +54,12 @@ def main() -> None: else: idf_path = os.path.realpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) - parser = argparse.ArgumentParser(description='ESP32 esp_err_to_name lookup generator for esp_err_t') - parser.add_argument( - '--c_input', - help='Path to the esp_err_to_name.c.in template input.', - default=idf_path + '/components/esp_common/src/esp_err_to_name.c.in', - ) - parser.add_argument( - '--c_output', - help='Path to the esp_err_to_name.c output.', - default=idf_path + '/components/esp_common/src/esp_err_to_name.c', - ) - parser.add_argument('--rst_output', help='Generate .rst output and save it into this file') - parser.add_argument('--exceptions_file', default=idf_path + '/tools/ci/esp_err_to_name_exceptions.txt') + parser = argparse.ArgumentParser(description='ESP-IDF error code RST documentation generator') + parser.add_argument('--rst_output', required=True, help='Generate .rst output and save it into this file') args = parser.parse_args() - include_as_pattern = re.compile(rf'\s*//\s*{os.path.basename(__file__)}: [^"]* "([^"]+)"') - define_pattern = re.compile(r'\s*#define\s+(ESP_ERR_|ESP_OK|ESP_FAIL)') - - if args.exceptions_file and os.path.exists(args.exceptions_file): - with builtins.open(args.exceptions_file, encoding='utf-8') as exceptions_file: - for line in exceptions_file: - entry = line.strip() - if not entry or entry.startswith('#'): - continue - if entry.endswith('/'): - ignore_dirs.append(entry.rstrip('/')) - else: - ignore_files.append(entry) - - for root, dirnames, filenames in os.walk(os.path.join(idf_path, 'components'), topdown=True): - # When topdown is True, we can modify the dirnames list in-place - # walk() will only recurse into the subdirectories whose names remain in dirnames - dirnames[:] = [d for d in dirnames if d not in exclude_search_dirs] - for filename in fnmatch.filter(filenames, '*.[ch]'): - full_path = os.path.join(root, filename) - path_in_idf = os.path.relpath(full_path, idf_path) - if path_in_idf in ignore_files or any(path_in_idf.startswith(d) for d in ignore_dirs): - continue - with builtins.open(full_path, encoding='utf-8') as f: - try: - include_as = None - for line in f: - line = line.strip() - m = include_as_pattern.search(line) - if m: - include_as = m.group(1) - # match also ESP_OK and ESP_FAIL because some of ESP_ERRs are referencing them - elif define_pattern.match(line): - try: - process(line, path_in_idf, include_as) - except InputError as e: - print(e) - except UnicodeDecodeError: - raise ValueError(f'The encoding of {path_in_idf} is not Unicode.') - - process_remaining_errors() - - if args.rst_output is not None: - with builtins.open(args.rst_output, 'w', encoding='utf-8') as fout: - generate_rst_output(fout) - else: - with ( - builtins.open(args.c_input, encoding='utf-8') as fin, - builtins.open(args.c_output, 'w', encoding='utf-8') as fout, - ): - generate_c_output(fin, fout) + with open(args.rst_output, 'w', encoding='utf-8') as fout: + generate_rst_output(idf_path, fout) if __name__ == '__main__': diff --git a/tools/test_apps/build_system/.build-test-rules.yml b/tools/test_apps/build_system/.build-test-rules.yml index c02632e34af..9423a9d1eac 100644 --- a/tools/test_apps/build_system/.build-test-rules.yml +++ b/tools/test_apps/build_system/.build-test-rules.yml @@ -16,6 +16,10 @@ tools/test_apps/build_system/embed_test: temporary: false reason: Hardware independent feature, no need to test on all targets +tools/test_apps/build_system/err_codes_check: + enable: + - if: IDF_TARGET in ["esp32", "linux"] + tools/test_apps/build_system/external_project: enable: - if: IDF_TARGET in ["esp32", "esp32c3"] diff --git a/tools/test_apps/build_system/err_codes_check/CMakeLists.txt b/tools/test_apps/build_system/err_codes_check/CMakeLists.txt new file mode 100644 index 00000000000..510f25ff19f --- /dev/null +++ b/tools/test_apps/build_system/err_codes_check/CMakeLists.txt @@ -0,0 +1,14 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.22) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +# On Linux/macOS, limit to components that support the host target. +# The generated test uses #ifdef guards, so error codes from excluded +# components are simply skipped. +if(IDF_TARGET STREQUAL "linux") + set(COMPONENTS main) +endif() + +project(err_codes_check) diff --git a/tools/test_apps/build_system/err_codes_check/README.md b/tools/test_apps/build_system/err_codes_check/README.md new file mode 100644 index 00000000000..5bed777fe37 --- /dev/null +++ b/tools/test_apps/build_system/err_codes_check/README.md @@ -0,0 +1,25 @@ +| Supported Targets | ESP32 | Linux | +| ----------------- | ----- | ----- | + +# Error Codes Registration Check + +This test app verifies that all `ESP_ERR_*` error codes defined across ESP-IDF +components are correctly registered via `idf_define_esp_err_codes()` and can be +resolved by `esp_err_to_name()`. + +## How it works + +1. At CMake configure time, `err_codes_extract.py` scans all ESP-IDF component + headers to collect every `ESP_ERR_*` define. +2. A test source file is generated that calls `esp_err_to_name()` for each + collected error code and asserts the result matches the expected name. +3. The app is built and run (e.g., in QEMU or on hardware). + +## Building and running + +```bash +cd tools/test_apps/build_system/err_codes_check +idf.py set-target esp32 +idf.py build +# Run in QEMU or flash to hardware +``` diff --git a/tools/test_apps/build_system/err_codes_check/gen_err_codes_test.py b/tools/test_apps/build_system/err_codes_check/gen_err_codes_test.py new file mode 100644 index 00000000000..7ec027d0115 --- /dev/null +++ b/tools/test_apps/build_system/err_codes_check/gen_err_codes_test.py @@ -0,0 +1,78 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# +# Generate a C test source file that validates all ESP-IDF error codes +# are correctly registered and resolvable via esp_err_to_name(). +# + +import csv +import os +import sys + + +def generate_test_source(csv_path: str, output_path: str) -> None: + """Generate a C source file that tests all error codes.""" + entries = [] + with open(csv_path, encoding='utf-8') as f: + reader = csv.DictReader(f) + for row in reader: + name = row['name'] + value = int(row['value']) + entries.append((name, value)) + + lines = [] + lines.append('/*') + lines.append(' * Auto-generated by gen_err_codes_test.py — do not edit') + lines.append(' */') + lines.append('') + lines.append('#include ') + lines.append('#include ') + lines.append('#include "esp_err.h"') + lines.append('') + lines.append('/* Forward declaration */') + lines.append('int check_err_code(esp_err_t code, const char *expected_name);') + lines.append('') + lines.append('int check_err_code(esp_err_t code, const char *expected_name)') + lines.append('{') + lines.append(' const char *actual = esp_err_to_name(code);') + lines.append(' if (strcmp(actual, expected_name) != 0) {') + lines.append(' printf("FAIL: esp_err_to_name(0x%x) = \\"%s\\", expected \\"%s\\"\\n",') + lines.append(' (unsigned)code, actual, expected_name);') + lines.append(' return 1;') + lines.append(' }') + lines.append(' return 0;') + lines.append('}') + lines.append('') + lines.append('int err_codes_check_all(void)') + lines.append('{') + lines.append(' int failures = 0;') + + for name, value in entries: + # Use #ifdef guards as the error code may not be defined for all targets + lines.append(f'#ifdef {name}') + lines.append(f' failures += check_err_code({name}, "{name}");') + lines.append('#endif') + + lines.append('') + lines.append(' if (failures == 0) {') + lines.append(f' printf("All {len(entries)} error code checks passed.\\n");') + lines.append(' } else {') + lines.append(f' printf("%d of {len(entries)} error code checks failed.\\n", failures);') + lines.append(' }') + lines.append(' return failures;') + lines.append('}') + lines.append('') + + os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) + with open(output_path, 'w', encoding='utf-8') as f: + f.write('\n'.join(lines)) + + print(f'Generated {output_path} with {len(entries)} error code checks') + + +if __name__ == '__main__': + if len(sys.argv) != 3: + print(f'Usage: {sys.argv[0]} ', file=sys.stderr) + sys.exit(1) + + generate_test_source(sys.argv[1], sys.argv[2]) diff --git a/tools/test_apps/build_system/err_codes_check/main/CMakeLists.txt b/tools/test_apps/build_system/err_codes_check/main/CMakeLists.txt new file mode 100644 index 00000000000..e839343071a --- /dev/null +++ b/tools/test_apps/build_system/err_codes_check/main/CMakeLists.txt @@ -0,0 +1,33 @@ +set(idf_path "$ENV{IDF_PATH}") +set(project_dir "${CMAKE_CURRENT_LIST_DIR}/..") + +# Step 1: Extract all error codes from the entire ESP-IDF components directory +set(all_err_csv "${CMAKE_CURRENT_BINARY_DIR}/all_esp_err_codes.csv") +execute_process( + COMMAND ${PYTHON} "${idf_path}/tools/err_codes_extract.py" + --search-dirs "components" + --base-path "${idf_path}" + --output "${all_err_csv}" + RESULT_VARIABLE extract_result + OUTPUT_VARIABLE extract_output + ERROR_VARIABLE extract_error +) +if(NOT extract_result EQUAL 0) + message(FATAL_ERROR "err_codes_extract.py failed: ${extract_error}") +endif() + +# Step 2: Generate the test source file from the CSV +set(test_src "${CMAKE_CURRENT_BINARY_DIR}/err_codes_test_cases.c") +execute_process( + COMMAND ${PYTHON} "${project_dir}/gen_err_codes_test.py" + "${all_err_csv}" "${test_src}" + RESULT_VARIABLE gen_result + OUTPUT_VARIABLE gen_output + ERROR_VARIABLE gen_error +) +if(NOT gen_result EQUAL 0) + message(FATAL_ERROR "gen_err_codes_test.py failed: ${gen_error}") +endif() + +idf_component_register(SRCS "test_main.c" "${test_src}" + INCLUDE_DIRS ".") diff --git a/tools/test_apps/build_system/err_codes_check/main/test_main.c b/tools/test_apps/build_system/err_codes_check/main/test_main.c new file mode 100644 index 00000000000..1db4dfde76d --- /dev/null +++ b/tools/test_apps/build_system/err_codes_check/main/test_main.c @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_err.h" + +/* Defined in the generated err_codes_test_cases.c */ +extern int err_codes_check_all(void); + +void app_main(void) +{ + printf("Starting error codes registration check...\n"); + + int failures = err_codes_check_all(); + + if (failures == 0) { + printf("SUCCESS: All error codes are correctly registered.\n"); + } else { + printf("FAILURE: %d error codes are not correctly registered.\n", failures); + } +} diff --git a/tools/test_apps/build_system/err_codes_check/pytest_err_codes_check.py b/tools/test_apps/build_system/err_codes_check/pytest_err_codes_check.py new file mode 100644 index 00000000000..eab62dfe59b --- /dev/null +++ b/tools/test_apps/build_system/err_codes_check/pytest_err_codes_check.py @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +import pytest +from pytest_embedded_idf.dut import IdfDut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.host_test +@pytest.mark.qemu +@idf_parametrize('target', ['esp32'], indirect=['target']) +def test_err_codes_check(dut: IdfDut) -> None: + dut.expect_exact('SUCCESS: All error codes are correctly registered.', timeout=120) + + +@pytest.mark.host_test +@idf_parametrize('target', ['linux'], indirect=['target']) +def test_err_codes_check_linux(dut: IdfDut) -> None: + dut.expect_exact('SUCCESS: All error codes are correctly registered.', timeout=120) + + +@pytest.mark.host_test +@pytest.mark.macos +@idf_parametrize('target', ['linux'], indirect=['target']) +def test_err_codes_check_macos(dut: IdfDut) -> None: + dut.expect_exact('SUCCESS: All error codes are correctly registered.', timeout=120) From 23b8a17d3fe240d01a62b9cdeadfe620759b0c41 Mon Sep 17 00:00:00 2001 From: Guillaume Souchere Date: Wed, 8 Apr 2026 07:59:40 +0200 Subject: [PATCH 2/2] feat(ci): add error code registration check to the pre-commit hook --- .pre-commit-config.yaml | 6 + tools/ci/check_err_codes_registration.py | 146 +++++++++++++++++++++++ tools/ci/executable-list.txt | 1 + 3 files changed, 153 insertions(+) create mode 100755 tools/ci/check_err_codes_registration.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5cce9693e0a..bffb0293bb2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -189,6 +189,12 @@ repos: )$ pass_filenames: false verbose: true + - id: check-err-codes-registration + name: Check error code headers are registered + entry: tools/ci/check_err_codes_registration.py + language: python + files: 'components/.+\.h$|components/.+CMakeLists\.txt$' + pass_filenames: false - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.0.1 hooks: diff --git a/tools/ci/check_err_codes_registration.py b/tools/ci/check_err_codes_registration.py new file mode 100755 index 00000000000..3607ce901f7 --- /dev/null +++ b/tools/ci/check_err_codes_registration.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +""" +Pre-commit check: ensure every header defining ESP_ERR_* codes is registered +via idf_define_esp_err_codes() in its component's CMakeLists.txt. + +Compares the set of headers found by scanning all components against those +explicitly listed in idf_define_esp_err_codes(HEADERS ...) calls. +""" + +import os +import re +import sys + +# Add tools/ to path so we can import err_codes_extract +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) +from err_codes_extract import EXCLUDE_SEARCH_DIRS +from err_codes_extract import extract_from_file + +# Additional directories to exclude beyond those in err_codes_extract +EXTRA_EXCLUDE_DIRS = {'remote'} + +# Pattern matching idf_define_esp_err_codes(HEADERS ...) in CMakeLists.txt +ERR_CODES_CALL = re.compile(r'idf_define_esp_err_codes\s*\(') + + +def find_all_err_headers(components_dir: str) -> dict[str, set[str]]: + """ + Scan all component headers for ESP_ERR_* definitions using + err_codes_extract.extract_from_file(). + + Returns: + dict mapping component name -> set of header paths (relative to component dir) + """ + exclude = EXCLUDE_SEARCH_DIRS | EXTRA_EXCLUDE_DIRS + result: dict[str, set[str]] = {} + + for component_name in sorted(os.listdir(components_dir)): + component_dir = os.path.join(components_dir, component_name) + if not os.path.isdir(component_dir): + continue + + for root, dirnames, filenames in os.walk(component_dir, topdown=True): + dirnames[:] = [d for d in dirnames if d not in exclude] + for filename in filenames: + if not filename.endswith('.h'): + continue + filepath = os.path.join(root, filename) + rel_path = os.path.relpath(filepath, component_dir) + if extract_from_file(filepath, rel_path): + result.setdefault(component_name, set()).add(rel_path) + + return result + + +def parse_registered_headers(components_dir: str) -> dict[str, set[str]]: + """ + Parse all CMakeLists.txt files for idf_define_esp_err_codes(HEADERS ...) calls. + + Returns: + dict mapping component name -> set of header paths (relative to component dir) + """ + result: dict[str, set[str]] = {} + + for component_name in sorted(os.listdir(components_dir)): + component_dir = os.path.join(components_dir, component_name) + cmake_file = os.path.join(component_dir, 'CMakeLists.txt') + if not os.path.isfile(cmake_file): + continue + + try: + with open(cmake_file, encoding='utf-8') as f: + content = f.read() + except (UnicodeDecodeError, OSError): + continue + + # Find all idf_define_esp_err_codes() calls and extract HEADERS args + for match in ERR_CODES_CALL.finditer(content): + start = match.end() + # Find the matching closing paren, handling nesting + depth = 1 + pos = start + while pos < len(content) and depth > 0: + if content[pos] == '(': + depth += 1 + elif content[pos] == ')': + depth -= 1 + pos += 1 + + args_str = content[start : pos - 1] + # Remove comments + args_str = re.sub(r'#.*$', '', args_str, flags=re.MULTILINE) + # Extract tokens after HEADERS keyword + headers_match = re.search(r'\bHEADERS\b\s+(.*)', args_str, re.DOTALL) + if headers_match: + tokens = headers_match.group(1).split() + # Stop at next keyword (uppercase word) or end + for token in tokens: + if re.match(r'^[A-Z_]+$', token): + break + # Normalize path separators + header_path = token.replace('\\', '/') + result.setdefault(component_name, set()).add(header_path) + + return result + + +def main() -> int: + idf_path = os.environ.get('IDF_PATH', os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) + components_dir = os.path.join(idf_path, 'components') + + if not os.path.isdir(components_dir): + print(f'Error: components directory not found: {components_dir}', file=sys.stderr) + return 1 + + found_headers = find_all_err_headers(components_dir) + registered_headers = parse_registered_headers(components_dir) + + errors: list[str] = [] + + for component, headers in sorted(found_headers.items()): + registered = registered_headers.get(component, set()) + missing = headers - registered + if missing: + for header in sorted(missing): + errors.append( + f'{component}: {header} defines ESP_ERR_* codes but is not registered in idf_define_esp_err_codes()' + ) + + if errors: + print('Error code registration check failed:', file=sys.stderr) + for error in errors: + print(f' {error}', file=sys.stderr) + print( + f'\n{len(errors)} header(s) define ESP_ERR_* codes but are not registered.\n' + f"Add idf_define_esp_err_codes(HEADERS ...) to the component's CMakeLists.txt.", + file=sys.stderr, + ) + return 1 + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index 45b9d810dc5..e0097ed5cc0 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -57,6 +57,7 @@ tools/ci/check_api_violation.sh tools/ci/check_build_test_rules.py tools/ci/check_callgraph.py tools/ci/check_codeowners.py +tools/ci/check_err_codes_registration.py tools/ci/check_esp_memory_utils_headers.sh tools/ci/check_examples_extra_component_dirs.sh tools/ci/check_executables.py