mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
fix(cmakev2/compat): change idf_component_optional_requires behavior
The idf_component_optional_requires compatibility function in cmakev2
currently attempts to mimic the behavior of cmakev1 by adding a
dependency only if a component is already included in the build. In
cmakev1, this is handled by checking the BUILD_COMPONENTS list, which is
created during an early evaluation phase. Because cmakev2 removed this
early phase to allow for configuration-based component dependencies, the
build system does not inherently know which components will be part of
the build beforehand. To compensate, the current compatibility function
relies on the TARGET_EXISTS generator expression to determine if a
component should be linked.
This approach poses a problem because generator expressions are not
evaluated until the generation phase, but cmakev2 processes linker
scripts during the configuration phase by recursively scanning targets
linked to a library. Because the scanner does not recognize and cannot
evaluate generator expressions, any component linked optionally through
generator expression is skipped. If that component carries a linker
script, the script is never added to the library interface, resulting in
build failures. Since cmakev2 aims to support multiple libraries, a
component might also exist globally, causing TARGET_EXISTS to evaluate
to true, yet still be missed during a specific library's recursive scan,
leading to the same omission of necessary linker scripts.
To resolve this, the implementation of idf_component_optional_requires
is changed to check if a component is known to the build system in
COMPONENTS_DISCOVERED build property. If so, it is explicitly included
and linked directly via its interface target. While this may pull more
components into a build than the previous generator expression method,
potentially increasing build times, it ensures that dependency trees are
fully visible to the library target scanner. This serves as a practical
middle ground that maintains compatibility with existing components and
ensures build stability. This approach allows for the gradual fixing of
idf_component_optional_requires usage in components for cmakev2, with
the eventual goal of removing its usage in cmakev2 entirely.
This change also fixes cases where idf_component_optional_requires is
used to conditionally add requirements based on configuration options.
In cmakev2, the presence of a configuration option does not guarantee[1]
that a component has been included via add_subdirectory, unlike the
behavior in cmakev1. With this change even constructions like
```cmake
if(CONFIG_VFS_SUPPORT_IO)
idf_component_optional_requires(PRIVATE vfs)
endif()
```
will now work in cmakev2, as the updated idf_component_optional_requires
explicitly includes the required component if it is available to the
build system.
Closes https://github.com/espressif/esp-idf/issues/18133
[1] https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system-v2.html#id9
Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
This commit is contained in:
@@ -432,6 +432,13 @@ Strict Component Precedence
|
||||
|
||||
The v2 strictly adheres to the component precedence for components with the same name, as described in :ref:`cmake-components-same-name`. While v1 allows components discovered in directories specified with the ``EXTRA_COMPONENT_DIRS`` variable to be overridden by `Local Directory Dependencies`_ specified in the ``idf_component.yml`` manifest file, this is no longer possible in v2.
|
||||
|
||||
The Behavior of ``idf_component_optional_requires`` has Changed
|
||||
---------------------------------------------------------------
|
||||
|
||||
In v1, the ``idf_component_optional_requires`` function adds a dependency on a specified component only if that component is already included in the build (for instance, if it is already required by another component). To achieve this, v1 examines the ``BUILD_COMPONENTS`` build property, which is generated during the early evaluation phase and lists all components involved in the build.
|
||||
|
||||
In v2, the ``BUILD_COMPONENTS`` build property is no longer available because the early evaluation phase has been removed. Consequently, the function now adds the dependency if the component is recognized by the build system. This behavior may pull unnecessary components into the build, leading to longer build times. Using ``idf_component_optional_requires`` in v2 should be avoided, instead, optional dependencies should be added using conditional logic based on the project configuration.
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
|
||||
@@ -211,21 +211,44 @@ endfunction()
|
||||
The component name that should be added as a dependency to the
|
||||
evaluated component. It may be provided multiple times.
|
||||
|
||||
Add a dependency on a specific component only if it is included in the
|
||||
build.
|
||||
Add a dependency on a specific component only if the component is
|
||||
recognized by the build system. The component is included if needed and
|
||||
added as a requirement for the currently evaluated component using
|
||||
target_link_libraries. This function should be avoided in cmakev2, where
|
||||
dependencies should be added based on configuration options. This is purely
|
||||
for backward compatibility with cmakev1.
|
||||
#]]
|
||||
function(idf_component_optional_requires req_type)
|
||||
function(idf_component_optional_requires type)
|
||||
set(optional_reqs ${ARGN})
|
||||
foreach(req ${optional_reqs})
|
||||
__get_component_interface(COMPONENT "${req}" OUTPUT req_interface)
|
||||
if("${req_interface}" STREQUAL "NOTFOUND")
|
||||
# The component is not recognized by the build system.
|
||||
continue()
|
||||
endif()
|
||||
idf_component_get_property(req_alias "${req}" COMPONENT_ALIAS)
|
||||
# The component alias is created only after the component is included,
|
||||
# meaning the add_subdirectory command for it has been called. This can
|
||||
# be used to detect if a component has already been added to the build.
|
||||
target_link_libraries(${COMPONENT_TARGET} ${req_type} "$<$<TARGET_EXISTS:${req_alias}>:${req_interface}>")
|
||||
idf_component_include("${req}")
|
||||
|
||||
# Map the requested type into PRIV_REQUIRES and REQUIRES, allowing the
|
||||
# requirement to be added to the appropriate component dependency
|
||||
# property.
|
||||
if("${type}" STREQUAL "PRIVATE")
|
||||
set(req_type PRIV_REQUIRES)
|
||||
elseif("${type}" STREQUAL "PUBLIC")
|
||||
set(req_type REQUIRES)
|
||||
else()
|
||||
set(req_type "")
|
||||
endif()
|
||||
|
||||
if(req_type)
|
||||
idf_component_get_property(req_name "${req}" COMPONENT_NAME)
|
||||
idf_component_get_property(target_reqs ${COMPONENT_TARGET} ${req_type})
|
||||
if(NOT "${req_name}" IN_LIST target_reqs)
|
||||
idf_component_set_property(${COMPONENT_TARGET} ${req_type} "${req_name}" APPEND)
|
||||
target_link_libraries(${COMPONENT_TARGET} ${type} ${req_interface})
|
||||
endif()
|
||||
else()
|
||||
target_link_libraries(${COMPONENT_TARGET} ${type} ${req_interface})
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user