CMake02-熟悉FetchContent模块

725 阅读2分钟

一、该模块主要包括四个命令。

  1. FetchContent_Declare( ...) : 描述如何下载依赖库,name 声明下载库的名称,contentOptions 描述获取、更新外部库的方式(常用的有通过 Git Repo下载,通过 URL 下载等);
  2. FetchContent_MakeAvaliable( [...]) : 构建命令1所声明的 name1, name2, ...
  3. FetchContent_Populate() : 构建命令1所声明的 name 依赖, 该命令会定义三个变量:
    1. _POPULATED: 依赖是否已被构建
    2. _SOURCE_DIR: 依赖存储路径
    3. _BINARY_DIR: 依赖 Build 路径 若 FetchContent_Populate 不止有 name 参数,此时,不再使用 FetchContent_Declare 所定义的配置,而是由 FetchContent_Populate 给出,不再定义 _POPULATED,不会在全局定义*_SOURCE_DIR* 和 _BINARY_DIR,但仍会在当前作用域内定义,不再检测是否已经构建该依赖,具体语法为:
FetchContent_Populate(
  <name>
  [QUIET]
  [SUBBUILD_DIR <subBuildDir>]
  [SOURCE_DIR <srcDir>]
  [BINARY_DIR <binDir>]
  ...
)

其中,QUIET 表示隐藏与激活依赖相关的输出;SUBBUILD_DIR 用于指定 sub-build 路径;SOURCE_DIR 用于指定 source 路径,BINARY_DIR 用于指定 binary 路径,其余参数均会传递给 ExternalProject_Add()命令。

  1. FetchContent_GetProperties( [SOURCE_DIR ] [BINARY_DIR ] [POPULATED ]) : 获取与外部依赖 name 相关的属性。

二、举例应用场景

googletest 为例,使用 FetchContent 模块配置如下。

  1. 声明下载依赖库,指定获取方式为 git:
include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        e2239ee6043f73722e7aa812a459f54a28552929 # release-1.11.0
)
  1. 下载 googletest,使其可用:
FetchContent_MakeAvailable(googletest myCompanyIcons)

上述方式将库的声明与实际的下载分离,利于项目管理和维护。

三、使用总结

  • 总是优先使用 FetchContent_MakeAvailable 而不是 FetchContent_Populate
  • 总是优先使用 FetchContent_Declare 声明所有依赖,再使用 FetchContent_MakeAvailableFetchContent_Populate 构建依赖,确保当前项目能够控制依赖的具体内容,这是因为 FetchContent_Declare 不会覆盖先前声明,因此,当子项目与父项目有相同依赖时,子项目不会覆盖父项目的声明。假设依赖 use_other 也使用 FetchContent 声明依赖 other,我们需要:
# CORRECT: All details declared first, so they will take priority
FetchContent_Declare(uses_other ...)
FetchContent_Declare(other ...)
FetchContent_MakeAvailable(uses_other other)

而不是:

# WRONG: Should declare all details first
FetchContent_Declare(uses_other ...)
FetchContent_MakeAvailable(uses_other)

FetchContent_Declare(other ...)    # Will be ignored, uses_other beat us to it
FetchContent_MakeAvailable(other)  # Would use details declared by uses_other

FetchContent_MakeAvailable 会先检查依赖是否已经构建完成,因此不会重复构建,但 FetchContent_Populate 并不会,重复构建会报错,因此, 使用 FetchContent_Populate 前,必须按照上述示例,使用 FetchContent_GetProperties 获取变量 _POPULATED,检测是否需要构建该依赖。