I don’t think you will have problem with duplicate symbols if you have just a force_load
and a regular load of the same static library if and only if the force_load
occurs before the regular load. This is why I strongly recommend using the link options to specify the force_load
because link options are specified before the libraries on the final link command line.
Now I reworked your example given on GitHub
and I get all the force_load
before regular libraries:
cmake_minimum_required(VERSION 3.7)
project(cpp C CXX)
# 4 imported libraries. Note that there are dependencies between them.
add_library(fake_1 STATIC IMPORTED GLOBAL)
set_target_properties(fake_1
PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/imported_libs/libfake_1.a
)
add_library(fake_2 STATIC IMPORTED GLOBAL)
set_target_properties(fake_2
PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/imported_libs/libfake_2.a)
target_link_libraries (fake_2 INTERFACE fake_1)
add_library(fake_3 STATIC IMPORTED GLOBAL)
set_target_properties(fake_3
PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/imported_libs/libfake_3.a)
target_link_libraries (fake_3 INTERFACE fake_2)
add_library(fake_4 STATIC IMPORTED GLOBAL)
set_target_properties(fake_4
PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/imported_libs/libfake_4.a)
target_link_libraries(fake_4 INTERFACE fake_3)
add_subdirectory(lib_a)
add_subdirectory(lib_b)
add_subdirectory(exe)
lib_a subdir:
add_library(lib_a STATIC lib_a.cpp lib_a.h)
target_include_directories(lib_a PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_options(lib_a PUBLIC "LINKER:-force_load,$<TARGET_FILE:fake_4>"
"LINKER:-force_load,$<TARGET_FILE:fake_3>")
lib_b subdir:
add_library(lib_b STATIC lib_b.cpp lib_b.h)
target_include_directories(lib_b PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(lib_b PUBLIC lib_a)
target_link_options(lib_b PRIVATE "LINKER:-force_load,$<TARGET_FILE:fake_2>")
exe subdir:
add_executable(exe ./main.cpp)
target_link_libraries(exe PRIVATE lib_b)
target_link_options(exe PUBLIC "LINKER:-force_load,$<TARGET_FILE:fake_1>"
"LINKER:-force_load,$<TARGET_FILE:fake_2>")
Final link command for exe:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names -Xlinker -force_load -Xlinker /cmake-example-master/imported_libs/libfake_1.a -Xlinker -force_load -Xlinker /cmake-example-master/imported_libs/libfake_2.a -Xlinker -force_load -Xlinker /cmake-example-master/imported_libs/libfake_4.a -Xlinker -force_load -Xlinker /cmake-example-master/imported_libs/libfake_3.a CMakeFiles/exe.dir/main.cpp.o -o exe ../lib_b/liblib_b.a ../lib_a/liblib_a.a
But what I recommend for maximum flexibility is to introduce two set of imported targets:
- regular libraries (this is what you have already done)
- forced load libraries
So, depending on of the requirements, you can select one or the other… Using same approach (i.e. command target_link_libraries
) and you have a better control over the order of libraries.
add_library (force_fake_1 INTERFACE)
target_link_options(force_fake_1 INTERFACE "LINKER:-force_load,$<TARGET_FILE:fake_1>")
add_library(force_fake_2 INTERFACE)
target_link_options(force_fake_2 INTERFACE "LINKER:-force_load,$<TARGET_FILE:fake_2>")
target_link_libraries(force_fake_2 INTERFACE force_fake_1)
add_library(force_fake_3 INTERFACE)
target_link_options(force_fake_3 INTERFACE "LINKER:-force_load,$<TARGET_FILE:fake_3>")
target_link_libraries(force_fake_3 INTERFACE force_fake_2)
add_library(force_fake_4 INTERFACE)
target_link_options(force_fake_4 INTERFACE "LINKER:-force_load,$<TARGET_FILE:fake_4>")
target_link_libraries(force_fake_4 INTERFACE force_fake_3)
So, for example, to build the lib_a
we have:
add_library(lib_a STATIC lib_a.cpp lib_a.h)
target_include_directories(lib_a PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(lib_a PUBLIC force_fake_4)