mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03: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:
@@ -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