`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` and `CMAKE_TOOLCHAIN_FILE`

During the discussion about implementing CMAKE_PROJECT_TOP_LEVEL_INCLUDES (https://gitlab.kitware.com/cmake/cmake/-/issues/22619) there was some discussion on whether the includes should be read before or after the toolchain file, and it was decided that would be the latter.

Is it possible for a dependency provider, implemented via CMAKE_PROJECT_TOP_LEVEL_INCLUDES, to influence variables typically specified in a toolchain eg CMAKE_CXX_COMPILER?

For a concrete example, say the conan package integration downloads an sdk package that provides a toolchain file. That file won’t be ready until the package is downloaded so it can’t be provided to CMAKE_TOOLCHAIN_FILE (unless the depdendency provider can set this var?).

The workaround is to invoke conan separately but this is not ideal and having CMake orchestrate the entire process is a better developer experience.

Thanks!

Could Conan provide a bootstrap toolchain file that downloads it if not yet done and just includes it if so?

1 Like

Oh that’s an interesting idea.

I’ll try it out and get back to you if it works.

By design, no it shouldn’t be. When the file included via CMAKE_PROJECT_TOP_LEVEL_INCLUDES is pulled in (which is where the dependency provider is registered), the project should be assuming the toolchain is fully set up and defined. This is so that things like dependency providers can query those details to decide how to provide the dependencies. The place to modify the toolchain details is the toolchain file. Don’t try to do that within the dependency provider, that’s not what a dependency provider is for.

In the case of cmake-conan, it looks at what the build is using and writes things like host and build profiles to match it. In more complex project hierarchies, being able to work with arbitrary toolchain files that come from outside the project and honouring what those toolchain files specify can be important. From personal experience, the less you mess with those things the better. Stay on the well-trodden happy path as much as you can. Once you stray from that, things quickly become hard to maintain and fragile.

Where is your toolchain file coming from when you use cmake-conan? The developer building the project should be in control of that. You shouldn’t be needing to resort to having conan drive the whole build unless you’re creating conan binary packages for other builds to consume.

Conan doesn’t drive the build, it does however provide the artifacts needed for the build, which includes an sdk and toolchain binaries such as clang, libc++, compiler buitlins etc. These are versioned and under source control just like any other package that conan provides. It does this by means of a toolchain package, described here. As cmake does not initially know where in the conan cache the compiler will be, the conan package generates a toolchain file containing the correct paths.

As long as the developer uses the typical two-stage conan developer flow:

  1. conan install ...
  2. cmake --preset ...

Then this works fine.

However, developers have to be reminded to run conan install whenever the dependencies or the toolchain needs to be updated.

What I’d really prefer, is for developers to be able to run a one-shot command, for example

$ cmake --preset raspberry-pi

Which would leverage cmake-conan to automatically update the dependencies when required, possibly downloading and installing a toolchain, and configure the project.

Unfortunately I can’t do this (although I haven’t tried Ben’s suggestion yet) because the first thing CMake does is load a toolchain file, and of course conan hasn’t generated it yet.

From the history of CMAKE_PROJECT_TOP_LEVEL_INCLUDES, it does seem that this workflow was considered at one point, and I was wondering if it’s still feasible (obviously not currently, but maybe with some changes that won’t break existing workflows) :

Dedicated Provider Injection Point

…This needs to happen early enough to support what some dependency providers may want to do, but not so early that some important things are not defined yet. Some providers want to control the toolchain details, potentially setting or overriding the CMAKE_TOOLCHAIN_FILE, which means they need a point before the first project() call starts doing things.

Perhaps this is a separate issue, but I’ve run into issues in trying to access variables such as CMAKE_CXX_COMPILER from within the CMAKE_PROJECT_TOP_LEVEL_INCLUDES, which is indeed the documented behavior:

The files will be included immediately after the toolchain file has been read (if one is specified) and platform variables have been set, but before any languages have been enabled. Therefore, language-specific variables, including things like CMAKE_<LANG>_COMPILER, might not be set.

This makes it quite difficult to do anything that reacts to the toolchain from within the top level includes file.