Effect of force overwriting variables

Hi everyone,

in my project I include a dependency with FetchContent with such a line in it:

set(BUILD_TESTING OFF CACHE INTERNAL "" FORCE)

BUILD_TESTING is a variable declared by CTest as far as I understood it. And with the FORCE option you cann override it’s value.

The problem is that I can’t set it via the command line anymore I think.

So I wonder two things:

  • Is it wrong that the author has such a line in it’s code? Can I do anything against it?
  • How can I set BUILD_TESTING to False for my dependencies, but not for my project. I fetch all dependencies in a different scope, so with a normal variable, I can just set it there and it has no effect on other things. But with CACHE Variables the story is different, right? Can I set a cache variable with scope?

Yes. Forcing end-user variables like this is not the way to do it. AFAIK, the only way around it is to set a local variable, but this probably has other oddities associated with it.

Just set it to OFF as a local variable. Local variables “win” over cache variables, so when subprojects look it up, they’ll see OFF, but the cache will stay the same.

CACHE variables always have global scope.

1 Like

Thanks for the responses, I patched the lib, but it still don’t works because on of their dependencies - curl.

I could write a small sample, which shows the problem:

include(CMakeDependentOption)
message(STATUS "Before" ${BUILD_TESTING})
# set(BUILD_TESTING OFF)
cmake_dependent_option(BUILD_TESTING "" ON "" OFF)
message(STATUS "After" ${BUILD_TESTING}) 
include_subdirectory("whatever file the above is in") 
message(STATUS "Paren Scope After" ${BUILD_TESTING}) 

The first message and the last message should always be true. But in fact they are not if both these are set at the same time:

set(BUILD_TESTING OFF)
cmake_dependent_option(BUILD_TESTING "" ON "" OFF)

Is this a bug or what is happening here?

In general I wonder: What can I do about all this? As a CMake User I often feel like I have no control with FetchContent. The library authors can do thing like force caching variables and there is nothing I can do about it (except nicely asking that they don’t do this).
Can CMake add possibilities for me to fix the libraries from my project? For example unforcing a force cache variable perhaps?

Is there a difference between the first and second configure run? If so, this issue should resolve this problem once and for all.

I know people want to use arbitrary projects with FetchContent and control things, but in my experience, if the project doesn’t support being a subproject of another, it’s usually best to just patch the thing (usually skipping cache variables and just setting local values instead). I don’t believe FetchContent can fix all the embeddability problems that arise with projects in the wild. Personally, I think the effort would be better spent on enshrining better behaviors such as:

  • using GNUInstallDirs
  • properly exporting targets
  • not using the top-level PROJECT_ and SOURCE_DIR variables
  • other best practices which consider the embedding use case

Of course, to actually be viable, mangling needs to be supported in-library (it’s the hairiest part of the vendoring process used in VTK and ParaView), but that’s not exactly the easiest thing to support. With Git these days, it’s easy enough to track a fork and rebase patches onto new releases as things progress that I think it’s still a better solution than FetchContent using vanilla upstream sources.

There is no difference. It’s always ON - OFF - ON.

What does that command actually (supposed to behave like). Is cmake_dependent_option like option(), like set(CACHE), like set(CACHE FORCE), like set() or something completly new or different.

Btw. these things work:

include(CMakeDependentOption)
message(STATUS "Before !!!" ${BUILD_TESTING})
set(BUILD_TESTING ON)
cmake_dependent_option(BUILD_TESTING "" ON "" OFF)
message(STATUS "After !!! " ${BUILD_TESTING})

→ Doesn’t affect parent scope

Neither is the order in cmake_depent_option() relevant.

Only this fails:

include(CMakeDependentOption)
message(STATUS "Before !!!" ${BUILD_TESTING})
set(BUILD_TESTING OFF)
cmake_dependent_option(BUILD_TESTING "" "" "" "")
message(STATUS "After !!! " ${BUILD_TESTING})

So setting the variables locally to OFF and calling cmake_depenent_option with anything (well the variable name has to be BUILD_TESTING, but except that)

I think the problem is that a lot of people don’t understand CMake correctly or fully. Can’t blame anyone here, there are some quite complicated things in cmake (variable behaviour is one of them).

The library I use fully supports being a subproject, except the fact that the library did some mistake and perhaps the library this library depends on as well (or cmake has a bug).
Sometimes it would be just nice to some tools to fix common problems.

Correct me if I’m wrong, but using the build-in Variables or rely on them like “BUILD_TESTING” or “BUILD_SHARED_LIBS” is actually recommended. Because it’s consistent and it’s always clear how you disable the tests for example.

But in such situations people don’t just fix the library or don’t use it all. They just use their own variable called “MYPROJECT_BUILD_TESTING”. Issue fixed for them. But that leads to cmake code being really inconsistent, which isn’t the goal I assume.

Any updates here @ben.boeckel?

I don’t have any, though now that CMP0125 and CMP0126 are out; maybe it helps here?

I think these didn’t help. Should I open an issue over on gitlab, so this is tracked?

Yeah, an issue can’t hurt.

Done: https://gitlab.kitware.com/cmake/cmake/-/issues/22909

Sorry took a while

1 Like