project: Define variables indicating whether project is top level

Define `PROJECT_IS_TOP_LEVEL` and `<PROJECT-NAME>_IS_TOP_LEVEL`.  The
latter is a STATIC cache entry just like other `<PROJECT-NAME>_*`
variables so that it is globally scoped.

Issue: #20310
Fixes: #21961
This commit is contained in:
friendlyanon
2021-03-26 09:35:34 -04:00
committed by Brad King
parent 3706e9c97c
commit 96a7040107
14 changed files with 100 additions and 0 deletions
+5
View File
@@ -28,6 +28,11 @@ Also sets the variables:
:variable:`PROJECT_BINARY_DIR`, :variable:`<PROJECT-NAME>_BINARY_DIR`
Absolute path to the binary directory for the project.
:variable:`PROJECT_IS_TOP_LEVEL`, :variable:`<PROJECT-NAME>_IS_TOP_LEVEL`
.. versionadded:: 3.21
Boolean value indicating whether the project is top-level.
Further variables are set by the optional arguments described in the following.
If any of these arguments is not used, then the corresponding variables are
set to the empty string.
+2
View File
@@ -130,6 +130,7 @@ Variables that Provide Information
/variable/PROJECT-NAME_BINARY_DIR
/variable/PROJECT-NAME_DESCRIPTION
/variable/PROJECT-NAME_HOMEPAGE_URL
/variable/PROJECT-NAME_IS_TOP_LEVEL
/variable/PROJECT-NAME_SOURCE_DIR
/variable/PROJECT-NAME_VERSION
/variable/PROJECT-NAME_VERSION_MAJOR
@@ -139,6 +140,7 @@ Variables that Provide Information
/variable/PROJECT_BINARY_DIR
/variable/PROJECT_DESCRIPTION
/variable/PROJECT_HOMEPAGE_URL
/variable/PROJECT_IS_TOP_LEVEL
/variable/PROJECT_NAME
/variable/PROJECT_SOURCE_DIR
/variable/PROJECT_VERSION
@@ -0,0 +1,6 @@
project-is-top-level
--------------------
* :command:`project` now sets variables :variable:`PROJECT_IS_TOP_LEVEL` and
:variable:`<PROJECT-NAME>_IS_TOP_LEVEL` to indicate whether it was called
in a top level ``CMakeLists.txt`` file.
@@ -0,0 +1,11 @@
<PROJECT-NAME>_IS_TOP_LEVEL
---------------------------
.. versionadded:: 3.21
A boolean variable indicating whether the named project was called in a top
level ``CMakeLists.txt`` file.
To obtain the value from the most recent call to :command:`project` in
the current directory scope or above, see the
:variable:`PROJECT_IS_TOP_LEVEL` variable.
+21
View File
@@ -0,0 +1,21 @@
PROJECT_IS_TOP_LEVEL
--------------------
.. versionadded:: 3.21
A boolean variable indicating whether :command:`project` was called in a top
level ``CMakeLists.txt`` file.
Some modules should only be included as part of the top level
``CMakeLists.txt`` file to not cause unintended side effects in the build
tree, and this variable can be used to conditionally execute such code. For
example, consider the :module:`CTest` module, which creates targets and
options:
.. code-block:: cmake
project(MyProject)
...
if(PROJECT_IS_TOP_LEVEL)
include(CTest)
endif()
+5
View File
@@ -59,6 +59,11 @@ bool cmProjectCommand(std::vector<std::string> const& args,
mf.AddDefinition("PROJECT_NAME", projectName);
mf.AddDefinitionBool("PROJECT_IS_TOP_LEVEL", mf.IsRootMakefile());
mf.AddCacheDefinition(projectName + "_IS_TOP_LEVEL",
mf.IsRootMakefile() ? "ON" : "OFF",
"Value Computed by CMake", cmStateEnums::STATIC);
// Set the CMAKE_PROJECT_NAME variable to be the highest-level
// project name in the tree. If there are two project commands
// in the same CMakeLists.txt file, and it is the top level
@@ -0,0 +1,2 @@
-- PROJECT_IS_TOP_LEVEL=ON
-- ProjectIsTopLevel_IS_TOP_LEVEL=ON
@@ -0,0 +1,9 @@
# no project() call, includer already calls project(${RunCMake_TEST} NONE)
if(NOT DEFINED PROJECT_IS_TOP_LEVEL)
message(FATAL_ERROR "PROJECT_IS_TOP_LEVEL is not defined")
endif()
if(NOT DEFINED "CACHE{${RunCMake_TEST}_IS_TOP_LEVEL}")
message(FATAL_ERROR "IsTopLevel_IS_TOP_LEVEL is not defined")
endif()
message(STATUS "PROJECT_IS_TOP_LEVEL=${PROJECT_IS_TOP_LEVEL}")
message(STATUS "${RunCMake_TEST}_IS_TOP_LEVEL=${${RunCMake_TEST}_IS_TOP_LEVEL}")
@@ -0,0 +1,3 @@
-- PROJECT_IS_TOP_LEVEL=ON
-- ProjectIsTopLevelMultiple_IS_TOP_LEVEL=ON
-- IsTopLevel_IS_TOP_LEVEL=ON
@@ -0,0 +1,14 @@
# only one project() call, includer already calls project(${RunCMake_TEST} NONE)
project(IsTopLevel NONE)
if(NOT DEFINED PROJECT_IS_TOP_LEVEL)
message(FATAL_ERROR "PROJECT_IS_TOP_LEVEL is not defined")
endif()
if(NOT DEFINED "CACHE{${RunCMake_TEST}_IS_TOP_LEVEL}")
message(FATAL_ERROR "${RunCMake_TEST}_IS_TOP_LEVEL is not defined")
endif()
if(NOT DEFINED CACHE{IsTopLevel_IS_TOP_LEVEL})
message(FATAL_ERROR "IsTopLevel_IS_TOP_LEVEL is not defined")
endif()
message(STATUS "PROJECT_IS_TOP_LEVEL=${PROJECT_IS_TOP_LEVEL}")
message(STATUS "${RunCMake_TEST}_IS_TOP_LEVEL=${${RunCMake_TEST}_IS_TOP_LEVEL}")
message(STATUS "IsTopLevel_IS_TOP_LEVEL=${IsTopLevel_IS_TOP_LEVEL}")
@@ -0,0 +1,6 @@
-- PROJECT_IS_TOP_LEVEL=ON
-- PROJECT_IS_TOP_LEVEL=ON
-- PROJECT_IS_TOP_LEVEL=OFF
-- PROJECT_IS_TOP_LEVEL=ON
-- ProjectIsTopLevelSubdirectory_IS_TOP_LEVEL=ON
-- NotTopLevel_IS_TOP_LEVEL=OFF
@@ -0,0 +1,8 @@
# no project() call, includer already calls project(${RunCMake_TEST} NONE)
message(STATUS "PROJECT_IS_TOP_LEVEL=${PROJECT_IS_TOP_LEVEL}")
add_subdirectory(ProjectIsTopLevelSubdirectory)
message(STATUS "PROJECT_IS_TOP_LEVEL=${PROJECT_IS_TOP_LEVEL}")
message(STATUS "${RunCMake_TEST}_IS_TOP_LEVEL=${${RunCMake_TEST}_IS_TOP_LEVEL}")
message(STATUS "NotTopLevel_IS_TOP_LEVEL=${NotTopLevel_IS_TOP_LEVEL}")
@@ -0,0 +1,5 @@
message(STATUS "PROJECT_IS_TOP_LEVEL=${PROJECT_IS_TOP_LEVEL}")
project(NotTopLevel NONE)
message(STATUS "PROJECT_IS_TOP_LEVEL=${PROJECT_IS_TOP_LEVEL}")
@@ -15,6 +15,9 @@ run_cmake(ProjectDescriptionNoArg2)
run_cmake(ProjectHomepage)
run_cmake(ProjectHomepage2)
run_cmake(ProjectHomepageNoArg)
run_cmake(ProjectIsTopLevel)
run_cmake(ProjectIsTopLevelMultiple)
run_cmake(ProjectIsTopLevelSubdirectory)
run_cmake(ProjectTwice)
run_cmake(VersionAndLanguagesEmpty)
run_cmake(VersionEmpty)