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");
}
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)
If you have never used FetchContent
before:
- You include the script, which is provided natively by CMake.
- You declare your dependencies.
- 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)
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})
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")
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)
I always use it when I'm not already using vcpkg and it is a fantastic tool !
vcpkg is instead the next level. Good to have a feedback for CPM.cmake!
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.