Browse Source

Add CMAKE_POLICY_VERSION_MINIMUM to help configure outdated projects

Provide packagers and end users with a way to try configuring projects that
have not been updated to set their policy version to a supported level.

Closes: #26698
Brad King 8 months ago
parent
commit
1a35351125

+ 3 - 1
Help/command/POLICY_VERSION.txt

@@ -2,7 +2,9 @@ This specifies that the current CMake code is written for the given range of
 CMake versions, ``<min>[...<max>]``. It sets the "policy version" to:
 
 * the range's ``<max>`` version, if specified, or to
-* the ``<min>`` version.
+* the ``<min>`` version, or to
+* the value of the :variable:`CMAKE_POLICY_VERSION_MINIMUM` variable
+  if it is higher than the other two versions.
 
 The policy version effectively requests behavior preferred as of a given CMake
 version and tells newer CMake versions to warn about their new policies.

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -254,6 +254,7 @@ Variables that Change Behavior
    /variable/CMAKE_MFC_FLAG
    /variable/CMAKE_MODULE_PATH
    /variable/CMAKE_POLICY_DEFAULT_CMPNNNN
+   /variable/CMAKE_POLICY_VERSION_MINIMUM
    /variable/CMAKE_POLICY_WARNING_CMPNNNN
    /variable/CMAKE_PREFIX_PATH
    /variable/CMAKE_PROGRAM_PATH

+ 4 - 0
Help/release/4.0.rst

@@ -72,6 +72,10 @@ Variables
   to select runtime checks for compilers targeting the MSVC ABI.
   See policy :policy:`CMP0184`.
 
+* The :variable:`CMAKE_POLICY_VERSION_MINIMUM` variable was added to
+  help pacakgers and end users try to configure existing projects that
+  have not been updated to work with supported CMake versions.
+
 * The :variable:`CMAKE_XCODE_SCHEME_LLDB_INIT_FILE` variable and corresponding
   :prop_tgt:`XCODE_SCHEME_LLDB_INIT_FILE` target property were added to tell
   the :generator:`Xcode` generator what to put in the scheme's "LLDB Init File"

+ 3 - 0
Help/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.rst

@@ -22,3 +22,6 @@ not itself been updated:
 * Projects may set this variable before a call to :command:`add_subdirectory`
   that adds a third-party project in order to set its policies without
   modifying third-party code.
+
+See :variable:`CMAKE_POLICY_VERSION_MINIMUM` set policies to ``NEW``
+based on the version of CMake that introduced them.

+ 23 - 0
Help/variable/CMAKE_POLICY_VERSION_MINIMUM.rst

@@ -0,0 +1,23 @@
+CMAKE_POLICY_VERSION_MINIMUM
+----------------------------
+
+.. versionadded:: 4.0
+
+Specify a minimum :ref:`Policy Version` for a project without modifying
+its calls to :command:`cmake_minimum_required(VERSION)` and
+:command:`cmake_policy(VERSION)`.
+
+This variable should not be set by a project in CMake code as a way to
+set its own policy version.  Use :command:`cmake_minimum_required(VERSION)`
+and/or :command:`cmake_policy(VERSION)` for that.  This variable is meant
+to externally set policies for which a project has not itself been updated:
+
+* Users running CMake may set this variable in the cache, e.g.,
+  ``-DCMAKE_POLICY_VERSION_MINIMUM=3.5``, to try configuring a project
+  that has not been updated to set at least that policy version itself.
+
+* Projects may set this variable before a call to :command:`add_subdirectory`
+  that adds a third-party project in order to set its policy version without
+  modifying third-party code.
+
+See :variable:`CMAKE_POLICY_DEFAULT_CMP<NNNN>` to set individual policies.

+ 28 - 1
Source/cmPolicies.cxx

@@ -15,6 +15,7 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 
 static bool stringToId(char const* input, cmPolicies::PolicyID& pid)
@@ -294,6 +295,30 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer,
                                     unsigned int patchVer,
                                     WarnCompat warnCompat)
 {
+  cmValue varVer = mf->GetDefinition("CMAKE_POLICY_VERSION_MINIMUM");
+  if (!varVer.IsEmpty()) {
+    unsigned int varMajor = 0;
+    unsigned int varMinor = 0;
+    unsigned int varPatch = 0;
+    unsigned int varTweak = 0;
+    if (sscanf(varVer.GetCStr(), "%u.%u.%u.%u", &varMajor, &varMinor,
+               &varPatch, &varTweak) < 2) {
+      mf->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Invalid CMAKE_POLICY_VERSION_MINIMUM value \"", varVer,
+                 "\".  "
+                 "A numeric major.minor[.patch[.tweak]] must be given."));
+      return false;
+    }
+    if (varMajor > majorVer || (varMajor == majorVer && varMinor > minorVer) ||
+        (varMajor == majorVer && varMinor == minorVer &&
+         varPatch > patchVer)) {
+      majorVer = varMajor;
+      minorVer = varMinor;
+      patchVer = varPatch;
+    }
+  }
+
   // Error on policy versions for which support has been removed.
   if (majorVer < 3 || (majorVer == 3 && minorVer < 5)) {
     if (IsFromLegacyInstallEXPORT(mf, majorVer, minorVer, patchVer)) {
@@ -305,7 +330,9 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer,
     } else {
       mf->IssueMessage(MessageType::FATAL_ERROR,
                        "Compatibility with CMake < 3.5 has been removed "
-                       "from CMake.\n" ADVICE_UPDATE_VERSION_ARGUMENT);
+                       "from CMake.\n" ADVICE_UPDATE_VERSION_ARGUMENT "\n"
+                       "Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3.5 to try "
+                       "configuring anyway.");
       cmSystemTools::SetFatalErrorOccurred();
       return false;
     }

+ 2 - 0
Tests/RunCMake/cmake_minimum_required/BeforeVersionRemoved-stderr.txt

@@ -4,5 +4,7 @@
   Update the VERSION argument <min> value\.  Or, use the <min>\.\.\.<max> syntax
   to tell CMake that the project requires at least <min> but has been updated
   to work with policies introduced by <max> or earlier\.
+
+  Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3\.5 to try configuring anyway\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$

+ 2 - 0
Tests/RunCMake/cmake_minimum_required/PolicyBeforeVersionRemoved-stderr.txt

@@ -4,5 +4,7 @@
   Update the VERSION argument <min> value\.  Or, use the <min>\.\.\.<max> syntax
   to tell CMake that the project requires at least <min> but has been updated
   to work with policies introduced by <max> or earlier\.
+
+  Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3\.5 to try configuring anyway\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$

+ 3 - 0
Tests/RunCMake/cmake_minimum_required/PolicyVersionVariable-stderr.txt

@@ -0,0 +1,3 @@
+^CMAKE_MINIMUM_REQUIRED_VERSION='3\.1'
+CMP0071='NEW'
+CMP0072=''$

+ 7 - 0
Tests/RunCMake/cmake_minimum_required/PolicyVersionVariable.cmake

@@ -0,0 +1,7 @@
+set(CMAKE_POLICY_VERSION_MINIMUM 3.10)
+cmake_minimum_required(VERSION 3.1...3.4)
+message("CMAKE_MINIMUM_REQUIRED_VERSION='${CMAKE_MINIMUM_REQUIRED_VERSION}'")
+foreach(policy CMP0071 CMP0072)
+  cmake_policy(GET ${policy} status)
+  message("${policy}='${status}'")
+endforeach()

+ 1 - 0
Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at PolicyVersionVariableBad\.cmake:2 \(cmake_minimum_required\):
+  Invalid CMAKE_POLICY_VERSION_MINIMUM value "\.\.\.3\.10"\.  A numeric
+  major\.minor\[\.patch\[\.tweak\]\] must be given\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$

+ 2 - 0
Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad.cmake

@@ -0,0 +1,2 @@
+set(CMAKE_POLICY_VERSION_MINIMUM ...3.10)
+cmake_minimum_required(VERSION 3.1...3.4)

+ 2 - 0
Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake

@@ -7,3 +7,5 @@ run_cmake(BeforeVersionDeprecated)
 run_cmake(Range)
 run_cmake(RangeBad)
 run_cmake(Unknown)
+run_cmake(PolicyVersionVariable)
+run_cmake(PolicyVersionVariableBad)