I have a small project with 2 static libraries and a single executable.
utils
: a static library.lib1
: a static library. Some of the functions inlib1
usesutils
code, but most don't.main
: an executable that uses functions fromlib1
, that don't use code fromutils
.
In a simple Visual Studio project, main
would only link lib1
. But since lib1 needs some of utils' target_include_directories
to compile, the cmake code for lib1
contains a target_link_libraries
call:
utils CMakeLists.txt:
target_include_directories(utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) # lib1 needs this
lib1: CMakeLists.txt:
target_link_libraries(lib1 PRIVATE utils) # some of lib1 code uses utils
main: CMakeLists.txt:
target_link_libraries(main PRIVATE lib1) # this causes utils to link as well
The problem is that in the generated project, main
contains 2 dependencies instead of 1. If I manually remove utils
from the list of dependencies, everything works just fine.
But right now I have to compile utils in order to compile main.
What is the right approach to have main
link only with lib1
? And more generally, how to avoid linking unnecessary static libraries with cmake?
CodePudding user response:
The main problem is that static libraries are nothing more than archives of object files. Linking with a static library is just like linking with the object files themselves.
This leads to two things:
- Static libraries are not linked themselves;
- And you need to link with all dependencies of the static libraries.
For your project, the main
program must link with both the static libraries.
CodePudding user response:
I found a solution to the problem. I haven't tested it on other compilers other than MSVC, but it works for the limited case I described.
Basically, instead of using target_link_libraries
inside lib1's CMakeLists.txt, I use the following function:
function(add_interface_includes target_library src_library)
get_target_property(library_interface_includes ${src_library} INTERFACE_INCLUDE_DIRECTORIES)
set_target_properties(${target_library} PROPERTIES INCLUDE_DIRECTORIES "${library_interface_includes}")
endfunction()
When I write add_interface_includes(lib1 utils)
I basically mimic the behavior of target_link_libraries
without creating linker dependencies. When main
links lib1
, it will only link utils
if I specify it explicitly.
CodePudding user response:
If some features of the utils
library can be used by getting only headers, then I would make the header-only target.
utils
CMakeLists.txt:
add_library(utils_hdr INTERFACE)
target_include_directories(utils_hdr INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
add_library(utils STATIC *.c)
target_link_libraries(utils PUBLIC utils_hdr)
lib1
CMakeLists.txt:
target_link_libraries(lib1 PRIVATE utils_hdr)
main
CMakeLists.txt:
target_link_libraries(main PRIVATE lib1)