CMake含公共库的两个独立项目打包方案问询
首先得澄清一下:你之前觉得CPack只能生成一个包,这个说法是针对单个CMake项目而言的——确实,一个CMake构建树(对应一个顶级CMakeLists.txt)只能通过CPack生成一套打包产物(哪怕用组件,那也是同一个包的不同部分,不是独立包)。但咱们完全可以绕开这个限制,不用把libcommon装到系统级,就能生成projA和projB两个独立的包。
下面给你两种实用的方案,适配不同的场景:
方案一:libcommon本地独立构建+projA/B引用本地安装包
适合需要单独维护libcommon(比如单独更新、单独打包)的场景,libcommon会被安装到项目目录下的本地文件夹,不会碰系统路径。
步骤1:给libcommon配置CMakeLists.txt
在libcommon/目录下创建CMakeLists.txt,配置成可安装的CMake项目:
cmake_minimum_required(VERSION 3.15) project(libcommon VERSION 1.0.0) # 编译共享库 add_library(common SHARED src/utils.c) # 配置头文件路径:构建时用源码目录,安装后用安装目录 target_include_directories(common PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include> ) # 安装规则:指定安装到项目根目录下的install/libcommon文件夹 install(TARGETS common EXPORT commonTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib INCLUDES DESTINATION include ) install(DIRECTORY include/ DESTINATION include) # 生成CMake配置文件,方便其他项目查找libcommon install(EXPORT commonTargets FILE commonTargets.cmake NAMESPACE common:: DESTINATION lib/cmake/common ) include(CMakePackageConfigHelpers) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/commonConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/commonConfig.cmake" INSTALL_DESTINATION lib/cmake/common ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/commonConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/commonConfigVersion.cmake" DESTINATION lib/cmake/common )
同时在libcommon/目录下创建Config.cmake.in模板文件:
@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/commonTargets.cmake") check_required_components(common)
步骤2:构建并本地安装libcommon
在终端执行:
cd libcommon mkdir build && cd build # 指定安装到根目录下的install/libcommon,绝对路径也行 cmake .. -DCMAKE_INSTALL_PREFIX=../../install/libcommon cmake --build . --target install
步骤3:给projA配置CMakeLists.txt(projB同理)
在projA/目录下创建CMakeLists.txt,引用本地安装的libcommon并配置CPack:
cmake_minimum_required(VERSION 3.15) project(projA VERSION 1.0.0) # 告诉CMake去哪里找本地安装的libcommon set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../install/libcommon") find_package(common REQUIRED) # 编译projA的可执行文件 add_executable(projA src/mainA.c) target_link_libraries(projA PRIVATE common::common) # 安装projA install(TARGETS projA RUNTIME DESTINATION bin ) # 配置CPack生成独立包 set(CPACK_PACKAGE_NAME "projA") set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "独立打包的Project A,依赖本地libcommon") # 可选:指定打包格式,比如DEB、RPM、ZIP等 set(CPACK_GENERATOR "ZIP;DEB") include(CPack)
步骤4:分别构建并打包projA和projB
对projA执行:
cd projA mkdir build && cd build cmake .. cmake --build . cpack
projB重复同样的操作,就能得到两个完全独立的包文件。
方案二:用FetchContent让projA/B自动拉取libcommon源码构建
适合不需要单独维护libcommon包的场景,projA和projB在构建时会自动拉取libcommon的源码并编译,打包时可以选择把libcommon的库一起打包进去,让projA/B的包完全独立。
步骤1:给projA配置CMakeLists.txt(projB同理)
cmake_minimum_required(VERSION 3.15) project(projA VERSION 1.0.0) # 引入FetchContent模块,拉取本地的libcommon源码 include(FetchContent) FetchContent_Declare( libcommon SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../libcommon ) FetchContent_MakeAvailable(libcommon) # 编译projA可执行文件 add_executable(projA src/mainA.c) target_link_libraries(projA PRIVATE common::common) # 安装projA和libcommon的库(让打包后的projA包含依赖) install(TARGETS projA RUNTIME DESTINATION bin ) install(TARGETS common LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) install(DIRECTORY ${libcommon_SOURCE_DIR}/include/ DESTINATION include) # 配置CPack生成独立包 set(CPACK_PACKAGE_NAME "projA") set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "包含libcommon的独立Project A包") set(CPACK_GENERATOR "ZIP;RPM") include(CPack)
步骤2:直接构建并打包projA/B
对projA执行:
cd projA mkdir build && cd build cmake .. cmake --build . cpack
这种方式下,projA的包会包含自己的可执行文件和libcommon的库,完全独立,不需要提前安装任何依赖。
额外说明
如果你的场景需要把libcommon也做成独立包,让projA/B的包声明依赖它,那也可以给libcommon配置自己的CPack,然后在projA/B的CPack配置里添加依赖声明(比如DEB包的CPACK_DEBIAN_PACKAGE_DEPENDS,RPM包的CPACK_RPM_PACKAGE_REQUIRES),这种情况下libcommon可以安装到系统级,也可以用本地包仓库来管理,不用全局安装。
内容的提问来源于stack exchange,提问作者P. Dmitry

