Unexpected build of unused target


In my project, I have a library called “Library” and a library called “LibraryTest”. They both compile from the same sources. The former is exported, the latter is compiled with address sanitation and linked against the test suite.

To get faster builds, I want to link the test suite against “Library” in the “Devel” configuration, with the intention/expectation that “LibraryTest” would not have to be compiled. In Release mode, both are compiled and the test suite is run. I’m using the Ninja Multi-Config generator. My list file looks like this:

cmake_minimum_required(VERSION 3.27)
project(Example LANGUAGES CXX)
add_library(Library lib.cc)
add_library(LibraryTest lib.cc)
target_compile_options(LibraryTest PUBLIC -fsanitize=address)
target_link_libraries(LibraryTest PUBLIC -fsanitize=address)
add_executable(test test.cc)
target_link_libraries(test Library$<$<CONFIG:Release>:Test>)

The list file works as expected, and the correct library is linked depending on the configuration. However, both libraries are always compiled.

Looking at the generated Ninja file for “Debug”, the “Devel/test” target has an order-only dependency on the test library:

build Devel/test: CXX_EXECUTABLE_LINKER__test_Devel CMakeFiles/test.dir/Devel/test.cc.o | Devel/libLibrary.a || Devel/libLibrary.a Devel/libLibraryTest.a

Removing this order-only dependency stops building the test library.

Is this behavior expected? Any thoughts on how I could make my approach work?

AFAIK order-only dependencies are not built, it’s just defining the order, in case somewhere else a build was required.

Do you explicitly build the executable with e.g. ninja test, then it should not be built.
Or do you simply call ninja, which means all targets?
Libraries and executables are automatically added to all. To avoid this, you need to use the option EXCLUDE_FROM_ALL.

I am building the executable with “ninja test,” and both Library and LibraryTest get built… This is CMake 3.27.8 and Ninja 1.11.1.

They do tend to get built as the default order-only behavior includes the linker output.

My guess is that there’s some config-unaware processing that joins the per-config dependencies improperly. A new issue would be appreciated.

Cc: @kyle.edwards

Issue: https://gitlab.kitware.com/cmake/cmake/-/issues/25521

If other folks are running into this, a workaround is to only add sources to the LibraryTest target in the Release configuration. In the Devel configuration, the test library will still be built, but it will be empty and therefore very fast to build. Something like:

target_sources(LibraryTest PRIVATE $<$<CONFIG:Release>:${Library_SOURCES}>)