project: Always set <PROJECT-NAME>_* as normal variables

Re-introduce the behavior originally introduced in CMake 3.30.3 by
commit c1ece78d11 (project: non cache <project> prefix variables are
also created, 2024-08-27, v3.30.3~2^2), but this time with a policy for
compatibility.

Issue: #25714
Issue: #26243
This commit is contained in:
Craig Scott 2024-10-06 19:59:20 +11:00 committed by Brad King
parent ddf1d2944f
commit e5a9ccbcc8
16 changed files with 119 additions and 34 deletions

View File

@ -44,27 +44,18 @@ Projects should not rely on ``<PROJECT-NAME>_SOURCE_DIR`` or
``<PROJECT-NAME>_BINARY_DIR`` holding a particular value outside of the scope
of the call to ``project()`` or one of its child scopes.
.. versionchanged:: 3.30.3
.. versionchanged:: 3.30
``<PROJECT-NAME>_SOURCE_DIR``, ``<PROJECT-NAME>_BINARY_DIR``, and
``<PROJECT-NAME>_IS_TOP_LEVEL`` are always set as non-cache variables by
``project(<PROJECT-NAME> ...)``.
``<PROJECT-NAME>_IS_TOP_LEVEL``, if already set as normal variables when
``project(<PROJECT-NAME> ...)`` is called, are updated by the call.
Cache entries by the same names are always set as before.
See release notes for 3.30.3, 3.30.4, and 3.30.5 for details.
.. versionchanged:: 3.30.4
The variables ``<PROJECT-NAME>_SOURCE_DIR``, ``<PROJECT-NAME>_BINARY_DIR``,
and ``<PROJECT-NAME>_IS_TOP_LEVEL`` are only set as non-cache variables if
they are already set as cache or non-cache variables when
``project(<PROJECT-NAME> ...)`` is called.
Note that this logic is flawed, as it can result in different behavior
between the first and subsequent runs because cache variables won't exist
on the first run, but they will on subsequent runs.
.. versionchanged:: 3.30.5
The variables ``<PROJECT-NAME>_SOURCE_DIR``, ``<PROJECT-NAME>_BINARY_DIR``,
and ``<PROJECT-NAME>_IS_TOP_LEVEL`` are only set as non-cache variables if
they are already set as non-cache variables when
``project(<PROJECT-NAME> ...)`` is called.
Unlike the flawed behavior of 3.30.4, non-cache variables will not be set
if only cache variables of the same name are set.
.. versionchanged:: 3.31
``<PROJECT-NAME>_SOURCE_DIR``, ``<PROJECT-NAME>_BINARY_DIR``, and
``<PROJECT-NAME>_IS_TOP_LEVEL`` are always set as normal variables by
``project(<PROJECT-NAME> ...)``. See policy :policy:`CMP0180`.
Cache entries by the same names are always set as before.
Options
^^^^^^^

View File

@ -57,6 +57,7 @@ Policies Introduced by CMake 3.31
.. toctree::
:maxdepth: 1
CMP0180: project() always sets <PROJECT-NAME>_* as normal variables. </policy/CMP0180>
CMP0179: De-duplication of static libraries on link lines keeps first occurrence. </policy/CMP0179>
CMP0178: Test command lines preserve empty arguments. </policy/CMP0178>
CMP0177: install() DESTINATION paths are normalized. </policy/CMP0177>

36
Help/policy/CMP0180.rst Normal file
View File

@ -0,0 +1,36 @@
CMP0180
-------
.. versionadded:: 3.31
:command:`project` always sets ``<PROJECT-NAME>_*`` as normal variables.
In CMake 3.29 and below, the :command:`project` command set
:variable:`<PROJECT-NAME>_SOURCE_DIR`, :variable:`<PROJECT-NAME>_BINARY_DIR`,
and :variable:`<PROJECT-NAME>_IS_TOP_LEVEL` as cache entries, but not as
normal variables. CMake 3.30 started setting them as normal variables,
but only if they are already set as normal variables. This was needed to
preserve support for some :module:`FetchContent` use cases under policy
:policy:`CMP0169`'s NEW behavior, while also preserving behavior of nested
directories that call :command:`project` with the same project name.
See release notes for 3.30.3, 3.30.4, and 3.30.5 for details.
CMake 3.31 and later prefer to always set ``<PROJECT-NAME>_SOURCE_DIR``,
``<PROJECT-NAME>_BINARY_DIR``, and ``<PROJECT-NAME>_IS_TOP_LEVEL``, as both
cache entries and normal variables, regardless of what cache or normal
variables already exist. This policy provides compatibility for projects
that have not been updated to expect this behavior.
The ``OLD`` behavior for this policy will only set normal variables for
``<PROJECT-NAME>_SOURCE_DIR``, ``<PROJECT-NAME>_BINARY_DIR``, and
``<PROJECT-NAME>_IS_TOP_LEVEL`` if there is already a normal variable by that
name when :command:`project` is called.
The ``NEW`` behavior for this policy will always set normal variables for
``<PROJECT-NAME>_SOURCE_DIR``, ``<PROJECT-NAME>_BINARY_DIR``, and
``<PROJECT-NAME>_IS_TOP_LEVEL`` when :command:`project` is called.
.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31
.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
.. include:: STANDARD_ADVICE.txt
.. include:: DEPRECATED.txt

View File

@ -0,0 +1,7 @@
project-vars-policy
-------------------
* The :command:`project` command now always sets
:variable:`<PROJECT-NAME>_SOURCE_DIR`, :variable:`<PROJECT-NAME>_BINARY_DIR`,
and :variable:`<PROJECT-NAME>_IS_TOP_LEVEL` as both normal variables and
cache entries. See policy :policy:`CMP0180`.

View File

@ -549,7 +549,10 @@ class cmMakefile;
SELECT(POLICY, CMP0179, \
"De-duplication of static libraries on link lines keeps first " \
"occurrence.", \
3, 31, 0, cmPolicies::WARN)
3, 31, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0180, \
"project() always sets <PROJECT-NAME>_* as normal variables.", 3, \
31, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \

View File

@ -58,11 +58,13 @@ bool cmProjectCommand(std::vector<std::string> const& args,
mf.SetProjectName(projectName);
cmPolicies::PolicyStatus cmp0180 = mf.GetPolicyStatus(cmPolicies::CMP0180);
std::string varName = cmStrCat(projectName, "_BINARY_DIR"_s);
bool nonCacheVarAlreadySet = mf.IsNormalDefinitionSet(varName);
mf.AddCacheDefinition(varName, mf.GetCurrentBinaryDirectory(),
"Value Computed by CMake", cmStateEnums::STATIC);
if (nonCacheVarAlreadySet) {
if (cmp0180 == cmPolicies::NEW || nonCacheVarAlreadySet) {
mf.AddDefinition(varName, mf.GetCurrentBinaryDirectory());
}
@ -70,7 +72,7 @@ bool cmProjectCommand(std::vector<std::string> const& args,
nonCacheVarAlreadySet = mf.IsNormalDefinitionSet(varName);
mf.AddCacheDefinition(varName, mf.GetCurrentSourceDirectory(),
"Value Computed by CMake", cmStateEnums::STATIC);
if (nonCacheVarAlreadySet) {
if (cmp0180 == cmPolicies::NEW || nonCacheVarAlreadySet) {
mf.AddDefinition(varName, mf.GetCurrentSourceDirectory());
}
@ -85,7 +87,7 @@ bool cmProjectCommand(std::vector<std::string> const& args,
nonCacheVarAlreadySet = mf.IsNormalDefinitionSet(varName);
mf.AddCacheDefinition(varName, mf.IsRootMakefile() ? "ON" : "OFF",
"Value Computed by CMake", cmStateEnums::STATIC);
if (nonCacheVarAlreadySet) {
if (cmp0180 == cmPolicies::NEW || nonCacheVarAlreadySet) {
mf.AddDefinition(varName, mf.IsRootMakefile() ? "ON" : "OFF");
}

View File

@ -0,0 +1,16 @@
(-- )?From subdir1:
CMP0180-NEW_SOURCE_DIR = [^
]+/project/subdir1
CMP0180-NEW_BINARY_DIR = [^
]+/project/CMP0180-NEW-build/subdir1
CMP0180-NEW_IS_TOP_LEVEL = OFF
(-- )?From subdir2:
CMP0180-NEW_SOURCE_DIR = [^
]+/project
CMP0180-NEW_BINARY_DIR = [^
]+/project/CMP0180-NEW-build
CMP0180-NEW_IS_TOP_LEVEL = ON
(-- )? sub2proj_SOURCE_DIR = [^
]+/project/subdir2
sub2proj_BINARY_DIR = [^
]+/project/CMP0180-NEW-build/subdir2

View File

@ -0,0 +1,2 @@
# CMP0180 is handled in CMakeLists.txt
include(CMP0180.cmake)

View File

@ -0,0 +1,16 @@
(-- )?From subdir1:
CMP0180-OLD_SOURCE_DIR = [^
]+/project/subdir1
CMP0180-OLD_BINARY_DIR = [^
]+/project/CMP0180-OLD-build/subdir1
CMP0180-OLD_IS_TOP_LEVEL = OFF
(-- )?From subdir2:
CMP0180-OLD_SOURCE_DIR = [^
]+/project/subdir1
CMP0180-OLD_BINARY_DIR = [^
]+/project/CMP0180-OLD-build/subdir1
CMP0180-OLD_IS_TOP_LEVEL = OFF
(-- )? sub2proj_SOURCE_DIR = [^
]+/project/subdir2
sub2proj_BINARY_DIR = [^
]+/project/CMP0180-OLD-build/subdir2

View File

@ -0,0 +1,2 @@
# CMP0180 is handled in CMakeLists.txt
include(CMP0180.cmake)

View File

@ -4,6 +4,12 @@ elseif(RunCMake_TEST MATCHES "^CMP0048")
cmake_minimum_required(VERSION 2.8.12) # old enough to not set CMP0048
else()
cmake_minimum_required(VERSION 3.10)
# CMP0180 needs to be set before the project() call for these tests
if("x${RunCMake_TEST}" STREQUAL "xCMP0180-NEW")
cmake_policy(SET CMP0180 NEW)
elseif("x${RunCMake_TEST}" STREQUAL "xCMP0180-OLD")
cmake_policy(SET CMP0180 OLD)
endif()
endif()
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)

View File

@ -63,9 +63,13 @@ run_cmake(CMP0096-OLD)
run_cmake(CMP0096-NEW)
# We deliberately run these twice to verify behavior of the second CMake run
run_cmake(SameProjectVarsSubdir)
run_cmake(CMP0180-OLD)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake(SameProjectVarsSubdir)
run_cmake(CMP0180-OLD)
set(RunCMake_TEST_NO_CLEAN 0)
run_cmake(CMP0180-NEW)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake(CMP0180-NEW)
set(RunCMake_TEST_NO_CLEAN 0)
run_cmake(NoMinimumRequired)

View File

@ -1,9 +0,0 @@
(-- )? SameProjectVarsSubdir_SOURCE_DIR = [^
]+/subdir1
SameProjectVarsSubdir_BINARY_DIR = [^
]+/subdir1
SameProjectVarsSubdir_IS_TOP_LEVEL = OFF
(-- )? sub2proj_SOURCE_DIR = [^
]+/subdir2
sub2proj_BINARY_DIR = [^
]+/subdir2

View File

@ -1 +1,8 @@
project(${RunCMake_TEST} LANGUAGES NONE)
message(STATUS
"From subdir1:\n"
" ${RunCMake_TEST}_SOURCE_DIR = ${${RunCMake_TEST}_SOURCE_DIR}\n"
" ${RunCMake_TEST}_BINARY_DIR = ${${RunCMake_TEST}_BINARY_DIR}\n"
" ${RunCMake_TEST}_IS_TOP_LEVEL = ${${RunCMake_TEST}_IS_TOP_LEVEL}"
)

View File

@ -1,4 +1,5 @@
message(STATUS
"From subdir2:\n"
" ${RunCMake_TEST}_SOURCE_DIR = ${${RunCMake_TEST}_SOURCE_DIR}\n"
" ${RunCMake_TEST}_BINARY_DIR = ${${RunCMake_TEST}_BINARY_DIR}\n"
" ${RunCMake_TEST}_IS_TOP_LEVEL = ${${RunCMake_TEST}_IS_TOP_LEVEL}"