CMake detect if project is top level
CMake can detect if a project is “top level” that is, NOT via FetchContent using PROJECT_IS_TOP_LEVEL and PROJECT_NAME_IS_TOP_LEVEL . For simplicity, we denote these variables in this article as “*_IS_TOP_LEVEL”.
Example use:
if(${PROJECT_NAME}_IS_TOP_LEVEL)
message(STATUS "${PROJECT_NAME} directly building, not FetchContent")
endif()
For CMake < 3.21:
if(CMAKE_VERSION VERSION_LESS 3.21)
get_property(not_top DIRECTORY PROPERTY PARENT_DIRECTORY)
if(not_top)
set(${PROJECT_NAME}_IS_TOP_LEVEL false)
else()
set(${PROJECT_NAME}_IS_TOP_LEVEL true)
endif()
endif()
Caveats
Directory property
PARENT_DIRECTORY
and *_IS_TOP_LEVEL
are NOT useful for detecting if the child project is being used as an ExternalProject.
These variables are based on the last “project()” command and so are not as universally useful as it first seems.
For example, these variables do not work as expected when using ExternalProject.
Even setting CMAKE_CACHE_ARGS
of ExternalProject does not help, nor does cmake (1) command line options–the CMake-internal setting of *_IS_TOP_LEVEL overrides this attempt to set it.
To workaround this, use an arbitrary auxiliary variable to detect if the project is top level.
Example:
Top-level CMakeLists.txt:
ExternalProject_Add(sub1
...
CMAKE_ARGS -DSUB1_IS_TOP:BOOL=false
)
ExternalProject_Add(sub2
...
CMAKE_ARGS -DSUB2_IS_TOP:BOOL=false
)
Subproject CMakeLists.txt
if(DEFINED SUB1_IS_TOP)
set(SUB1_IS_TOP_LEVEL ${SUB1_IS_TOP})
endif()
Rather than try to directly workaround all the corner cases of *_IS_TOP_LEVEL, using this auxiliary variable allows the user to clearly force the intended behavior. This is useful when the subprojects and main project can build required ExternalProjects, and you want to only build the required ExternalProjects once.