当前位置: 首页 > news >正文

CMake进阶: externalproject_add用于在构建阶段下载、配置、构建和安装外部项目

目录

1.简介

2.核心作用

3.完整示例:集成 fmt 库

4.externalproject_add与FetchContent区别

5.总结

相关链接


1.简介

        externalproject_add 是 CMake 中 ExternalProject 模块提供的核心命令,用于在构建阶段下载、配置、构建和安装外部项目(如第三方库)。它与 FetchContent 的核心区别在于:FetchContent 在配置阶段处理依赖,而 externalproject_add 在构建阶段处理,更适合复杂的外部项目集成(如非 CMake 项目、需要自定义构建步骤的项目)。

CMake进阶: 使用FetchContent方法基于gTest的C++单元测试_cmake fetchcontent-CSDN博客

        基本语法:

include(ExternalProject)  # 必须先引入模块ExternalProject_Add(<项目名称>  # 自定义名称,用于标识外部项目[SOURCE_DIR <源码目录>]  # 外部项目源码路径(绝对路径或相对路径)[BINARY_DIR <构建目录>]  # 外部项目构建路径[INSTALL_DIR <安装目录>]  # 外部项目安装路径(默认与构建目录关联)# 下载方式(选一种)[GIT_REPOSITORY <Git仓库地址>]  # 从Git仓库下载[GIT_TAG <标签/分支/commit>]    # Git版本标识[URL <压缩包地址>]              # 从压缩包下载(.zip/.tar.gz等)[URL_HASH <算法>=<哈希值>]      # 验证压缩包完整性# 配置步骤(针对CMake项目)[CMAKE_ARGS <参数1> <参数2>...]  # 传递给外部项目的CMake参数(如编译选项)# 构建步骤[BUILD_COMMAND <命令>]  # 自定义构建命令(默认由生成器决定,如make、msbuild)[BUILD_IN_SOURCE ON]    # 是否在源码目录内构建(默认在BINARY_DIR)# 安装步骤[INSTALL_COMMAND <命令>]  # 自定义安装命令(默认是make install等)# 依赖关系[DEPENDS <其他外部项目>]  # 声明依赖的其他外部项目(确保先构建依赖)
)

2.核心作用

externalproject_add 用于在当前项目的构建过程中,动态集成外部项目,支持:

  1. 从 Git 仓库、压缩包等来源下载外部项目源码;
  2. 自定义外部项目的配置(如编译选项、安装路径);
  3. 控制外部项目的构建和安装流程;
  4. 声明外部项目之间的依赖关系(确保构建顺序)。

3.完整示例:集成 fmt 库

以下示例使用 externalproject_add 集成 fmt 库(一个 C++ 格式化库),并在主项目中使用。

1.项目结构

external_demo/
├── CMakeLists.txt  # 根配置
└── main.cpp        # 主程序(使用 fmt 库)

2.CMakeLists.txt 配置

cmake_minimum_required(VERSION 3.14)
project(ExternalDemo)# 引入 ExternalProject 模块
include(ExternalProject)# 定义外部项目安装路径(可选,统一管理外部依赖安装目录)
set(EXTERNAL_INSTALL_DIR ${CMAKE_BINARY_DIR}/external_install)
file(MAKE_DIRECTORY ${EXTERNAL_INSTALL_DIR}/include)  # 确保头文件目录存在
file(MAKE_DIRECTORY ${EXTERNAL_INSTALL_DIR}/lib)      # 确保库目录存在# -------------------------- 声明外部项目 fmt --------------------------
ExternalProject_Add(fmt_external  # 外部项目名称(自定义)# 下载配置(从Git仓库获取)GIT_REPOSITORY https://github.com/fmtlib/fmt.gitGIT_TAG 10.2.1  # 版本标签GIT_SHALLOW ON  # 浅克隆# 目录配置SOURCE_DIR ${CMAKE_BINARY_DIR}/external_src/fmt  # 源码目录BINARY_DIR ${CMAKE_BINARY_DIR}/external_build/fmt  # 构建目录INSTALL_DIR ${EXTERNAL_INSTALL_DIR}  # 安装目录(自定义)# 传递给 fmt 的 CMake 配置参数(如指定安装路径、禁用测试)CMAKE_ARGS-DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_DIR}  # 指定安装路径-DFMT_BUILD_TESTS=OFF  # 禁用 fmt 自身的测试-DCMAKE_CXX_STANDARD=17  # 指定 C++ 标准# 安装后,fmt 会将头文件安装到 ${EXTERNAL_INSTALL_DIR}/include,库文件到 lib
)# -------------------------- 主项目配置 --------------------------
# 定义主程序目标
add_executable(myapp main.cpp)# 依赖外部项目 fmt_external(确保 fmt 先安装完成)
add_dependencies(myapp fmt_external)# 链接 fmt 库(需手动指定头文件和库路径)
target_include_directories(myapp PRIVATE ${EXTERNAL_INSTALL_DIR}/include)
target_link_directories(myapp PRIVATE ${EXTERNAL_INSTALL_DIR}/lib)
target_link_libraries(myapp PRIVATE fmt)  # fmt 库名称# 指定 C++ 标准
target_compile_features(myapp PRIVATE cxx_std_17)

3.main.cpp 代码

#include <fmt/core.h>int main() {fmt::print("Hello, {}! 2+3={}\n", "fmt", 2+3);  // 使用 fmt 库return 0;
}

4.构建与运行

# 创建构建目录
mkdir build && cd build# 生成构建文件
cmake ..# 编译(会先下载、构建、安装 fmt,再编译主程序)
cmake --build .# 运行主程序
./Debug/myapp.exe  # Windows(Debug 配置)
# 或 ./myapp  # Linux/macOS

输出:Hello, fmt! 2+3=5

5.关键参数说明

  1. CMAKE_ARGS:向外部 CMake 项目传递配置参数(如安装路径、编译选项),是最常用的参数之一。
  2. INSTALL_DIR:指定外部项目的安装目录,便于主项目统一引用头文件和库。
  3. add_dependencies:声明主项目依赖外部项目,确保外部项目先构建安装完成。
  4. SOURCE_DIR/BINARY_DIR:手动指定源码和构建目录(可选,默认在构建目录的 ExternalProject 子目录)。

4.externalproject_add与FetchContent区别

1.处理阶段不同

  • FetchContent:在配置阶段(cmake 命令执行时) 处理外部依赖。
    它会在 CMake 配置项目时下载 / 获取外部源码,并将其作为当前项目的一部分直接集成到构建系统中(类似 add_subdirectory)。配置完成后,外部依赖的目标(targets)会直接暴露给当前项目。

  • ExternalProject_Add:在构建阶段(make/ninja 等命令执行时) 处理外部依赖。
    它仅在 CMake 配置时声明外部项目的构建规则,实际的下载、配置、编译、安装等操作会延迟到用户执行构建命令时才执行。配置阶段不会获取源码或构建依赖,仅记录构建步骤。

2.集成方式不同

  • FetchContent深度集成,外部依赖作为当前项目的 “子项目”。
    通过 FetchContent_MakeAvailable() 会自动调用 add_subdirectory() 将外部项目添加到当前构建系统,因此外部项目的目标(如库目标 gtest)可以直接被当前项目的目标(如 target_link_libraries)引用,无需手动指定路径。

    示例:

include(FetchContent)
FetchContent_Declare(googletestURL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
)
FetchContent_MakeAvailable(googletest)  # 相当于 add_subdirectory(外部源码目录)# 直接使用外部项目的目标
add_executable(my_test test.cpp)
target_link_libraries(my_test gtest_main)  # gtest_main 是 googletest 的目标
  • ExternalProject_Add独立构建,外部依赖作为 “独立项目”。
    它会为外部依赖创建一个独立的构建流程(下载、配置、编译、安装),生成的库 / 二进制文件位于单独的目录中。当前项目需要手动指定外部依赖的路径(源码目录、二进制目录)才能引用其产物(如库文件),无法直接使用其目标。

    示例:

include(ExternalProject)
ExternalProject_Add(googletestURL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zipPREFIX ${CMAKE_BINARY_DIR}/googletest  # 外部项目的构建/安装根目录INSTALL_COMMAND ""  # 禁用安装(如需安装可指定路径)
)# 获取外部项目的路径
ExternalProject_Get_Property(googletest SOURCE_DIR BINARY_DIR)# 手动指定库路径并链接
add_executable(my_test test.cpp)
target_include_directories(my_test PRIVATE ${SOURCE_DIR}/googletest/include)
target_link_libraries(my_test PRIVATE ${BINARY_DIR}/lib/libgtest_main.a)
add_dependencies(my_test googletest)  # 确保外部项目先于当前目标构建

3.依赖关系管理不同

  • FetchContent:依赖关系由 CMake 自动处理。
    由于外部项目的目标在配置阶段已暴露,当前项目的目标可以直接依赖外部目标(如 target_link_libraries),CMake 会自动调整构建顺序(先构建外部依赖,再构建当前目标)。

  • ExternalProject_Add:依赖关系需要手动声明。
    外部项目是独立构建的,当前项目必须通过 add_dependencies() 显式指定依赖关系,否则可能出现 “当前目标先于外部依赖构建” 的错误。

4.适用场景不同

  • FetchContent 更适合

    • 外部依赖是 CMake 项目(可通过 add_subdirectory 集成)。
    • 需要在配置阶段确定依赖是否可用(如条件编译依赖于外部项目的特性)。
    • 依赖规模较小,希望简化集成(直接使用目标,无需手动处理路径)。
    • 希望外部依赖的构建产物(如库)与当前项目的构建目录合并管理。
  • ExternalProject_Add 更适合

    • 外部依赖非 CMake 项目(如 Makefile 项目、Autotools 项目),需要自定义构建命令(如 CONFIGURE_COMMANDBUILD_COMMAND)。
    • 依赖规模较大,希望延迟构建(避免配置阶段耗时过长)。
    • 需要独立控制外部项目的安装路径(如安装到系统目录或自定义前缀)。
    • 依赖需要跨项目共享(如多个子项目共用同一个外部依赖的构建产物)。

5.其他关键差异

特性FetchContentExternalProject_Add
源码获取时机配置阶段(cmake 时)构建阶段(make/ninja 时)
目标(targets)暴露自动暴露,可直接引用不暴露,需手动处理路径
支持非 CMake 项目不支持(依赖 add_subdirectory支持(可自定义构建命令)
配置阶段耗时可能较长(需下载 / 处理源码)较短(仅声明规则,不执行实际操作)
安装支持通常作为子项目构建,不单独安装支持自定义安装步骤(INSTALL_COMMAND

总结:

  • 优先用 FetchContent:依赖是 CMake 项目,追求简单集成,需要在配置阶段确定依赖。
  • 优先用 ExternalProject_Add:依赖是非 CMake 项目,需要独立控制构建 / 安装,或希望延迟构建。

5.总结

externalproject_add 是处理复杂外部依赖的灵活工具,适合非 CMake 项目或需要自定义构建流程的场景。但由于其在构建阶段执行且不自动暴露目标,配置复杂度高于 FetchContent。实际项目中,优先使用 FetchContent 处理 CMake 依赖,复杂场景再考虑 externalproject_add

相关链接

  • CMake 官网 CMake - Upgrade Your Software Build System
  • CMake 官方文档:CMake Tutorial — CMake 4.1.0 Documentation
  • CMake 源码:https://github.com/Kitware/CMake
  • CMake 源码:CMake · GitLab
  • 中文版基础介绍: CMake 入门实战 | HaHack
  • wiki: Home · Wiki · CMake / Community · GitLab
  • Modern CMake 简体中文版:  Introduction · Modern CMake
http://www.lryc.cn/news/621380.html

相关文章:

  • Google Gemini 的深度研究终于进入 API 阶段
  • 入门概述(面试常问)
  • CodeTop 复习
  • C#WPF实战出真汁01--项目介绍
  • C++入门自学Day11-- List类型的自实现
  • Claude Code频繁出错怎么办?深入架构层面的故障排除指南
  • 力扣-5.最长回文子串
  • Python3 详解:从基础到进阶的完整指南
  • RS232串行线是什么?
  • 机器学习-支持向量机器(SVM)
  • 机器学习——TF-IDF算法
  • 2025天府杯数学建模A题分析
  • Docker存储卷备份策略于VPS服务器环境的实施标准与恢复测试
  • 【ai写代码】lua-判断表是否被修改
  • 【JDK】Linux 系统下 JDK 安装与环境变量配置全教程
  • Auto-Coder的CLI 和 Python API
  • TOTP算法与HOTP算法
  • 下标访问操作符 [] 与函数调用操作符 ()
  • 【软考中级网络工程师】知识点之常用网络诊断和配置命令
  • Qt---Qt函数库
  • 深度学习-卷积神经网络CNN-膨胀卷积、可分离卷积(空间可分离、深度可分离)、分组卷积
  • 小知识点:splice与slice
  • 5.Ansible-playbook-模块介绍(知识点补充)
  • 【从零开始学习Redis】项目实战-黑马点评D1
  • Rabbitmq+STS+discovery_k8s +localpv部署排坑详解
  • 迅雷链接在线解密解析工具系统源码/本地化API/开源(源码下载)
  • TCP 连接管理:深入分析四次握手与三次挥手
  • NetLimiter:精准掌控网络流量,优化网络体验
  • vue3+leaflet案例:告警系统GIS一张图(附源码下载)
  • 新增和编辑共用弹窗模板