

Introduction:
Every project needs to handle dependencies, and using CMake
makes it easy to check whether these dependencies exist in the configured project. In previous notes, we demonstrated how to find dependencies installed on the system, and so far we have been using this pattern. However, when dependencies are not met, we can only fail the configuration and warn the user about the failure reason. Nevertheless, using CMake
allows us to organize our project so that if dependencies cannot be found on the system, they can be automatically fetched and built. Subsequent notes will introduce and analyze the standard modules ExternalProject.cmake
and FetchContent.cmake
, as well as their use in superbuild mode
. The former allows retrieving project dependencies at build time, while the latter allows retrieving dependencies at configuration time (added in CMake
version 3.11
). By using superbuild mode, we can leverage CMake
as a package manager: dependencies will be handled in the same way across the same project, whether they are already available on the system or need to be rebuilt.
First, we will introduce superbuild mode through a simple example. We will demonstrate how to use the ExternalProject_Add
command to build a hello_world
program.

✦
Project Structure
✦
.
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── hello-world.cpp
Project Structure:
https://gitee.com/jiangli01/tutorials/tree/master/cmake-tutorial/chapter8/01
✦
Related Source Code
✦
src/CMakeLists.txt
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(example_core LANGUAGES CXX)
add_executable(hello-world hello_world.cpp)
src/hello_world.cpp
#include <cstdlib>
#include <iostream>
#include <string>
std::string say_hello() {
return std::string("Hello, CMake superbuild world!");
}
int main() {
std::cout << say_hello() << std::endl;
return EXIT_SUCCESS;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(example LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set_property(DIRECTORY PROPERTY EP_BASE ${CMAKE_BINARY_DIR}/subprojects)
include(ExternalProject)
ExternalProject_Add(${PROJECT_NAME}_core
SOURCE_DIR
${CMAKE_CURRENT_LIST_DIR}/src
CMAKE_ARGS
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
-DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
-DCMAKE_CXX_STANDARD_REQUIRED=${CMAKE_CXX_STANDARD_REQUIRED}
CMAKE_CACHE_ARGS
-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}
BUILD_ALWAYS
1
INSTALL_COMMAND
""
)
set_property(DIRECTORY PROPERTY EP_BASE ${CMAKE_BINARY_DIR}/subprojects)
Set the EP_BASE
directory property for the current directory and underlying directories.
include(ExternalProject)
Include the ExternalProject.cmake
standard module. This module provides the ExternalProject_Add
function.
ExternalProject_Add(${PROJECT_NAME}_core
SOURCE_DIR
${CMAKE_CURRENT_LIST_DIR}/src
CMAKE_ARGS
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
-DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
-DCMAKE_CXX_STANDARD_REQUIRED=${CMAKE_CXX_STANDARD_REQUIRED}
CMAKE_CACHE_ARGS
-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}
BUILD_ALWAYS
1
INSTALL_COMMAND
""
)
The Hello, World
source code is added as an external project by calling the ExternalProject_Add
function. The name of the external project is example_core
.
The ExternalProject_Add
command can be used to add third-party sources. This article manages its own project as a collection of different CMake
projects. In this example, both the main CMakeLists.txt
and the sub CMakeLists.txt
declare a CMake
project, both using the project
command.
The ExternalProject_Add
has many options for configuring and compiling external projects. These options can be categorized into the following types:
-
Directory: Used to tune the structure of the source code and the build directory for external projects. In this article, we use the
SOURCE_DIR
option to letCMake
know that the source files are in the${CMAKE_CURRENT_LIST_DIR}/src
folder. The directory for building the project and storing temporary files can also be specified in such options or directory properties. By setting theEP_BASE
directory property,CMake
will set all directories for each subproject according to the following layout:TMP_DIR = <EP_BASE>/tmp/<name> STAMP_DIR = <EP_BASE>/Stamp/<name> DOWNLOAD_DIR = <EP_BASE>/Download/<name> SOURCE_DIR = <EP_BASE>/Source/<name> BINARY_DIR = <EP_BASE>/Build/<name> INSTALL_DIR = <EP_BASE>/Install/<name>
-
Download: The code for external projects may need to be downloaded from online repositories or resources.
-
Update and Patch: Can be used to define how to update the source code of external projects or how to apply patches.
-
Configure: By default,
CMake
assumes that the external project is configured usingCMake
. As shown below, we are not limited to this case. If the external project is aCMake
project,ExternalProject_Add
will call theCMake
executable and pass options. For this example, we pass configuration parameters through theCMAKE_ARGS
andCMAKE_CACHE_ARGS
options. The former is passed as command line arguments directly, while the latter is passed throughCMake
script files. In practice, the script file is located atbuild/subprojects/tmp/example_core/example_core-cache-.cmake
. The configuration is as follows:loading initial cache file /home/jiangli/repo/tutorials/cmake-tutorial/chapter8/01/build/subprojects/tmp/example_core/example_core-cache-.cmake -- The CXX compiler identification is GNU 9.4.0 -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: /home/jiangli/repo/tutorials/cmake-tutorial/chapter8/01/build/subprojects/Build/example_core
-
Build: Can be used to adjust the actual compilation of external projects. We use the
BUILD_ALWAYS
option to ensure that the external project is always rebuilt. -
Install: These options are used to configure how the external project should be installed. We leave
INSTALL_COMMAND
empty. -
Test: Run tests for software built from source code. Options like these for
ExternalProject_Add
can be used for this purpose. We did not use these options because theHello, World
example does not have any tests.
✦
Results Display
✦
$ mkdir -p build
$ cmake ..
$ cmake --build .
The structure of the build directory is a bit more complex, and the contents of the subprojects
folder are as follows:
build/subprojects/
├── Build
│ └── example_core
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── hello-world
│ └── Makefile
├── Download
│ └── example_core
├── Install
│ └── example_core
├── Stamp
│ └── exampleq_core
│ ├── example_core-configure
│ ├── example_core-done
│ ├── example_core-download
│ ├── example_core-install
│ ├── example_core-mkdir
│ ├── example_core-patch
│ └── example_core-update
└── tmp
└── example_core
├── example_core-cache-.cmake
├── example_core-cfgcmd.txt
└── example_core-cfgcmd.txt.in
✦
Additional Content
✦
ExternalProject.cmake
defines the ExternalProject_Get_Property
command, which is very useful for retrieving properties of external projects. The properties of external projects are set when the ExternalProject_Add
command is first called. For example, when configuring example_core
, retrieving the parameters to be passed to CMake
can be achieved as follows:
ExternalProject_Get_Property(${PROJECT_NAME}_core CMAKE_ARGS)
message(STATUS "CMAKE_ARGS of ${PROJECT_NAME}_core ${CMAKE_ARGS}")
The ExternalProject.cmake
module defines the following additional commands:
-
ExternalProject_Add_Step
: When adding an external project, this command allows locking additional commands as custom steps on it. -
ExternalProject_Add_StepTargets
: Allows defining steps (such as build and test steps) in the external project as separate targets. This means these steps can be triggered separately from the complete external project, allowing fine-grained control over complex dependencies in the project. -
ExternalProject_Add_StepDependencies
: Sometimes, the steps of an external project may depend on external targets, and this command is designed to handle those situations.
Finally, I wish everyone to become stronger!!! If this note has helped you, please like and share!!!🌹🌹🌹

