CPM.cmake to make CMake's FetchContent easier

Pierre Gradot - Dec 28 '22 - - Dev Community

Scrolling at my list of starred projets of GitHub, I saw a tool I had forgotten, CPM.cmake, and decided that it was time to try it!

CPM.cmake (or just CPM) describes itself as "a cross-platform CMake script that adds dependency management capabilities to CMake". It's in fact a wrapper around CMake's FetchContent module that makes must easier to download packages.

Do demonstrate its usage, I will build a project that depends on 3 libraries from GitHub: the famous Catch2 and fmt, and the probably not-as-famous-as-it-deserves scnlib.

If you want to try the code below, here is a main.cpp that uses these libraries:

#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include <scn/scn.h>
#include <string>

TEST_CASE("My test case") {

    // Scan with scn
    std::string firstName;
    std::string lastName;
    unsigned int number;
    const auto result = scn::scan("Bill Clinton was the 42nd president of the United States",
                            "{} {} was the {}",
                            firstName, lastName, number);

    // Format with fmt
    const auto output = fmt::format("{} {} {}", firstName, lastName, number);

    // Test with Catch2
    CHECK(result);
    CHECK(output == "Bill Clinton 42");
}
Enter fullscreen mode Exit fullscreen mode

Fetching the libraries from GitHub wihtout CPM

Here is how you declare and download the libraries from GitHub without CPM, directly with FetchContent:

include(FetchContent)

FetchContent_Declare(
        Catch2
        GIT_REPOSITORY https://github.com/catchorg/Catch2.git
        GIT_TAG v3.0.0-preview4
)

FetchContent_Declare(
        fmt
        GIT_REPOSITORY https://github.com/fmtlib/fmt.git
        GIT_TAG 9.1.0
)

FetchContent_Declare(
        scnlib
        GIT_REPOSITORY https://github.com/eliaskosunen/scnlib.git
        GIT_TAG v1.1.2
)

FetchContent_MakeAvailable(Catch2 fmt scnlib)
Enter fullscreen mode Exit fullscreen mode

If you have never used FetchContent before:

  1. You include the script, which is provided natively by CMake.
  2. You declare your dependencies.
  3. You download them.

The targets from these libraries are then available and can be used with our own targets:

add_executable(${PROJECT_NAME}
        sources/main.cpp)

target_link_libraries(${PROJECT_NAME}
        PRIVATE
            Catch2::Catch2WithMain
            fmt
            scn)
Enter fullscreen mode Exit fullscreen mode

With CPM instead

CPM is just a single .cmake file but it is not provided out-of-the-box by CMake. You have to download it from GitHub.

The simplest solution is to browse the release page, download CPM.cmake from the desired release, and copy it to your project's directory.

Another nice solution is to ask CMake to download the file for you. To do so, you can simply add the following lines at the beginning of your CMakeLists.txt :

set(CPM_DOWNLOAD_LOCATION ${CMAKE_BINARY_DIR}/CPM.cmake)

file(DOWNLOAD
        https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/cpm.cmake
        ${CPM_DOWNLOAD_LOCATION})

include(${CPM_DOWNLOAD_LOCATION})
Enter fullscreen mode Exit fullscreen mode

And then, it's so really simple to get our libraries from GitHub! We just have to replace all calls to FetchContent_xxx() functions with:

CPMAddPackage("gh:catchorg/Catch2#v3.0.0-preview4")
CPMAddPackage("gh:eliaskosunen/scnlib#v1.1.2")
CPMAddPackage("gh:fmtlib/fmt#9.1.0")
Enter fullscreen mode Exit fullscreen mode

There is no equivalent call to FetchContent_MakeAvailable().

The call to target_link_libraries() is unchanged.

The SYSTEM property

In my previous article "The SYSTEM property from CMake 3.25", I talked about the SYSTEM option for the function FetchContent_Declare(). As of Decembre 28th 2022, there is unfortunately no matching option in CPM... An issue has been created in July but it's still open.

Conclusion

CPM can do more than just simplifying calls to FetchContent_Declare(). For instance, it has a cache so that dependencies used by several projects can be downloaded only once.

But honnestly, the simplicity of CPMAddPackage() compared to FetchContent_Declare() seems enough to use CPM 🥳

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player