Home > Software design >  How to avoid linking unnecessary static libraries with CMake?
How to avoid linking unnecessary static libraries with CMake?

Time:11-02

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 in lib1 uses utils code, but most don't.
  • main : an executable that uses functions from lib1, that don't use code from utils.

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:

  1. Static libraries are not linked themselves;
  2. 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)
  • Related