Files
esp-idf/tools/cmakev2/build.cmake
Frantisek Hrbata 08a416ae1a feat(cmakev2/project): move all project-related items into project.cmake
The current approach involves transparently calling __init_build within
idf_build_library and performing all post-project() initialization
there. The issue with this method is that project_include.cmake files
also need to be included during post-project() initialization, but they
should be included in the global scope. This cannot be achieved within
the __init_build function unless it is converted into a macro. Although
using a macro is a potential solution, it risks causing global scope
pollution. Another complication is the location where project() can be
invoked; it must be explicitly[1] stated in the project's
CMakeLists.txt.  This requirement conflicts with our intention to wrap
it within the idf_project_default helper. Given these challenges, it
makes sense to introduce an explicit idf_project_init macro, where all
post-project() initialization occurs, including the inclusion of
project_include.cmake files. While we can still encapsulate this within
idf_project_default, for the plumbing commands (idf_build_library,
idf_build_executable, ...), it will need to be explicitly called after
the project() invocation.

Usage for default project:

cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmakev2/idf.cmake)
project()
idf_project_default()

Usage for plumbing commands:

cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmakev2/idf.cmake)
project()
idf_project_init()
idf_build_library()
...

[1] https://cmake.org/cmake/help/latest/command/project.html#usage

Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
2025-11-04 07:48:12 +01:00

275 lines
8.9 KiB
CMake

# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
include_guard(GLOBAL)
include(utilities)
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
#[[api
.. cmakev2:function:: idf_build_set_property
.. code-block:: cmake
idf_build_set_property(<property> <value> [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})
set(append)
if(ARG_APPEND)
set(append APPEND)
endif()
__set_property(TARGET idf_build_properties
PROPERTY "${property}"
PROPERTIES BUILD_PROPERTIES
VALUE "${value}"
${append})
endfunction()
#[[api
.. cmakev2:function:: idf_build_get_property
.. code-block:: cmake
idf_build_get_property(<var> <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()
set(genexpr)
if(ARG_GENERATOR_EXPRESSION)
set(genexpr GENERATOR_EXPRESSION)
endif()
__get_property(TARGET idf_build_properties
PROPERTY "${property}"
OUTPUT value
${genexpr})
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()
#[[
__get_library_interface_or_die(LIBRARY <library>
OUTPUT <variable>)
:LIBRARY[in]: Library interface or alias.
:OUTPUT[out]: Output variable to store the library interface.
Verify that "LIBRARY" is a known interface created by ``idf_build_library``
or its alias. If it is, return the library interface; otherwise, terminate
the build process.
#]]
function(__get_library_interface_or_die)
set(options)
set(one_value LIBRARY OUTPUT)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
if(NOT DEFINED ARG_LIBRARY)
idf_die("LIBRARY option is required")
endif()
if(NOT DEFINED ARG_OUTPUT)
idf_die("OUTPUT option is required")
endif()
__get_real_target(TARGET ${ARG_LIBRARY} OUTPUT library_interface)
idf_build_get_property(library_interfaces LIBRARY_INTERFACES)
if(NOT "${library_interface}" IN_LIST library_interfaces)
idf_die("Library interface '${ARG_LIBRARY}' does not exist")
endif()
set(${ARG_OUTPUT} ${library_interface} PARENT_SCOPE)
endfunction()
#[[api
.. cmakev2:function:: idf_library_set_property
.. code-block:: cmake
idf_library_set_property(<library> <property> <value> [APPEND])
:library[in]: Library interface target or alias.
: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 library property. The property is also
added to the internal list of library properties if it isn't already
there.
#]]
function(idf_library_set_property library property value)
set(options APPEND)
set(one_value)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
set(append)
if(ARG_APPEND)
set(append APPEND)
endif()
__get_library_interface_or_die(LIBRARY "${library}" OUTPUT library_interface)
__set_property(TARGET "${library_interface}"
PROPERTY "${property}"
PROPERTIES LIBRARY_PROPERTIES
VALUE "${value}"
${append})
endfunction()
#[[api
.. cmakev2:function:: idf_library_get_property
.. code-block:: cmake
idf_library_get_property(<variable> <library> <property> [GENERATOR_EXPRESSION])
:variable[out]: Variable to store the value in.
:library[in]: Library interface target or alias.
: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 library property.
#]]
function(idf_library_get_property variable library property)
set(options GENERATOR_EXPRESSION)
set(one_value)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
set(genexpr)
if(ARG_GENERATOR_EXPRESSION)
set(genexpr GENERATOR_EXPRESSION)
endif()
__get_library_interface_or_die(LIBRARY "${library}" OUTPUT library_interface)
__get_property(TARGET "${library_interface}"
PROPERTY "${property}"
OUTPUT value
${genexpr})
set(${variable} ${value} PARENT_SCOPE)
endfunction()
#[[
__dump_library_properties(<libraries>)
:libraries: List of library interfaces whose properties should be displayed.
Dump all properties for the libraries listed in ``<libraries>``.
#]]
function(__dump_library_properties libraries)
foreach(library IN LISTS libraries)
idf_library_get_property(properties "${library}" LIBRARY_PROPERTIES)
idf_msg("library '${library}' properties: ${properties}")
foreach(property IN LISTS properties)
idf_library_get_property(value "${library}" "${property}")
idf_msg(" ${property}: ${value}")
endforeach()
endforeach()
endfunction()
#[[api
.. cmakev2:function:: idf_build_library
.. code-block:: cmake
idf_build_library(INTERFACE <interface>
[COMPONENTS <component>...])
:INTERFACE[in,out]: Name of the library interface to be created.
:COMPONENTS[in,opt]: List of component names to add to the library.
Create a new library interface target with the name specified in the
``INTERFACE`` option and link component targets to it based on the component
names provided in the ``COMPONENTS`` option. If ``COMPONENTS`` option is not
set, link component targets of all discovered components.
#]]
function(idf_build_library)
set(options)
set(one_value INTERFACE)
set(multi_value COMPONENTS)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
idf_build_get_property(project_initialized __PROJECT_INITIALIZED)
if(NOT project_initialized)
idf_die("The IDF project is not initialized. The 'idf_project_init()' must be called first.")
endif()
if(NOT DEFINED ARG_INTERFACE)
idf_die("INTERFACE option is required")
endif()
if(NOT DEFINED ARG_COMPONENTS)
# The library should include all discovered components.
idf_build_get_property(component_names COMPONENTS_DISCOVERED)
set(ARG_COMPONENTS "${component_names}")
endif()
# Create library interface.
add_library("${ARG_INTERFACE}" INTERFACE)
idf_build_set_property(LIBRARY_INTERFACES "${ARG_INTERFACE}" APPEND)
idf_library_set_property("${ARG_INTERFACE}" LIBRARY_COMPONENTS "${ARG_COMPONENTS}")
# Add global include directories, such as the directory containing
# sdkconfig, to the library's include directories.
idf_build_get_property(include_directories INCLUDE_DIRECTORIES GENERATOR_EXPRESSION)
target_include_directories("${ARG_INTERFACE}" INTERFACE "${include_directories}")
# Add link options.
idf_build_get_property(link_options LINK_OPTIONS)
target_link_options(${ARG_INTERFACE} INTERFACE "${link_options}")
# Include the requested components and link their interface targets to the
# library.
foreach(component_name IN LISTS ARG_COMPONENTS)
idf_component_include("${component_name}")
idf_component_get_property(component_interface "${component_name}" COMPONENT_INTERFACE)
target_link_libraries("${ARG_INTERFACE}" INTERFACE "${component_interface}")
endforeach()
endfunction()