Passing all elemental environment into ExternalProject_Add

Greetings, I have to add a 3rd party CMake project to our own one. The 3rd party project has several submodules that it adds with ExternalProject_Add. Problem: The submodules use find_package extensively and apparently the CMAKE_FIND_ROOT_PATH variable is not transferred to the submodule by ExternalProject_Add. The project does not explicitly transfer CMAKE_FIND_ROOT_PATH in the CMAKE_ARGS list.

Is there a way for the outer project to make sure essential variables are transferred into all submodules imported by ExternalProject_Add or is this just a design flaw by the 3rd party lib?


You can pass it explicitly with -DCMAKE_FIND_ROOT_PATH:STRING=$ENV{CMAKE_FIND_ROOT_PATH} when configuring the projects. But there’s no built-in way to set up the environment for ExternalProject projects.

1 Like

So that means I would have to edit the ExternalProject_Add calls in the 3rd party library to make sure all Variables I need are passed, right?

Is there an alternative to ExternalProject_Add that is more comfortable in this regard?


@craig.scott ?

ExternalProject_Add() only really works well if you’re following the classic superbuild pattern closely. Once you have sub-projects that call ExternalProject_Add(), or your main project tries to add anything to its build other than through ExternalProject_Add(), it starts to fall apart pretty quickly.

If a dependency uses ExternalProject_Add(), that’s usually a pretty clear signal that it isn’t all that well suited to being absorbed into a larger build other than as a pre-built package. I’m afraid I don’t have any good news or workarounds to offer you for your situation that don’t involve editing the third party project.

1 Like

Ok, just to be sure:

What would the author have used if he wanted the variables to be handed over? add_subdirectory?

In general, the most flexible strategy is for your project to use find_package() to bring in its dependencies. The project is then specifying the “what”, but not the “how”. It is up to the user to decide how they want to make the dependencies available, and they can choose the strategy that best fits their situation.

CMake 3.24 and later has dedicated support for facilitating the “how” through a feature called dependency providers. With earlier CMake versions, the user has to essentially prepare the dependencies pre-built beforehand, then tell the project where to find them by setting variables like CMAKE_PREFIX_PATH.

Another strategy is to use FetchContent instead of find_package(). This is most suitable for companies where all the sources that are needed by the project will be under the organisation’s control. Some dependencies might be internal, others may be open source. This approach is well-suited to cases where you want to build all your dependencies from sources, but it can be used to provide pre-built binaries if needed (not typically what you’d use FetchContent for though). Dependency providers also support intercepting FetchContent too, not just find_package(), so the user is still in control and able to override details the project provides in its calls to FetchContent_Declare().

The FetchContent approach has the advantage that the dependencies are absorbed directly into your project, so they would see variables like CMAKE_FIND_ROOT_PATH. But they wouldn’t need those variables unless they were calling find_package() for some of their own dependencies. find_package() has the advantage that it is very mature and well-supported with mainstream package managers like conan and vcpkg.

For the third party dependency you’re talking about, it’s not likely to be a simple case of replacing ExternalProject_Add() calls with something else. If they are all CMake projects, you might be able to replace them with equivalent FetchContent calls, in which case the close relationship between ExternalProject and FetchContent may make that easier. But that would only work if all the dependencies were written in such a way that they support being added to a larger parent project. Many older projects use patterns or techniques that prevent that though.

1 Like

Ok, thank you!

Btw the $ENV{ doesn’t seem to work for me, an empty CMAKE_FIND_ROOT_PATH appears in the dependent object. Does this work if CMAKE_FIND_ROOT_PATH is multiple semicolon separated paths?

Oh…no. You’ll need to replace ; with a separator to get it through the ExternalProject calls. See LIST_SEPARATOR docs.

1 Like

Ah yeah I see tx.

Just out of curiosity: The semicolon is a cmake standard, right? Why does cmake have troubles passing it through its own calls?

There’s no way to escape it to arbitrary levels (and even if so, to know how many layers are needed for a given call site). It’s just one of those weird unfortunate things baked into CMake’s DNA at this point though.