Using Conan as a CMake Dependency Provider
Managing dependencies in CMake is hard. It’s a common pain point for C++ developers, especially when working on multi-platform projects or with complex dependencies. The introduction of dependency providers in CMake 3.24 aims to simplify this process by allowing package managers like Conan to provide dependency information directly to CMake. Conan and CMake are already a powerful combination for managing C++ dependencies, and this new feature further enhances their integration. In this post, we’ll explore how to use Conan as a CMake dependency provider, making dependency management in CMake projects more seamless and efficient. A sample project can be found on my github account
CMake Dependency Providers in a nutshell
Dependency providers are a new feature introduced in CMake 3.24 that allows package managers to provide dependency information directly to CMake. This information includes the location of the libraries, their include directories, and other necessary information for building the project. When a dependency provider is registered in CMake each call to find_package
or FetchContent_MakeAvailable
will trigger the corresponding function in the dependency provider script.
Behind the curtains, a dependency provider is a CMake script that provides functions to locate dependencies through whatever means it can. In the case of Conan, this means calling calling conan install
with the appropriate toolchain configuration and then extracting the necessary information from the files generated by Conan. To install a depenndency provider the script containing the provider definition is passed using the CMAKE_PROJECT_TOP_LEVEL_INCLUDES
variable either from the command line or through a CMake preset
The official CMake documentation provides an in-depth explanation of how dependency providers work and how to create your own.
Setting up Conan as a CMake Dependency Provider
To set up Conan as a dependency provider we need three things:
- CMake 3.24 or later
- Conan 2.0.2 or later
- The Conan CMake helper script to install it as a dependency provider
Installing CMake and Conan is straightforward and well-documented, so we won’t cover it here. The Conan CMake helper script is a CMake script that sets up Conan as a dependency provider in your project can be found in the cmake-conan repository. As of May 2024 the support is still experimental, but it should be stable enough for most use cases. To use the Conan CMake helper script, you can either download it manually or use it as a git submodule in your project. Since dependency providers have to be configured at the very beginning of the CMake configuration process, getting it over FetchContent
or similar is not an option.
I usually end up with a directory structure like this:
├── cmake-conan # git submodule
│ ├── conan_provider.cmake
│ └── ...
├── CMakeLists.txt
├── conanfile.txt
└── src
└── ...
The conan_provider.cmake
script is the Conan CMake helper script that we will use to set up Conan as a dependency provider. The conanfile.txt
file is the Conan configuration file that lists the dependencies of the project. The src
directory contains the source code of the project and the CMakeLists.txt
file contains the main CMake configuration.
Preparing the Conan and CMake configuration
Let’s assume we are building a simple project that depends on the fmt library for string formatting, which is available from the conan center repository. One of the nice things about dependency providers is that the CMakeLists.txt
does not need any special configuration and can just use find_package
and target_link_libraries
as usual. The Conan CMake helper script will take care of the rest. The CMakeLists.txt
file for the project looks like this:
cmake_minimum_required(VERSION 3.24)
project(
hello_world
LANGUAGES CXX
)
find_package(fmt 10.2.1 REQUIRED)
add_executable(hello src/main.cpp)
target_link_libraries(
hello
PRIVATE fmt::fmt
)
Next, The conanfile.txt
file lists the dependencies of the project:
[requires]
fmt/10.2.1
[generators]
CMakeDeps
Using the CMakeDeps
generator in the conanfile.txt
file tells Conan to generate the necessary information for CMake’s find_package function. This is necessary for the Conan CMake helper script to work correctly. There is also the CMakeToolchain
generator that generates a toolchain file for CMake, but this is not recommended when using Conan as a dependency provider.
With that in place, we can now set up Conan as a dependency provider in the CMakeLists.txt
file:
Using Conan as a CMake Dependency Provider
To set up Conan as a dependency provider, we need to pass the conan_provider.cmake
script to CMake using the CMAKE_PROJECT_TOP_LEVEL_INCLUDES
variable. This can be done either from the command line or through a CMake preset. The conan_provider.cmake
script is the Conan CMake helper script that sets up Conan as a dependency provider.
cmake -S . -B build -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./cmake-conan/conan_provider.cmake -DCMAKE_BUILD_TYPE=Debug
Note that Conan requires the CMAKE_BUILD_TYPE to be set in order to download or build the correct version of the dependencies. The conan_provider.cmake
script will take care of setting up the necessary Conan profiles and installing the dependencies in the local cache. The binaries and include files for the dependencies will be placed in the local Conan cache, which is usually located in the user’s home directory. Only the configuration files generated by Conan will be placed in the build directory. By default the Conan helper script is configured to use the default
Conan profile and it tries to build any missing dependencies from source. These settings can be changed by setting the CONAN_BUILD_PROFILE
and CONAN_INSTALL_ARGS
variables respectively.
After running CMake, the project can be built as usual:
cmake --build build
And with that the project the project is set up to use Conan as a dependency provider. I recommend to use CMake presets to make the configuration more reproducible and to avoid having to remember the command line arguments, especially when working with multiple configurations or platforms. For public projects I generally include the Conan helper script as a git submodule but depending on the project it might be easier to just download it manually.
Is it really that easy?
The introduction of CMakes dependency providers closes a long-existing gap in CMake’s dependency management and like CMake Presets I consider them a vast improvement regarding the tooling situation around C++ projects. So far I have only used Conan as a dependency provider but I am looking forward to seeing how other package managers will integrate with CMake. What I particularly like is that the CMake configuration can remain simple and platform agnostic while the complex dependency management is handled by Conan. This makes it easier to switch between different build systems or to integrate the project into a larger build system and in my opinion is a great incentive to use Conan for C++ projects.