summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Help/command/project.rst16
-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/CMakeVersion.cmake2
-rw-r--r--Source/cmCommandLineArgument.h14
-rw-r--r--Source/cmProjectCommand.cxx5
-rw-r--r--Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-number-trailing--invalid-target-result.txt1
-rw-r--r--Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-number-trailing--invalid-target-stderr.txt1
-rw-r--r--Tests/RunCMake/CommandLine/RunCMakeTest.cmake4
-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
19 files changed, 122 insertions, 11 deletions
diff --git a/Help/command/project.rst b/Help/command/project.rst
index 6c931b6..8a6bc1e 100644
--- a/Help/command/project.rst
+++ b/Help/command/project.rst
@@ -20,12 +20,18 @@ Sets the name of the project, and stores it in the variable
``CMakeLists.txt`` also stores the project name in the
variable :variable:`CMAKE_PROJECT_NAME`.
-Also sets the variables
+Also sets the variables:
-* :variable:`PROJECT_SOURCE_DIR`,
- :variable:`<PROJECT-NAME>_SOURCE_DIR`
-* :variable:`PROJECT_BINARY_DIR`,
- :variable:`<PROJECT-NAME>_BINARY_DIR`
+:variable:`PROJECT_SOURCE_DIR`, :variable:`<PROJECT-NAME>_SOURCE_DIR`
+ Absolute path to the source directory for the project.
+
+: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
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/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index fa9d567..64077c4 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
# CMake version number components.
set(CMake_VERSION_MAJOR 3)
set(CMake_VERSION_MINOR 20)
-set(CMake_VERSION_PATCH 20210326)
+set(CMake_VERSION_PATCH 20210329)
#set(CMake_VERSION_RC 0)
set(CMake_VERSION_IS_DIRTY 0)
diff --git a/Source/cmCommandLineArgument.h b/Source/cmCommandLineArgument.h
index 6b75da0..b14cfc2 100644
--- a/Source/cmCommandLineArgument.h
+++ b/Source/cmCommandLineArgument.h
@@ -76,8 +76,9 @@ struct cmCommandLineArgument
} else if (this->Type == Values::One || this->Type == Values::ZeroOrOne) {
if (input.size() == this->Name.size()) {
- ++index;
- if (index >= allArgs.size() || allArgs[index][0] == '-') {
+ auto nextValueIndex = index + 1;
+ if (nextValueIndex >= allArgs.size() ||
+ allArgs[nextValueIndex][0] == '-') {
if (this->Type == Values::ZeroOrOne) {
parseState =
this->StoreCall(std::string{}, std::forward<CallState>(state)...)
@@ -87,10 +88,11 @@ struct cmCommandLineArgument
parseState = ParseMode::ValueError;
}
} else {
- parseState =
- this->StoreCall(allArgs[index], std::forward<CallState>(state)...)
+ parseState = this->StoreCall(allArgs[nextValueIndex],
+ std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
+ index = nextValueIndex;
}
} else {
// parse the string to get the value
@@ -137,7 +139,8 @@ struct cmCommandLineArgument
} else if (this->Type == Values::OneOrMore) {
if (input.size() == this->Name.size()) {
auto nextValueIndex = index + 1;
- if (nextValueIndex >= allArgs.size() || allArgs[index + 1][0] == '-') {
+ if (nextValueIndex >= allArgs.size() ||
+ allArgs[nextValueIndex][0] == '-') {
parseState = ParseMode::ValueError;
} else {
std::string buffer = allArgs[nextValueIndex++];
@@ -149,6 +152,7 @@ struct cmCommandLineArgument
this->StoreCall(buffer, std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
+ index = (nextValueIndex - 1);
}
}
}
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/CommandLine/BuildDir--build-jobs-no-number-trailing--invalid-target-result.txt b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-number-trailing--invalid-target-result.txt
new file mode 100644
index 0000000..3beecb0
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-number-trailing--invalid-target-result.txt
@@ -0,0 +1 @@
+(1|2)
diff --git a/Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-number-trailing--invalid-target-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-number-trailing--invalid-target-stderr.txt
new file mode 100644
index 0000000..ee4bcc0
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-number-trailing--invalid-target-stderr.txt
@@ -0,0 +1 @@
+.*(ninja: error: unknown target 'invalid-target'|No rule to make target [`']invalid-target').*
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index a2b2044..c497472 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -212,6 +212,10 @@ function(run_BuildDir)
${CMAKE_COMMAND} --build BuildDir-build -j)
run_cmake_command(BuildDir--build-jobs-no-number-trailing--target ${CMAKE_COMMAND} -E chdir ..
${CMAKE_COMMAND} --build BuildDir-build -j --target CustomTarget)
+ if(RunCMake_GENERATOR MATCHES "Unix Makefiles" OR RunCMake_GENERATOR MATCHES "Ninja")
+ run_cmake_command(BuildDir--build-jobs-no-number-trailing--invalid-target ${CMAKE_COMMAND} -E chdir ..
+ ${CMAKE_COMMAND} --build BuildDir-build -j --target invalid-target)
+ endif()
run_cmake_command(BuildDir--build--parallel-no-number ${CMAKE_COMMAND} -E chdir ..
${CMAKE_COMMAND} --build BuildDir-build --parallel)
run_cmake_command(BuildDir--build--parallel-no-number-trailing--target ${CMAKE_COMMAND} -E chdir ..
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)