From d6bc39ebbdc964e0a4dfa330bca01a9c755ac063 Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Thu, 26 Jun 2025 17:33:13 +0200 Subject: [PATCH 001/196] feat(cmakev2): add basic component initialization Discover component directories and initialize components within them. This process does not include managed components, which should be added separately at a later stage. To facilitate this, some minimal functionalities are introduced, such as build properties, component properties, and other helper functions. Signed-off-by: Frantisek Hrbata --- tools/cmakev2/build.cmake | 85 +++++++ tools/cmakev2/component.cmake | 432 ++++++++++++++++++++++++++++++++++ tools/cmakev2/idf.cmake | 171 ++++++++++++++ tools/cmakev2/utilities.cmake | 165 +++++++++++++ 4 files changed, 853 insertions(+) create mode 100644 tools/cmakev2/build.cmake create mode 100644 tools/cmakev2/component.cmake create mode 100644 tools/cmakev2/idf.cmake create mode 100644 tools/cmakev2/utilities.cmake diff --git a/tools/cmakev2/build.cmake b/tools/cmakev2/build.cmake new file mode 100644 index 00000000000..9839027a4c1 --- /dev/null +++ b/tools/cmakev2/build.cmake @@ -0,0 +1,85 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +include_guard(GLOBAL) + +include(utilities) + +#[[api +.. cmakev2:function:: idf_build_set_property + + .. code-block:: cmake + + idf_build_set_property( [APPEND]) + + :property[in]: Property name. + :value[in]: Property value. + :APPEND: Append the value to the property's current value instead of + replacing it. + + Set the value of the specified property related to the ESP-IDF build. The + property is also added to the internal list of build properties if it isn't + already there. +#]] +function(idf_build_set_property property value) + set(options APPEND) + set(one_value) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + get_property(properties TARGET idf_build_properties PROPERTY BUILD_PROPERTIES) + if(NOT property IN_LIST properties) + list(APPEND properties "${property}") + set_property(TARGET idf_build_properties PROPERTY BUILD_PROPERTIES "${properties}") + endif() + if(ARG_APPEND) + set_property(TARGET idf_build_properties APPEND PROPERTY "${property}" "${value}") + else() + set_property(TARGET idf_build_properties PROPERTY "${property}" "${value}") + endif() +endfunction() + +#[[api +.. cmakev2:function:: idf_build_get_property + + .. code-block:: cmake + + idf_build_get_property( [GENERATOR_EXPRESSION]) + + :variable[out]: Variable to store the value in. + :property[in]: Property name to get the value of. + :GENERATOR_EXPRESSION: Obtain the generator expression for the property + rather than the actual value. + + Get the value of the specified property related to the ESP-IDF build. +#]] +function(idf_build_get_property variable property) + set(options GENERATOR_EXPRESSION) + set(one_value) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if("${property}" STREQUAL BUILD_COMPONENTS) + idf_die("Build property 'BUILD_COMPONENTS' is not supported") + endif() + if(ARG_GENERATOR_EXPRESSION) + set(value "$") + else() + get_property(value TARGET idf_build_properties PROPERTY ${property}) + endif() + set(${variable} ${value} PARENT_SCOPE) +endfunction() + +#[[ + __dump_build_properties() + + Dump all build properties. +#]] +function(__dump_build_properties) + idf_build_get_property(properties BUILD_PROPERTIES) + idf_msg("build properties: ${properties}") + foreach(property IN LISTS properties) + idf_build_get_property(value ${property}) + idf_msg(" ${property}: ${value}") + endforeach() +endfunction() diff --git a/tools/cmakev2/component.cmake b/tools/cmakev2/component.cmake new file mode 100644 index 00000000000..3136ef2d394 --- /dev/null +++ b/tools/cmakev2/component.cmake @@ -0,0 +1,432 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +include_guard(GLOBAL) + +include(utilities) +include(build) + +#[[api +.. cmakev2:function:: idf_component_set_property + + .. code-block:: cmake + + idf_component_set_property( [APPEND]) + + :property[in]: Property name. + :value[in]: Property value. + :APPEND: Append the value to the property's current value instead of + replacing it. + + Set the value of the specified component property. The property is also + added to the internal list of component properties if it isn't already + there. +#]] +function(idf_component_set_property component property value) + set(options APPEND) + set(one_value) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + __get_component_interface_or_die(COMPONENT "${component}" OUTPUT component_interface) + get_property(properties TARGET ${component_interface} PROPERTY COMPONENT_PROPERTIES) + if(NOT property IN_LIST properties) + list(APPEND properties "${property}") + set_property(TARGET ${component_interface} PROPERTY COMPONENT_PROPERTIES "${properties}") + endif() + if(ARG_APPEND) + set_property(TARGET ${component_interface} APPEND PROPERTY ${property} "${value}") + else() + set_property(TARGET ${component_interface} PROPERTY ${property} "${value}") + endif() +endfunction() + +#[[api +.. cmakev2:function:: idf_component_get_property + + .. code-block:: cmake + + idf_component_get_property( [GENERATOR_EXPRESSION]) + + :variable[out]: Variable to store the value in. + :property[in]: Property name to get the value of. + :GENERATOR_EXPRESSION: Obtain the generator expression for the property + rather than the actual value. + + Retrieve the value of the specified component property. +#]] +function(idf_component_get_property variable component property) + set(options GENERATOR_EXPRESSION) + set(one_value) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + __get_component_interface_or_die(COMPONENT "${component}" OUTPUT component_interface) + if(ARG_GENERATOR_EXPRESSION) + set(value "$") + else() + get_property(value TARGET ${component_interface} PROPERTY ${property}) + endif() + set(${variable} ${value} PARENT_SCOPE) +endfunction() + +#[[ + __get_component_paths(PATHS ... + [EXCLUDE_PATHS ...] + [SOURCE ] + [CHECK] + OUTPUT ) + + :PATHS[in]: List of paths to search for component directories. + :EXCLUDE_PATHS[in,opt]: Optional list of paths to exclude from the search of + component directories. + :SOURCE[in,opt]: Source of the ``PATHS``. If provided, it will be included + in the error message when ``CHECK`` is specified. + :CHECK[in,opt]: Verify whether the paths listed in "PATHS" exist. If any + path is missing, abort the build process. + :OUTPUT[out]: Output variable to store the list of found component + directories. + + Search for component directories in the specified ``PATHS``, excluding those + in ``EXCLUDE_PATHS``, and store the list of absolute component paths in the + ``OUTPUT`` variable. If ``CHECK`` is specified, ensure that the paths listed + in ``PATHS`` exist, and stop the build process if they do not. +#]] +function(__get_component_paths) + set(options CHECK) + set(one_value SOURCE OUTPUT) + set(multi_value PATHS EXCLUDE_PATHS) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if(NOT DEFINED ARG_PATHS) + idf_die("PATHS option is required") + endif() + + if(NOT DEFINED ARG_OUTPUT) + idf_die("OUTPUT option is required") + endif() + + set(include_paths "") + set(exclude_paths "") + __get_absolute_paths(PATHS "${ARG_PATHS}" OUTPUT include_paths) + if(DEFINED ARG_EXCLUDE_PATHS) + __get_absolute_paths(PATHS "${ARG_EXCLUDE_PATHS}" OUTPUT exclude_paths) + endif() + + if(ARG_CHECK) + foreach(path IN LISTS include_paths) + if(NOT IS_DIRECTORY "${path}") + if(DEFINED ARG_SOURCE) + idf_die("Directory specified in '${ARG_SOURCE}' doesn't exist: '${path}'") + else() + idf_die("Directory doesn't exist: '${path}'") + endif() + endif() + endforeach() + endif() + + set(paths "${include_paths}") + set(component_paths "") + + while(paths) + list(POP_FRONT paths path) + + if(NOT IS_DIRECTORY "${path}" OR "${path}" IN_LIST exclude_paths) + continue() + endif() + + if(EXISTS "${path}/CMakeLists.txt") + list(APPEND component_paths "${path}") + elseif("${path}" IN_LIST include_paths) + file(GLOB dirs "${path}/*") + __get_absolute_paths(PATHS "${dirs}" OUTPUT dirs_abs) + list(APPEND paths "${dirs_abs}") + endif() + + endwhile() + + set(${ARG_OUTPUT} "${component_paths}" PARENT_SCOPE) +endfunction() + +#[[ + __get_component_interface(COMPONENT + OUTPUT ) + + :COMPONENT[int]: Component name, target, target alias or interface. + :OUTPUT[out]: Output variable to store the component interface. + + Identify the component interface target using the ```` value, + which could be a component name, target, target alias, or interface. Return + the component interface target, or NOTFOUND if the interface cannot be + located. + + component interface: _ + component target: __ + component alias: :: +#]] +function(__get_component_interface) + set(options) + set(one_value COMPONENT OUTPUT) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if(NOT DEFINED ARG_COMPONENT) + idf_die("COMPONENT option is required") + endif() + + if(NOT DEFINED ARG_OUTPUT) + idf_die("OUTPUT option is required") + endif() + + idf_build_get_property(component_names COMPONENTS_DISCOVERED) + idf_build_get_property(component_interfaces COMPONENT_INTERFACES) + idf_build_get_property(component_prefix PREFIX) + + set(component_interface NOTFOUND) + if("${ARG_COMPONENT}" IN_LIST component_names) + # The component name is among the discovered components, and the + # component interface is simply the component name with a prefix. + set(component_interface "${component_prefix}_${ARG_COMPONENT}") + else() + # The component name might be an alias, so retrieve the actual target + # name. + __get_real_target(TARGET ${ARG_COMPONENT} OUTPUT real_target) + if("${real_target}" IN_LIST component_interfaces) + # The component name is already a component interface or its alias. + set(component_interface "${real_target}") + else() + string(SUBSTRING "${ARG_COMPONENT}" 1 -1 interface) + if("${interface}" IN_LIST component_interfaces) + # The component name is the actual target of the component. + set(component_interface "${interface}") + endif() + endif() + endif() + + # Sanity check + if(NOT "${component_interface}" STREQUAL "NOTFOUND" + AND NOT "${component_interface}" IN_LIST component_interfaces) + idf_warn("Interface target '${component_interface}' found for component " + "'${ARG_COMPONENT}', but it's not present in the component " + "interface list.") + set(component_interface NOTFOUND) + endif() + + set(${ARG_OUTPUT} ${component_interface} PARENT_SCOPE) +endfunction() + +#[[ + __get_component_interface_or_die(COMPONENT + OUTPUT ) + + :COMPONENT[int]: Component name, target, target alias or interface. + :OUTPUT[out]: Output variable to store the component interface. + + A simple wrapper for ``__get_component_interface`` that aborts the build + process if the component interface is not found. +#]] +function(__get_component_interface_or_die) + set(options) + set(one_value COMPONENT OUTPUT) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if(NOT DEFINED ARG_COMPONENT) + idf_die("COMPONENT option is required") + endif() + + if(NOT DEFINED ARG_OUTPUT) + idf_die("OUTPUT option is required") + endif() + + __get_component_interface(COMPONENT "${ARG_COMPONENT}" OUTPUT component_interface) + if("${component_interface}" STREQUAL "NOTFOUND") + idf_die("Component interface for component '${ARG_COMPONENT}' does not exist") + endif() + set(${ARG_OUTPUT} ${component_interface} PARENT_SCOPE) +endfunction() + +#[[ + __get_component_priority(SOURCE + OUTPUT ) + + :SOURCE[in]: String identifying the component source. + :OUTPUT[out]: Output variable to store the component priority. + + Return the priority number of a component, where a higher number indicates a + higher priority, based on the given ``source`` string. If the ``source`` is + not valid, return ``NOTFOUND``. +#]] +function(__get_component_priority) + set(options) + set(one_value SOURCE OUTPUT) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if(NOT DEFINED ARG_SOURCE) + idf_die("SOURCE option is required") + endif() + + if(NOT DEFINED ARG_OUTPUT) + idf_die("OUTPUT option is required") + endif() + + if("${ARG_SOURCE}" STREQUAL "project_components") + set(priority 3) + elseif("${ARG_SOURCE}" STREQUAL "project_extra_components") + set(priority 2) + elseif("${ARG_SOURCE}" STREQUAL "project_managed_components") + set(priority 1) + elseif("${ARG_SOURCE}" STREQUAL "idf_components") + set(priority 0) + else() + set(priority NOTFOUND) + endif() + + set(${ARG_OUTPUT} "${priority}" PARENT_SCOPE) +endfunction() + +#[[ + __init_component(DIRECTORY + PREFIX + SOURCE ) + + :DIRECTORY[in]: ```` where the component is located. + :PREFIX[in]: Prefix for component target names. + :SOURCE[in]: String identifying the component source. + + Initialize the component by creating a component interface target, allowing + properties to be attached to it and also add initial component properties. + At this stage, the component is not included in the build. The actual + component target, named as specified in the `COMPONENT_LIB` property, is + created later when ``add_subdirectory`` is called for the component's + directory based on the project or other component requirements. + + Components are identified by the directory name they reside in. This means + that components with the same name might exist in different directory paths. + In such cases, the component with the higher priority is used. Priority is + determined by the component's source, as defined in + ``__get_component_priority``. If a component with a higher priority than an + existing one is initialized, its name, targets, and other properties remain + the same. Only the directory, priority, and source are updated in the + already initialized component. + + The name of the initialized component is added to the + ``COMPONENTS_DISCOVERED`` build property, and its interface target is added + to the ``COMPONENT_INTERFACES`` build property. These two lists are used by + ``__get_component_interface``, which searches for a component interface + based on the component name, alias, or targets, enabling the setting and + retrieval of component properties. +#]] +function(__init_component) + set(options) + set(one_value DIRECTORY PREFIX SOURCE) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if(NOT DEFINED ARG_DIRECTORY) + idf_die("DIRECTORY option is required") + endif() + + if(NOT DEFINED ARG_SOURCE) + idf_die("SOURCE option is required") + endif() + + if(NOT DEFINED ARG_PREFIX) + idf_die("PREFIX option is required") + endif() + + set(component_source "${ARG_SOURCE}") + set(component_prefix "${ARG_PREFIX}") + get_filename_component(component_directory ${ARG_DIRECTORY} ABSOLUTE) + get_filename_component(component_name ${component_directory} NAME) + __get_component_priority(SOURCE "${component_source}" OUTPUT component_priority) + + if(NOT EXISTS "${component_directory}/CMakeLists.txt") + idf_die("Directory '${component_directory}' does not contain a component") + endif() + + if("${component_priority}" STREQUAL "NOTFOUND") + idf_die("Unknown component source '${component_source}' " + "for directory '${component_directory}'") + endif() + + __get_component_interface(COMPONENT "${component_name}" OUTPUT existing_component_interface) + if(NOT "${existing_component_interface}" STREQUAL "NOTFOUND") + # A component with the same name is already initialized. Check if it + # should be replaced with the component currently being initialized. + idf_component_get_property(existing_component_priority + "${existing_component_interface}" + COMPONENT_PRIORITY) + idf_component_get_property(existing_component_directory + "${existing_component_interface}" + COMPONENT_DIR) + idf_component_get_property(existing_component_source + "${existing_component_interface}" + COMPONENT_SOURCE) + + if(${component_priority} EQUAL ${existing_component_priority}) + idf_die("Component directory '${component_directory}' has the same " + "priority '${component_source}' as component directory " + "'${existing_component_directory}'") + + elseif(${component_priority} LESS ${existing_component_priority}) + idf_warn("Component directory '${component_directory}' has lower " + "priority '${component_source}' than component directory " + "'${existing_component_directory}' with priority " + "'${existing_component_source}' and will be ignored") + else() + idf_warn("Component '${component_name}' directory '${component_directory}' " + "with higher priority '${component_source}' will be used instead of " + "component directory '${existing_component_directory}' " + "with lower priority '${existing_component_source}'") + # The newly added component has a higher priority than the existing + # one. Since the component name and targets are identical, update + # the existing component with the new directory and priority. + idf_component_set_property("${component_name}" COMPONENT_DIR "${component_directory}") + idf_component_set_property("${component_name}" COMPONENT_SOURCE "${component_source}") + idf_component_set_property("${component_name}" COMPONENT_PRIORITY ${component_priority}) + endif() + + return() + endif() + + set(component_interface "${component_prefix}_${component_name}") + set(component_alias "${component_prefix}::${component_name}") + # Real component library target that needs to be created by the component. + set(component_target "_${component_interface}") + + # Interface target is used to attach all component properties and is also + # used when the component is linked to other targets. + add_library("${component_interface}" INTERFACE) + add_library("${component_alias}" ALIAS "${component_interface}") + + idf_build_set_property(COMPONENTS_DISCOVERED "${component_name}" APPEND) + idf_build_set_property(COMPONENT_INTERFACES "${component_interface}" APPEND) + + idf_component_set_property("${component_name}" COMPONENT_LIB "${component_target}") + idf_component_set_property("${component_name}" COMPONENT_NAME "${component_name}") + idf_component_set_property("${component_name}" COMPONENT_DIR "${component_directory}") + idf_component_set_property("${component_name}" COMPONENT_ALIAS "${component_alias}") + idf_component_set_property("${component_name}" COMPONENT_SOURCE "${component_source}") + idf_component_set_property("${component_name}" COMPONENT_INTERFACE "${component_interface}") + idf_component_set_property("${component_name}" COMPONENT_PRIORITY ${component_priority}) +endfunction() + +#[[ + __dump_component_properties() + + :components: List of components whose properties should be displayed. + + Dump all properties for the components listed in ````. +#]] +function(__dump_component_properties components) + foreach(component IN LISTS components) + idf_component_get_property(properties "${component}" COMPONENT_PROPERTIES) + idf_msg("component '${component}' properties: ${properties}") + foreach(property IN LISTS properties) + idf_component_get_property(value "${component}" "${property}") + idf_msg(" ${property}: ${value}") + endforeach() + endforeach() +endfunction() diff --git a/tools/cmakev2/idf.cmake b/tools/cmakev2/idf.cmake new file mode 100644 index 00000000000..2c1b10588b7 --- /dev/null +++ b/tools/cmakev2/idf.cmake @@ -0,0 +1,171 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +include_guard(GLOBAL) + +cmake_minimum_required(VERSION 3.22) + +# Update CMAKE_MODULE_PATH to ensure that other build system modules can be +# included. +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}" ${CMAKE_MODULE_PATH}) + +include(component) +include(build) + +#[[ + __init_idf_path() + + Determine the IDF_PATH value, either from the IDF_PATH environmental + variable or based on the location of this file. Also check there is no + inconsistency between the two. + + Set the IDF_PATH global variable, environment variable and build property. +#]] +function(__init_idf_path) + get_filename_component(idf_path_infer "${CMAKE_CURRENT_LIST_DIR}/../.." REALPATH) + + if(NOT DEFINED ENV{IDF_PATH}) + idf_warn("IDF_PATH environment variable not found. " + "Setting IDF_PATH to '${idf_path_infer}'.") + set(idf_path "${idf_path_infer}") + else() + get_filename_component(idf_path_env "$ENV{IDF_PATH}" REALPATH) + if(NOT "${idf_path_env}" STREQUAL "${idf_path_infer}") + idf_warn("IDF_PATH environment variable is different from inferred IDF_PATH. " + "Check if your project's top-level CMakeLists.txt includes the right " + "CMake files. Environment IDF_PATH will be used for the build: " + "'${idf_path_env}'") + endif() + set(idf_path "${idf_path_env}") + endif() + + idf_build_set_property(IDF_PATH "${idf_path}") + set(IDF_PATH ${idf_path} PARENT_SCOPE) + set(ENV{IDF_PATH} ${idf_path}) +endfunction() + +#[[ + __init_components() + + Search for possible component directories categorized by their source, which + could be ``idf_components``, ``project_extra_components``, or + ``project_components``. Components added by the component manager are + initialized later as ``project_managed_components`` after the component + manager is called. + + The search respects the variables set by the user e.g. in the project's + CMakeLists.txt file. These are maintained for backward compatibility. + + :COMPONENT_DIRS: If set, component directories are searched exclusively in + the paths provided in ``COMPONENT_DIRS``. + :EXTRA_COMPONENT_DIRS: Includes extra paths to search if `COMPONENT_DIRS` is not specified. + :EXTRA_COMPONENT_EXCLUDE_DIRS: List of paths to exclude from searching the + component directories. + + Each component is initialized for every component directory found. +#]] +function(__init_components) + idf_build_get_property(idf_path IDF_PATH) + idf_build_get_property(prefix PREFIX) + + __get_component_paths(PATHS "${idf_path}/components" + OUTPUT idf_components) + if(COMPONENT_DIRS) + # The user explicitly stated the locations to search for components. + # For backward compatibility, check that the paths in + # COMPONENT_DIRS exist. + __get_component_paths(PATHS ${COMPONENT_DIRS} + EXCLUDE_PATHS ${EXTRA_COMPONENT_EXCLUDE_DIRS} + SOURCE "COMPONENT_DIRS" + CHECK + OUTPUT project_components) + else() + __get_component_paths(PATHS "${CMAKE_CURRENT_SOURCE_DIR}/main" + "${CMAKE_CURRENT_SOURCE_DIR}/components" + EXCLUDE_PATHS ${EXTRA_COMPONENT_EXCLUDE_DIRS} + OUTPUT project_components) + if(EXTRA_COMPONENT_DIRS) + # For backward compatibility, check that the paths in + # EXTRA_COMPONENT_DIRS exist. + __get_component_paths(PATHS ${EXTRA_COMPONENT_DIRS} + EXCLUDE_PATHS ${EXTRA_COMPONENT_EXCLUDE_DIRS} + SOURCE "EXTRA_COMPONENT_DIRS" + CHECK + OUTPUT project_extra_components) + endif() + endif() + + foreach(path IN LISTS idf_components) + __init_component(DIRECTORY "${path}" + PREFIX "${prefix}" + SOURCE "idf_components") + endforeach() + + foreach(path IN LISTS project_components) + __init_component(DIRECTORY "${path}" + PREFIX "${prefix}" + SOURCE "project_components") + endforeach() + + foreach(path IN LISTS project_extra_components) + __init_component(DIRECTORY "${path}" + PREFIX "${prefix}" + SOURCE "project_extra_components") + endforeach() +endfunction() + +#[[ + The idf_build_properties interface target is exclusively used to store + information about global build properties and is not linked or used in any + other way. This is created very early so that all the initialization + functions can use it. + + List of build properties + + :IDF_PATH: Path to esp-idf directory. + :PREFIX: Prefix used for component target names. + :COMPONENTS_DISCOVERED: List of component names identified by the build + system. These components are initialized and can + have properties attached to them. However, they + are not necessarily included in the build through + add_subdirectory. + :COMPONENT_INTERFACES: This is a list of component interface targets for + the components in ``COMPONENTS_DISCOVERED``. It is + used when searching for a component, such as by its + name, to set or retrieve the component's properties. + +#]] +add_library(idf_build_properties INTERFACE) + +# Set build system prefix for component targets. +idf_build_set_property(PREFIX "idf") + +# Initialize IDF_PATH and set it as a global and environmental variable, as +# well as a build property. +__init_idf_path() + +# Discover and initialize components. +__init_components() + +#[[ TODO + +Many of the following things are already implemented in PoC !38337, but they +need to be reviewed. + +* Set IDF version. +* Set and check python interpreter. +* Set build target. +* Set the toolchain before invoking project(). +* Enable ccache if requested and available. +* Generate initial sdkconfig for component manager. +* Call component manager with initial sdkconfig to download requested components + and include the generated cmake. See the generated + ``build/managed_components_list.temp.cmake``. We probably need shim for + ``idf_build_component`` calling ``__init_component``. +* Generate final sdkconfig and include its cmake version. + +There may be additional steps. We should initialize everything necessary before +calling the ``project()`` function, as well as any global settings that cannot be +modified later. + +#]] diff --git a/tools/cmakev2/utilities.cmake b/tools/cmakev2/utilities.cmake new file mode 100644 index 00000000000..dd678924844 --- /dev/null +++ b/tools/cmakev2/utilities.cmake @@ -0,0 +1,165 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +# Note: CMake does not support nested lists. The functions idf_die, idf_warn, +# idf_msg, and idf_dbg use ARGV# values because this is the only way to prevent +# arguments from being altered by CMake. ARGV and ARGN contain a flattened list +# of arguments, making it impossible to determine if any argument was +# originally a list. +# +# Using these functions has a side effect: the actual origin of the message +# appears as the first line of the backtrace. + +#[[api +.. cmakev2:function:: idf_die + + .. code-block:: cmake + + idf_die(...) + + :msg[in]: Message to print. + + Print error ```` and abort the build process. Multiple messages are + concatenated into a single message with no separator between them. + +#]] +function(idf_die) + set(joined "") + math(EXPR last "${ARGC} - 1") + foreach(i RANGE 0 ${last}) + string(APPEND joined "${ARGV${i}}") + endforeach() + message(FATAL_ERROR " IDF: ${joined}") +endfunction() + +#[[api +.. cmakev2:function:: idf_warn + + .. code-block:: cmake + + idf_warn(...) + + :msg[in]: Message to print. + + Print warning ````. Multiple messages are concatenated into a single + message with no separator between them. +#]] +function(idf_warn) + set(joined "") + math(EXPR last "${ARGC} - 1") + foreach(i RANGE 0 ${last}) + string(APPEND joined "${ARGV${i}}") + endforeach() + message(WARNING " IDF: ${joined}") +endfunction() + +#[[api +.. cmakev2:function:: idf_msg + + .. code-block:: cmake + + idf_msg(...) + + :msg[in]: Message to print. + + Print status ````. Multiple messages are concatenated into a single + message with no separator between them. +#]] +function(idf_msg) + set(joined "") + math(EXPR last "${ARGC} - 1") + foreach(i RANGE 0 ${last}) + string(APPEND joined "${ARGV${i}}") + endforeach() + message(STATUS " IDF: ${joined}") +endfunction() + +#[[api +.. cmakev2:function:: idf_dbg + + .. code-block:: cmake + + idf_dbg(...) + + :msg[in]: Message to print. + + Print debug ````. Multiple messages are concatenated into a single + message with no separator between them. +#]] +function(idf_dbg) + set(joined "") + math(EXPR last "${ARGC} - 1") + foreach(i RANGE 0 ${last}) + string(APPEND joined "${ARGV${i}}") + endforeach() + message(DEBUG " IDF: ${joined}") +endfunction() + +#[[ + __get_real_target(TARGET + OUTPUT ) + + :TARGET[int]: Target name or target alias name. + :OUTPUT[out]: Output variable to store the real target name. + + For a given ````, return the actual target if ```` is an + alias. If ```` is the target itself, return it. If ```` is + not a target at all, return ``NOTFOUND``. +#]] +function(__get_real_target) + set(options) + set(one_value TARGET OUTPUT) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if(NOT DEFINED ARG_TARGET) + idf_die("TARGET option is required") + endif() + + if(NOT DEFINED ARG_OUTPUT) + idf_die("OUTPUT option is required") + endif() + + set(real_target NOTFOUND) + if(TARGET "${ARG_TARGET}") + get_target_property(aliased ${ARG_TARGET} ALIASED_TARGET) + if(aliased) + set(real_target ${aliased}) + else() + set(real_target ${ARG_TARGET}) + endif() + endif() + set(${ARG_OUTPUT} "${real_target}" PARENT_SCOPE) +endfunction() + +#[[ + __get_absolute_paths(PATHS ... + OUTPUT ) + + :PATHS[in]: List of paths to convert to absolute paths. + :OUTPUT[out]: Output variable to store absolute paths. + + For a given ``PATHS``, return the absolute paths in ``OUTPUT``. +#]] +function(__get_absolute_paths) + set(options) + set(one_value OUTPUT) + set(multi_value PATHS) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if(NOT DEFINED ARG_PATHS) + idf_die("PATHS option is required") + endif() + + if(NOT DEFINED ARG_OUTPUT) + idf_die("OUTPUT option is required") + endif() + + set(absolute_paths "") + foreach(path IN LISTS ARG_PATHS) + get_filename_component(path_abs ${path} ABSOLUTE) + list(APPEND absolute_paths "${path_abs}") + endforeach() + + set(${ARG_OUTPUT} "${absolute_paths}" PARENT_SCOPE) +endfunction() From b3bce8828cc60a280c6f63d563fb94b45e825e90 Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Thu, 26 Jun 2025 17:39:04 +0200 Subject: [PATCH 002/196] ci(cmakev2): temporarily exclude cmakev2 from CI testing There are currently no CI tests for CMakeV2, so add it to the exclude_check_tools_files.txt file. Once CI testing is implemented, this change should be reverted. Signed-off-by: Frantisek Hrbata --- tools/ci/exclude_check_tools_files.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ci/exclude_check_tools_files.txt b/tools/ci/exclude_check_tools_files.txt index 5ccf75a904a..a1dd3874ca3 100644 --- a/tools/ci/exclude_check_tools_files.txt +++ b/tools/ci/exclude_check_tools_files.txt @@ -52,3 +52,4 @@ tools/templates/sample_component/main.c tools/templates/sample_project/CMakeLists.txt tools/templates/sample_project/main/CMakeLists.txt tools/templates/sample_project/main/main.c +tools/cmakev2/**/* From 8e14ed199ed65fc1e43968d1bf1aaf785c0bc7c0 Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Thu, 26 Jun 2025 17:45:27 +0200 Subject: [PATCH 003/196] test(cmakev2): add testing project CMakeLists.txt This is a basic CMake project that currently includes tests for displaying build and component properties, as well as testing component priority. These tests should be removed once proper CI testing is in place. Signed-off-by: Frantisek Hrbata --- tools/cmakev2/test/CMakeLists.txt | 70 +++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tools/cmakev2/test/CMakeLists.txt diff --git a/tools/cmakev2/test/CMakeLists.txt b/tools/cmakev2/test/CMakeLists.txt new file mode 100644 index 00000000000..fc3cf158156 --- /dev/null +++ b/tools/cmakev2/test/CMakeLists.txt @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +# Simple project for basic cmakev2 testing +# Run: cmake -S . -B build +cmake_minimum_required(VERSION 3.22) + +include($ENV{IDF_PATH}/tools/cmakev2/idf.cmake) + +project(cmakev2 C) + +# Dump build and component properties +function(test_dump_properties) + __dump_build_properties() + idf_build_get_property(component_names COMPONENTS_DISCOVERED) + __dump_component_properties("${component_names}") +endfunction() + +# Test component priority +function(test_component_priority) + # Set the idf component to be replaced with a testing component of higher + # priority. + set(component_name "esp_system") + + # Check that idf component is between discovered components. + __get_component_interface(COMPONENT "${component_name}" + OUTPUT component_interface) + if("${component_interface}" STREQUAL "NOTFOUND") + idf_die("Component '${component_name}' not found") + endif() + + # Check that idf component has "idf_components" as source. + idf_component_get_property(component_source + ${component_interface} + COMPONENT_SOURCE) + if(NOT "${component_source}" STREQUAL "idf_components") + idf_die("Unexpected idf component '${component_name}' source '${component_source}'") + endif() + + # Create fake component with same name as idf component. + set(component_dir "${CMAKE_CURRENT_BINARY_DIR}/${component_name}") + file(MAKE_DIRECTORY "${component_dir}") + file(TOUCH "${component_dir}/CMakeLists.txt") + + # Initialize fake component with higher "project_components" priority. + idf_build_get_property(component_prefix PREFIX) + __init_component(DIRECTORY "${component_dir}" + PREFIX "${component_prefix}" + SOURCE "project_components") + + # Check that the idf component was replaced with fake component. + idf_component_get_property(component_source + ${component_interface} + COMPONENT_SOURCE) + if(NOT "${component_source}" STREQUAL "project_components") + idf_die("Unexpected fake component '${component_name}' source '${component_source}'") + endif() + + idf_component_get_property(component_dir + ${component_interface} + COMPONENT_DIR) + if(NOT "${component_dir}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/${component_name}") + idf_die("Unexpected fake component '${component_name}' directory '${component_dir}'") + endif() + __dump_component_properties("${component_name}") +endfunction() + +# Run tests +test_dump_properties() +test_component_priority() From 0ffb07df1e45118e90a3cd4973033361c476130c Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Fri, 11 Jul 2025 12:27:46 +0200 Subject: [PATCH 004/196] fix(cmakev2/utilities): make the PATHS option optional in __get_absolute_paths With the default signature of `cmake_parse_arguments`, without using `PARSE_ARGV`, it's not possible to determine if options were not specified or set as an empty string (or empty list)[1]. If an empty string is passed to the `PATHS` option, the variable parsed by `cmake_parse_arguments` is not defined. This issue can be addressed by using `PARSE_ARGV`, but this approach only works for functions and requires CMake version 3.31 or newer. Additionally, when `PARSE_ARGV` is used for multiple value option, the values are not concatenated into a single list, which is inconvenient, as the lists are instead escaped. If the `PATHS` option is not defined, set it to an empty string. This allows passing an empty string, as well as a mix of lists and individual strings, through PATHS. The behaviour can be seen with a simple example: $ cmake -P test.cmake ```test.cmake cmake_minimum_required(VERSION 3.22) function(test) set(options) set(one_value) set(multi_value PATHS) cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) #cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${one_value}" "${multi_value}") message("PATHS: ${ARG_PATHS}") endfunction() test(PATHS "one;two;three" "four" "five;six" "seven") ``` ``` PATHS: one;two;three;four;five;six;seven vs PATHS: one\;two\;three;four;five\;six;seven ``` Also update the current usage of __get_absolute_paths, as the check for empty PATHS is no longer necessary. [1] https://cmake.org/cmake/help/latest/policy/CMP0174.html#policy:CMP0174 Signed-off-by: Frantisek Hrbata --- tools/cmakev2/component.cmake | 6 +----- tools/cmakev2/utilities.cmake | 8 ++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/tools/cmakev2/component.cmake b/tools/cmakev2/component.cmake index 3136ef2d394..746d6d9c982 100644 --- a/tools/cmakev2/component.cmake +++ b/tools/cmakev2/component.cmake @@ -106,12 +106,8 @@ function(__get_component_paths) idf_die("OUTPUT option is required") endif() - set(include_paths "") - set(exclude_paths "") __get_absolute_paths(PATHS "${ARG_PATHS}" OUTPUT include_paths) - if(DEFINED ARG_EXCLUDE_PATHS) - __get_absolute_paths(PATHS "${ARG_EXCLUDE_PATHS}" OUTPUT exclude_paths) - endif() + __get_absolute_paths(PATHS "${ARG_EXCLUDE_PATHS}" OUTPUT exclude_paths) if(ARG_CHECK) foreach(path IN LISTS include_paths) diff --git a/tools/cmakev2/utilities.cmake b/tools/cmakev2/utilities.cmake index dd678924844..34ef335de6e 100644 --- a/tools/cmakev2/utilities.cmake +++ b/tools/cmakev2/utilities.cmake @@ -147,14 +147,14 @@ function(__get_absolute_paths) set(multi_value PATHS) cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) - if(NOT DEFINED ARG_PATHS) - idf_die("PATHS option is required") - endif() - if(NOT DEFINED ARG_OUTPUT) idf_die("OUTPUT option is required") endif() + if(NOT DEFINED ARG_PATHS) + set(ARG_PATHS "") + endif() + set(absolute_paths "") foreach(path IN LISTS ARG_PATHS) get_filename_component(path_abs ${path} ABSOLUTE) From 04a91f68cd8831dc2f890940633688751ac6d4e0 Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Fri, 11 Jul 2025 11:30:56 +0200 Subject: [PATCH 005/196] feat(cmakev2/utilities): add __get_default_value helper Helper for obtaining the default value of a variable. It returns the value of the specified variable based on the following order of precedence, with the highest precedence first: 1. Environmental variable 2. CMake cache variable 3. Provided default value This can be used to retrieve the value of variables that might also be set in the environment or cache, such as PYTHON or SDKCONFIG. Signed-off-by: Frantisek Hrbata --- tools/cmakev2/utilities.cmake | 47 +++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tools/cmakev2/utilities.cmake b/tools/cmakev2/utilities.cmake index 34ef335de6e..5c300324d84 100644 --- a/tools/cmakev2/utilities.cmake +++ b/tools/cmakev2/utilities.cmake @@ -163,3 +163,50 @@ function(__get_absolute_paths) set(${ARG_OUTPUT} "${absolute_paths}" PARENT_SCOPE) endfunction() + +#[[ + __get_default_value(VARIABLE + DEFAULT ) + OUTPUT ) + + :VARIABLE[in]: The name of the variable for which to obtain its default + value. + :DEFAULT[in]: Default variable value. + :OUTPUT[out]: Variable to store the default value of ````. + + Set the ```` to the value of ```` based on the following + order of precedence, with the first having the highest precedence. + + 1. environmental variable + 2. CMake cache variable + 3. ```` as specified with the ``DEFAULT`` option +#]] +function(__get_default_value) + set(options) + set(one_value VARIABLE DEFAULT OUTPUT) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if(NOT DEFINED ARG_VARIABLE) + idf_die("VARIABLE option is required") + endif() + + if(NOT DEFINED ARG_OUTPUT) + idf_die("OUTPUT option is required") + endif() + + if(NOT DEFINED ARG_DEFAULT) + set(ARG_DEFAULT "") + endif() + + + if(DEFINED ENV{${ARG_VARIABLE}}) + set(value "$ENV{${ARG_VARIABLE}}") + elseif(DEFINED CACHE{${ARG_VARIABLE}}) + set(value "$CACHE{${ARG_VARIABLE}}") + else() + set(value "${ARG_DEFAULT}") + endif() + + set(${ARG_OUTPUT} "${value}" PARENT_SCOPE) +endfunction() From 163bcbad3cdd72a311888b1045d9f7bc9a7e8540 Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Thu, 10 Jul 2025 14:12:15 +0200 Subject: [PATCH 006/196] feat(cmakev2/idf): add IDF_VERSION variables The version.cmake file should be the sole file used from the cmakev1 build system. There's no need to maintain the IDF_VERSION information in two separate locations. Signed-off-by: Frantisek Hrbata --- tools/cmakev2/idf.cmake | 5 ++++- tools/cmakev2/test/CMakeLists.txt | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/tools/cmakev2/idf.cmake b/tools/cmakev2/idf.cmake index 2c1b10588b7..1d13cabd79a 100644 --- a/tools/cmakev2/idf.cmake +++ b/tools/cmakev2/idf.cmake @@ -9,6 +9,10 @@ cmake_minimum_required(VERSION 3.22) # included. set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}" ${CMAKE_MODULE_PATH}) +# The version.cmake file, which contains the IDF_VERSION variables, is the only +# file included from the cmakev1 build system. +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/version.cmake) + include(component) include(build) @@ -152,7 +156,6 @@ __init_components() Many of the following things are already implemented in PoC !38337, but they need to be reviewed. -* Set IDF version. * Set and check python interpreter. * Set build target. * Set the toolchain before invoking project(). diff --git a/tools/cmakev2/test/CMakeLists.txt b/tools/cmakev2/test/CMakeLists.txt index fc3cf158156..77835c5ffc1 100644 --- a/tools/cmakev2/test/CMakeLists.txt +++ b/tools/cmakev2/test/CMakeLists.txt @@ -65,6 +65,19 @@ function(test_component_priority) __dump_component_properties("${component_name}") endfunction() +# Test that IDF_VERSION is set +function(test_idf_version) + if(NOT DEFINED IDF_VERSION_MAJOR OR + NOT DEFINED IDF_VERSION_MINOR OR + NOT DEFINED IDF_VERSION_PATCH OR + NOT DEFINED ENV{IDF_VERSION}) + idf_die("IDF_VERSION not set") + endif() +endfunction() + # Run tests test_dump_properties() test_component_priority() +test_idf_version() + +message("ALL TESTS PASSED") From 7efff764cbf454f2c1c660ee5a51ba2d89a4dfc4 Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Thu, 10 Jul 2025 16:19:17 +0200 Subject: [PATCH 007/196] feat(cmakev2/idf): add Python interpreter detection Determine the Python interpreter and verify package dependencies if the CMake cache variable PYTHON_DEPS_CHECKED is not set. Signed-off-by: Frantisek Hrbata --- tools/cmakev2/idf.cmake | 51 +++++++++++++++++++++++++++++-- tools/cmakev2/test/CMakeLists.txt | 8 +++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/tools/cmakev2/idf.cmake b/tools/cmakev2/idf.cmake index 1d13cabd79a..d38aad17285 100644 --- a/tools/cmakev2/idf.cmake +++ b/tools/cmakev2/idf.cmake @@ -48,6 +48,50 @@ function(__init_idf_path) set(ENV{IDF_PATH} ${idf_path}) endfunction() +#[[ + __init_python() + + Determine Python interpreter, either from the PYTHON CMake cache variable + or environmental variable or default it to "python". + + If the PYTHON_DEPS_CHECKED CMake cache variable is not set, check if all + Python packages dependencies are satisfied. For instance, if a tool calling + CMake has already performed this check, it doesn't need to be repeated. + + Set the global PYTHON variable, environment variable and build property. +#]] +function(__init_python) + __get_default_value(VARIABLE PYTHON + DEFAULT "python" + OUTPUT python) + file(TO_CMAKE_PATH ${python} python) + + idf_build_set_property(PYTHON "${python}") + set(PYTHON "${python}" PARENT_SCOPE) + + if(PYTHON_DEPS_CHECKED) + idf_dbg("Python dependencies have already been verified.") + return() + endif() + + idf_build_get_property(idf_path IDF_PATH) + + idf_msg("Checking Python dependencies...") + execute_process( + COMMAND "${python}" "${idf_path}/tools/idf_tools.py" "check-python-dependencies" + RESULT_VARIABLE result + ) + if(result EQUAL 1) + # The function check_python_dependencies returns an error code of 1 if + # it fails. + idf_die("Some Python dependencies must be installed. Check above message for details.") + elseif(NOT result EQUAL 0) + # This means that check_python_dependencies.py failed to run entirely, + # and the result should be an error message. + idf_die("Failed to run Python dependency check. Python: ${python}, Error: ${result}") + endif() +endfunction() + #[[ __init_components() @@ -62,7 +106,8 @@ endfunction() :COMPONENT_DIRS: If set, component directories are searched exclusively in the paths provided in ``COMPONENT_DIRS``. - :EXTRA_COMPONENT_DIRS: Includes extra paths to search if `COMPONENT_DIRS` is not specified. + :EXTRA_COMPONENT_DIRS: Includes extra paths to search if ``COMPONENT_DIRS`` + is not specified. :EXTRA_COMPONENT_EXCLUDE_DIRS: List of paths to exclude from searching the component directories. @@ -148,6 +193,9 @@ idf_build_set_property(PREFIX "idf") # well as a build property. __init_idf_path() +# Determine the Python interpreter and check package dependencies if necessary. +__init_python() + # Discover and initialize components. __init_components() @@ -156,7 +204,6 @@ __init_components() Many of the following things are already implemented in PoC !38337, but they need to be reviewed. -* Set and check python interpreter. * Set build target. * Set the toolchain before invoking project(). * Enable ccache if requested and available. diff --git a/tools/cmakev2/test/CMakeLists.txt b/tools/cmakev2/test/CMakeLists.txt index 77835c5ffc1..e040d05a66b 100644 --- a/tools/cmakev2/test/CMakeLists.txt +++ b/tools/cmakev2/test/CMakeLists.txt @@ -75,9 +75,17 @@ function(test_idf_version) endif() endfunction() +# Test that Python interpreter is set +function(test_python) + if(NOT DEFINED PYTHON OR "${PYTHON}" STREQUAL "") + idf_die("PYTHON variable not defined or empty") + endif() +endfunction() + # Run tests test_dump_properties() test_component_priority() test_idf_version() +test_python() message("ALL TESTS PASSED") From 71d21b45e1eb3dcdb889ffbc50c461be22c5bc3f Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Fri, 11 Jul 2025 13:40:58 +0200 Subject: [PATCH 008/196] feat(cmakev2/idf): initialize SDKCONFIG and SDKCONFIG_DEFAULTS build properties Based on the environmental variables, CMake cache variables, or default values, set the DKCONFIG and SDKCONFIG_DEFAULTS build properties. Signed-off-by: Frantisek Hrbata --- tools/cmakev2/idf.cmake | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tools/cmakev2/idf.cmake b/tools/cmakev2/idf.cmake index d38aad17285..e31b535e590 100644 --- a/tools/cmakev2/idf.cmake +++ b/tools/cmakev2/idf.cmake @@ -92,6 +92,41 @@ function(__init_python) endif() endfunction() +#[[ + __init_sdkconfig_files() + + Set the SDKCONFIG and SDKCONFIG_DEFAULTS build properties using environment + variables, CMake cache variables, or default values. +#]] +function(__init_sdkconfig_files) + if(EXISTS "${CMAKE_SOURCE_DIR}/sdkconfig.defaults") + set(sdkconfig_defaults "${CMAKE_SOURCE_DIR}/sdkconfig.defaults") + else() + set(sdkconfig_defaults "") + endif() + + __get_default_value(VARIABLE SDKCONFIG + DEFAULT "${CMAKE_SOURCE_DIR}/sdkconfig" + OUTPUT sdkconfig) + __get_default_value(VARIABLE SDKCONFIG_DEFAULTS + DEFAULT "${sdkconfig_defaults}" + OUTPUT sdkconfig_defaults) + + __get_absolute_paths(PATHS "${sdkconfig}" OUTPUT sdkconfig) + __get_absolute_paths(PATHS "${sdkconfig_defaults}" OUTPUT sdkconfig_defaults) + + set(sdkconfig_defaults_checked "") + foreach(sdkconfig_default ${sdkconfig_defaults}) + if(NOT EXISTS "${sdkconfig_default}") + idf_die("SDKCONFIG_DEFAULTS '${sdkconfig_default}' does not exist.") + endif() + list(APPEND sdkconfig_defaults_checked ${sdkconfig_default}) + endforeach() + + idf_build_set_property(SDKCONFIG "${sdkconfig}") + idf_build_set_property(SDKCONFIG_DEFAULTS "${sdkconfig_defaults_checked}") +endfunction() + #[[ __init_components() @@ -196,6 +231,9 @@ __init_idf_path() # Determine the Python interpreter and check package dependencies if necessary. __init_python() +# Set SDKCONFIG and SDKCONFIG_DEFAULTS build properties. +__init_sdkconfig_files() + # Discover and initialize components. __init_components() From 4548558132dfe9a077b8815f7984551544308e96 Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Fri, 11 Jul 2025 14:42:45 +0200 Subject: [PATCH 009/196] feat(cmakev2/utilities): add __get_sdkconfig_option helper Search for the configuration option value in the specified sdkconfig file. Signed-off-by: Frantisek Hrbata --- tools/cmakev2/utilities.cmake | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tools/cmakev2/utilities.cmake b/tools/cmakev2/utilities.cmake index 5c300324d84..72205a7bf5d 100644 --- a/tools/cmakev2/utilities.cmake +++ b/tools/cmakev2/utilities.cmake @@ -210,3 +210,53 @@ function(__get_default_value) set(${ARG_OUTPUT} "${value}" PARENT_SCOPE) endfunction() + +#[[ + __get_config_option(OPTION