Merge branch 'task/cmakev2_internal_fixes' into 'master'

fix(cmakev2): internal bug fixes — kconfgen warnings, dep parsing, managed-deps ordering, link templates

See merge request espressif/esp-idf!48740
This commit is contained in:
Sudeep Mohanty
2026-05-25 16:01:24 +02:00
6 changed files with 129 additions and 8 deletions

View File

@@ -532,6 +532,13 @@ function(idf_component_register)
PRIV_REQUIRES REQUIRED_IDF_TARGETS EMBED_FILES EMBED_TXTFILES)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
# Some managed components pass their REQUIRES / PRIV_REQUIRES as a single
# quoted space-separated string (e.g. PRIV_REQUIRES "log esp_eth").
# cmake_parse_arguments stores that as one list element. Normalise the
# lists so that each component name is a separate element.
separate_arguments(ARG_REQUIRES)
separate_arguments(ARG_PRIV_REQUIRES)
# Initialize and include commonly required components.
__init_common_components()
@@ -592,7 +599,7 @@ function(idf_component_register)
endforeach()
if(sources OR ARG_EMBED_FILES OR ARG_EMBED_TXTFILES)
add_library("${COMPONENT_TARGET}" STATIC "${sources}")
add_library("${COMPONENT_TARGET}" STATIC ${sources})
foreach(include_dir IN LISTS include_dirs)
target_include_directories("${COMPONENT_TARGET}" PUBLIC "${include_dir}")
@@ -660,6 +667,55 @@ function(idf_component_register)
idf_component_set_property("${COMPONENT_NAME}" COMPONENT_TYPE "${component_type}")
endfunction()
#[[
.. cmakev2:function:: idf_component_add_link_dependency
Backward-compatible shim for ``idf_component_add_link_dependency()`` used
by some managed components (e.g. espressif__esp_flash_nor).
#]]
function(idf_component_add_link_dependency)
set(single_value FROM TO)
cmake_parse_arguments(_ "" "${single_value}" "" ${ARGN})
idf_component_get_property(from_lib ${__FROM} COMPONENT_LIB)
if(__TO)
idf_component_get_property(to_lib ${__TO} COMPONENT_LIB)
else()
set(to_lib ${COMPONENT_LIB})
endif()
set_property(TARGET ${from_lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES $<LINK_ONLY:${to_lib}>)
endfunction()
#[[
.. cmakev2:macro:: register_component
Backward-compatible shim for the legacy ``register_component()`` macro used
by some older ESP-IDF examples (ULP apps, BLE mesh demos, etc.). It converts
the old-style ``COMPONENT_*`` variables to ``idf_component_register()`` calls.
#]]
macro(register_component)
# Convert space-separated variables to CMake lists.
foreach(_var COMPONENT_SRCS COMPONENT_SRCDIRS COMPONENT_ADD_INCLUDEDIRS
COMPONENT_PRIV_INCLUDEDIRS COMPONENT_REQUIRES COMPONENT_PRIV_REQUIRES
COMPONENT_ADD_LDFRAGMENTS COMPONENT_EMBED_FILES COMPONENT_EMBED_TXTFILES
COMPONENT_SRCEXCLUDE)
if(DEFINED ${_var})
separate_arguments(${_var})
endif()
endforeach()
idf_component_register(SRCS "${COMPONENT_SRCS}"
SRC_DIRS "${COMPONENT_SRCDIRS}"
INCLUDE_DIRS "${COMPONENT_ADD_INCLUDEDIRS}"
PRIV_INCLUDE_DIRS "${COMPONENT_PRIV_INCLUDEDIRS}"
REQUIRES "${COMPONENT_REQUIRES}"
PRIV_REQUIRES "${COMPONENT_PRIV_REQUIRES}"
LDFRAGMENTS "${COMPONENT_ADD_LDFRAGMENTS}"
EMBED_FILES "${COMPONENT_EMBED_FILES}"
EMBED_TXTFILES "${COMPONENT_EMBED_TXTFILES}"
EXCLUDE_SRCS "${COMPONENT_SRCEXCLUDE}")
endmacro()
#[[
.. cmakev2:function:: idf_build_component

View File

@@ -94,7 +94,9 @@ function(__init_idf_path)
endif()
idf_build_set_property(IDF_PATH "${idf_path}")
# Components reference either ${IDF_PATH} or ${idf_path}; publish both.
set(IDF_PATH ${idf_path} PARENT_SCOPE)
set(idf_path ${idf_path} PARENT_SCOPE)
set(ENV{IDF_PATH} ${idf_path})
endfunction()

View File

@@ -752,14 +752,45 @@ function(__run_kconfgen)
set(kconfgen_cmd ${base_kconfgen_cmd} ${kconfgen_outputs_cmd})
idf_dbg("Running kconfgen: ${kconfgen_cmd}")
execute_process(
COMMAND ${kconfgen_cmd}
--env-file "${config_env_path}"
RESULT_VARIABLE kconfgen_result
)
if(kconfgen_result)
idf_die("Failed to run kconfgen: ${kconfgen_result}")
# kconfgen runs once during the bootstrap pass (before the component
# manager fetches managed components) and again on every iteration of
# the component-manager loop in __fetch_components_from_registry. Every
# pass except the converged one operates on an incomplete component
# set and can emit spurious "unknown kconfig symbol" warnings for
# symbols defined in not-yet-downloaded components, which
# idf-build-apps would otherwise treat as build failures. The quiet
# path captures stdout/stderr instead of streaming them; the
# converged pass flips __KCONFGEN_QUIET to NO and reaches the else
# branch, where warnings are emitted normally.
idf_build_get_property(quiet __KCONFGEN_QUIET)
if(quiet)
# Capture stdout/stderr into variables instead of discarding them.
# On success the captured output is dropped (so transient warnings
# never reach the build log). On non-zero exit we re-emit both
# streams so genuine errors (Kconfig parse errors, FatalError, etc.)
# remain debuggable.
execute_process(
COMMAND ${kconfgen_cmd}
--env-file "${config_env_path}"
RESULT_VARIABLE kconfgen_result
OUTPUT_VARIABLE kconfgen_stdout
ERROR_VARIABLE kconfgen_stderr
)
if(kconfgen_result)
message("${kconfgen_stdout}")
message("${kconfgen_stderr}")
idf_die("Failed to run kconfgen: ${kconfgen_result}")
endif()
else()
execute_process(
COMMAND ${kconfgen_cmd}
--env-file "${config_env_path}"
RESULT_VARIABLE kconfgen_result
)
if(kconfgen_result)
idf_die("Failed to run kconfgen: ${kconfgen_result}")
endif()
endif()
endfunction()

View File

@@ -51,6 +51,14 @@ endfunction()
3. If the component manager run failed, error out.
#]]
function(__fetch_components_from_registry)
# __KCONFGEN_QUIET stays YES (set by the bootstrap path in project.cmake)
# across intermediate iterations of the loop below, because each
# iteration's kconfgen pass runs against a partial component set when the
# component manager exits 10. Warnings from those passes are transient.
# We only flip the flag to NO on the iteration where the component
# manager succeeds, so the final kconfgen pass against the complete
# component set emits genuine warnings.
# Iteratively run the component manager and Kconfig until stable or error out.
set(__cmgr_round 0)
while(TRUE)
@@ -60,6 +68,11 @@ function(__fetch_components_from_registry)
# Run the component manager for all discovered components
__download_component_level_managed_components(RESULT cmgr_result)
# Only the final (successful) iteration should emit kconfgen warnings.
if(cmgr_result EQUAL 0)
idf_build_set_property(__KCONFGEN_QUIET NO)
endif()
# Re-collect Kconfig and regenerate sdkconfig with managed components included
__generate_sdkconfig()

View File

@@ -592,6 +592,14 @@ macro(idf_project_init)
# Only creates a backup when the component manager is enabled.
__create_sdkconfig_orig_copy()
# When the component manager is enabled, suppress kconfgen warnings
# on this initial pass. Managed component symbols are unknown at this
# stage and will be resolved after __fetch_components_from_registry().
idf_build_get_property(idf_component_manager IDF_COMPONENT_MANAGER)
if(idf_component_manager EQUAL 1)
idf_build_set_property(__KCONFGEN_QUIET YES)
endif()
# Generate initial sdkconfig with discovered components
__generate_sdkconfig()

View File

@@ -835,6 +835,17 @@ endfunction()
function(target_add_binary_data target embed_file embed_type)
cmake_parse_arguments(_ "" "RENAME_TO" "DEPENDS" ${ARGN})
idf_build_get_property(build_dir BUILD_DIR)
# In cmakev1, the executable target was named "${project}.elf".
# In cmakev2, the executable is named "${project}" and ".elf" is just the
# output suffix. Strip the ".elf" suffix if the target does not exist but
# the bare name does. This keeps existing app CMakeLists.txt files working.
if(NOT TARGET "${target}")
string(REGEX REPLACE "\\.elf$" "" target_bare "${target}")
if(TARGET "${target_bare}")
set(target "${target_bare}")
endif()
endif()
idf_build_get_property(idf_path IDF_PATH)
get_filename_component(embed_file "${embed_file}" ABSOLUTE)