我也找不到理想的方法来做到这一点。但这是我现在使用的解决方法。这是额外的工作,而不是 DRY,但我认为它实现了正确的目标。
想象一下 lib B 依赖于第三方 lib A。 A 要么定义了一个查找模块,要么我们可以为它实现一个自定义查找模块。两者都是可行的。假设我们已经编写了FindA.cmake 并存储在${CMAKE_SOURCE_DIR}/cmake 中。另外,假设当你运行cmake生成B的构建系统时,你提供了A_ROOT来帮助cmake定位A。
那么在B的顶层CMakeLists.txt我们需要:
# Use FindA.cmake, defined in the cmake/ directory.
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
find_package(A REQUIRED)
# ... (define the B target here)...
# Copy the config file to the build dir
configure_file(cmake/BConfig.cmake cmake/BConfig.cmake @ONLY)
# Copy A's find module (skip this step if FindA is not custom).
configure_file(cmake/FindA.cmake cmake/FindA.cmake COPYONLY)
# Create the target export.
export(EXPORT BTargets
FILE ${CMAKE_BINARY_DIR}/cmake/BTargets.cmake
NAMESPACE B::
)
# Register B so cmake can find B's config file.
export(PACKAGE B)
现在在cmake/BConfig.cmake:
# Get the exported find module onto the module path.
# This step is unnecessary if the find module is part of cmake.
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
# Store the root so that find_package will work.
# You may have to store other variables here, too, but
# the principle is just more of the same.
set(A_ROOT @A_ROOT@) # **Could optionally cache this.
find_package(A MODULE REQUIRED)
# The usual one-liner for a config file.
include("${CMAKE_CURRENT_LIST_DIR}/BTargets.cmake")
为了解决问题,让我们看第二个示例,Rocket 这次使用Boost,它已经定义了一个查找模块。
CMakeLists.txt:
option(Boost_USE_MULTITHREADED ON)
option(Boost_USE_STATIC_LIBS OFF)
find_package(Boost REQUIRED COMPONENTS filesystem program_options)
add_library(Rocket rocket.cpp)
target_link_libraries(Rocket
PUBLIC Boost::filesystem
PRIVATE Boost::program_options
)
configure_file(cmake/BConfig.cmake cmake/BConfig.cmake @ONLY)
export(EXPORT RocketTargets
FILE ${CMAKE_BINARY_DIR}/RocketTargets.cmake
NAMESPACE Rocket::
)
export(PACKAGE Rocket)
那么cmake/RocketConfig.cmake 会:
set(BOOST_ROOT @BOOST_ROOT@ CACHE PATH "In case boost was relocated.")
set(Boost_USE_MULTITHREADED @Boost_USE_MULTITHREADED@)
set(Boost_USE_STATIC LIBS @Boost_USE_STATIC_LIBS@)
find_package(Boost REQUIRED COMPONENTS filesystem program_options)
include("${CMAKE_CURRENT_LIST_DIR}/RocketTargets.cmake")
是的。好像打字太多了。似乎 cmake 应该能够从 CMakeLists.txt 中的 export 语句生成所有这些代码。但这个策略似乎达到了目的。
另外,我还没有测试过,但我怀疑,如果你的传递依赖使用CONFIG 而不是MODULE 来表示find_package,那么添加到CMAKE_PREFIX_PATH 应该很容易你的配置文件也是如此。
编辑(2021):Tsyvarev 指出了find_dependency() 宏,它可以在一定程度上简化上述方法。考虑使用它而不是 find_package()。