DEV Community

Pierre Gradot
Pierre Gradot

Posted on • Updated on

CPM.cmake to make CMake's FetchContent easier

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 🥳

Top comments (3)

Collapse
 
baduit profile image
Lena

I always use it when I'm not already using vcpkg and it is a fantastic tool !

Collapse
 
pgradot profile image
Pierre Gradot

vcpkg is instead the next level. Good to have a feedback for CPM.cmake!

Collapse
 
baduit profile image
Lena

One good point for CPM.cmake over vcpkg is that it is easier to use your own projects as depencies. With vcpkg you need to add your project on the official registry or on your own registry (that you must create then) and it requires a little bit of work. With CPM.cmake, if your CMake configuration is not too exotic you just one line to include it.