When the maintainers of Catch2 announced that the next major version (v3) would be migrating away from its long-standing header-only approach to distribution, there was much uproar about how this would hurt adoption and migration. After all: What can be easier than just dropping a header file in your project and #include-ing it?

To be completely fair to detractors, there’s much precedent to be upset about the idea of a migration from header-only to a compiled library: Building, distributing, and obtaining libraries in C++ has been a longstanding nightmare throughout the community. This precedent has been the status quo for far too long. With dds I am working to change that.

So here I’ll show a simple demo of how to use Catch2 v3 by dropping a single file in your CMake project and include()-ing it. Other build systems will not be discussed here, but I hope to have solutions for them in the future as well.

The Project

Here’s my simple CMake project file:

cmake_minimum_required(VERSION 3.15)
project(MyProject)

add_library(my-library stuff.cpp)

add_executable(my-test test.cpp)
target_link_libraries(my-test PRIVATE my-library)

Suppose I want to use Catch2 v3 with the my-test executable. To do this with Catch2 v2, one would need to download and correctly place the header file, then set the proper preprocessor definitions and custom library targets to use it.

In the case of Catch2 v3, PMM and dds make it even more trivial.

Adding Catch2 v3 with PMM and dds

First, download the PMM entrypoint script and save and commit it to your CMake project. With wget:

$ wget "https://raw.githubusercontent.com/vector-of-bool/pmm/master/pmm.cmake"

or in PowerShell:

> iwr "https://raw.githubusercontent.com/vector-of-bool/pmm/master/pmm.cmake" `
    -OutFile pmm.cmake

Next we include() the file in our CMakeLists.txt:

include(pmm.cmake)

then we utter a small magic pmm() incantation:

pmm(DDS DEPENDS catch2@3.0.0-preview3)

and now we can link Catch2 into our test executable:

target_link_libraries(my-test PRIVATE catch2::catch2)

and that’s all there is to it. With this, can write our tests and use Catch2 as normal:

// - test.cpp -

#include <catch2/catch_test_macros.hpp>

#include <my-library.hpp>

TEST_CASE("This is my test case") {
    REQUIRE(my_lib::add_together(3, 5) == 8);
}

That’s All for Now

This post will be short and sweet to exemplify what I am really shooting for with dds and PMM, and to emphasize just how simple I believe that tooling could be. “Just build everything from source” can be viable if we use our tools to their fullest extent.

I won’t detail here how PMM and dds do their deeds, but you are always free to read the source for PMM and the source for dds. If there is sufficient interest in those nitty-gritty details, I may write a follow-up post.

Unfortunately there are a few (temporary) caveats to go along with this: At the time of writing, dds is still in a (seemingly eternal) alpha stage, and the above example does not yet support providing a custom main() (this is a limitation to the Catch2 package in the dds repository, and not due to an issue in Catch2 or dds itself). dds is not fully “production-ready”, but I encourage readers to experiment and consider that their tools could be doing more for them.

Happy coding!