如何避免CMake ExternalProject切换配置时重复克隆大型外部库
我用CMake构建跨平台库,通过CMakePresets.txt存储配置与构建预设,搭配Visual Studio Code的CMake Tools扩展,涉及Win x32、Win x64、UWP ARM、UWP ARM64、Android ARM、Android ARM64等多种配置。该库依赖一个20GB的外部库,我用ExternalProject模块克隆并构建它。为了只克隆一次、多平台复用,我把SOURCE_DIR设为my_library/external/external_library,但切换配置预设时,已经克隆好的库会被删除并重新克隆,耗时极长,给团队协作带来很大困扰。
请问能不能在SOURCE_DIR已存在时,让ExternalProject不再重新克隆这个外部库?如果可以,我希望继续用ExternalProject提供的Git下载命令。
代码示例
my_library/CMakePresets.txt
{ "version": 4, "cmakeMinimumRequired": { "major": 3, "minor": 23, "patch": 0 }, "configurePresets": [ { "name": "win_x64", "binaryDir": "${sourceDir}/build/win_x64", "generator": "Ninja Multi-Config" }, { "name": "android_arm64", "binaryDir": "${sourceDir}/build/android_arm64", "generator": "Ninja Multi-Config" }, ... ], "buildPresets": [ { "name": "rel_win_x64", "configurePreset": "win_x64", "configuration": "Release" }, { "name": "rel_android_arm64", "configurePreset": "android_arm64", "configuration": "Release" }, ... ] }
my_library/CMakeLists.txt
cmake_minimum_required(VERSION 3.24) project(my_library VERSION 1.0) add_subdirectory(external) add_library(my_library src/example.cc include/example.h) target_link_libraries(my_library external_library)
my_library/external/CMakeLists.txt
include(ExternalProject) ExternalProject_Add(external_library_src SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_library GIT_REPOSITORY https://github.com/ExternalLibrary/external_library.git GIT_TAG version_tag UPDATES_DISCONNECTED true CONFIGURE_COMMAND configure_command BUILD_COMMAND build_command ) add_library(external_library STATIC IMPORTED GLOBAL) add_dependencies(external_library external_library_src)
你遇到的问题是因为ExternalProject默认会在每次配置时检查Git仓库状态,甚至可能因不同配置的构建目录隔离导致误删源目录。可以通过以下设置解决,同时保留Git下载能力:
- 自定义下载命令,仅在源码目录不存在时执行克隆
通过判断源码目录的.git文件夹是否存在,决定是否执行Git克隆命令,替换掉ExternalProject默认的Git参数:
修改my_library/external/CMakeLists.txt中的ExternalProject配置:
include(ExternalProject) # 检查外部库源码目录是否已存在Git仓库 if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/external_library/.git) set(DOWNLOAD_CMD git clone --branch version_tag https://github.com/ExternalLibrary/external_library.git ${CMAKE_CURRENT_SOURCE_DIR}/external_library) else() set(DOWNLOAD_CMD "") endif() ExternalProject_Add(external_library_src SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_library # 清空默认Git参数,改用自定义下载命令 GIT_REPOSITORY "" GIT_TAG "" DOWNLOAD_COMMAND ${DOWNLOAD_CMD} UPDATES_DISCONNECTED true CONFIGURE_COMMAND configure_command BUILD_COMMAND build_command # 给每个配置单独设置构建目录,避免产物互相覆盖 BINARY_DIR ${CMAKE_BINARY_DIR}/external_library_build ) add_library(external_library STATIC IMPORTED GLOBAL) add_dependencies(external_library external_library_src)
保留
UPDATES_DISCONNECTED true
这个参数会阻止CMake主动检查外部库的远程更新,避免不必要的网络交互和目录操作。单独设置外部库的构建目录
给BINARY_DIR指定${CMAKE_BINARY_DIR}下的子目录,这样不同平台配置的编译产物会分开存放,不会互相干扰,同时源码目录始终复用。
内容的提问来源于stack exchange,提问作者Aidan Howie

