Consider this CMake setup:
add_library( A STATIC modules/a/src/src1.cpp modules/a/src/src2.cpp )
target_include_directories( A PUBLIC modules/a/inc )
target_compile_definitions( A PUBLIC USING_A=1 )
add_library( B STATIC modules/b/src/src1.cpp modules/b/src/src2.cpp )
target_include_directories( B PUBLIC modules/b/inc )
target_compile_definitions( B PUBLIC USING_B_WRAPPER=1 )
target_link_libraries( B PUBLIC A )
add_library( C STATIC modules/c/src/src1.cpp )
target_include_directories( C PUBLIC modules/c/inc )
target_link_libraries( C PUBLIC B )
Let's assume that headers from modules/b/inc
include headers from modules/a/inc
, so any consumer of B
library must also add modules/a/inc
to its includes path and also add USING_A=1
preprocessor definition.
Let's use the Ninja
Generator (however the problem occurs with any generator, including Makefile
, Xcode
and Visual Studio
):
cmake -GNinja ../path/to/source
And let's build the C
library:
ninja libC.a
Everything builds correctly, however lots of time gets wasted into building libA.a
and libB.a
just to build the libC.a
.
If I change the C's
dependency on B
to INTERFACE
, then libA.a
and libB.a
are not built, but compilation of modules/c/src/src1.cpp
fails because include directories and compile definitions that should be inherited from B
and its dependencies are not inherited.
Is there a way to tell CMake that specific target should not compile-depend on specific target specified in target_link_libraries
list (link-dependency should remain)? I am aware that sometimes those dependencies are required (such as if, for example, A
depended on some custom command generating headers for it dependees), but this is not the case here. I am searching for a solution to specify for each static library target whether or not it should compile-depend on another static library target, while still keeping the link dependency and obtaining all correct compile flags from its dependencies.
Currently I know no way for issue target_link_libraries
without complete target-level dependencies. And not sure that things will change in a near future. CMake developer's post from the bugreport:
Using target_link_libraries has always been enough to get an ordering dependency and many projects depend on this. We cannot change that for any target type.
What can be done without changing semantics is for the Ninja generator to detect when the transitive closure of a dependency doesn't have any custom commands and in that case drop ordering dependencies on it from the compilation rules and custom commands. Only the full dependency of the link step on the dependency's library file is needed. Work on this would be better discussed on the developer mailing list.
That answer on related question suggests to refuse from target_link_libraries
between STATIC libraries, which removes some unneded dependencies. I have adapted that scenario for your purpose:
Express "compile-time dependencies" with INTERFACE libraries. Real libraries will be used only for link an executable or a SHARED library.
# Compile-time dependency.
add_library( A_compile INTERFACE )
target_include_directories( A_compile PUBLIC modules/a/inc )
target_compile_definitions( A_compile PUBLIC USING_A=1 )
# Real library.
add_library( A STATIC modules/a/src/src1.cpp modules/a/src/src2.cpp )
target_link_libraries(A A_compile)
# Compile-time dependency.
add_library( B_compile INTERFACE )
target_include_directories( B_compile PUBLIC modules/b/inc )
target_compile_definitions( B_compile PUBLIC USING_B_WRAPPER=1 )
target_link_libraries( B_compile PUBLIC A_compile )
# Real library
add_library( B STATIC modules/b/src/src1.cpp modules/b/src/src2.cpp )
target_link_libraries(B B_compile)
# Final STATIC library.
add_library( C STATIC modules/c/src/src1.cpp )
target_include_directories( C PUBLIC modules/c/inc )
target_link_libraries( C PUBLIC B_compile )
# ...
# Creation executable or non-STATIC library, linked with C
add_executable(my_exe ...)
# Need to manually list libraries, "compile-time linked" to C.
target_link_libraries(my_exe C B A)
Becase for executable or non-STATIC library target_link_libraries
uses real STATIC libraries, these STATIC libraries will be built before even object files for such executable/SHARED library.
We've been using the following function to link static libraries against one another without causing build order dependencies between them for several years now. While not perfect, we've been quite happy with it overall.
function(target_link_static_libraries target)
if(BUILD_STATIC_LIBS_IN_PARALLEL)
target_link_libraries(${target} INTERFACE ${ARGN})
foreach(lib ${ARGN})
target_include_directories(${target} PUBLIC $<TARGET_PROPERTY:${lib},INTERFACE_INCLUDE_DIRECTORIES>)
target_include_directories(${target} SYSTEM PUBLIC $<TARGET_PROPERTY:${lib},INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>)
target_compile_definitions(${target} PUBLIC $<TARGET_PROPERTY:${lib},INTERFACE_COMPILE_DEFINITIONS>)
target_compile_options(${target} PUBLIC $<TARGET_PROPERTY:${lib},INTERFACE_COMPILE_OPTIONS>)
endforeach()
else()
target_link_libraries(${target} PUBLIC ${ARGN})
endif()
endfunction()
Known drawbacks:
Improvements or fixes for any of the above would be very much appreciated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With