diff --git a/tools/cmakev2/idf.cmake b/tools/cmakev2/idf.cmake index 631309e7a6..691c4c85ed 100644 --- a/tools/cmakev2/idf.cmake +++ b/tools/cmakev2/idf.cmake @@ -594,6 +594,9 @@ __init_python() # Initialize Kconfig system infrastructure. __init_kconfig() +# Initialize component manager build properties (IDF_COMPONENT_MANAGER, etc.). +__init_component_manager() + # Set IDF_TARGET. __init_idf_target() diff --git a/tools/cmakev2/kconfig.cmake b/tools/cmakev2/kconfig.cmake index de38a6ab7b..b4af854a54 100644 --- a/tools/cmakev2/kconfig.cmake +++ b/tools/cmakev2/kconfig.cmake @@ -55,6 +55,7 @@ function(__init_kconfig) endforeach() idf_build_set_property(SDKCONFIG "${sdkconfig}") + idf_build_set_property(__SDKCONFIG_ORIG "${sdkconfig}") idf_build_set_property(SDKCONFIG_DEFAULTS "${sdkconfig_defaults_checked}") # Setup ESP-IDF root Kconfig and sdkconfig.rename files. @@ -83,6 +84,16 @@ endfunction() and write the actual file. #]] function(__create_sdkconfig_orig_copy) + # The backup is only needed when the component manager is enabled. + # Managed components may introduce Kconfig options unknown to kconfgen + # during intermediate sdkconfig regeneration rounds. The backup preserves + # those options. When the manager is disabled, __SDKCONFIG_ORIG already + # points to the real sdkconfig (set in __init_kconfig). + idf_build_get_property(idf_component_manager IDF_COMPONENT_MANAGER) + if(NOT idf_component_manager EQUAL 1) + return() + endif() + idf_build_get_property(sdkconfig SDKCONFIG) idf_build_get_property(build_dir BUILD_DIR) set(sdkconfig_orig "${build_dir}/sdkconfig.orig") @@ -704,12 +715,8 @@ function(__create_base_kconfgen_command sdkconfig sdkconfig_defaults) endif() # Use __SDKCONFIG_ORIG for --config so that unknown options from managed - # components are preserved during intermediate kconfgen runs. Falls back - # to the real sdkconfig when __SDKCONFIG_ORIG is not yet set. + # components are preserved during intermediate kconfgen runs. idf_build_get_property(sdkconfig_orig __SDKCONFIG_ORIG) - if(NOT sdkconfig_orig) - set(sdkconfig_orig "${sdkconfig}") - endif() # Create base kconfgen command set(base_kconfgen_cmd ${python} -m kconfgen diff --git a/tools/cmakev2/manager.cmake b/tools/cmakev2/manager.cmake index c0488e6ec0..ed90fb4f57 100644 --- a/tools/cmakev2/manager.cmake +++ b/tools/cmakev2/manager.cmake @@ -20,6 +20,8 @@ function(__init_component_manager) OUTPUT component_manager_env) if(component_manager_env STREQUAL "" OR NOT component_manager_env STREQUAL "0") idf_build_set_property(IDF_COMPONENT_MANAGER 1) + else() + idf_build_set_property(IDF_COMPONENT_MANAGER 0) endif() # Set IDF_COMPONENT_MANAGER_INTERFACE_VERSION. @@ -29,13 +31,9 @@ function(__init_component_manager) OUTPUT cmgr_iface) idf_build_set_property(IDF_COMPONENT_MANAGER_INTERFACE_VERSION ${cmgr_iface}) - # Set DEPENDENCIES_LOCK if set by the user. Otherwise, use the - # project directory and IDF_TARGET to determine the lock file path. - # Note: This deviates from the build system v1 behavior where we allow - # users to specify the lock file path via idf_build_set_property. - idf_build_get_property(deps_lock_file DEPENDENCIES_LOCK) + # Set DEPENDENCIES_LOCK if provided via environment or CMake variable. __get_default_value(VARIABLE DEPENDENCIES_LOCK - DEFAULT "${deps_lock_file}" + DEFAULT "" OUTPUT deps_lock_file) idf_build_set_property(DEPENDENCIES_LOCK "${deps_lock_file}") endfunction() @@ -48,15 +46,11 @@ endfunction() kconfig option. This behavior is similar to the build system v1. This routine performs the following steps: - 1. Initialize the component manager. - 2. Run the component manager for all discovered components. - 3. Re-collect Kconfig and regenerate sdkconfig with managed components included. - 4. If the component manager run failed, error out. + 1. Run the component manager for all discovered components. + 2. Re-collect Kconfig and regenerate sdkconfig with managed components included. + 3. If the component manager run failed, error out. #]] function(__fetch_components_from_registry) - # Initialize the component manager. - __init_component_manager() - # Iteratively run the component manager and Kconfig until stable or error out. set(__cmgr_round 0) while(TRUE) @@ -71,8 +65,6 @@ function(__fetch_components_from_registry) # If component manager run failed, use the failure result if(cmgr_result EQUAL 0) - # If manager is disabled but manifests were detected, issue a warning - __component_manager_warn_if_disabled_and_manifests_exist() break() elseif(cmgr_result EQUAL 10 AND __cmgr_round LESS 2) # We can retry once if the manager fails with a missing kconfig option @@ -130,12 +122,6 @@ function(__download_managed_component) idf_die("RESULT option is required") endif() - idf_build_get_property(idf_component_manager IDF_COMPONENT_MANAGER) - if(NOT idf_component_manager EQUAL 1) - set(${ARG_RESULT} 0 PARENT_SCOPE) - return() - endif() - idf_build_get_property(python PYTHON) idf_build_get_property(project_dir PROJECT_DIR) idf_build_get_property(component_manager_interface_version IDF_COMPONENT_MANAGER_INTERFACE_VERSION) diff --git a/tools/cmakev2/project.cmake b/tools/cmakev2/project.cmake index 4122691c17..dddfaafa95 100644 --- a/tools/cmakev2/project.cmake +++ b/tools/cmakev2/project.cmake @@ -579,14 +579,20 @@ macro(idf_project_init) # Discover and initialize components __init_components() - # Save original sdkconfig before kconfgen may drop unknown options + # Save original sdkconfig before kconfgen may drop unknown options. + # Only creates a backup when the component manager is enabled. __create_sdkconfig_orig_copy() # Generate initial sdkconfig with discovered components __generate_sdkconfig() - # Initialize the component manager and fetch components in a loop - __fetch_components_from_registry() + # Fetch managed components from registry if the component manager is enabled + idf_build_get_property(idf_component_manager IDF_COMPONENT_MANAGER) + if(idf_component_manager EQUAL 1) + __fetch_components_from_registry() + else() + __component_manager_warn_if_disabled_and_manifests_exist() + endif() # Include sdkconfig.cmake idf_build_get_property(sdkconfig_cmake __SDKCONFIG_CMAKE) diff --git a/tools/test_build_system/buildv2/test_build.py b/tools/test_build_system/buildv2/test_build.py index c4f295cd82..5899d02bf1 100644 --- a/tools/test_build_system/buildv2/test_build.py +++ b/tools/test_build_system/buildv2/test_build.py @@ -6,8 +6,10 @@ import subprocess from pathlib import Path import pytest +from test_build_system_helpers import EnvDict from test_build_system_helpers import IdfPyFunc from test_build_system_helpers import replace_in_file +from test_build_system_helpers import run_idf_py @pytest.mark.usefixtures('test_app_copy') @@ -377,3 +379,18 @@ def test_depgraph_generation(idf_py: IdfPyFunc) -> None: dot_content = dot_file.read_text() assert 'digraph' in dot_content, 'component_deps.dot should contain a digraph definition' assert '->' in dot_content, 'component_deps.dot should contain at least one dependency edge (->)' + + +@pytest.mark.usefixtures('test_app_copy') +def test_build_with_component_manager_disabled(default_idf_env: EnvDict) -> None: + """Setting IDF_COMPONENT_MANAGER=0 should skip the component manager + flow entirely and still produce a successful build.""" + logging.info('Testing build with IDF_COMPONENT_MANAGER=0') + + default_idf_env['IDF_COMPONENT_MANAGER'] = '0' + ret = run_idf_py('build', env=default_idf_env) + + assert 'Component manager round' not in (ret.stdout or ''), ( + 'Component manager should not run when IDF_COMPONENT_MANAGER=0' + ) + assert Path('build/build_test_app.elf').exists(), 'Build should succeed with component manager disabled'