diff --git a/components/ulp/cmake/CMakeLists_v2.txt b/components/ulp/cmake/CMakeLists_v2.txt index 81fbe209f83..a8e52e733f2 100644 --- a/components/ulp/cmake/CMakeLists_v2.txt +++ b/components/ulp/cmake/CMakeLists_v2.txt @@ -15,29 +15,46 @@ ulp_project_init() # safe and keeps the linked component set minimal. idf_build_set_property(IDF_COMPONENT_OPTIONAL_REQUIRES_MODE DEFERRED) +# CMake v1 linked the ulp_embed_binary() call-site sources before the ULP runtime. +# In CMake v2 the runtime's WHOLE_ARCHIVE is emitted as a link option, before +# executable objects, so attaching these sources directly would reverse that +# order. Keep them in a separate archive that can be placed before the runtime, +# preserving legacy code placement and strong-over-weak symbol resolution. +function(__ulp_embed_binary_add_legacy_sources executable) + set(legacy_sources_lib "${executable}_legacy_sources") + add_library(${legacy_sources_lib} STATIC ${ULP_S_SOURCES}) + target_link_libraries(${legacy_sources_lib} PRIVATE "library_${executable}") + if(ADD_PICOLIBC_SPECS) + target_compile_options(${legacy_sources_lib} PRIVATE $<$:-specs=picolibc.specs>) + target_compile_options(${legacy_sources_lib} PRIVATE $<$:-specs=picolibcpp.specs>) + endif() + + if(ULP_PARENT_SDKCONFIG_HEADER) + # Legacy ulp_embed_binary() sources are authored against the parent app's + # sdkconfig, including project-local Kconfig.projbuild symbols. + get_filename_component(parent_sdkconfig_dir "${ULP_PARENT_SDKCONFIG_HEADER}" DIRECTORY) + target_include_directories(${legacy_sources_lib} BEFORE PRIVATE "${parent_sdkconfig_dir}") + endif() + if(COMPONENT_INCLUDES) + target_include_directories(${legacy_sources_lib} PRIVATE ${COMPONENT_INCLUDES}) + endif() + # CMake v1 exposed COMPONENT_DIR only to the FSM assembly preprocessing, not to + # the compilation of ULP C sources. The v2 FSM toolchain preprocesses inside + # the compile rule, which takes its include flags from the target, so keep the + # directory on the target for FSM builds only. + if(BUILD_FSM AND COMPONENT_DIR) + target_include_directories(${legacy_sources_lib} PRIVATE ${COMPONENT_DIR}) + endif() + + __idf_build_link_whole_archive(${executable} PRIVATE ${legacy_sources_lib} BEFORE) +endfunction() + set(executable_args COMPONENTS ulp) if(NOT BUILD_FSM) list(APPEND executable_args MAPFILE_TARGET ${ULP_APP_NAME}_mapfile) endif() ulp_build_executable(${ULP_APP_NAME} ${executable_args}) - -target_sources(${ULP_APP_NAME} PRIVATE ${ULP_S_SOURCES}) -if(ULP_PARENT_SDKCONFIG_HEADER) - # Legacy ulp_embed_binary() sources are authored against the parent app's - # sdkconfig, including project-local Kconfig.projbuild symbols. - get_filename_component(parent_sdkconfig_dir "${ULP_PARENT_SDKCONFIG_HEADER}" DIRECTORY) - target_include_directories(${ULP_APP_NAME} BEFORE PRIVATE "${parent_sdkconfig_dir}") -endif() -if(COMPONENT_INCLUDES) - target_include_directories(${ULP_APP_NAME} PRIVATE ${COMPONENT_INCLUDES}) -endif() -# CMake v1 exposed COMPONENT_DIR only to the FSM assembly preprocessing, not to -# the compilation of ULP C sources. The v2 FSM toolchain preprocesses inside -# the compile rule, which takes its include flags from the target, so keep the -# directory on the target for FSM builds only. -if(BUILD_FSM AND COMPONENT_DIR) - target_include_directories(${ULP_APP_NAME} PRIVATE ${COMPONENT_DIR}) -endif() +__ulp_embed_binary_add_legacy_sources(${ULP_APP_NAME}) if(DEFINED ULP_VAR_PREFIX AND NOT "${ULP_VAR_PREFIX}" STREQUAL "") ulp_build_binary(${ULP_APP_NAME} PREFIX ${ULP_VAR_PREFIX}) diff --git a/tools/cmakev2/build.cmake b/tools/cmakev2/build.cmake index 49fa5548847..0f408b26114 100644 --- a/tools/cmakev2/build.cmake +++ b/tools/cmakev2/build.cmake @@ -298,6 +298,25 @@ function(__dump_library_properties libraries) endforeach() endfunction() +function(__idf_build_link_whole_archive target scope library) + idf_build_get_property(linker_type LINKER_TYPE) + if(linker_type STREQUAL "GNU") + set(link_option "SHELL:-Wl,--whole-archive $ -Wl,--no-whole-archive") + elseif(linker_type STREQUAL "Darwin") + set(link_option "SHELL:-Wl,-force_load $") + elseif(linker_type STREQUAL "ULP_FSM") + set(link_option "SHELL:--whole-archive $ --no-whole-archive") + else() + target_link_libraries(${target} ${scope} ${library}) + return() + endif() + + # ARGN may contain BEFORE to place the archive ahead of existing link options. + # Also link the target normally so CMake tracks its build and relink dependencies. + target_link_options(${target} ${ARGN} ${scope} "${link_option}") + target_link_libraries(${target} ${scope} ${library}) +endfunction() + #[[api .. cmakev2:function:: idf_build_library diff --git a/tools/cmakev2/component.cmake b/tools/cmakev2/component.cmake index e1569ed1fe3..0f83bd72699 100644 --- a/tools/cmakev2/component.cmake +++ b/tools/cmakev2/component.cmake @@ -1102,20 +1102,7 @@ function(idf_component_include name) elseif("${component_real_target_type}" STREQUAL "STATIC_LIBRARY") idf_component_get_property(whole_archive "${component_name}" WHOLE_ARCHIVE) if(whole_archive) - idf_build_get_property(linker_type LINKER_TYPE) - if(linker_type STREQUAL "GNU") - target_link_options("${component_interface}" INTERFACE - "SHELL:-Wl,--whole-archive $ -Wl,--no-whole-archive") - target_link_libraries("${component_interface}" INTERFACE "${component_real_target}") - elseif(linker_type STREQUAL "Darwin") - target_link_options("${component_interface}" INTERFACE - "SHELL:-Wl,-force_load $") - target_link_libraries("${component_interface}" INTERFACE "${component_real_target}") - elseif(linker_type STREQUAL "ULP_FSM") - target_link_options("${component_interface}" INTERFACE - "SHELL:--whole-archive $ --no-whole-archive") - target_link_libraries("${component_interface}" INTERFACE "${component_real_target}") - endif() + __idf_build_link_whole_archive("${component_interface}" INTERFACE "${component_real_target}") else() target_link_libraries("${component_interface}" INTERFACE "${component_real_target}") endif()