DEV Community

Ronaldd Pinho
Ronaldd Pinho

Posted on

Loading dependencies in C++ projects using CMake

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.

Some solutions were created to solve this problem, such as conan and vcpkg 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, 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:

include(FetchContent)
FetchContent_Declare(GoogleTest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG        master
)
FetchContent_MakeAvailable(GoogleTest) 
Enter fullscreen mode Exit fullscreen mode

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:

# 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)
Enter fullscreen mode Exit fullscreen mode

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.

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()
Enter fullscreen mode Exit fullscreen mode

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).
get_dependency(GoogleTest "https://github.com/google/googletest.git" master)
Enter fullscreen mode Exit fullscreen mode

To add more dependencies...

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)
Enter fullscreen mode Exit fullscreen mode

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 -S . -B <build-dir>
Enter fullscreen mode Exit fullscreen mode

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:

<build-dir>/
|   _deps/
|   |   googletest-src/
|   |   googletest-subbuild/
|   |   googletest-build/
Enter fullscreen mode Exit fullscreen mode
  • 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.

Discussion (0)