当前位置: 首页 > 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.dtcms.com/a/330749.html

相关文章:

  • 常见的Jmeter压测问题
  • 飞算 JavaAI 云原生实践:基于 Docker 与 K8s 的自动化部署架构解析
  • 用架构建模工具Sparx EA绘制企业转型路线图
  • C++状态模式详解:从OpenBMC源码看架构、原理与应用
  • NineData云原生智能数据管理平台新功能发布|2025年7月版
  • 云原生俱乐部-k8s知识点归纳(2)
  • 生产环境中Debezium CDC与Kafka实时流处理实战指南
  • 3ds MAX文件/贴图名称乱码?6大根源及解决方案
  • .NET 在鸿蒙系统(HarmonyOS Next)上的适配探索与实践
  • 界面设计风格解析 | ABB 3D社交媒体视觉效果设计
  • 【力扣56】合并区间
  • 一种适用于 3D 低剂量和少视角心脏单光子发射计算机断层成像(SPECT)的可泛化扩散框架|文献速递-深度学习人工智能医疗图像
  • MTK平台Wi-Fi学习--wifi channel 通过国家码进行功率限制和wifi eFEM 基本配置和wifi Tx SEM问题
  • 【深度学习】深度学习的四个核心步骤:从房价预测看机器学习本质
  • Navicat 全量增量数据库迁移
  • 【经验分享】如何在Vscode的Jupyter Notebook中设置默认显示行号
  • OpenCv(三)——图像平滑处理
  • dockerfile示例
  • 【论文阅读-Part1】PIKE-RAG: sPecIalized KnowledgE and Rationale Augmented Generation
  • ACCESS SQL句子最长是多少个字符?
  • 机器学习-支持向量机器(SVM)
  • 如何查看SQL Server的当前端口
  • mysql 提示符及快捷执行
  • 苹果新专利曝光-或将实现六面玻璃外壳 iPhone
  • GO学习记录五——数据库表的增删改查
  • DataHub IoT Gateway:工业现场设备与云端平台安全互联的高效解决方案
  • DataHub OPC Gateway:实现OPC UA与OPC DA无缝集成的高性能网关
  • 解密Redis速度神话:从I/O多路复用到零拷贝
  • MySQL工具包中的其他程序
  • uniapp自定义封装支付密码组件(vue3)