如何为类Boost多组件C++库编写CMake模块
解决方案:TestLib多子模块CMake配置优化
看起来你碰到的问题核心是构建树内部子模块依赖找不到——毕竟find_package默认找的是已安装的库配置,而本地构建时还没生成TestLibConfig.cmake呢。我给你梳理一套完整的解决方案,完美适配你的多仓库子模块结构:
1. 先搞定根仓库的CMake主导配置
根仓库的CMakeLists.txt要承担起全局版本管理、组件声明和配置文件生成的职责,这样后续子模块的依赖和导出才能统一:
cmake_minimum_required(VERSION 3.14) # 定义库的基础信息 project(TestLib VERSION 0.1 LANGUAGES CXX) # 声明我们支持的两个组件 set(TestLib_COMPONENTS Base Ext) # 引入两个子模块仓库 add_subdirectory(Base) add_subdirectory(Ext) # 生成版本配置文件,让find_package能识别版本 include(CMakePackageConfigHelpers) write_basic_package_version_file( TestLibConfigVersion.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ) # 生成核心的TestLibConfig.cmake模板,需要先在根目录建TestLibConfig.cmake.in文件 configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/TestLibConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/TestLibConfig.cmake INSTALL_DESTINATION lib/cmake/TestLib ) # 安装这些配置文件,方便外部项目find_package install( FILES ${CMAKE_CURRENT_BINARY_DIR}/TestLibConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/TestLibConfigVersion.cmake DESTINATION lib/cmake/TestLib ) # 导出全局包配置,方便本地其他项目直接引用构建中的TestLib export(PACKAGE TestLib)
根目录需要新建一个TestLibConfig.cmake.in模板,内容如下:
@PACKAGE_INIT@ # 自动引入各个组件的目标导出文件 foreach(component @TestLib_COMPONENTS@) include("${CMAKE_CURRENT_LIST_DIR}/TestLib${component}Targets.cmake") endforeach() check_required_components(TestLib)
2. 完善Base模块的CMake配置
Base模块要正确设置头文件路径、导出目标,确保无论是构建树内部还是安装后都能被正确引用:
cmake_minimum_required(VERSION 3.14) set(TARGET_NAME testlibbase) add_library(${TARGET_NAME} src/ClassA.cpp src/ClassB.cpp) # 区分构建时和安装后的头文件路径,两种场景都能找到头文件 target_include_directories(${TARGET_NAME} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include> ) # 定义别名,构建树内部直接用TestLib::Base就能引用 add_library(TestLib::Base ALIAS ${TARGET_NAME}) # 安装库文件到指定目录 install( TARGETS ${TARGET_NAME} EXPORT TestLibBaseTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin ) # 安装头文件,注意要保留TestLib命名空间目录结构 install( DIRECTORY include/TestLib DESTINATION include ) # 导出Base的目标文件,安装后find_package能识别 install( EXPORT TestLibBaseTargets FILE TestLibBaseTargets.cmake NAMESPACE TestLib:: DESTINATION lib/cmake/TestLib ) # 本地构建时导出目标,让Ext模块能直接找到Base export( EXPORT TestLibBaseTargets FILE ${CMAKE_CURRENT_BINARY_DIR}/TestLibBaseTargets.cmake NAMESPACE TestLib:: )
3. 修改Ext模块的CMake配置(核心解决依赖问题)
这里不用再写find_package(TestLib COMPONENTS Base)了,直接用Base的别名目标就行,同时做好自己的导出配置:
cmake_minimum_required(VERSION 3.14) set(TARGET_NAME testlibext) add_library(${TARGET_NAME} src/ClassC.cpp) # 直接链接TestLib::Base,不管是构建树内部还是安装后都能生效 target_link_libraries(${TARGET_NAME} PUBLIC TestLib::Base ) # 同样处理头文件路径 target_include_directories(${TARGET_NAME} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include> ) # 定义Ext的别名 add_library(TestLib::Ext ALIAS ${TARGET_NAME}) # 安装Ext库文件 install( TARGETS ${TARGET_NAME} EXPORT TestLibExtTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin ) # 安装Ext的头文件 install( DIRECTORY include/TestLib DESTINATION include ) # 导出Ext的目标文件 install( EXPORT TestLibExtTargets FILE TestLibExtTargets.cmake NAMESPACE TestLib:: DESTINATION lib/cmake/TestLib ) # 本地构建时导出Ext的目标 export( EXPORT TestLibExtTargets FILE ${CMAKE_CURRENT_BINARY_DIR}/TestLibExtTargets.cmake NAMESPACE TestLib:: )
为什么这样能解决问题?
- 构建树内部:我们通过
add_library(TestLib::Base ALIAS ...)和export(EXPORT ...),让Ext模块在本地构建时可以直接通过别名找到Base库,完全不需要依赖find_package。 - 安装后使用:当用户安装TestLib后,
find_package(TestLib 0.1 REQUIRED COMPONENTS Base Ext)会自动加载根目录生成的TestLibConfig.cmake,进而加载每个组件的目标导出文件,就能正确找到TestLib::Base和TestLib::Ext。 - 头文件兼容:用
BUILD_INTERFACE和INSTALL_INTERFACE分别指定构建时和安装后的头文件路径,确保两种场景下编译器都能找到正确的头文件位置。
这样修改后,你想要的两种调用方式都能完美实现啦:
- 外部项目可以用
find_package(TestLib 0.1 REQUIRED COMPONENTS Base Ext) - 不管是构建树内部还是外部项目,都能直接用
target_link_libraries(someapplication PUBLIC TestLib::Base)或者TestLib::Ext
内容的提问来源于stack exchange,提问作者PowerSupplyTopologies
相关产品推荐
相关产品推荐

