What is CMake FetchContent?

Introduction

In CMake, FetchContent module is used to fetch and add an external cmake project or subdirectory to our project at the configure step. The process is as if add_subdirectory() is run for them. Due to this,

  • All the targets of external project become available in our project.
  • When we build our project, the external is built as a subdirectory of our project.
  • If a target of our project links to the external project, the interface and public include directories are also passed to our target. Therefore, we can use the external project’s public and interface headers in the C++ code of our target.

How to use

We can give the url of a git repository as an external project. I created an example repo on my Github account for this:

# To include FetchContent functions
include(FetchContent)

# Give all details of the external project
FetchContent_Declare(
  geometry # a custom name to refer to in MakeAvailable
  GIT_REPOSITORY https://github.com/sorush-khajepor/geometry.git
  GIT_TAG 1.0
)

# The external is downloaded by this:
FetchContent_MakeAvailable(geometry)

The source code of geometry is downloaded and unpacked in

build/_deps/geometrygithub-src

and it is built in

build/_deps/geometrygithub-build

In the example below, we are giving the URL of a zip file:

include(FetchContent)
FetchContent_Declare(
  geometry
  URL https://github.com/sorush-khajepor/geometry/archive/refs/tags/1.0.zip
  
  # set files' timestamps to time of extraction
  DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
FetchContent_MakeAvailable(geometry)

Note that, it is automatically unzipped. There are many download and git repository options mentioned in Download Step Options of CMake Manual for ExternalProject_Add() which is valid for FetchContent.

If the external project is on a local storage, you can fetch it as:

FetchContent_Declare(
  mylib
  SOURCE_DIR /absolute/path/to/mylib_source
)
FetchContent_MakeAvailable(mylib)

Repeated dependency

I explain this with an example. We have a build system that contains project A and project B. Project A depends on project B and Eigen library, and project B depends on Eigen library. Both projects run FetchContent separately, what will happen?

Both will use the same downloaded Eigen. The first FetchContent_MakeAvailable(Eigen) will download Eigen, the next one will be ignored even if it has different details.

Also, ensure the name of a fetched content is universal (preferably the name of its project without prefix or postfix) in our build system. In other words, differnt projects in our build system fetch it with the same name. For instance, for prject A, we have:

FetchContent_Declare(
  Eigen
  # details
)
FetchContent_MakeAvailable(Eigen)

And for project B we have

FetchContent_Declare(
  Eigen34
  # details
)
FetchContent_MakeAvailable(Eigen2)

As Eigen and Eigen34 are different names, CMake downloads both and includes them as subdirectories, then we get conflicts.

Avoid install

If a fetched content has install instructions, it will be installed along our project. If we are linking statically against the fetched content, we may not need its library at install step. To disable install step of a fetched content we use option EXCLUDE_FROM_ALL that results in removing the fetched project from ALL target. It also removes the fetched project from IDEs. Note that with this option, the fetched content will be built if a target depepends on it (which is the goal of fetched content), but not by default.

FetchContent VS Find_Package

FetchContent is for when our a project depends on an external CMake source project that we want to build along our project. However, Find_Package() is for when the dependency is already built and installed somewhere.

FetchContent VS ExternalProject_Add

ExternalProject_Add() treats the external code as a completely separate build. It downloads, configures, builds, and installs the project in its own build tree. Our main project can then depend on the installed artifacts (e.g., libraries or binaries) rather than the source code itself (FetchedContent way).

ExternalProject_Add() is useful for complex scenarios where the external project has its own build system or requires a different configuration than your main project.

Another detailed difference is that FetchContent_MakeAvailable() downloads immidiately at the configure step, but ExternalProject_Add() defers the download to the build step. Therefore, if you need to use your material at the configure step, use FetchContent.

Fetch but not add_subdirectory()

FetchContent can also be used to download a content that is not a CMake project like some data files. But if the content is a CMake project, it will be added to the build. To avoid that:

  • The first option is to use FetchContent and have it in the build, but exclude it from default build by using EXCLUDE_FROM_ALL.
  • The second option is ExternalProject_Add().
  • The third option is that instead of FetchContent_MakeAvailable(), we can use FetchContent_Populate() to download the content and skip add_subdirectory() command. However, CMake documentation is strongly against using FetchContent_Populate().
  • The forth option is to use file(DOWNLOAD) and file(ARCHIVE_EXTRACT) command. For file(DOWNLOAD), to avoid re-downloading with every CMake call, use EXPECTED_HASH.
Tags ➡ C++

Subscribe

I notify you of my new posts

Latest Posts

Comments

0 comment