From ead48b7dbda97bc3b06437df7aee8820dacca2eb Mon Sep 17 00:00:00 2001 From: Daniel Paul Date: Thu, 11 Dec 2025 14:06:44 +0100 Subject: [PATCH 1/2] feat: Component Manager interface version 5 --- tools/cmake/build.cmake | 37 +++++++++++++++++++++++++++++++------ tools/cmake/component.cmake | 4 ++-- tools/cmake/project.cmake | 8 +++++--- tools/cmakev2/manager.cmake | 15 +++++++-------- 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index aaac726b19d..049510b0eeb 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -661,12 +661,16 @@ macro(idf_build_process target) foreach(__build_component_target ${build_component_targets}) __component_get_property(__component_name ${__build_component_target} COMPONENT_NAME) __component_get_property(__component_dir ${__build_component_target} COMPONENT_DIR) + __component_get_property(__component_source ${__build_component_target} COMPONENT_SOURCE) # Exclude components could be passed with -DEXCLUDE_COMPONENTS # after the call to __component_add finished in the last run. # Need to check if the component is excluded again if(NOT __component_name IN_LIST EXCLUDE_COMPONENTS) - set(__contents "${__contents} - name: \"${__component_name}\"\n path: \"${__component_dir}\"\n") + string(CONCAT __contents "${__contents}" + " - name: \"${__component_name}\"\n" + " path: \"${__component_dir}\"\n" + " source: \"${__component_source}\"\n") endif() endforeach() @@ -677,27 +681,38 @@ macro(idf_build_process target) idf_build_get_property(component_manager_interface_version __COMPONENT_MANAGER_INTERFACE_VERSION) idf_build_get_property(dependencies_lock_file DEPENDENCIES_LOCK) + set(use_sdk_json TRUE) + if(retried EQUAL 0) + set(use_sdk_json FALSE) + endif() + execute_process(COMMAND ${python} "-m" "idf_component_manager.prepare_components" "--project_dir=${project_dir}" "--lock_path=${dependencies_lock_file}" - "--sdkconfig_json_file=${build_dir}/config/sdkconfig.json" "--interface_version=${component_manager_interface_version}" + "--use_sdk_json=${use_sdk_json}" "prepare_dependencies" "--local_components_list_file=${local_components_list_file}" + "--build_dir=${build_dir}" "--managed_components_list_file=${managed_components_list_file}" RESULT_VARIABLE result ERROR_VARIABLE error) if(NOT result EQUAL 0) - if(result EQUAL ${__RERUN_EXITCODE}) - message(WARNING "${error}") - else() + # If KConfig variables are missing, allow rerunning 2 times + if(NOT (result EQUAL ${__RERUN_EXITCODE} AND retried LESS 2)) message(FATAL_ERROR "${error}") endif() + else() + if(error) + message(WARNING "${error}") + endif() endif() + + include(${managed_components_list_file}) # Add managed components to list of all components @@ -744,7 +759,7 @@ macro(idf_build_process target) # It is here we retrieve the public and private requirements of each component. # It is also here we add the common component requirements to each component's # own requirements. - __component_get_requirements() + __component_get_requirements(${use_sdk_json}) idf_build_get_property(component_targets __COMPONENT_TARGETS) @@ -811,6 +826,16 @@ macro(idf_build_process target) add_subdirectory(${idf_path} ${build_dir}/esp-idf) unset(ESP_PLATFORM) + else() + # We have to clear all build properties set in + # __build_expand_requirements if we are doing a retry + # This fixes order of dependencies when KConfig is used + idf_build_unset_property(__BUILD_COMPONENT_TARGETS) + idf_build_unset_property(__COMPONENT_TARGETS_SEEN) + idf_build_unset_property(__BUILD_COMPONENTS) + idf_build_unset_property(BUILD_COMPONENT_ALIASES) + idf_build_unset_property(BUILD_COMPONENTS) + idf_build_unset_property(__BUILD_COMPONENT_DEPGRAPH) endif() endmacro() diff --git a/tools/cmake/component.cmake b/tools/cmake/component.cmake index f1e35454f8f..cb3e0a06450 100644 --- a/tools/cmake/component.cmake +++ b/tools/cmake/component.cmake @@ -207,7 +207,7 @@ endfunction() # Given a component directory, get the requirements by expanding it early. The expansion is performed # using a separate CMake script (the expansion is performed in a separate instance of CMake in scripting mode). # -function(__component_get_requirements) +function(__component_get_requirements use_sdk_json) idf_build_get_property(idf_path IDF_PATH) idf_build_get_property(build_dir BUILD_DIR) @@ -245,8 +245,8 @@ function(__component_get_requirements) "idf_component_manager.prepare_components" "--project_dir=${project_dir}" "--lock_path=${DEPENDENCIES_LOCK}" - "--sdkconfig_json_file=${build_dir}/config/sdkconfig.json" "--interface_version=${component_manager_interface_version}" + "--use_sdk_json=${use_sdk_json}" "inject_requirements" "--idf_path=${idf_path}" "--build_dir=${build_dir}" diff --git a/tools/cmake/project.cmake b/tools/cmake/project.cmake index 8899f78dcd0..aecb4dc6384 100644 --- a/tools/cmake/project.cmake +++ b/tools/cmake/project.cmake @@ -61,7 +61,7 @@ if(NOT "$ENV{IDF_COMPONENT_MANAGER}" EQUAL "0") idf_build_set_property(IDF_COMPONENT_MANAGER 1) endif() # Set component manager interface version -idf_build_set_property(__COMPONENT_MANAGER_INTERFACE_VERSION 4) +idf_build_set_property(__COMPONENT_MANAGER_INTERFACE_VERSION 5) # # Parse and store the VERSION argument provided to the project() command. @@ -783,10 +783,12 @@ macro(project project_name) if(result EQUAL 0) break() elseif(result EQUAL 10 AND retried EQUAL 0) - message(WARNING "Missing kconfig option. Re-run the build process...") set(retried 1) elseif(result EQUAL 10 AND retried EQUAL 1) - message(FATAL_ERROR "Missing required kconfig option after retry.") + message(WARNING "Missing required kconfig option after retry. Re-running the build process.") + set(retried 2) + elseif(result EQUAL 10 AND retried EQUAL 2) + message(FATAL_ERROR "Missing required kconfig option after last retry. Terminating build.") else() message(FATAL_ERROR "idf_build_process failed with exit code ${result}") endif() diff --git a/tools/cmakev2/manager.cmake b/tools/cmakev2/manager.cmake index ed90fb4f57d..0cb5264a5eb 100644 --- a/tools/cmakev2/manager.cmake +++ b/tools/cmakev2/manager.cmake @@ -25,9 +25,9 @@ function(__init_component_manager) endif() # Set IDF_COMPONENT_MANAGER_INTERFACE_VERSION. - # Defaults to 4. Allow overriding via env/CMake. + # Defaults to 5. Allow overriding via env/CMake. __get_default_value(VARIABLE IDF_COMPONENT_MANAGER_INTERFACE_VERSION - DEFAULT 4 + DEFAULT 5 OUTPUT cmgr_iface) idf_build_set_property(IDF_COMPONENT_MANAGER_INTERFACE_VERSION ${cmgr_iface}) @@ -126,18 +126,17 @@ function(__download_managed_component) idf_build_get_property(project_dir PROJECT_DIR) idf_build_get_property(component_manager_interface_version IDF_COMPONENT_MANAGER_INTERFACE_VERSION) idf_build_get_property(dependencies_lock_file DEPENDENCIES_LOCK) - idf_build_get_property(sdkconfig_json __SDKCONFIG_JSON) - # Invoke the component manager execute_process(COMMAND ${python} "-m" "idf_component_manager.prepare_components" "--project_dir=${project_dir}" "--lock_path=${dependencies_lock_file}" - "--sdkconfig_json_file=${sdkconfig_json}" "--interface_version=${component_manager_interface_version}" + "--use_sdk_json=true" "prepare_dependencies" "--local_components_list_file=${ARG_COMPONENTS_LIST_FILE}" + "--build_dir=${build_dir}" "--managed_components_list_file=${ARG_MANAGED_OUTPUT_FILE}" RESULT_VARIABLE result ERROR_VARIABLE error) @@ -183,7 +182,8 @@ function(__download_component_level_managed_components) foreach(interface ${component_interfaces}) __idf_component_get_property_unchecked(name ${interface} COMPONENT_NAME) __idf_component_get_property_unchecked(dir ${interface} COMPONENT_DIR) - set(__contents "${__contents} - name: \"${name}\"\n path: \"${dir}\"\n") + __idf_component_get_property_unchecked(source ${interface} COMPONENT_SOURCE) + set(__contents "${__contents} - name: \"${name}\"\n path: \"${dir}\"\n source: \"${source}\"\n") endforeach() file(WRITE ${local_components_list_file} "${__contents}") @@ -289,7 +289,6 @@ function(__inject_requirements_for_component_from_manager component_name) idf_build_get_property(project_dir PROJECT_DIR) idf_build_get_property(build_dir BUILD_DIR) idf_build_get_property(dependencies_lock_file DEPENDENCIES_LOCK) - idf_build_get_property(sdkconfig_json __SDKCONFIG_JSON) idf_build_get_property(component_manager_interface_version IDF_COMPONENT_MANAGER_INTERFACE_VERSION) idf_build_get_property(idf_path IDF_PATH) idf_build_get_property(component_prefix PREFIX) @@ -325,8 +324,8 @@ function(__inject_requirements_for_component_from_manager component_name) "idf_component_manager.prepare_components" "--project_dir=${project_dir}" "--lock_path=${dependencies_lock_file}" - "--sdkconfig_json_file=${sdkconfig_json}" "--interface_version=${component_manager_interface_version}" + "--use_sdk_json=true" "inject_requirements" "--idf_path=${idf_path}" "--build_dir=${build_dir}" From df6ee7dc260964bc9afb8e9a8defcc0ae5bf5910 Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Thu, 5 Feb 2026 08:08:22 +0100 Subject: [PATCH 2/2] fix(build): prevent sdkconfig corruption during multiple component manager runs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The build system currently suffers from a bug where custom configuration settings are lost when the component manager is invoked multiple times. During an initial reconfigure or set-target command, the component manager may return no managed components. Because the build system lacks the Kconfig definitions for these components at this stage, it automatically prunes any related configuration symbols—specifically those defined in managed component Kconfig files—from the sdkconfig file. By the time the component manager finishes downloading the dependencies in a subsequent run, these original settings have already been overwritten and discarded because they were previously unrecognized by the build system. To resolve this, the generation of the final sdkconfig should be deferred until all component manager resolution cycles are complete. We need to ensure that the build system does not treat missing Kconfig definitions as invalid until the full dependency graph is loaded. A potential fix involves using a temporary configuration file for intermediate component manager passes to prevent the main sdkconfig from being prematurely scrubbed of valid user settings that belong to yet-to-be-resolved components. Edit: Added cleanup of sdkconfig.cm file and comments Signed-off-by: Frantisek Hrbata Edited by: Daniel Paul --- tools/cmake/build.cmake | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index 049510b0eeb..1def8e5690e 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -649,6 +649,7 @@ macro(idf_build_process target) idf_build_get_property(idf_component_manager IDF_COMPONENT_MANAGER) set(result 0) + set(use_sdk_json FALSE) if(idf_component_manager EQUAL 1) idf_build_get_property(build_dir BUILD_DIR) set(managed_components_list_file ${build_dir}/managed_components_list.temp.cmake) @@ -797,11 +798,23 @@ macro(idf_build_process target) idf_build_get_property(sdkconfig SDKCONFIG) idf_build_get_property(sdkconfig_defaults SDKCONFIG_DEFAULTS) + set(sdkconfig_cm "${build_dir}/sdkconfig.cm") # add target here since we have all components if(result EQUAL 0) __kconfig_generate_config("${sdkconfig}" "${sdkconfig_defaults}" CREATE_MENUCONFIG_TARGET) + # Delete the sdkconfig.cm backup if the last run of Component Manager was successful (cleanup) + if(EXISTS "${sdkconfig_cm}") + file(REMOVE "${sdkconfig_cm}") + endif() else() - __kconfig_generate_config("${sdkconfig}" "${sdkconfig_defaults}") + # We need to create backup of sdkconfig because the build system may + # lacks the Kconfig definitions for managed components. + if(EXISTS "${sdkconfig}") + file(COPY_FILE "${sdkconfig}" "${sdkconfig_cm}") + else() + file(REMOVE "${sdkconfig_cm}") + endif() + __kconfig_generate_config("${sdkconfig_cm}" "${sdkconfig_defaults}") endif() __build_import_configs()