简体   繁体   中英

CMake Interface dependency with custom build type

So, I've found really strange behaviour in CMake creating dependency on target_link_library.. It's hard to explain in one sentence, so here is a list of requirements (I hope this all will make sence in the end)

  • your project must have custom build type defined through CMAKE_CONFIGURATION_TYPES ('Tools' in this example)
  • you must have at least 3 targets:
    • executable (or simply main target) (test-exe in this example)
    • interface library which link to main target (this could be something other than INTERFACE library, but the next target must be linked to it via interface property only) (test-env in this example)
    • static library which links to the interface library with specific generator expression, which is depends on custom build type (something like that 'target_link_libraries(test-env INTERFACE $<$CONFIG:Tools:test-lib>)') (test-lib in this example)

Here is the code of the CMakeLists.txt file to make it little bit clearer:


cmake_minimum_required(VERSION 3.19.0 FATAL_ERROR)
project(multiconfiguration-test LANGUAGES CXX)

set(CMAKE_CONFIGURATION_TYPES Debug Release Tools)

set(CMAKE_CXX_FLAGS_TOOLS               ${CMAKE_CXX_FLAGS_DEBUG})

set(CMAKE_EXE_LINKER_FLAGS_TOOLS        ${CMAKE_EXE_LINKER_FLAGS_DEBUG})
set(CMAKE_STATIC_LINKER_FLAGS_TOOLS     ${CMAKE_STATIC_LINKER_FLAGS_DEBUG})

add_library(test-env INTERFACE)
# EXCLUDE_FROM_ALL used to not build this target by default
add_library(test-lib STATIC EXCLUDE_FROM_ALL "test-lib.cpp")
target_link_libraries(test-env INTERFACE $<$<CONFIG:Tools>:test-lib>)

add_executable(test-exe "test-exe.cpp")
target_link_libraries(test-exe PRIVATE test-env)

(Files test-exe.cpp and test-lib.cpp are trivial, test-lib.cpp has a simple function, and test-exe.cpp declares this function and then calls it from main.)

When you would try to build this project with visual studio 2019/2017 generators (with "Tools" as configuration type of course: cmake --build. --config Tools ), you will have next error:

LINK : fatal error LNK1104: cannot open file 'Tools\test-lib.lib' [<none_important_path_to_the_project>\test-exe.vcxproj]

And, what is important, you will not see in the terminal target test-lib being build.

So, what happened is target test-exe knows it must be linked against test-lib, but the build system doesn't know that target test-exe is dependent on target test-lib.

And now the most strange thing: If we will link this library like that: target_link_libraries(test-env INTERFACE $<$<CONFIG:Debug>:test-lib>) (so build type must be Debug), and still build project with Tools as a build type, you will see in the terminal that target test-lib is now building, (yes we have link error because test-exe can't find the function which is defined in test-lib, but this is at least expected)

So, what actually happens, the link flag of the target test-exe is correctly depends on the generator expression BUT, the actual build dependency, somehow, transforms any custom build type in this generator expression to the Debug.

Again this only happens with requirements I pointed up above, so it's not only the fault of generator expression, it's also connected to the interface dependency as well..

If we can't break any of the requirements, one possible solution will be to add direct dependency of test-lib target to test-env (like that: add_dependecies(test-env test-lib) ), but this is not perfect, because even if we will use test-lib only then where is Tools as build type, we still will build this library each time, which can be undesired behavior.

I'm not really asking for solution here (but if you have one please share), I'm asking for the explanation why this even happening? Is this a bug or a really strange feature?

EDIT Small update I've found just now: It must not even be the custom build type. The same bug can be encountered even with Release build type, so the final code can look as simple as this:

cmake_minimum_required(VERSION 3.19.0 FATAL_ERROR)
project(multiconfiguration-test LANGUAGES CXX)

add_library(test-env INTERFACE)
add_library(test-lib STATIC EXCLUDE_FROM_ALL "test-lib.cpp")
target_link_libraries(test-env INTERFACE $<$<CONFIG:Release>:test-lib>)

add_executable(test-exe "test-exe.cpp")
target_link_libraries(test-exe PRIVATE test-env)

and be build with next command: cmake --build. --config Release cmake --build. --config Release

Looks like a bug with the Visual Studio generator to me. I've just tested the Ninja Multi-Config generator both on Linux and on Windows and there cmake --build <build-dir> --config Release works just fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM