BOOST_ROOT transitive behavior problem

Hello folks!

I’m observing a problem I’d consider “weird” that I’ve been trying to track down & resolve unsuccessfully for a few days.

Let there be three projects, A, B and C.

  • C is a C++ library which depends on Boost.
  • B is a C++ library which depends on C and Boost.
  • A is a C++ library which is a loadable plugin for an existing application which depends on B.

CMake’s FetchContent is used to pull in B from A as well as to pull in C from B.
Both B and C use find_package(Boost). However, C needs a more recent versions of Boost than what my system’s package manager provides. Therefore, I simply download the Boost release to a location on my system and specify BOOST_ROOT via CMake’s -D option.

C compiles fine on its own.
B compiles fine on its own.
A fails to compile for a reason unknown to me. When observing the compilation messages, it becomes clear that when compiling A, dependency C is using (i.e. including) the boost headers installed on the system rather than the ones supplied via BOOST_ROOT.

I fail to understand why this is the case, where the problem originates from and how to fix it.
Is there something special about the use of BOOST_ROOT that I’m missing? It appears to be “non transitive” (i.e. when compiling A, potentially both B and C are using the system’s boost headers rather than the ones specified via BOOST_ROOT.
But this doesn’t make much sense to me either because C is the library that actually needs the more recent version of the boost headers and compiling B as the top-level CMake project (which depends on C) works just fine. The problem only exists when trying to compile A (which depends on B which depends on C).

Both B and C simply do #include <boost/...>, use find_package(Boost 1.77 REQUIRED) and target_link_libraries(${TARGET} PUBLIC Boost::headers).

I’d appreciate any sort of input here that would help me either tracking down the problem or understanding the situation.

My environment:

  • FreeBSD 13
  • CMake 3.22
  • Clang 14


I suspect that FetchContent may need to be passed BOOST_ROOT down manually as it uses ExternalProject internally and doesn’t share a cache. At least that’s my understanding.

No that’s incorrect. FetchContent only uses ExternalProject to perform the download. Boost would still be getting added to the main build via a call to add_subdirectory(), so it will see all the same variables, etc. as the main build.

You might want to try the new --debug-find-pkg=Boost option for the cmake command line, available with CMake 3.23. That might reveal why C is selecting the system location instead of the expected one. If C actually is picking up the expected Boost, but A is picking up headers from the system instead, then I’d be digging into what the INCLUDE_DIRECTORIES and INTERFACE_INCLUDE_DIRECTORIES target properties are for A, B and C. Something might be messing with one or more of those, causing A to drop the expected Boost header search path.

Incidentally, at what path is your expected Boost located? Main reason I’m asking is in case it gets caught as some sort of special case and gets stripped out (but I then couldn’t explain why it would only do that when A is involved).