CMakeToolchain: Inject arbitrary CMake variables into dependencies

You can find the sources to recreate this project in the examples2 repository in GitHub:

$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/examples/tools/cmake/cmake_toolchain/user_toolchain_profile

In the general case, Conan package recipes provide the necessary abstractions via settings, confs, and options to control different aspects of the build. Many recipes define options to activate or deactivate features, optional dependencies, or binary characteristics. Configurations like tools.build:cxxflags can be used to inject arbitrary C++ compile flags.

In some exceptional cases, it might be desired to inject CMake variables directly into dependencies doing CMake builds. This is possible when these dependencies use the CMakeToolchain integration. Let’s check it in this simple example.

If we have the following package recipe, with a simple conanfile.py and a CMakeLists.txt printing a variable:

conanfile.py
from conan import ConanFile
from conan.tools.cmake import CMake

class AppConan(ConanFile):
    name = "foo"
    version = "1.0"
    settings = "os", "compiler", "build_type", "arch"
    exports_sources = "CMakeLists.txt"

    generators = "CMakeToolchain"

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(foo LANGUAGES NONE)
message(STATUS "MYVAR1 ${MY_USER_VAR1}!!")

We can define a profile file and a myvars.cmake file (both in the same folder) like the following:

myprofile
include(default)
[conf]
tools.cmake.cmaketoolchain:user_toolchain+={{profile_dir}}/myvars.cmake

Note the {{profile_dir}} is a jinja template expression that evaluates to the current profile folder, allowing to compute the necessary path to myvars.cmake file. The tools.cmake.cmaketoolchain:user_toolchain is a list of files to inject to the generated conan_toolchain.cmake, so the += operator is used to append to it.

The myvars.cmake can define as many variables as we want:

myvars.cmake
set(MY_USER_VAR1 "MYVALUE1")

Applying this profile, we can see that the package CMake build effectively uses the variable provided in the external myvars.cmake file:

$ conan create . -pr=myprofile
...
-- MY_USER_VAR1 MYVALUE1

Note that using user_toolchain while defining values for confs like tools.cmake.cmaketoolchain:system_name is supported.

Also, user_toolchain files can define variables for cross-building, such as CMAKE_SYSTEM_NAME, CMAKE_SYSTEM_VERSION and CMAKE_SYSTEM_PROCESSOR. If these variables are defined in the user toolchain file, they will be respected, and the conan_toolchain.cmake deduced ones will not overwrite the user defined ones. If those variables are not defined in the user toolchain file, then the Conan automatically deduced ones will be used.

The tools.cmake.cmaketoolchain:user_toolchain conf value might also be passed in the command line -c argument, but the location of the myvars.cmake needs to be absolute to be found, as jinja replacement doesn’t happen in the command line.