cmake_minimum_required: Optionally set policies with version range

Teach `cmake_minimum_required` and `cmake_policy(VERSION)` to support a
version range of the form `<min>[...<max>]`.  Define this to mean that
version `<min>` is required, but known policies up to those introduced
by `<max>` will be set to `NEW`.  This will allow projects to easily
specify a range of versions for which they have been updated.
This commit is contained in:
Brad King 2018-03-16 12:42:36 -04:00
parent 6a41aa2abd
commit 45408b5ea1
18 changed files with 210 additions and 41 deletions

View File

@ -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
When a ``<min>`` version higher than 2.4 is specified the command
implicitly invokes::
cmake_policy(VERSION <min>[...<max>])
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 major[.minor[.patch[.tweak]]])
which sets the cmake policy version level to the version specified.
When 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.

View File

@ -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]])
cmake_policy(VERSION <min>[...<max>])
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
``<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.

View File

@ -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.

View File

@ -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.

View File

@ -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"

View File

@ -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;

View File

@ -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;
}

View File

@ -1464,7 +1464,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;
@ -4149,9 +4149,10 @@ void cmMakefile::PopSnapshot(bool reportError)
assert(this->StateSnapshot.IsValid());
}
bool cmMakefile::SetPolicyVersion(std::string const& version_min)
bool cmMakefile::SetPolicyVersion(std::string const& version_min,
std::string const& version_max)
{
return cmPolicies::ApplyPolicyVersion(this, version_min);
return cmPolicies::ApplyPolicyVersion(this, version_min, version_max);
}
bool cmMakefile::HasCMP0054AlreadyBeenReported(

View File

@ -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(std::string const& version_min);
bool SetPolicyVersion(std::string const& version_min,
std::string const& version_max);
void RecordPolicies(cmPolicies::PolicyMap& pm);
//@}

View File

@ -154,7 +154,8 @@ static bool GetPolicyDefault(cmMakefile* mf, std::string const& policy,
}
bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf,
std::string const& version_min)
std::string const& version_min,
std::string const& version_max)
{
// Parse components of the minimum version.
unsigned int minMajor = 2;
@ -205,6 +206,42 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf,
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);
}

View File

@ -289,7 +289,8 @@ public:
///! Set a policy level for this listfile
static bool ApplyPolicyVersion(cmMakefile* mf,
std::string const& version_min);
std::string const& version_min,
std::string const& version_max);
static bool ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer,
unsigned int minorVer, unsigned int patchVer);

View File

@ -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());
}

View File

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

View File

@ -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()

View File

@ -0,0 +1 @@
1

View File

@ -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\)$

View File

@ -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)

View File

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