summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfriendlyanon <friendlyanon_@hotmail.com>2021-03-24 19:59:14 (GMT)
committerBrad King <brad.king@kitware.com>2021-03-26 13:35:34 (GMT)
commit96a704010713833e9ec17fa78495df3e0852bbf5 (patch)
tree81dcf4a132a5461584c773a2ad34532f5977a3b0
parent3706e9c97cef2ad8097adfd4ed2f04ff08398787 (diff)
downloadCMake-96a704010713833e9ec17fa78495df3e0852bbf5.zip
CMake-96a704010713833e9ec17fa78495df3e0852bbf5.tar.gz
CMake-96a704010713833e9ec17fa78495df3e0852bbf5.tar.bz2
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
-rw-r--r--Help/command/project.rst5
-rw-r--r--Help/manual/cmake-variables.7.rst2
-rw-r--r--Help/release/dev/project-is-top-level.rst6
-rw-r--r--Help/variable/PROJECT-NAME_IS_TOP_LEVEL.rst11
-rw-r--r--Help/variable/PROJECT_IS_TOP_LEVEL.rst21
-rw-r--r--Source/cmProjectCommand.cxx5
-rw-r--r--Tests/RunCMake/project/ProjectIsTopLevel-stdout.txt2
-rw-r--r--Tests/RunCMake/project/ProjectIsTopLevel.cmake9
-rw-r--r--Tests/RunCMake/project/ProjectIsTopLevelMultiple-stdout.txt3
-rw-r--r--Tests/RunCMake/project/ProjectIsTopLevelMultiple.cmake14
-rw-r--r--Tests/RunCMake/project/ProjectIsTopLevelSubdirectory-stdout.txt6
-rw-r--r--Tests/RunCMake/project/ProjectIsTopLevelSubdirectory.cmake8
-rw-r--r--Tests/RunCMake/project/ProjectIsTopLevelSubdirectory/CMakeLists.txt5
-rw-r--r--Tests/RunCMake/project/RunCMakeTest.cmake3
14 files changed, 100 insertions, 0 deletions
diff --git a/Help/command/project.rst b/Help/command/project.rst
index a726cbb..8a6bc1e 100644
--- a/Help/command/project.rst
+++ b/Help/command/project.rst
@@ -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.
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 4317dd4..37ef053 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -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
diff --git a/Help/release/dev/project-is-top-level.rst b/Help/release/dev/project-is-top-level.rst
new file mode 100644
index 0000000..568afe0
--- /dev/null
+++ b/Help/release/dev/project-is-top-level.rst
@@ -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.
diff --git a/Help/variable/PROJECT-NAME_IS_TOP_LEVEL.rst b/Help/variable/PROJECT-NAME_IS_TOP_LEVEL.rst
new file mode 100644
index 0000000..953e978
--- /dev/null
+++ b/Help/variable/PROJECT-NAME_IS_TOP_LEVEL.rst
@@ -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.
diff --git a/Help/variable/PROJECT_IS_TOP_LEVEL.rst b/Help/variable/PROJECT_IS_TOP_LEVEL.rst
new file mode 100644
index 0000000..e5eb6c1
--- /dev/null
+++ b/Help/variable/PROJECT_IS_TOP_LEVEL.rst
@@ -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()
diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx
index ed32de9..acdb09f 100644
--- a/Source/cmProjectCommand.cxx
+++ b/Source/cmProjectCommand.cxx
@@ -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
diff --git a/Tests/RunCMake/project/ProjectIsTopLevel-stdout.txt b/Tests/RunCMake/project/ProjectIsTopLevel-stdout.txt
new file mode 100644
index 0000000..af8abfd
--- /dev/null
+++ b/Tests/RunCMake/project/ProjectIsTopLevel-stdout.txt
@@ -0,0 +1,2 @@
+-- PROJECT_IS_TOP_LEVEL=ON
+-- ProjectIsTopLevel_IS_TOP_LEVEL=ON
diff --git a/Tests/RunCMake/project/ProjectIsTopLevel.cmake b/Tests/RunCMake/project/ProjectIsTopLevel.cmake
new file mode 100644
index 0000000..5a0af0a
--- /dev/null
+++ b/Tests/RunCMake/project/ProjectIsTopLevel.cmake
@@ -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}")
diff --git a/Tests/RunCMake/project/ProjectIsTopLevelMultiple-stdout.txt b/Tests/RunCMake/project/ProjectIsTopLevelMultiple-stdout.txt
new file mode 100644
index 0000000..75d787f
--- /dev/null
+++ b/Tests/RunCMake/project/ProjectIsTopLevelMultiple-stdout.txt
@@ -0,0 +1,3 @@
+-- PROJECT_IS_TOP_LEVEL=ON
+-- ProjectIsTopLevelMultiple_IS_TOP_LEVEL=ON
+-- IsTopLevel_IS_TOP_LEVEL=ON
diff --git a/Tests/RunCMake/project/ProjectIsTopLevelMultiple.cmake b/Tests/RunCMake/project/ProjectIsTopLevelMultiple.cmake
new file mode 100644
index 0000000..98ba55b
--- /dev/null
+++ b/Tests/RunCMake/project/ProjectIsTopLevelMultiple.cmake
@@ -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}")
diff --git a/Tests/RunCMake/project/ProjectIsTopLevelSubdirectory-stdout.txt b/Tests/RunCMake/project/ProjectIsTopLevelSubdirectory-stdout.txt
new file mode 100644
index 0000000..d9e15ff
--- /dev/null
+++ b/Tests/RunCMake/project/ProjectIsTopLevelSubdirectory-stdout.txt
@@ -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
diff --git a/Tests/RunCMake/project/ProjectIsTopLevelSubdirectory.cmake b/Tests/RunCMake/project/ProjectIsTopLevelSubdirectory.cmake
new file mode 100644
index 0000000..b5df84b
--- /dev/null
+++ b/Tests/RunCMake/project/ProjectIsTopLevelSubdirectory.cmake
@@ -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}")
diff --git a/Tests/RunCMake/project/ProjectIsTopLevelSubdirectory/CMakeLists.txt b/Tests/RunCMake/project/ProjectIsTopLevelSubdirectory/CMakeLists.txt
new file mode 100644
index 0000000..d2f16ea
--- /dev/null
+++ b/Tests/RunCMake/project/ProjectIsTopLevelSubdirectory/CMakeLists.txt
@@ -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}")
diff --git a/Tests/RunCMake/project/RunCMakeTest.cmake b/Tests/RunCMake/project/RunCMakeTest.cmake
index 6914699..349e8ac 100644
--- a/Tests/RunCMake/project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/project/RunCMakeTest.cmake
@@ -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)