From e5a9ccbcc88ab252cf75f005227cdeb808299266 Mon Sep 17 00:00:00 2001 From: Craig Scott Date: Sun, 6 Oct 2024 19:59:20 +1100 Subject: project: Always set _* as normal variables Re-introduce the behavior originally introduced in CMake 3.30.3 by commit c1ece78d11 (project: non cache 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 --- Help/command/project.rst | 31 +++++++------------ Help/manual/cmake-policies.7.rst | 1 + Help/policy/CMP0180.rst | 36 ++++++++++++++++++++++ Help/release/dev/project-vars-policy.rst | 7 +++++ Source/cmPolicies.h | 5 ++- Source/cmProjectCommand.cxx | 8 +++-- Tests/RunCMake/project/CMP0180-NEW-stdout.txt | 16 ++++++++++ Tests/RunCMake/project/CMP0180-NEW.cmake | 2 ++ Tests/RunCMake/project/CMP0180-OLD-stdout.txt | 16 ++++++++++ Tests/RunCMake/project/CMP0180-OLD.cmake | 2 ++ Tests/RunCMake/project/CMP0180.cmake | 17 ++++++++++ Tests/RunCMake/project/CMakeLists.txt | 6 ++++ Tests/RunCMake/project/RunCMakeTest.cmake | 8 +++-- .../project/SameProjectVarsSubdir-stdout.txt | 9 ------ Tests/RunCMake/project/SameProjectVarsSubdir.cmake | 17 ---------- Tests/RunCMake/project/subdir1/CMakeLists.txt | 7 +++++ Tests/RunCMake/project/subdir2/CMakeLists.txt | 1 + 17 files changed, 137 insertions(+), 52 deletions(-) create mode 100644 Help/policy/CMP0180.rst create mode 100644 Help/release/dev/project-vars-policy.rst create mode 100644 Tests/RunCMake/project/CMP0180-NEW-stdout.txt create mode 100644 Tests/RunCMake/project/CMP0180-NEW.cmake create mode 100644 Tests/RunCMake/project/CMP0180-OLD-stdout.txt create mode 100644 Tests/RunCMake/project/CMP0180-OLD.cmake create mode 100644 Tests/RunCMake/project/CMP0180.cmake delete mode 100644 Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt delete mode 100644 Tests/RunCMake/project/SameProjectVarsSubdir.cmake diff --git a/Help/command/project.rst b/Help/command/project.rst index d220b83..b42a034 100644 --- a/Help/command/project.rst +++ b/Help/command/project.rst @@ -44,27 +44,18 @@ Projects should not rely on ``_SOURCE_DIR`` or ``_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 ``_SOURCE_DIR``, ``_BINARY_DIR``, and - ``_IS_TOP_LEVEL`` are always set as non-cache variables by - ``project( ...)``. - -.. versionchanged:: 3.30.4 - The variables ``_SOURCE_DIR``, ``_BINARY_DIR``, - and ``_IS_TOP_LEVEL`` are only set as non-cache variables if - they are already set as cache or non-cache variables when - ``project( ...)`` 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 ``_SOURCE_DIR``, ``_BINARY_DIR``, - and ``_IS_TOP_LEVEL`` are only set as non-cache variables if - they are already set as non-cache variables when - ``project( ...)`` 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. + ``_IS_TOP_LEVEL``, if already set as normal variables when + ``project( ...)`` 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.31 + ``_SOURCE_DIR``, ``_BINARY_DIR``, and + ``_IS_TOP_LEVEL`` are always set as normal variables by + ``project( ...)``. See policy :policy:`CMP0180`. + Cache entries by the same names are always set as before. Options ^^^^^^^ diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index c6fcc1f..c62fb48 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -57,6 +57,7 @@ Policies Introduced by CMake 3.31 .. toctree:: :maxdepth: 1 + CMP0180: project() always sets _* as normal variables. CMP0179: De-duplication of static libraries on link lines keeps first occurrence. CMP0178: Test command lines preserve empty arguments. CMP0177: install() DESTINATION paths are normalized. diff --git a/Help/policy/CMP0180.rst b/Help/policy/CMP0180.rst new file mode 100644 index 0000000..f69c0bc --- /dev/null +++ b/Help/policy/CMP0180.rst @@ -0,0 +1,36 @@ +CMP0180 +------- + +.. versionadded:: 3.31 + +:command:`project` always sets ``_*`` as normal variables. + +In CMake 3.29 and below, the :command:`project` command set +:variable:`_SOURCE_DIR`, :variable:`_BINARY_DIR`, +and :variable:`_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 ``_SOURCE_DIR``, +``_BINARY_DIR``, and ``_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 +``_SOURCE_DIR``, ``_BINARY_DIR``, and +``_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 +``_SOURCE_DIR``, ``_BINARY_DIR``, and +``_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 diff --git a/Help/release/dev/project-vars-policy.rst b/Help/release/dev/project-vars-policy.rst new file mode 100644 index 0000000..02287aa --- /dev/null +++ b/Help/release/dev/project-vars-policy.rst @@ -0,0 +1,7 @@ +project-vars-policy +------------------- + +* The :command:`project` command now always sets + :variable:`_SOURCE_DIR`, :variable:`_BINARY_DIR`, + and :variable:`_IS_TOP_LEVEL` as both normal variables and + cache entries. See policy :policy:`CMP0180`. diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 55fce2e..dbd6ce8 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -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 _* 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) \ diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 90c1042..5d854cd 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx @@ -58,11 +58,13 @@ bool cmProjectCommand(std::vector 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 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 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"); } diff --git a/Tests/RunCMake/project/CMP0180-NEW-stdout.txt b/Tests/RunCMake/project/CMP0180-NEW-stdout.txt new file mode 100644 index 0000000..34e5d49 --- /dev/null +++ b/Tests/RunCMake/project/CMP0180-NEW-stdout.txt @@ -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 diff --git a/Tests/RunCMake/project/CMP0180-NEW.cmake b/Tests/RunCMake/project/CMP0180-NEW.cmake new file mode 100644 index 0000000..71f8b84 --- /dev/null +++ b/Tests/RunCMake/project/CMP0180-NEW.cmake @@ -0,0 +1,2 @@ +# CMP0180 is handled in CMakeLists.txt +include(CMP0180.cmake) diff --git a/Tests/RunCMake/project/CMP0180-OLD-stdout.txt b/Tests/RunCMake/project/CMP0180-OLD-stdout.txt new file mode 100644 index 0000000..4ef55a4 --- /dev/null +++ b/Tests/RunCMake/project/CMP0180-OLD-stdout.txt @@ -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 diff --git a/Tests/RunCMake/project/CMP0180-OLD.cmake b/Tests/RunCMake/project/CMP0180-OLD.cmake new file mode 100644 index 0000000..71f8b84 --- /dev/null +++ b/Tests/RunCMake/project/CMP0180-OLD.cmake @@ -0,0 +1,2 @@ +# CMP0180 is handled in CMakeLists.txt +include(CMP0180.cmake) diff --git a/Tests/RunCMake/project/CMP0180.cmake b/Tests/RunCMake/project/CMP0180.cmake new file mode 100644 index 0000000..36a7960 --- /dev/null +++ b/Tests/RunCMake/project/CMP0180.cmake @@ -0,0 +1,17 @@ +add_subdirectory(subdir1) + +# Simulate a situation that FetchContent_MakeAvailable() used to be able to +# create, but that should no longer be possible. If depname_SOURCE_DIR and +# depname_BINARY_DIR variables are defined as non-cache variables before the +# project(depname) call, those non-cache variables used to prevent project() +# from setting those variables itself due to CMP0126 (if set to NEW). This only +# showed up if the project(depname) call was not in the dependency's top level +# CMakeLists.txt file, but rather in a subdirectory (googletest is one example +# that used to do this). Since CMake 3.30.3, the dependency's project() call +# should set non-cache variables that will make the variable values visible +# and avoid any masking from variables set before the project() call. We want +# to verify this 3.30.3+ behavior here and in subdir2. +set(sub2proj_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(sub2proj_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +add_subdirectory(subdir2) diff --git a/Tests/RunCMake/project/CMakeLists.txt b/Tests/RunCMake/project/CMakeLists.txt index 7ddd81d..28146c4 100644 --- a/Tests/RunCMake/project/CMakeLists.txt +++ b/Tests/RunCMake/project/CMakeLists.txt @@ -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) diff --git a/Tests/RunCMake/project/RunCMakeTest.cmake b/Tests/RunCMake/project/RunCMakeTest.cmake index ed4b8ba..554cdff 100644 --- a/Tests/RunCMake/project/RunCMakeTest.cmake +++ b/Tests/RunCMake/project/RunCMakeTest.cmake @@ -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) diff --git a/Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt b/Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt deleted file mode 100644 index 73dadfd..0000000 --- a/Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt +++ /dev/null @@ -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 diff --git a/Tests/RunCMake/project/SameProjectVarsSubdir.cmake b/Tests/RunCMake/project/SameProjectVarsSubdir.cmake deleted file mode 100644 index 36a7960..0000000 --- a/Tests/RunCMake/project/SameProjectVarsSubdir.cmake +++ /dev/null @@ -1,17 +0,0 @@ -add_subdirectory(subdir1) - -# Simulate a situation that FetchContent_MakeAvailable() used to be able to -# create, but that should no longer be possible. If depname_SOURCE_DIR and -# depname_BINARY_DIR variables are defined as non-cache variables before the -# project(depname) call, those non-cache variables used to prevent project() -# from setting those variables itself due to CMP0126 (if set to NEW). This only -# showed up if the project(depname) call was not in the dependency's top level -# CMakeLists.txt file, but rather in a subdirectory (googletest is one example -# that used to do this). Since CMake 3.30.3, the dependency's project() call -# should set non-cache variables that will make the variable values visible -# and avoid any masking from variables set before the project() call. We want -# to verify this 3.30.3+ behavior here and in subdir2. -set(sub2proj_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(sub2proj_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) - -add_subdirectory(subdir2) diff --git a/Tests/RunCMake/project/subdir1/CMakeLists.txt b/Tests/RunCMake/project/subdir1/CMakeLists.txt index d6be229..d2d3729 100644 --- a/Tests/RunCMake/project/subdir1/CMakeLists.txt +++ b/Tests/RunCMake/project/subdir1/CMakeLists.txt @@ -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}" +) diff --git a/Tests/RunCMake/project/subdir2/CMakeLists.txt b/Tests/RunCMake/project/subdir2/CMakeLists.txt index c28e0c9..cab547f 100644 --- a/Tests/RunCMake/project/subdir2/CMakeLists.txt +++ b/Tests/RunCMake/project/subdir2/CMakeLists.txt @@ -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}" -- cgit v0.12