具有自定义构建类型的 CMake 接口依赖项
CMake Interface dependency with custom build type
所以,我在 CMake 中发现了非常奇怪的行为,它创建了对 target_link_library 的依赖。
一句话很难说清楚,所以这里列出了一些要求(希望最后能说得通)
- 您的项目必须具有通过 CMAKE_CONFIGURATION_TYPES 定义的自定义构建类型(在此示例中为 'Tools')
- 您必须至少有 3 个目标:
- 可执行文件(或简单的主要目标)(本例中为 test-exe)
- link 到主要目标的接口库(这可能是 INTERFACE 库以外的东西,但下一个目标必须 link 仅通过接口 属性 连接到它)(本例中为 test-env)
- 静态库,links 到具有特定生成器表达式的接口库,这取决于自定义构建类型(类似 'target_link_libraries(test-env INTERFACE $<$CONFIG:Tools:test-lib>)')(本例中为 test-lib)
这里是 CMakeLists.txt 文件的代码,让它更清晰一些:
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)
(文件test-exe.cpp和test-lib.cpp很简单,test-lib.cpp有一个简单的函数,test-exe.cpp声明了这个函数然后从 main 调用它。)
当您尝试使用 visual studio 2019/2017 生成器构建此项目时(当然使用“工具”作为配置类型:cmake --build . --config Tools
),您将遇到下一个错误:
LINK : fatal error LNK1104: cannot open file 'Tools\test-lib.lib' [<none_important_path_to_the_project>\test-exe.vcxproj]
而且,重要的是,您不会在正在构建的终端目标 test-lib 中看到。
所以,发生的事情是目标 test-exe 知道它必须针对 test-lib linked,但是构建系统不知道目标 test-exe 依赖于目标 test-lib。
而现在最奇怪的事情!如果我们将 link 这个库像这样:target_link_libraries(test-env INTERFACE $<$<CONFIG:Debug>:test-lib>)
(所以构建类型必须是 Debug),并且仍然使用 Tools 作为构建类型构建项目,您将在终端中看到目标 test-lib 是现在建设! (是的,我们有 link 错误,因为 test-exe 找不到在 test-lib 中定义的函数,但这至少是预期的)
所以,实际发生了什么,目标测试 exe 的 link 标志正确地依赖于生成器表达式但是,实际构建依赖性,以某种方式将此生成器表达式中的任何自定义构建类型转换为调试。
同样,这只发生在我上面指出的要求中,所以这不仅是生成器表达式的错误,它也与接口依赖性有关..
如果我们不能打破任何要求,一个可能的解决方案是将 test-lib 目标的直接依赖添加到 test-env(例如:add_dependecies(test-env test-lib)
),但这并不完美, 因为即使我们只使用 test-lib 然后 where is Tools 作为构建类型,我们仍然每次都会构建这个库,这可能是不希望的行为。
我并不是真的在这里寻求解决方案(但如果您有解决方案,请分享),我是在寻求解释为什么会发生这种情况?这是一个错误还是一个非常奇怪的功能?
编辑 我刚刚发现的小更新:
它甚至不能是自定义构建类型。即使使用 Release 构建类型也会遇到相同的错误,因此最终代码看起来像这样简单:
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)
并使用下一个命令构建:cmake --build . --config Release
我觉得 Visual Studio 生成器有问题。我刚刚在 Linux 和 Windows 上测试了 Ninja Multi-Config 生成器,cmake --build <build-dir> --config Release
工作正常。
所以,我在 CMake 中发现了非常奇怪的行为,它创建了对 target_link_library 的依赖。 一句话很难说清楚,所以这里列出了一些要求(希望最后能说得通)
- 您的项目必须具有通过 CMAKE_CONFIGURATION_TYPES 定义的自定义构建类型(在此示例中为 'Tools')
- 您必须至少有 3 个目标:
- 可执行文件(或简单的主要目标)(本例中为 test-exe)
- link 到主要目标的接口库(这可能是 INTERFACE 库以外的东西,但下一个目标必须 link 仅通过接口 属性 连接到它)(本例中为 test-env)
- 静态库,links 到具有特定生成器表达式的接口库,这取决于自定义构建类型(类似 'target_link_libraries(test-env INTERFACE $<$CONFIG:Tools:test-lib>)')(本例中为 test-lib)
这里是 CMakeLists.txt 文件的代码,让它更清晰一些:
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)
(文件test-exe.cpp和test-lib.cpp很简单,test-lib.cpp有一个简单的函数,test-exe.cpp声明了这个函数然后从 main 调用它。)
当您尝试使用 visual studio 2019/2017 生成器构建此项目时(当然使用“工具”作为配置类型:cmake --build . --config Tools
),您将遇到下一个错误:
LINK : fatal error LNK1104: cannot open file 'Tools\test-lib.lib' [<none_important_path_to_the_project>\test-exe.vcxproj]
而且,重要的是,您不会在正在构建的终端目标 test-lib 中看到。
所以,发生的事情是目标 test-exe 知道它必须针对 test-lib linked,但是构建系统不知道目标 test-exe 依赖于目标 test-lib。
而现在最奇怪的事情!如果我们将 link 这个库像这样:target_link_libraries(test-env INTERFACE $<$<CONFIG:Debug>:test-lib>)
(所以构建类型必须是 Debug),并且仍然使用 Tools 作为构建类型构建项目,您将在终端中看到目标 test-lib 是现在建设! (是的,我们有 link 错误,因为 test-exe 找不到在 test-lib 中定义的函数,但这至少是预期的)
所以,实际发生了什么,目标测试 exe 的 link 标志正确地依赖于生成器表达式但是,实际构建依赖性,以某种方式将此生成器表达式中的任何自定义构建类型转换为调试。
同样,这只发生在我上面指出的要求中,所以这不仅是生成器表达式的错误,它也与接口依赖性有关..
如果我们不能打破任何要求,一个可能的解决方案是将 test-lib 目标的直接依赖添加到 test-env(例如:add_dependecies(test-env test-lib)
),但这并不完美, 因为即使我们只使用 test-lib 然后 where is Tools 作为构建类型,我们仍然每次都会构建这个库,这可能是不希望的行为。
我并不是真的在这里寻求解决方案(但如果您有解决方案,请分享),我是在寻求解释为什么会发生这种情况?这是一个错误还是一个非常奇怪的功能?
编辑 我刚刚发现的小更新: 它甚至不能是自定义构建类型。即使使用 Release 构建类型也会遇到相同的错误,因此最终代码看起来像这样简单:
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)
并使用下一个命令构建:cmake --build . --config Release
我觉得 Visual Studio 生成器有问题。我刚刚在 Linux 和 Windows 上测试了 Ninja Multi-Config 生成器,cmake --build <build-dir> --config Release
工作正常。