如何避免CMake中共享依赖库Utils重复构建
解决Utils重复构建的方案
核心问题
A和App各自通过ExternalProject_Add独立引入并构建Utils,且两者的构建输出目录相互独立,导致Utils被构建两次。下面是几种可行的解决思路:
方案一:让App仅依赖A,由A传递Utils的依赖(推荐)
调整A的构建配置,让它在安装时导出自身的依赖信息,这样App引入A后会自动获取Utils的相关配置,无需再单独引入Utils。
修改A的CMakeLists.txt
cmake_minimum_required(VERSION 3.16.3) project(A) include_directories(${PROJECT_SOURCE_DIR}/Inc) file(GLOB SOURCE_FILES ${PROJECT_SOURCE_DIR}/Src/*.cpp) add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) # 安装目标时同时导出配置文件 install(TARGETS ${PROJECT_NAME} EXPORT A-targets DESTINATION ${CMAKE_INSTALL_PREFIX}) # 生成并安装A的配置文件 install(EXPORT A-targets FILE AConfig.cmake DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake) include(ExternalProject) # 修改Utils的安装路径到全局可访问目录,而非A的源码目录 ExternalProject_Add(Utils GIT_REPOSITORY ${SOME_GIT_URL}/_git/Utils GIT_TAG master CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} ) # 安装Utils后通过find_package找到它 ExternalProject_Get_Property(Utils INSTALL_DIR) set(CMAKE_PREFIX_PATH ${INSTALL_DIR} ${CMAKE_PREFIX_PATH}) find_package(Utils REQUIRED) # 直接链接Utils目标,替代硬编码路径 target_link_libraries(${PROJECT_NAME} Utils) target_include_directories(${PROJECT_NAME} PRIVATE ${Utils_INCLUDE_DIRS}) add_dependencies(${PROJECT_NAME} Utils)
修改Utils的CMakeLists.txt,添加配置导出
让Utils支持find_package调用:
cmake_minimum_required(VERSION 3.16.3) project(Utils) include_directories(${PROJECT_SOURCE_DIR}/Inc) file(GLOB SOURCE_FILES ${PROJECT_SOURCE_DIR}/Src/*.cpp) add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) # 安装目标并导出配置 install(TARGETS ${PROJECT_NAME} EXPORT Utils-targets DESTINATION ${CMAKE_INSTALL_PREFIX}) install(EXPORT Utils-targets FILE UtilsConfig.cmake DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake) # 导出头文件路径,方便依赖方获取 set(Utils_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/Inc PARENT_SCOPE)
修改App的CMakeLists.txt,仅依赖A
去掉自身的Utils相关代码,通过find_package引入A,自动获取Utils依赖:
cmake_minimum_required(VERSION 3.16.3) project(App) include_directories(${PROJECT_SOURCE_DIR}/Inc) file(GLOB SOURCE_FILES ${PROJECT_SOURCE_DIR}/Src/*.cpp) add_executable(${PROJECT_NAME} ${SOURCE_FILES}) include(ExternalProject) ExternalProject_Add(A GIT_REPOSITORY ${SOME_GIT_URL}/_git/A GIT_TAG master CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} ) # 安装A后通过find_package引入 ExternalProject_Get_Property(A INSTALL_DIR) set(CMAKE_PREFIX_PATH ${INSTALL_DIR} ${CMAKE_PREFIX_PATH}) find_package(A REQUIRED) # 直接链接A,Utils会自动被链接进来 target_link_libraries(${PROJECT_NAME} A) target_include_directories(${PROJECT_NAME} PRIVATE ${A_INCLUDE_DIRS}) add_dependencies(${PROJECT_NAME} A)
方案二:统一Utils的构建输出目录
让A和App使用同一个目录来构建和安装Utils,这样第二次构建时CMake会检测到Utils已存在,跳过重复构建。
修改A的CMakeLists.txt中Utils的安装路径
把Utils的安装路径改成App的Lib目录(或一个独立的第三方依赖目录):
ExternalProject_Add(Utils GIT_REPOSITORY ${SOME_GIT_URL}/_git/Utils GIT_TAG master CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_SOURCE_DIR}/../App/Lib )
修改App的CMakeLists.txt中Utils的配置
保持Utils的CMAKE_INSTALL_PREFIX不变,同时添加EXCLUDE_FROM_ALL避免重复触发构建:
ExternalProject_Add(Utils GIT_REPOSITORY ${SOME_GIT_URL}/_git/Utils GIT_TAG master CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PROJECT_SOURCE_DIR}/Lib EXCLUDE_FROM_ALL TRUE )
方案三:使用FetchContent替代ExternalProject(CMake 3.14+)
FetchContent是CMake官方推荐的模块,会自动缓存下载的源码和构建结果,避免重复构建。
修改A的CMakeLists.txt
cmake_minimum_required(VERSION 3.16.3) project(A) include_directories(${PROJECT_SOURCE_DIR}/Inc) file(GLOB SOURCE_FILES ${PROJECT_SOURCE_DIR}/Src/*.cpp) add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}) include(FetchContent) FetchContent_Declare( Utils GIT_REPOSITORY ${SOME_GIT_URL}/_git/Utils GIT_TAG master ) # 确保Utils被构建并可用 FetchContent_MakeAvailable(Utils) target_link_libraries(${PROJECT_NAME} Utils) target_include_directories(${PROJECT_NAME} PRIVATE ${Utils_SOURCE_DIR}/Inc)
修改App的CMakeLists.txt
cmake_minimum_required(VERSION 3.16.3) project(App) include_directories(${PROJECT_SOURCE_DIR}/Inc) file(GLOB SOURCE_FILES ${PROJECT_SOURCE_DIR}/Src/*.cpp) add_executable(${PROJECT_NAME} ${SOURCE_FILES}) install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}) include(FetchContent) # 先引入A,A会自动处理Utils的依赖 FetchContent_Declare( A GIT_REPOSITORY ${SOME_GIT_URL}/_git/A GIT_TAG master ) FetchContent_MakeAvailable(A) # 引入Utils时,FetchContent会检测到已被A构建,直接复用 FetchContent_Declare( Utils GIT_REPOSITORY ${SOME_GIT_URL}/_git/Utils GIT_TAG master ) FetchContent_MakeAvailable(Utils) target_link_libraries(${PROJECT_NAME} A Utils) target_include_directories(${PROJECT_NAME} PRIVATE ${A_SOURCE_DIR}/Inc ${Utils_SOURCE_DIR}/Inc)
内容的提问来源于stack exchange,提问作者Omer Kawaz
相关产品推荐
相关产品推荐

