如何导出带有依赖项包含目录的CMake目标?
你的核心问题在于:接口库foobar_interface导出时没有让CMake自动处理其依赖项foobar_result的配置传递,导致下游项目foobar_implA调用find_package(foobar_interface)时,无法自动获取foobar_result的包含目录。下面是分步的解决方案:
关键原理
CMake不会自动在导出的包配置中添加依赖查找逻辑,你需要通过标准的CMake包配置工具CMakePackageConfigHelpers来生成包含依赖查找的配置文件,同时确保依赖通过target_link_libraries的INTERFACE/PUBLIC模式传递,这样所有相关的INTERFACE属性(包含目录、链接库等)都会被自动传递给下游项目。
1. 优化foobar_result的CMake配置(规范包导出)
首先让foobar_result生成符合CMake标准的包配置,确保它的目标属性能被正确导入:
修改foobar_result/CMakeLists.txt:
cmake_minimum_required(VERSION 3.5) project(foobar_result) include(GNUInstallDirs) include(CMakePackageConfigHelpers) add_library(result SHARED src/result.cpp) target_include_directories(result PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) # 生成版本配置文件(可选但推荐) write_basic_package_version_file( ${PROJECT_NAME}ConfigVersion.cmake VERSION 1.0.0 COMPATIBILITY SameMajorVersion ) # 导出目标到Targets文件 install(EXPORT ${PROJECT_NAME} DESTINATION share/${PROJECT_NAME}/cmake FILE ${PROJECT_NAME}Targets.cmake ) # 配置Config.cmake模板 configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake INSTALL_DESTINATION share/${PROJECT_NAME}/cmake ) # 安装目标、头文件和配置文件 install(TARGETS result EXPORT ${PROJECT_NAME}) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake DESTINATION share/${PROJECT_NAME}/cmake )
创建foobar_result/Config.cmake.in模板文件:
@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components(@PROJECT_NAME@)
2. 修复foobar_interface的CMake配置(传递依赖查找逻辑)
重点是在foobar_interface的包配置中添加对foobar_result的自动查找,确保下游项目能间接获取foobar_result的所有属性:
修改foobar_interface/CMakeLists.txt:
cmake_minimum_required(VERSION 3.5) project(foobar_interface) include(GNUInstallDirs) include(CMakePackageConfigHelpers) # 查找依赖的foobar_result find_package(foobar_result REQUIRED) add_library(foobar_interface INTERFACE) # 用INTERFACE模式链接result,确保依赖属性被传递 target_link_libraries(foobar_interface INTERFACE result) target_include_directories(foobar_interface INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) # 生成版本配置文件 write_basic_package_version_file( ${PROJECT_NAME}ConfigVersion.cmake VERSION 1.0.0 COMPATIBILITY SameMajorVersion ) # 导出目标 install(EXPORT ${PROJECT_NAME} DESTINATION share/${PROJECT_NAME}/cmake FILE ${PROJECT_NAME}Targets.cmake ) # 配置Config.cmake模板,添加自动查找foobar_result的逻辑 configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake INSTALL_DESTINATION share/${PROJECT_NAME}/cmake ) # 安装目标、头文件和配置文件 install(TARGETS foobar_interface EXPORT ${PROJECT_NAME}) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake DESTINATION share/${PROJECT_NAME}/cmake )
创建foobar_interface/Config.cmake.in模板文件:
@PACKAGE_INIT@ # 自动查找依赖的foobar_result,确保下游项目能获取其包含目录 find_dependency(foobar_result REQUIRED) include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components(@PROJECT_NAME@)
3. 验证foobar_implA的CMake配置(无需修改核心逻辑)
foobar_implA的原有配置无需手动添加foobar_result的查找,只需要确保通过target_link_libraries链接foobar_interface即可自动获取所有依赖属性:
cmake_minimum_required(VERSION 3.5) project(foobar_implA) include(GNUInstallDirs) # 只需要查找foobar_interface,依赖会自动传递 find_package(foobar_interface REQUIRED) add_library(foobar_implA SHARED src/foobar_implA.cpp) target_link_libraries(foobar_implA PUBLIC foobar_interface) target_include_directories(foobar_implA PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) # 可选:同样用CMakePackageConfigHelpers优化自身导出 include(CMakePackageConfigHelpers) write_basic_package_version_file( ${PROJECT_NAME}ConfigVersion.cmake VERSION 1.0.0 COMPATIBILITY SameMajorVersion ) install(EXPORT ${PROJECT_NAME} DESTINATION share/${PROJECT_NAME}/cmake FILE ${PROJECT_NAME}Targets.cmake ) configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake INSTALL_DESTINATION share/${PROJECT_NAME}/cmake ) install(TARGETS foobar_implA EXPORT ${PROJECT_NAME}) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake DESTINATION share/${PROJECT_NAME}/cmake )
为什么原来的配置失效?
你之前的foobar_interface导出配置仅设置了INTERFACE_LINK_LIBRARIES "result",但CMake不知道result目标的来源——没有自动触发find_package(foobar_result),因此无法导入result的INTERFACE_INCLUDE_DIRECTORIES属性。通过在foobar_interface的Config.cmake.in中添加find_dependency(foobar_result),CMake会自动查找并导入foobar_result的所有目标属性,最终传递给foobar_implA。
内容的提问来源于stack exchange,提问作者Christian Rauch

