mirror of
https://github.com/espressif/esp-idf.git
synced 2026-07-13 06:23:04 +03:00
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
143 lines
4.9 KiB
Python
143 lines
4.9 KiB
Python
# 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()
|