CMake FetchContent与ExternalProject

一、FetchContent

FetchContent 是 CMake 3.11+ 引入的模块,用于在配置时自动下载和管理外部依赖。它允许项目直接从 Git 仓库、URL 等位置获取依赖,无需手动下载或使用 Git 子模块。

基本用法

包含模块

cmake 复制代码
include(FetchContent)

声明依赖

cmake 复制代码
FetchContent_Declare(
    <name>
    <contentOptions>...
)

获取并填充依赖

cmake 复制代码
FetchContent_MakeAvailable(<name>)

完整示例

cmake 复制代码
cmake_minimum_required(VERSION 3.14)  # 推荐 3.14+ 使用 MakeAvailable
project(MyProject)

include(FetchContent)

# 声明依赖
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG        release-1.12.1
)

FetchContent_Declare(
    nlohmann_json
    URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz
    URL_HASH SHA256=somehashvalue
)

# 获取依赖
FetchContent_MakeAvailable(googletest nlohmann_json)

# 使用依赖
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE nlohmann_json::nlohmann_json)

# gtest 会自动创建测试目标
enable_testing()
add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE gtest_main)
add_test(NAME tests COMMAND tests)

常见内容选项

Git 仓库

cmake 复制代码
FetchContent_Declare(
    mylib
    GIT_REPOSITORY https://github.com/user/mylib.git
    GIT_TAG v1.2.3  # 可以是 tag, commit hash, branch
    GIT_SHALLOW TRUE  # 浅克隆,只获取最新提交
    GIT_PROGRESS TRUE  # 显示克隆进度
)

URL 下载

cmake 复制代码
FetchContent_Declare(
    mylib
    URL https://example.com/mylib-1.2.3.zip
    URL_HASH SHA256=0123456789abcdef...
    # 或使用 MD5
    URL_MD5 abcdef123456
)

SVN 仓库

cmake 复制代码
FetchContent_Declare(
    mylib
    SVN_REPOSITORY https://svn.example.com/mylib/trunk
    SVN_REVISION -r 1234
)

高级用法

使用源目录

cmake 复制代码
FetchContent_Declare(
    mylib
    SOURCE_DIR ${CMAKE_BINARY_DIR}/custom/mylib
    GIT_REPOSITORY https://github.com/user/mylib.git
)

自定义构建步骤

cmake 复制代码
FetchContent_Declare(
    mylib
    GIT_REPOSITORY https://github.com/user/mylib.git
)

# 手动处理依赖
FetchContent_GetProperties(mylib)
if(NOT mylib_POPULATED)
    FetchContent_Populate(mylib)
    add_subdirectory(${mylib_SOURCE_DIR} ${mylib_BINARY_DIR})
endif()

条件性获取

cmake 复制代码
if(NOT TARGET some_dependency)
    FetchContent_Declare(...)
    FetchContent_MakeAvailable(...)
endif()

与 find_package 结合

cmake 复制代码
# 优先尝试 find_package,找不到则下载
find_package(OpenSSL QUIET)

if(NOT OpenSSL_FOUND)
    FetchContent_Declare(
        openssl
        URL https://www.openssl.org/source/openssl-1.1.1.tar.gz
    )
    FetchContent_MakeAvailable(openssl)
endif()

配置变量

cmake 复制代码
# 设置下载目录
set(FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/deps)

# 强制重新下载(忽略缓存)
set(FETCHCONTENT_TRY_FIND_PACKAGE_MODE NEVER)

# 启用详细输出
set(FETCHCONTENT_QUIET OFF)

版本管理

复制代码
# 锁定版本
FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG 8.1.1
    GIT_SHALLOW TRUE
)

# 使用最新 master(不推荐生产环境)
FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG master
)

多依赖示例

cmake 复制代码
include(FetchContent)

# 声明多个依赖
set(dependencies
    googletest
    nlohmann_json
    fmt
)

foreach(dep IN LISTS dependencies)
    if(dep STREQUAL "googletest")
        FetchContent_Declare(
            googletest
            GIT_REPOSITORY https://github.com/google/googletest.git
            GIT_TAG v1.14.0
        )
    elseif(dep STREQUAL "nlohmann_json")
        FetchContent_Declare(
            nlohmann_json
            URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
        )
    elseif(dep STREQUAL "fmt")
        FetchContent_Declare(
            fmt
            GIT_REPOSITORY https://github.com/fmtlib/fmt.git
            GIT_TAG 10.0.0
        )
    endif()
endforeach()

FetchContent_MakeAvailable(${dependencies})

覆盖依赖版本

cmake 复制代码
# 在命令行覆盖版本
cmake -Dfmt_GIT_TAG=9.1.0 ..

# 或在 CMakeLists.txt 中提前设置
set(fmt_GIT_TAG 9.1.0)
FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG ${fmt_GIT_TAG}  # 使用变量
)

注意事项

  1. CMake 版本:

    • FetchContent_MakeAvailable 需要 CMake 3.14+
    • CMake 3.11-3.13 需要手动调用 FetchContent_Populate
  2. 构建时 vs 配置时:依赖在 CMake 配置阶段下载,不是构建时

  3. 缓存:下载的内容会缓存,多次构建不会重复下载

  4. 代理:支持 HTTP 代理环境变量(http_proxy, https_proxy

  5. 网络要求:首次配置时需要网络连接

最佳实践

cmake 复制代码
cmake_minimum_required(VERSION 3.14)
project(MyApp)

# 允许用户禁用自动获取依赖
option(FETCH_DEPS "Automatically fetch dependencies" ON)

if(FETCH_DEPS)
    include(FetchContent)
    
    # 设置下载位置
    set(FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps)
    
    # 优先使用系统包
    find_package(fmt QUIET)
    if(NOT fmt_FOUND)
        FetchContent_Declare(
            fmt
            GIT_REPOSITORY https://github.com/fmtlib/fmt.git
            GIT_TAG 10.0.0
        )
        FetchContent_MakeAvailable(fmt)
    endif()
else()
    # 用户负责提供依赖
    find_package(fmt REQUIRED)
endif()

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE fmt::fmt)

二、ExternalProject

ExternalProject 是 CMake 内置模块,用于在构建过程中自动下载、配置、编译和安装外部依赖

基本用法

cmake 复制代码
include(ExternalProject)

ExternalProject_Add(
    <目标名称>
    # 下载方式(三选一)
    URL <下载地址>                          # 从URL下载源码包
    GIT_REPOSITORY <git仓库地址>            # 从Git克隆
    SVN_REPOSITORY <svn仓库地址>            # 从SVN检出
    
    # 目录配置
    SOURCE_DIR <源码目录>                   # 源码存放位置
    BINARY_DIR <构建目录>                   # 构建目录
    INSTALL_DIR <安装目录>                  # 安装目录
    
    # 构建步骤配置
    CONFIGURE_COMMAND <配置命令>            # 配置命令(如cmake/configure)
    BUILD_COMMAND <构建命令>                # 构建命令
    INSTALL_COMMAND <安装命令>              # 安装命令
    TEST_COMMAND <测试命令>                 # 测试命令
    
    # 下载配置
    TIMEOUT <秒数>                          # 下载超时
    TLS_VERIFY <ON/OFF>                     # SSL证书验证
)

常见使用场景

场景一:下载并编译开源库

cmake 复制代码
cmake_minimum_required(VERSION 3.10)
project(MyProject)

include(ExternalProject)

# 下载并编译 fmt 库
ExternalProject_Add(
    fmt_lib
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG 10.1.0
    SOURCE_DIR ${CMAKE_BINARY_DIR}/external/fmt
    BINARY_DIR ${CMAKE_BINARY_DIR}/external/fmt/build
    CMAKE_ARGS
        -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external/fmt/install
        -DCMAKE_CXX_STANDARD=17
        -DFMT_TEST=OFF
        -DFMT_DOC=OFF
    BUILD_COMMAND cmake --build . --config Release
    INSTALL_COMMAND cmake --install .
)

# 使用 find_package 查找
set(fmt_DIR ${CMAKE_BINARY_DIR}/external/fmt/install/lib/cmake/fmt)
find_package(fmt REQUIRED)

场景二:下载源码包

cmake 复制代码
ExternalProject_Add(
    zlib
    URL https://zlib.net/zlib-1.2.13.tar.gz
    URL_HASH SHA256=1525952a0a567581792613a9723333d7f8cc20b87a81f920fb8bc7e3f2251428
    SOURCE_DIR ${CMAKE_BINARY_DIR}/external/zlib
    CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/zlib/install
    BUILD_COMMAND make
    INSTALL_COMMAND make install
)

场景三:多步骤依赖

cmake 复制代码
# 定义依赖关系
ExternalProject_Add(
    dependency_a
    GIT_REPOSITORY https://github.com/example/libA.git
    INSTALL_DIR ${CMAKE_BINARY_DIR}/install/libA
)

ExternalProject_Add(
    dependency_b
    GIT_REPOSITORY https://github.com/example/libB.git
    DEPENDS dependency_a          # 等待 dependency_a 完成
    CMAKE_ARGS
        -DlibA_DIR=${CMAKE_BINARY_DIR}/install/libA/lib/cmake/libA
        -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/install/libB
)

高级配置技巧

使用 LOG_* 输出日志

cmake 复制代码
ExternalProject_Add(
    mylib
    GIT_REPOSITORY https://github.com/example/mylib.git
    LOG_DOWNLOAD ON      # 记录下载日志
    LOG_CONFIGURE ON     # 记录配置日志
    LOG_BUILD ON         # 记录构建日志
    LOG_INSTALL ON       # 记录安装日志
)

自定义构建步骤

cmake 复制代码
ExternalProject_Add(
    mylib
    GIT_REPOSITORY https://github.com/example/mylib.git
    CONFIGURE_COMMAND ""
    BUILD_COMMAND ${CMAKE_COMMAND} -E echo "Building..."
    INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory
        ${CMAKE_BINARY_DIR}/external/mylib/include
        ${CMAKE_BINARY_DIR}/install/include
    TEST_COMMAND ${CMAKE_CTEST_COMMAND} -C Release
)

使用 PATCH_COMMAND 打补丁

cmake 复制代码
ExternalProject_Add(
    mylib
    GIT_REPOSITORY https://github.com/example/mylib.git
    PATCH_COMMAND git apply ${CMAKE_SOURCE_DIR}/patches/mylib.patch
    # 或者使用 patch 命令
    # PATCH_COMMAND patch -p1 < ${CMAKE_SOURCE_DIR}/patches/mylib.patch
)

查找和使用已安装的库

cmake 复制代码
# 设置安装路径
set(INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install)

ExternalProject_Add(
    opencv
    GIT_REPOSITORY https://github.com/opencv/opencv.git
    GIT_TAG 4.8.0
    CMAKE_ARGS
        -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX}
        -DBUILD_EXAMPLES=OFF
        -DBUILD_TESTS=OFF
        -DBUILD_PERF_TESTS=OFF
)

# 设置 CMake 查找路径
set(OpenCV_DIR ${INSTALL_PREFIX}/lib/cmake/opencv4)

# 等待 OpenCV 构建完成
add_custom_target(WaitForOpenCV DEPENDS opencv)

# 在主项目中使用
add_executable(myapp main.cpp)
add_dependencies(myapp WaitForOpenCV)

# 查找并链接
find_package(OpenCV REQUIRED)
target_link_libraries(myapp ${OpenCV_LIBS})

处理子项目中的 ExternalProject

如果主项目本身也是用 ExternalProject 管理:

cmake 复制代码
# 父项目 CMakeLists.txt
include(ExternalProject)

ExternalProject_Add(
    SubProject
    SOURCE_DIR ${CMAKE_SOURCE_DIR}/subproject
    CMAKE_ARGS
        -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/install
        -DCMAKE_PREFIX_PATH=${CMAKE_BINARY_DIR}/install  # 传递安装路径
)

# 子项目 CMakeLists.txt (subproject/CMakeLists.txt)
find_package(dependency REQUIRED PATHS ${CMAKE_PREFIX_PATH})

常见问题和解决方案

问题1:重复下载/构建

cmake 复制代码
# 使用标记文件避免重复操作
ExternalProject_Add_Step(mylib force_reconfigure
    COMMAND ${CMAKE_COMMAND} -E echo "Forcing reconfigure"
    DEPENDEES configure
    ALWAYS 1  # 总是执行
)

问题2:传递编译选项

cmake 复制代码
# 将主项目的编译选项传递给外部项目
set(common_cxx_flags "-O3 -Wall -Wextra")

ExternalProject_Add(mylib
    CMAKE_CACHE_ARGS
        -DCMAKE_CXX_FLAGS:STRING=${common_cxx_flags}
        -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
        -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
)

问题3:并行构建控制

cmake 复制代码
# 控制并行构建数量
include(ProcessorCount)
ProcessorCount(N)
if(NOT N EQUAL 0)
    set(MAKE_JOBS "-j${N}")
endif()

ExternalProject_Add(mylib
    BUILD_COMMAND make ${MAKE_JOBS}
)

总结

  • ExternalProject:适合构建独立的、不常变化的外部依赖,支持复杂的构建流程
  • FetchContent:适合直接集成到主项目中的依赖,使用更简单
  • 选择建议:需要独立构建和安装的依赖用 ExternalProject,需要源码集成的依赖用 FetchContent

场景示例

场景一:集成 GoogleTest 进行单元测试

cmake 复制代码
cmake_minimum_required(VERSION 3.14)
project(MyTestProject)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 方法1:使用 FetchContent(推荐)
include(FetchContent)

FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG v1.14.0
)

# 关闭 gtest 的安装和测试
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)

FetchContent_MakeAvailable(googletest)

# 主项目代码
add_executable(my_tests
    tests/main_test.cpp
    src/math_utils.cpp
)

target_link_libraries(my_tests
    gtest_main
    gtest
)

include(GoogleTest)
gtest_discover_tests(my_tests)

对应的源文件

cpp 复制代码
// tests/main_test.cpp
#include <gtest/gtest.h>

int add(int a, int b) { return a + b; }

TEST(MathTest, AddTest) {
    EXPECT_EQ(add(2, 3), 5);
    EXPECT_EQ(add(-1, 1), 0);
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

场景二:集成 FFmpeg 库(复杂依赖)

cmake 复制代码
cmake_minimum_required(VERSION 3.15)
project(FFmpegProject)

include(ExternalProject)

set(DEPS_INSTALL_DIR ${CMAKE_BINARY_DIR}/deps_install)

# 1. 下载并编译 x264
ExternalProject_Add(
    x264
    GIT_REPOSITORY https://code.videolan.org/videolan/x264.git
    GIT_TAG stable
    SOURCE_DIR ${CMAKE_BINARY_DIR}/x264
    CONFIGURE_COMMAND ./configure 
        --prefix=${DEPS_INSTALL_DIR}
        --enable-shared
        --disable-cli
    BUILD_COMMAND make -j${CMAKE_HOST_SYSTEM_PROCESSOR_COUNT}
    INSTALL_COMMAND make install
    BUILD_IN_SOURCE 1
)

# 2. 下载并编译 FFmpeg(依赖 x264)
ExternalProject_Add(
    ffmpeg
    GIT_REPOSITORY https://git.ffmpeg.org/ffmpeg.git
    GIT_TAG n6.0
    DEPENDS x264
    SOURCE_DIR ${CMAKE_BINARY_DIR}/ffmpeg
    CONFIGURE_COMMAND ./configure 
        --prefix=${DEPS_INSTALL_DIR}
        --enable-gpl
        --enable-libx264
        --enable-shared
        --extra-cflags=-I${DEPS_INSTALL_DIR}/include
        --extra-ldflags=-L${DEPS_INSTALL_DIR}/lib
    BUILD_COMMAND make -j${CMAKE_HOST_SYSTEM_PROCESSOR_COUNT}
    INSTALL_COMMAND make install
    BUILD_IN_SOURCE 1
)

# 3. 使用 FFmpeg 的主程序
add_executable(video_processor src/main.cpp)

# 设置头文件和库路径
set_target_properties(video_processor PROPERTIES
    CXX_STANDARD 17
)

target_include_directories(video_processor PRIVATE
    ${DEPS_INSTALL_DIR}/include
)

target_link_directories(video_processor PRIVATE
    ${DEPS_INSTALL_DIR}/lib
)

target_link_libraries(video_processor
    avcodec
    avformat
    avutil
    swscale
    x264
)

# 确保 ffmpeg 先构建完成
add_dependencies(video_processor ffmpeg)

# 运行时设置库路径
set_target_properties(video_processor PROPERTIES
    INSTALL_RPATH ${DEPS_INSTALL_DIR}/lib
)

场景三:下载预编译库(不重新编译)

cmake 复制代码
cmake_minimum_required(VERSION 3.15)
project(PrecompiledLibProject)

include(ExternalProject)

# 下载 SDL2 预编译包
ExternalProject_Add(
    sdl2_download
    URL https://github.com/libsdl-org/SDL/releases/download/release-2.28.0/SDL2-devel-2.28.0-VC.zip
    URL_HASH SHA256=3f4c383d4e47a2c4638f7f79919a42a2c6f3e92fea9f4f549b6cfcf2772d1b79
    SOURCE_DIR ${CMAKE_BINARY_DIR}/sdl2_source
    CONFIGURE_COMMAND ""
    BUILD_COMMAND ""
    INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory 
        ${CMAKE_BINARY_DIR}/sdl2_source/SDL2-2.28.0 
        ${CMAKE_BINARY_DIR}/sdl2_install
    LOG_DOWNLOAD ON
)

# 使用 SDL2
set(SDL2_DIR ${CMAKE_BINARY_DIR}/sdl2_install/lib/cmake/SDL2)
find_package(SDL2 CONFIG REQUIRED)

add_executable(game src/main.cpp)
target_link_libraries(game SDL2::SDL2)

# 拷贝 DLL 到输出目录
add_custom_command(TARGET game POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
        ${CMAKE_BINARY_DIR}/sdl2_install/lib/x64/SDL2.dll
        $<TARGET_FILE_DIR:game>
)

场景四:多个项目依赖传递

cmake 复制代码
cmake_minimum_required(VERSION 3.15)
project(DependencyChain)

include(ExternalProject)

set(INSTALL_ROOT ${CMAKE_BINARY_DIR}/install)

# 依赖A:基础库
ExternalProject_Add(
    lib_a
    GIT_REPOSITORY https://github.com/example/lib_a.git
    GIT_TAG main
    CMAKE_ARGS
        -DCMAKE_INSTALL_PREFIX=${INSTALL_ROOT}/lib_a
        -DBUILD_SHARED_LIBS=ON
    INSTALL_DIR ${INSTALL_ROOT}/lib_a
)

# 依赖B:依赖A
ExternalProject_Add(
    lib_b
    GIT_REPOSITORY https://github.com/example/lib_b.git
    GIT_TAG main
    DEPENDS lib_a
    CMAKE_ARGS
        -DCMAKE_INSTALL_PREFIX=${INSTALL_ROOT}/lib_b
        -Dlib_a_DIR=${INSTALL_ROOT}/lib_a/lib/cmake/lib_a
        -DBUILD_SHARED_LIBS=ON
    INSTALL_DIR ${INSTALL_ROOT}/lib_b
)

# 依赖C:依赖B
ExternalProject_Add(
    lib_c
    GIT_REPOSITORY https://github.com/example/lib_c.git
    GIT_TAG main
    DEPENDS lib_b
    CMAKE_ARGS
        -DCMAKE_INSTALL_PREFIX=${INSTALL_ROOT}/lib_c
        -Dlib_b_DIR=${INSTALL_ROOT}/lib_b/lib/cmake/lib_b
        -DBUILD_SHARED_LIBS=ON
    INSTALL_DIR ${INSTALL_ROOT}/lib_c
)

# 主程序:依赖C
add_executable(my_app src/main.cpp)

# 等待所有依赖构建完成
add_custom_target(deps ALL DEPENDS lib_c)

# 查找已安装的库
set(lib_a_DIR ${INSTALL_ROOT}/lib_a/lib/cmake/lib_a)
set(lib_b_DIR ${INSTALL_ROOT}/lib_b/lib/cmake/lib_b)
set(lib_c_DIR ${INSTALL_ROOT}/lib_c/lib/cmake/lib_c)

find_package(lib_a REQUIRED)
find_package(lib_b REQUIRED)
find_package(lib_c REQUIRED)

target_link_libraries(my_app lib_a::lib_a lib_b::lib_b lib_c::lib_c)
add_dependencies(my_app deps)

场景五:自定义构建脚本

cmake 复制代码
cmake_minimum_required(VERSION 3.15)
project(CustomBuild)

include(ExternalProject)

# 使用自定义 Python 脚本构建
ExternalProject_Add(
    custom_lib
    GIT_REPOSITORY https://github.com/example/custom-lib.git
    SOURCE_DIR ${CMAKE_BINARY_DIR}/custom_lib
    
    # 自定义配置步骤
    CONFIGURE_COMMAND ${Python3_EXECUTABLE} 
        ${CMAKE_SOURCE_DIR}/scripts/prepare_build.py
        --source ${CMAKE_BINARY_DIR}/custom_lib
        --output ${CMAKE_BINARY_DIR}/custom_lib/build
    
    # 自定义构建步骤
    BUILD_COMMAND make -C ${CMAKE_BINARY_DIR}/custom_lib/build
    
    # 自定义安装步骤
    INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory
        ${CMAKE_BINARY_DIR}/install/include
        COMMAND ${CMAKE_COMMAND} -E copy_directory
            ${CMAKE_BINARY_DIR}/custom_lib/build/include
            ${CMAKE_BINARY_DIR}/install/include
        COMMAND ${CMAKE_COMMAND} -E copy
            ${CMAKE_BINARY_DIR}/custom_lib/build/libcustom.a
            ${CMAKE_BINARY_DIR}/install/lib/
)

# 主程序
add_executable(my_tool src/main.cpp)
target_include_directories(my_tool PRIVATE ${CMAKE_BINARY_DIR}/install/include)
target_link_directories(my_tool PRIVATE ${CMAKE_BINARY_DIR}/install/lib)
target_link_libraries(my_tool custom)
add_dependencies(my_tool custom_lib)

场景六:条件下载(根据平台)

cmake 复制代码
cmake_minimum_required(VERSION 3.15)
project(PlatformDependent)

include(ExternalProject)

# 根据平台选择不同的依赖源
if(WIN32)
    set(OPENSSL_URL https://slproweb.com/download/Win64OpenSSL-3_0_7.exe)
    set(OPENSSL_HASH SHA256=xxx)
elseif(APPLE)
    set(OPENSSL_URL https://www.openssl.org/source/openssl-3.0.7.tar.gz)
    set(OPENSSL_HASH SHA256=xxx)
else()
    set(OPENSSL_URL https://www.openssl.org/source/openssl-3.0.7.tar.gz)
    set(OPENSSL_HASH SHA256=xxx)
endif()

ExternalProject_Add(
    openssl
    URL ${OPENSSL_URL}
    URL_HASH ${OPENSSL_HASH}
    CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/build_openssl.sh
        ${CMAKE_BINARY_DIR}/openssl
    BUILD_IN_SOURCE 1
)

# 使用条件判断决定是否启用某些依赖
option(ENABLE_CUDA "Enable CUDA support" OFF)
if(ENABLE_CUDA)
    ExternalProject_Add(
        cuda_lib
        GIT_REPOSITORY https://github.com/example/cuda-lib.git
        ONLY_IF ${ENABLE_CUDA}  # 只在条件满足时下载
    )
endif()

实用技巧总结

加速重复构建

cmake 复制代码
# 使用缓存目录
set(DEPS_CACHE ${CMAKE_SOURCE_DIR}/.deps_cache)
file(MAKE_DIRECTORY ${DEPS_CACHE})

ExternalProject_Add(mylib
    DOWNLOAD_DIR ${DEPS_CACHE}  # 共享下载缓存
    SOURCE_DIR ${DEPS_CACHE}/src/mylib
)

调试构建过程

cmake 复制代码
# 开启详细日志
ExternalProject_Add(mylib
    LOG_DOWNLOAD ON
    LOG_CONFIGURE ON
    LOG_BUILD ON
    LOG_INSTALL ON
    # 调试命令
    CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Configuring..." 
        COMMAND actual_configure_command
)

与主项目共享工具链

cmake 复制代码
# 传递工具链文件
ExternalProject_Add(mylib
    CMAKE_ARGS
        -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
        -DCMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING}
)
相关推荐
三品吉他手会点灯2 小时前
STM32 VSCode 开发-C/C++的环境配置中,找不到C/C++: Edit Configurations选项
c语言·c++·vscode·stm32·单片机·嵌入式硬件·编辑器
6Hzlia3 小时前
【Hot 100 刷题计划】 LeetCode 287. 寻找重复数 | C++ 数组判环 (快慢指针终极解法)
c++·算法·leetcode
Robot_Nav4 小时前
DPMPC-Planner:复杂静态环境与动态障碍物下的无人机实时轨迹规划框架
c++·无人机·mpc
水饺编程6 小时前
第5章,[标签 Win32] :获取设备环境的信息
c语言·c++·windows·visual studio
lhbian6 小时前
C++、C与易语言:编程语言对比解析
c语言·开发语言·c++
hehelm6 小时前
二叉搜索树
c++
云泽8086 小时前
笔试算法 - 双指针篇(一):移动零、复写零、快乐数与盛水容器
c++·算法
小堃学编程6 小时前
【项目实战】基于protobuf的发布订阅式消息队列(4)—— 服务端
c语言·c++·vscode·消息队列·gtest·protobuf·muduo
小白学大数据7 小时前
解决 Python 爬虫被限制:延迟抓取指令深度解析
开发语言·c++·爬虫·python