Loading dependencies in C++ projects using CMake
Tags: cpp, cmake, beginners, productivity
Published: 2022-01-26 14:23:54

With the modern programming languages, has turned common has a way to manage dependencies of your projects, either using npm, yarn, pip, or cargo this process is easy. However, when using C or C++ this process is not trivial. With this in mind, I thought to write this post about how I automate the download and configuration of libraries from Git repositories using the [CMake](https://cmake.org/).

> Some solutions were created to solve this problem, such as [conan](https://conan.io/) and [vcpkg](https://vcpkg.io/) but it loads another dependency installed on your system and requires your dependency to be indexed by its server.

---
## The FetchContent module

CMake is a build system that automates the generation of Makefiles that define the compilation process of a source code project. It provides various modules that help with configuration in many ways. One of these modules is [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html), which helps to fetch external content at configuration time, ie when CMake is generating the Makefile.

It is quite simple to use it, in the example below we have added GoogleTest to our project:

```cmake
include(FetchContent)
FetchContent_Declare(GoogleTest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG        master
)
FetchContent_MakeAvailable(GoogleTest) 
```

After that, we have the GoogleTest project as well as its libraries accessible at configuration time, so we can link any executable (or library) with GoogleTest, for example:

```cmake
# create the main target for run the tests
add_executable(mytests
        test/main.cpp
        test/awesome_test.cpp)

# link with the googletest main library
target_link_libraries(mytests gtest_main)
```

## Create a CMake function

To automate this process, I usually create a simple function that concentrates the code that uses FetchContent, this makes the CMake script more readable and easier to add new dependencies.

```cmake
function(GET_DEPENDENCY D_NAME D_URL D_TAG)
    message(CHECK_START "Configuring ${D_NAME}")
    FetchContent_Declare(${D_NAME}
        GIT_REPOSITORY ${D_URL}
        GIT_TAG        ${D_TAG}
    )
    FetchContent_MakeAvailable(${D_NAME})
endfunction()
```

So I can use the `get_dependency` function passing three parameters:
- `D_NAME`: the name to refers to this dependency.
- `D_URL`: the link remote Git repository (example: `https://github.com/...`
- `D_TAG`: the Git tag to get, may be the commit hash or version (examples: `de6e5fa`,`v1.0.2`, etc).

```cmake
get_dependency(GoogleTest "https://github.com/google/googletest.git" master)
```

To add more dependencies...

```cmake
get_dependency(spdlog "https://github.com/gabime/spdlog" v1.8.5)
get_dependency(CLI11 "https://github.com/CLIUtils/CLI11" v1.9.1)
get_dependency(GoogleTest "https://github.com/google/googletest" master)
```

When I use this configuration, it becomes much easier to add and remove dependencies on external libraries, especially since I use open source libraries on Github. This also helps in team design by not having to have a complex set of steps to set up the development environment on the developers' different machines. It also brings a benefit to using CI (Continuous Integration).

## How it works

When you run the configuration command, such as 

```cmake
cmake -S . -B <build-dir>
```

CMake downloads the source code from `D_URL` and store into a directory `<build-dir>/_deps`. Inside the directory, FetchContent create three sub-directories: `${D_NAME}-src`, `${D_NAME}-build` and `${D_NAME}-subbuild`.

Example with GoogleTest:

```raw
<build-dir>/
|   _deps/
|   |   googletest-src/
|   |   googletest-subbuild/
|   |   googletest-build/
```

* `src`: source code of the dependency (from Git repository).
* `build`: CMakeFiles directory of the dependency.
* `subbuild`: created to perform the population of content.

For details, see [FecthContent documentation](https://cmake.org/cmake/help/latest/module/FetchContent.html).