创建CMake包时如何避免重复声明公共依赖?
正确创建可重定位CMake包的解决方案
你的思路方向没错,但在依赖管理的自动化和规范上可以优化,以下是具体的改进步骤:
1. 明确依赖的公共/私有属性
首先要区分公共依赖(下游项目使用你的库时必须依赖的,比如你的头文件里用到了Foo的类型)和私有依赖(仅你的库编译时需要,下游不需要知道),用PUBLIC/PRIVATE修饰target_link_libraries:
if (WITH_FOO) find_package(Foo REQUIRED) # 如果是公共依赖用PUBLIC,私有则用PRIVATE target_link_libraries(Lib PUBLIC Foo::Foo) endif() if (WITH_BAR) find_package(Bar REQUIRED) target_link_libraries(Lib PUBLIC Bar::Bar) endif() # 处理pthread这类系统库,不要直接用-lpthread,改用CMake提供的可导入目标 find_package(Threads REQUIRED) target_link_libraries(Lib PRIVATE Threads::Threads)
只有PUBLIC/INTERFACE修饰的依赖会被导出到目标的INTERFACE_LINK_LIBRARIES属性中,这是后续自动化处理的基础。
2. 自动化生成依赖查找代码
手动维护LibConfig.cmake.in里的find_dependency确实容易出错,你可以在CMakeLists.txt中收集公共依赖列表,再通过configure_package_config_file自动生成对应的查找代码:
在CMakeLists.txt中添加依赖收集逻辑
# 初始化公共依赖列表 set(LIB_PUBLIC_DEPENDENCIES "") if(WITH_FOO) list(APPEND LIB_PUBLIC_DEPENDENCIES "Foo") endif() if(WITH_BAR) list(APPEND LIB_PUBLIC_DEPENDENCIES "Bar") endif() # 配置Config文件时传递这个列表 configure_package_config_file( LibConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/LibConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Lib )
修改LibConfig.cmake.in
@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/LibTargets.cmake") include(CMakeFindDependencyMacro) # 自动遍历公共依赖列表,调用find_dependency foreach(dep @LIB_PUBLIC_DEPENDENCIES@) find_dependency(${dep}) endforeach() check_required_components(Lib)
这样新增公共依赖时,只需要在CMakeLists.txt里把依赖名加入LIB_PUBLIC_DEPENDENCIES,无需手动修改Config模板。
3. 正确处理自定义目标(如OtherTarget)
如果OtherTarget是Lib的公共依赖(下游需要链接它),必须将其一起安装并导出:
if (WITH_OTHER_TARGET) add_library(OtherTarget other_target.cpp other_target.h) # 公共依赖用PUBLIC,私有则用PRIVATE target_link_libraries(Lib PUBLIC OtherTarget) # 将OtherTarget加入导出列表 install(TARGETS OtherTarget EXPORT LibTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) endif()
这样OtherTarget会被导出到LibTargets.cmake中,下游使用Lib::Lib时会自动关联Lib::OtherTarget。
4. 补充CMake包规范内容
规范的可重定位包需要版本文件,添加以下代码到CMakeLists.txt:
include(CMakePackageConfigHelpers) # 生成版本文件 write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/LibConfigVersion.cmake VERSION ${PROJECT_VERSION} # 确保你的项目设置了PROJECT_VERSION COMPATIBILITY SameMajorVersion ) # 安装版本文件 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/LibConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Lib )
核心原则总结
- 依赖区分公共/私有,避免不必要的导出
- 优先使用CMake的可导入目标(如
Foo::Foo),不要直接用原始库名(如-lpthread) - 通过变量传递+模板遍历的方式自动化依赖查找代码,减少手动维护
- 自定义公共目标必须一起安装导出
- 遵循CMake包规范,添加版本文件
内容的提问来源于stack exchange,提问作者jacky la mouette
相关产品推荐
相关产品推荐

