Просмотр исходного кода

Merge topic 'policy-version-range'

45408b5ea1 cmake_minimum_required: Optionally set policies with version range
6a41aa2abd cmPolicies: Split parsing and impl of ApplyPolicyVersion
1d00ed7cf7 cmPolicies: Drop unnecessary check from ApplyPolicyVersion
0df559832b cmPolicies: Pass policy version as std::string

Acked-by: Kitware Robot <[email protected]>
Merge-request: !1864
Craig Scott 7 лет назад
Родитель
Сommit
82df2fe17e

+ 20 - 14
Help/command/cmake_minimum_required.rst

@@ -4,11 +4,15 @@ cmake_minimum_required
 Set the minimum required version of cmake for a project and
 update `Policy Settings`_ to match the version given::
 
-  cmake_minimum_required(VERSION major.minor[.patch[.tweak]]
-                         [FATAL_ERROR])
+  cmake_minimum_required(VERSION <min>[...<max>] [FATAL_ERROR])
 
-If the current version of CMake is lower than that required it will
-stop processing the project and report an error.
+``<min>`` and the optional ``<max>`` are each CMake versions of the form
+``major.minor[.patch[.tweak]]``, and the ``...`` is literal.
+
+If the running version of CMake is lower than the ``<min>`` required
+version it will stop processing the project and report an error.
+The optional ``<max>`` version, if specified, must be at least the
+``<min>`` version and affects policy settings as described below.
 
 The ``FATAL_ERROR`` option is accepted but ignored by CMake 2.6 and
 higher.  It should be specified so CMake versions 2.4 and lower fail
@@ -30,21 +34,23 @@ Policy Settings
 
 The ``cmake_minimum_required(VERSION)`` command implicitly invokes the
 :command:`cmake_policy(VERSION)` command to specify that the current
-project code is written for the given version of CMake.
-All policies introduced in the specified version or earlier will be
-set to use NEW behavior.  All policies introduced after the specified
-version will be unset.  This effectively requests behavior preferred
+project code is written for the given range of CMake versions.
+All policies known to the running version of CMake and introduced
+in the ``<min>`` (or ``<max>``, if specified) version or earlier will
+be set to use ``NEW`` behavior.  All policies introduced in later
+versions will be unset.  This effectively requests behavior preferred
 as of a given CMake version and tells newer CMake versions to warn
 about their new policies.
 
-When a version higher than 2.4 is specified the command implicitly
-invokes::
+When a ``<min>`` version higher than 2.4 is specified the command
+implicitly invokes::
 
-  cmake_policy(VERSION major[.minor[.patch[.tweak]]])
+  cmake_policy(VERSION <min>[...<max>])
 
-which sets the cmake policy version level to the version specified.
-When version 2.4 or lower is given the command implicitly invokes::
+which sets CMake policies based on the range of versions specified.
+When a ``<min>`` version 2.4 or lower is given the command implicitly
+invokes::
 
-  cmake_policy(VERSION 2.4)
+  cmake_policy(VERSION 2.4[...<max>])
 
 which enables compatibility features for CMake 2.4 and lower.

+ 13 - 8
Help/command/cmake_policy.rst

@@ -24,17 +24,22 @@ The ``cmake_policy`` command is used to set policies to ``OLD`` or ``NEW``
 behavior.  While setting policies individually is supported, we
 encourage projects to set policies based on CMake versions::
 
-  cmake_policy(VERSION major.minor[.patch[.tweak]])
-
-Specify that the current CMake code is written for the given
-version of CMake.  All policies introduced in the specified version or
-earlier will be set to use ``NEW`` behavior.  All policies introduced
-after the specified version will be unset (unless the
+  cmake_policy(VERSION <min>[...<max>])
+
+``<min>`` and the optional ``<max>`` are each CMake versions of the form
+``major.minor[.patch[.tweak]]``, and the ``...`` is literal.  The ``<min>``
+version must be at least ``2.4`` and at most the running version of CMake.
+The ``<max>`` version, if specified, must be at least the ``<min>`` version
+but may exceed the running version of CMake.
+
+This specifies that the current CMake code is written for the given
+range of CMake versions.  All policies known to the running version of CMake
+and introduced in the ``<min>`` (or ``<max>``, if specified) version
+or earlier will be set to use ``NEW`` behavior.  All policies
+introduced in later versions will be unset (unless the
 :variable:`CMAKE_POLICY_DEFAULT_CMP<NNNN>` variable sets a default).
 This effectively requests behavior preferred as of a given CMake
 version and tells newer CMake versions to warn about their new policies.
-The policy version specified must be at least 2.4 or the command will
-report an error.
 
 Note that the :command:`cmake_minimum_required(VERSION)`
 command implicitly calls ``cmake_policy(VERSION)`` too.

+ 8 - 0
Help/release/dev/policy-version-range.rst

@@ -0,0 +1,8 @@
+policy-version-range
+--------------------
+
+* The :command:`cmake_minimum_required` and :command:`cmake_policy(VERSION)`
+  commands now accept a version range using the form ``<min>[...<max>]``.
+  The ``<min>`` version is required but policies are set based on the
+  ``<max>`` version.  This allows projects to specify a range of versions
+  for which they have been updated and avoid explicit policy settings.

+ 2 - 4
Help/variable/CMAKE_MINIMUM_REQUIRED_VERSION.rst

@@ -1,7 +1,5 @@
 CMAKE_MINIMUM_REQUIRED_VERSION
 ------------------------------
 
-Version specified to :command:`cmake_minimum_required` command
-
-Variable containing the ``VERSION`` component specified in the
-:command:`cmake_minimum_required` command.
+The ``<min>`` version of CMake given to the most recent call to the
+:command:`cmake_minimum_required(VERSION)` command.

+ 2 - 1
Source/CPack/cmCPackGenerator.cxx

@@ -1002,7 +1002,8 @@ int cmCPackGenerator::DoPackage()
   { // scope that enables package generators to run internal scripts with
     // latest CMake policies enabled
     cmMakefile::ScopePushPop pp{ this->MakefileMap };
-    this->MakefileMap->SetPolicyVersion(cmVersion::GetCMakeVersion());
+    this->MakefileMap->SetPolicyVersion(cmVersion::GetCMakeVersion(),
+                                        std::string());
 
     if (!this->PackageFiles() || cmSystemTools::GetErrorOccuredFlag()) {
       cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem compressing the directory"

+ 21 - 6
Source/cmCMakeMinimumRequired.cxx

@@ -45,9 +45,24 @@ bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args,
     return this->EnforceUnknownArguments();
   }
 
+  // Separate the <min> version and any trailing ...<max> component.
+  std::string::size_type const dd = version_string.find("...");
+  std::string const version_min = version_string.substr(0, dd);
+  std::string const version_max = dd != std::string::npos
+    ? version_string.substr(dd + 3, std::string::npos)
+    : std::string();
+  if (dd != std::string::npos &&
+      (version_min.empty() || version_max.empty())) {
+    std::ostringstream e;
+    e << "VERSION \"" << version_string
+      << "\" does not have a version on both sides of \"...\".";
+    this->SetError(e.str());
+    return false;
+  }
+
   // Save the required version string.
   this->Makefile->AddDefinition("CMAKE_MINIMUM_REQUIRED_VERSION",
-                                version_string.c_str());
+                                version_min.c_str());
 
   // Get the current version number.
   unsigned int current_major = cmVersion::GetMajorVersion();
@@ -61,10 +76,10 @@ bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args,
   unsigned int required_minor = 0;
   unsigned int required_patch = 0;
   unsigned int required_tweak = 0;
-  if (sscanf(version_string.c_str(), "%u.%u.%u.%u", &required_major,
+  if (sscanf(version_min.c_str(), "%u.%u.%u.%u", &required_major,
              &required_minor, &required_patch, &required_tweak) < 2) {
     std::ostringstream e;
-    e << "could not parse VERSION \"" << version_string << "\".";
+    e << "could not parse VERSION \"" << version_min << "\".";
     this->SetError(e.str());
     return false;
   }
@@ -78,7 +93,7 @@ bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args,
        current_patch == required_patch && current_tweak < required_tweak)) {
     // The current version is too low.
     std::ostringstream e;
-    e << "CMake " << version_string
+    e << "CMake " << version_min
       << " or higher is required.  You are running version "
       << cmVersion::GetCMakeVersion();
     this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
@@ -95,9 +110,9 @@ bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args,
     this->Makefile->IssueMessage(
       cmake::AUTHOR_WARNING,
       "Compatibility with CMake < 2.4 is not supported by CMake >= 3.0.");
-    this->Makefile->SetPolicyVersion("2.4");
+    this->Makefile->SetPolicyVersion("2.4", version_max);
   } else {
-    this->Makefile->SetPolicyVersion(version_string.c_str());
+    this->Makefile->SetPolicyVersion(version_min, version_max);
   }
 
   return true;

+ 18 - 1
Source/cmCMakePolicyCommand.cxx

@@ -156,6 +156,23 @@ bool cmCMakePolicyCommand::HandleVersionMode(
     this->SetError("VERSION given too many arguments");
     return false;
   }
-  this->Makefile->SetPolicyVersion(args[1].c_str());
+  std::string const& version_string = args[1];
+
+  // Separate the <min> version and any trailing ...<max> component.
+  std::string::size_type const dd = version_string.find("...");
+  std::string const version_min = version_string.substr(0, dd);
+  std::string const version_max = dd != std::string::npos
+    ? version_string.substr(dd + 3, std::string::npos)
+    : std::string();
+  if (dd != std::string::npos &&
+      (version_min.empty() || version_max.empty())) {
+    std::ostringstream e;
+    e << "VERSION \"" << version_string
+      << "\" does not have a version on both sides of \"...\".";
+    this->SetError(e.str());
+    return false;
+  }
+
+  this->Makefile->SetPolicyVersion(version_min, version_max);
   return true;
 }

+ 4 - 3
Source/cmMakefile.cxx

@@ -1490,7 +1490,7 @@ void cmMakefile::Configure()
         this->SetCheckCMP0000(true);
 
         // Implicitly set the version for the user.
-        this->SetPolicyVersion("2.4");
+        this->SetPolicyVersion("2.4", std::string());
       }
     }
     bool hasProject = false;
@@ -4175,9 +4175,10 @@ void cmMakefile::PopSnapshot(bool reportError)
   assert(this->StateSnapshot.IsValid());
 }
 
-bool cmMakefile::SetPolicyVersion(const char* version)
+bool cmMakefile::SetPolicyVersion(std::string const& version_min,
+                                  std::string const& version_max)
 {
-  return cmPolicies::ApplyPolicyVersion(this, version);
+  return cmPolicies::ApplyPolicyVersion(this, version_min, version_max);
 }
 
 bool cmMakefile::HasCMP0054AlreadyBeenReported(

+ 2 - 1
Source/cmMakefile.h

@@ -285,7 +285,8 @@ public:
   bool SetPolicy(cmPolicies::PolicyID id, cmPolicies::PolicyStatus status);
   bool SetPolicy(const char* id, cmPolicies::PolicyStatus status);
   cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID id) const;
-  bool SetPolicyVersion(const char* version);
+  bool SetPolicyVersion(std::string const& version_min,
+                        std::string const& version_max);
   void RecordPolicies(cmPolicies::PolicyMap& pm);
   //@}
 

+ 69 - 28
Source/cmPolicies.cxx

@@ -153,31 +153,26 @@ static bool GetPolicyDefault(cmMakefile* mf, std::string const& policy,
   return true;
 }
 
-bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, const char* version)
+bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf,
+                                    std::string const& version_min,
+                                    std::string const& version_max)
 {
-  std::string ver = "2.4.0";
-
-  if (version && strlen(version) > 0) {
-    ver = version;
-  }
-
-  unsigned int majorVer = 2;
-  unsigned int minorVer = 0;
-  unsigned int patchVer = 0;
-  unsigned int tweakVer = 0;
-
-  // parse the string
-  if (sscanf(ver.c_str(), "%u.%u.%u.%u", &majorVer, &minorVer, &patchVer,
-             &tweakVer) < 2) {
+  // Parse components of the minimum version.
+  unsigned int minMajor = 2;
+  unsigned int minMinor = 0;
+  unsigned int minPatch = 0;
+  unsigned int minTweak = 0;
+  if (sscanf(version_min.c_str(), "%u.%u.%u.%u", &minMajor, &minMinor,
+             &minPatch, &minTweak) < 2) {
     std::ostringstream e;
-    e << "Invalid policy version value \"" << ver << "\".  "
+    e << "Invalid policy version value \"" << version_min << "\".  "
       << "A numeric major.minor[.patch[.tweak]] must be given.";
     mf->IssueMessage(cmake::FATAL_ERROR, e.str());
     return false;
   }
 
   // it is an error if the policy version is less than 2.4
-  if (majorVer < 2 || (majorVer == 2 && minorVer < 4)) {
+  if (minMajor < 2 || (minMajor == 2 && minMinor < 4)) {
     mf->IssueMessage(
       cmake::FATAL_ERROR,
       "Compatibility with CMake < 2.4 is not supported by CMake >= 3.0.  "
@@ -188,19 +183,19 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, const char* version)
 
   // It is an error if the policy version is greater than the running
   // CMake.
-  if (majorVer > cmVersion::GetMajorVersion() ||
-      (majorVer == cmVersion::GetMajorVersion() &&
-       minorVer > cmVersion::GetMinorVersion()) ||
-      (majorVer == cmVersion::GetMajorVersion() &&
-       minorVer == cmVersion::GetMinorVersion() &&
-       patchVer > cmVersion::GetPatchVersion()) ||
-      (majorVer == cmVersion::GetMajorVersion() &&
-       minorVer == cmVersion::GetMinorVersion() &&
-       patchVer == cmVersion::GetPatchVersion() &&
-       tweakVer > cmVersion::GetTweakVersion())) {
+  if (minMajor > cmVersion::GetMajorVersion() ||
+      (minMajor == cmVersion::GetMajorVersion() &&
+       minMinor > cmVersion::GetMinorVersion()) ||
+      (minMajor == cmVersion::GetMajorVersion() &&
+       minMinor == cmVersion::GetMinorVersion() &&
+       minPatch > cmVersion::GetPatchVersion()) ||
+      (minMajor == cmVersion::GetMajorVersion() &&
+       minMinor == cmVersion::GetMinorVersion() &&
+       minPatch == cmVersion::GetPatchVersion() &&
+       minTweak > cmVersion::GetTweakVersion())) {
     std::ostringstream e;
     e << "An attempt was made to set the policy version of CMake to \""
-      << version << "\" which is greater than this version of CMake.  "
+      << version_min << "\" which is greater than this version of CMake.  "
       << "This is not allowed because the greater version may have new "
       << "policies not known to this CMake.  "
       << "You may need a newer CMake version to build this project.";
@@ -208,6 +203,52 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, const char* version)
     return false;
   }
 
+  unsigned int polMajor = minMajor;
+  unsigned int polMinor = minMinor;
+  unsigned int polPatch = minPatch;
+
+  if (!version_max.empty()) {
+    // Parse components of the maximum version.
+    unsigned int maxMajor = 0;
+    unsigned int maxMinor = 0;
+    unsigned int maxPatch = 0;
+    unsigned int maxTweak = 0;
+    if (sscanf(version_max.c_str(), "%u.%u.%u.%u", &maxMajor, &maxMinor,
+               &maxPatch, &maxTweak) < 2) {
+      std::ostringstream e;
+      e << "Invalid policy max version value \"" << version_max << "\".  "
+        << "A numeric major.minor[.patch[.tweak]] must be given.";
+      mf->IssueMessage(cmake::FATAL_ERROR, e.str());
+      return false;
+    }
+
+    // It is an error if the min version is greater than the max version.
+    if (minMajor > maxMajor || (minMajor == maxMajor && minMinor > maxMinor) ||
+        (minMajor == maxMajor && minMinor == maxMinor &&
+         minPatch > maxPatch) ||
+        (minMajor == maxMajor && minMinor == maxMinor &&
+         minPatch == maxPatch && minTweak > maxTweak)) {
+      std::ostringstream e;
+      e << "Policy VERSION range \"" << version_min << "..." << version_max
+        << "\""
+        << " specifies a larger minimum than maximum.";
+      mf->IssueMessage(cmake::FATAL_ERROR, e.str());
+      return false;
+    }
+
+    // Use the max version as the policy version.
+    polMajor = maxMajor;
+    polMinor = maxMinor;
+    polPatch = maxPatch;
+  }
+
+  return cmPolicies::ApplyPolicyVersion(mf, polMajor, polMinor, polPatch);
+}
+
+bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer,
+                                    unsigned int minorVer,
+                                    unsigned int patchVer)
+{
   // now loop over all the policies and set them as appropriate
   std::vector<cmPolicies::PolicyID> ancientPolicies;
   for (PolicyID pid = cmPolicies::CMP0000; pid != cmPolicies::CMPCOUNT;

+ 5 - 1
Source/cmPolicies.h

@@ -290,7 +290,11 @@ public:
   static cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID id);
 
   ///! Set a policy level for this listfile
-  static bool ApplyPolicyVersion(cmMakefile* mf, const char* version);
+  static bool ApplyPolicyVersion(cmMakefile* mf,
+                                 std::string const& version_min,
+                                 std::string const& version_max);
+  static bool ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer,
+                                 unsigned int minorVer, unsigned int patchVer);
 
   ///! return a warning string for a given policy
   static std::string GetPolicyWarning(cmPolicies::PolicyID id);

+ 1 - 1
Source/cmQtAutoGenerator.cxx

@@ -612,7 +612,7 @@ bool cmQtAutoGenerator::Run(std::string const& infoFile,
     auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot);
     // The OLD/WARN behavior for policy CMP0053 caused a speed regression.
     // https://gitlab.kitware.com/cmake/cmake/issues/17570
-    makefile->SetPolicyVersion("3.9");
+    makefile->SetPolicyVersion("3.9", std::string());
     gg.SetCurrentMakefile(makefile.get());
     success = this->Init(makefile.get());
   }

+ 4 - 0
Tests/RunCMake/cmake_minimum_required/Range-stderr.txt

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

+ 6 - 0
Tests/RunCMake/cmake_minimum_required/Range.cmake

@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.10...3.11)
+message("CMAKE_MINIMUM_REQUIRED_VERSION='${CMAKE_MINIMUM_REQUIRED_VERSION}'")
+foreach(policy CMP0071 CMP0072 CMP0073)
+  cmake_policy(GET ${policy} status)
+  message("${policy}='${status}'")
+endforeach()

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

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

+ 56 - 0
Tests/RunCMake/cmake_minimum_required/RangeBad-stderr.txt

@@ -0,0 +1,56 @@
+^CMake Error at RangeBad.cmake:1 \(cmake_minimum_required\):
+  cmake_minimum_required VERSION "3.11..." does not have a version on both
+  sides of "...".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RangeBad.cmake:2 \(cmake_minimum_required\):
+  cmake_minimum_required VERSION "...3.11" does not have a version on both
+  sides of "...".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RangeBad.cmake:3 \(cmake_minimum_required\):
+  cmake_minimum_required VERSION "..." does not have a version on both sides
+  of "...".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RangeBad.cmake:4 \(cmake_minimum_required\):
+  Invalid policy max version value "4".  A numeric
+  major.minor\[.patch\[.tweak\]\] must be given.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RangeBad.cmake:5 \(cmake_minimum_required\):
+  Policy VERSION range "3.11...3.10" specifies a larger minimum than maximum.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RangeBad.cmake:6 \(cmake_policy\):
+  cmake_policy VERSION "3.11..." does not have a version on both sides of
+  "...".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RangeBad.cmake:7 \(cmake_policy\):
+  cmake_policy VERSION "...3.11" does not have a version on both sides of
+  "...".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RangeBad.cmake:8 \(cmake_policy\):
+  cmake_policy VERSION "..." does not have a version on both sides of "...".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RangeBad.cmake:9 \(cmake_policy\):
+  Invalid policy max version value "4".  A numeric
+  major.minor\[.patch\[.tweak\]\] must be given.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RangeBad.cmake:10 \(cmake_policy\):
+  Policy VERSION range "3.11...3.10" specifies a larger minimum than maximum.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 10 - 0
Tests/RunCMake/cmake_minimum_required/RangeBad.cmake

@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.11...)
+cmake_minimum_required(VERSION ...3.11)
+cmake_minimum_required(VERSION ...)
+cmake_minimum_required(VERSION 3.11...4)
+cmake_minimum_required(VERSION 3.11...3.10)
+cmake_policy(VERSION 3.11...)
+cmake_policy(VERSION ...3.11)
+cmake_policy(VERSION ...)
+cmake_policy(VERSION 3.11...4)
+cmake_policy(VERSION 3.11...3.10)

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

@@ -3,3 +3,5 @@ include(RunCMake)
 run_cmake(Before24)
 run_cmake(CompatBefore24)
 run_cmake(PolicyBefore24)
+run_cmake(Range)
+run_cmake(RangeBad)