diff --git a/tools/cmakev2/compat.cmake b/tools/cmakev2/compat.cmake index 8c6da91ade9..9a25c0f6547 100644 --- a/tools/cmakev2/compat.cmake +++ b/tools/cmakev2/compat.cmake @@ -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 $) +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 diff --git a/tools/cmakev2/idf.cmake b/tools/cmakev2/idf.cmake index e47bf6c601b..4472dec7e25 100644 --- a/tools/cmakev2/idf.cmake +++ b/tools/cmakev2/idf.cmake @@ -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() diff --git a/tools/cmakev2/kconfig.cmake b/tools/cmakev2/kconfig.cmake index 856e542c8e4..3aef3ef3528 100644 --- a/tools/cmakev2/kconfig.cmake +++ b/tools/cmakev2/kconfig.cmake @@ -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() diff --git a/tools/cmakev2/manager.cmake b/tools/cmakev2/manager.cmake index 7ff6e9c4816..58f2d30d1cd 100644 --- a/tools/cmakev2/manager.cmake +++ b/tools/cmakev2/manager.cmake @@ -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() diff --git a/tools/cmakev2/project.cmake b/tools/cmakev2/project.cmake index 715a122c63e..22189e962aa 100644 --- a/tools/cmakev2/project.cmake +++ b/tools/cmakev2/project.cmake @@ -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() diff --git a/tools/cmakev2/utilities.cmake b/tools/cmakev2/utilities.cmake index a2804964184..6ff966a7556 100644 --- a/tools/cmakev2/utilities.cmake +++ b/tools/cmakev2/utilities.cmake @@ -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)