Browse Source

Merge topic 'project-is-top-level'

96a7040107 project: Define variables indicating whether project is top level
3706e9c97c Help: Convert project() directory variables to a definition list

Acked-by: Kitware Robot <[email protected]>
Acked-by: Michael Hirsch <[email protected]>
Merge-request: !5938
Brad King 4 years ago
parent
commit
18a2e86385

+ 11 - 5
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

+ 2 - 0
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

+ 6 - 0
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.

+ 11 - 0
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.

+ 21 - 0
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()

+ 5 - 0
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

+ 2 - 0
Tests/RunCMake/project/ProjectIsTopLevel-stdout.txt

@@ -0,0 +1,2 @@
+-- PROJECT_IS_TOP_LEVEL=ON
+-- ProjectIsTopLevel_IS_TOP_LEVEL=ON

+ 9 - 0
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}")

+ 3 - 0
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

+ 14 - 0
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}")

+ 6 - 0
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

+ 8 - 0
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}")

+ 5 - 0
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}")

+ 3 - 0
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)