目录
[3.完整示例:集成 fmt 库](#3.完整示例:集成 fmt 库)
4.externalproject_add与FetchContent区别
1.简介
externalproject_add
是 CMake 中 ExternalProject
模块提供的核心命令,用于在构建阶段 下载、配置、构建和安装外部项目(如第三方库)。它与 FetchContent
的核心区别在于:FetchContent
在配置阶段 处理依赖,而 externalproject_add
在构建阶段处理,更适合复杂的外部项目集成(如非 CMake 项目、需要自定义构建步骤的项目)。
CMake进阶: 使用FetchContent方法基于gTest的C++单元测试_cmake fetchcontent-CSDN博客
基本语法:
cpp
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
用于在当前项目的构建过程中,动态集成外部项目,支持:
- 从 Git 仓库、压缩包等来源下载外部项目源码;
- 自定义外部项目的配置(如编译选项、安装路径);
- 控制外部项目的构建和安装流程;
- 声明外部项目之间的依赖关系(确保构建顺序)。
3.完整示例:集成 fmt 库
以下示例使用 externalproject_add
集成 fmt
库(一个 C++ 格式化库),并在主项目中使用。
1.项目结构
cpp
external_demo/
├── CMakeLists.txt # 根配置
└── main.cpp # 主程序(使用 fmt 库)
2.CMakeLists.txt
配置
cpp
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.git
GIT_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
代码
cpp
#include <fmt/core.h>
int main() {
fmt::print("Hello, {}! 2+3={}\n", "fmt", 2+3); // 使用 fmt 库
return 0;
}
4.构建与运行
cpp
# 创建构建目录
mkdir build && cd build
# 生成构建文件
cmake ..
# 编译(会先下载、构建、安装 fmt,再编译主程序)
cmake --build .
# 运行主程序
./Debug/myapp.exe # Windows(Debug 配置)
# 或 ./myapp # Linux/macOS
输出:Hello, fmt! 2+3=5
5.关键参数说明
CMAKE_ARGS
:向外部 CMake 项目传递配置参数(如安装路径、编译选项),是最常用的参数之一。INSTALL_DIR
:指定外部项目的安装目录,便于主项目统一引用头文件和库。add_dependencies
:声明主项目依赖外部项目,确保外部项目先构建安装完成。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
)引用,无需手动指定路径。示例:
cpp
include(FetchContent)
FetchContent_Declare(
googletest
URL 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
:独立构建 ,外部依赖作为 "独立项目"。它会为外部依赖创建一个独立的构建流程(下载、配置、编译、安装),生成的库 / 二进制文件位于单独的目录中。当前项目需要手动指定外部依赖的路径(源码目录、二进制目录)才能引用其产物(如库文件),无法直接使用其目标。
示例:
cpp
include(ExternalProject)
ExternalProject_Add(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
PREFIX ${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
集成)。 - 需要在配置阶段确定依赖是否可用(如条件编译依赖于外部项目的特性)。
- 依赖规模较小,希望简化集成(直接使用目标,无需手动处理路径)。
- 希望外部依赖的构建产物(如库)与当前项目的构建目录合并管理。
- 外部依赖是 CMake 项目 (可通过
-
ExternalProject_Add
更适合:- 外部依赖非 CMake 项目 (如 Makefile 项目、Autotools 项目),需要自定义构建命令(如
CONFIGURE_COMMAND
、BUILD_COMMAND
)。 - 依赖规模较大,希望延迟构建(避免配置阶段耗时过长)。
- 需要独立控制外部项目的安装路径(如安装到系统目录或自定义前缀)。
- 依赖需要跨项目共享(如多个子项目共用同一个外部依赖的构建产物)。
- 外部依赖非 CMake 项目 (如 Makefile 项目、Autotools 项目),需要自定义构建命令(如
5.其他关键差异
特性 | FetchContent |
ExternalProject_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