You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 19:02:12