<projectName>_SOURCE_DIR and <projectName>_BINARY_DIR can be masked by non-cache variables
Consider the following minimal project (not something you'd do directly, it's just a minimal reproducer):
cmake_minimum_required(VERSION 3.21) # CMP0126 set to NEW
set(something_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) # Something different just so we can see the result
project(something LANGUAGES NONE)
message(STATUS "something_SOURCE_DIR = ${something_SOURCE_DIR}")
The project()
command is documented as setting <projectName>_SOURCE_DIR
and <projectName>_BINARY_DIR
, but as the above snippet demonstrates, it doesn't quite work in all situations. What the project()
command actually does is set cache variables, but with CMP0126 set to NEW, if these variables already exist as non-cache variables in the calling scope, those non-cache variables will be hidden.
An important scenario where this crops up can be seen with GoogleTest. That project has a directory structure like so:
ROOT
+-- CMakeLists.txt
| ... project(googletest-distribution)
|
+-- googletest
| +-- CMakeLists.txt
| ... project(gtest)
+-- googlemock
+-- CMakeLists.txt
... project(gmock)
Other projects often pull in GoogleTest using FetchContent like so:
include(FetchContent)
FetchContent_Declare(
gtest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
FetchContent_MakeAvailable(gtest)
One of the effects of calling FetchContent_MakeAvailable(<depName>)
is that it will set non-cache variables named <lowercaseDepName>_SOURCE_DIR
and <lowercaseDepName>_BINARY_DIR
, and it will set those variables before populating the dependency. The <lowercaseDepName>_SOURCE_DIR
variable will point at the ROOT directory of GoogleTest, but note how the googletest
subdirectory below the ROOT has a call to project(gtest)
. Now we have two conflicting values for gtest_SOURCE_DIR
. Within GoogleTest's own source tree, the project(gtest)
call wants gtest_SOURCE_DIR
to point to the googletest
subdirectory below the ROOT, but FetchContent_MakeAvailable()
will set gtest_SOURCE_DIR
to point to the ROOT itself. This non-cache gtest_SOURCE_DIR
variable will exist when the GoogleTest sources are read and processed, which means the gtest_SOURCE_DIR
cache variable created by the call to project(gtest)
will never be seen outside of the directory scope where project(gtest)
is called. As it happens, the googlemock/CMakeLists.txt
file tries to do exactly that, so it ends up seeing a different value for gtest_SOURCE_DIR
than what it expected. I suspect this is related to the problem reported in #25294.
One idea would be to change project()
to set non-cache variables for <projectName>_SOURCE_DIR
and <projectName>_BINARY_DIR
instead of cache variables. This would mean code outside of the project's directory scope would no longer see these variables, so a policy would be needed. But a policy would only apply to where project()
is called, not to code outside of that directory scope where the cache variables may be expected. Such a change would also leave code outside the project's directory scope with no easy way to obtain the project's source or build directory. The GoogleTest example demonstrates this may be a problem.
At the very least, we should update the documentation for project()
and FetchContent
to highlight the unreliability of using <projectName>_SOURCE_DIR
and <projectName>_BINARY_DIR
variables, and the relevance of policy CMP0126 to the situation.