You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
434 lines
15 KiB
434 lines
15 KiB
#include "cmPolicies.h"
|
|
|
|
#include <cassert>
|
|
#include <cctype>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
#include "cmListFileCache.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMessageType.h"
|
|
#include "cmState.h"
|
|
#include "cmStateSnapshot.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmValue.h"
|
|
#include "cmVersion.h"
|
|
|
|
static bool stringToId(const char* input, cmPolicies::PolicyID& pid)
|
|
{
|
|
assert(input);
|
|
if (strlen(input) != 7) {
|
|
return false;
|
|
}
|
|
if (!cmHasLiteralPrefix(input, "CMP")) {
|
|
return false;
|
|
}
|
|
if (cmHasLiteralSuffix(input, "0000")) {
|
|
pid = cmPolicies::CMP0000;
|
|
return true;
|
|
}
|
|
for (int i = 3; i < 7; ++i) {
|
|
if (!isdigit(*(input + i))) {
|
|
return false;
|
|
}
|
|
}
|
|
long id;
|
|
if (!cmStrToLong(input + 3, &id)) {
|
|
return false;
|
|
}
|
|
if (id >= cmPolicies::CMPCOUNT) {
|
|
return false;
|
|
}
|
|
pid = static_cast<cmPolicies::PolicyID>(id);
|
|
return true;
|
|
}
|
|
|
|
#define CM_SELECT_ID_VERSION(F, A1, A2, A3, A4, A5, A6) F(A1, A3, A4, A5)
|
|
#define CM_FOR_EACH_POLICY_ID_VERSION(POLICY) \
|
|
CM_FOR_EACH_POLICY_TABLE(POLICY, CM_SELECT_ID_VERSION)
|
|
|
|
#define CM_SELECT_ID_DOC(F, A1, A2, A3, A4, A5, A6) F(A1, A2)
|
|
#define CM_FOR_EACH_POLICY_ID_DOC(POLICY) \
|
|
CM_FOR_EACH_POLICY_TABLE(POLICY, CM_SELECT_ID_DOC)
|
|
|
|
static const char* idToString(cmPolicies::PolicyID id)
|
|
{
|
|
switch (id) {
|
|
#define POLICY_CASE(ID) \
|
|
case cmPolicies::ID: \
|
|
return #ID;
|
|
CM_FOR_EACH_POLICY_ID(POLICY_CASE)
|
|
#undef POLICY_CASE
|
|
case cmPolicies::CMPCOUNT:
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static const char* idToVersion(cmPolicies::PolicyID id)
|
|
{
|
|
switch (id) {
|
|
#define POLICY_CASE(ID, V_MAJOR, V_MINOR, V_PATCH) \
|
|
case cmPolicies::ID: \
|
|
return #V_MAJOR "." #V_MINOR "." #V_PATCH;
|
|
// NOLINTNEXTLINE(bugprone-branch-clone)
|
|
CM_FOR_EACH_POLICY_ID_VERSION(POLICY_CASE)
|
|
#undef POLICY_CASE
|
|
case cmPolicies::CMPCOUNT:
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static bool isPolicyNewerThan(cmPolicies::PolicyID id, unsigned int majorV,
|
|
unsigned int minorV, unsigned int patchV)
|
|
{
|
|
switch (id) {
|
|
#define POLICY_CASE(ID, V_MAJOR, V_MINOR, V_PATCH) \
|
|
case cmPolicies::ID: \
|
|
return (majorV < (V_MAJOR) || \
|
|
(majorV == (V_MAJOR) && minorV + 1 < (V_MINOR) + 1) || \
|
|
(majorV == (V_MAJOR) && minorV == (V_MINOR) && \
|
|
patchV + 1 < (V_PATCH) + 1));
|
|
// NOLINTNEXTLINE(bugprone-branch-clone)
|
|
CM_FOR_EACH_POLICY_ID_VERSION(POLICY_CASE)
|
|
#undef POLICY_CASE
|
|
case cmPolicies::CMPCOUNT:
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static const char* idToShortDescription(cmPolicies::PolicyID id)
|
|
{
|
|
switch (id) {
|
|
#define POLICY_CASE(ID, SHORT_DESCRIPTION) \
|
|
case cmPolicies::ID: \
|
|
return SHORT_DESCRIPTION;
|
|
CM_FOR_EACH_POLICY_ID_DOC(POLICY_CASE)
|
|
#undef POLICY_CASE
|
|
case cmPolicies::CMPCOUNT:
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static void DiagnoseAncientPolicies(
|
|
std::vector<cmPolicies::PolicyID> const& ancient, unsigned int majorVer,
|
|
unsigned int minorVer, unsigned int patchVer, cmMakefile* mf)
|
|
{
|
|
std::ostringstream e;
|
|
e << "The project requests behavior compatible with CMake version \""
|
|
<< majorVer << '.' << minorVer << '.' << patchVer
|
|
<< "\", which requires the OLD behavior for some policies:\n";
|
|
for (cmPolicies::PolicyID i : ancient) {
|
|
e << " " << idToString(i) << ": " << idToShortDescription(i) << '\n';
|
|
}
|
|
e << "However, this version of CMake no longer supports the OLD "
|
|
"behavior for these policies. "
|
|
"Please either update your CMakeLists.txt files to conform to "
|
|
"the new behavior or use an older version of CMake that still "
|
|
"supports the old behavior.";
|
|
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
|
|
}
|
|
|
|
static bool GetPolicyDefault(cmMakefile* mf, std::string const& policy,
|
|
cmPolicies::PolicyStatus* defaultSetting)
|
|
{
|
|
std::string defaultVar = cmStrCat("CMAKE_POLICY_DEFAULT_", policy);
|
|
std::string const& defaultValue = mf->GetSafeDefinition(defaultVar);
|
|
if (defaultValue == "NEW") {
|
|
*defaultSetting = cmPolicies::NEW;
|
|
} else if (defaultValue == "OLD") {
|
|
*defaultSetting = cmPolicies::OLD;
|
|
} else if (defaultValue.empty()) {
|
|
*defaultSetting = cmPolicies::WARN;
|
|
} else {
|
|
mf->IssueMessage(
|
|
MessageType::FATAL_ERROR,
|
|
cmStrCat(defaultVar, " has value \"", defaultValue,
|
|
R"(" but must be "OLD", "NEW", or "" (empty).)"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf,
|
|
std::string const& version_min,
|
|
std::string const& version_max,
|
|
WarnCompat warnCompat)
|
|
{
|
|
// 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) {
|
|
mf->IssueMessage(
|
|
MessageType::FATAL_ERROR,
|
|
cmStrCat("Invalid policy version value \"", version_min,
|
|
"\". "
|
|
"A numeric major.minor[.patch[.tweak]] must be given."));
|
|
return false;
|
|
}
|
|
|
|
// it is an error if the policy version is less than 2.4
|
|
if (minMajor < 2 || (minMajor == 2 && minMinor < 4)) {
|
|
mf->IssueMessage(
|
|
MessageType::FATAL_ERROR,
|
|
"Compatibility with CMake < 2.4 is not supported by CMake >= 3.0. "
|
|
"For compatibility with older versions please use any CMake 2.8.x "
|
|
"release or lower.");
|
|
return false;
|
|
}
|
|
|
|
// It is an error if the policy version is greater than the running
|
|
// CMake.
|
|
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())) {
|
|
mf->IssueMessage(
|
|
MessageType::FATAL_ERROR,
|
|
cmStrCat("An attempt was made to set the policy version of CMake to \"",
|
|
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."));
|
|
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) {
|
|
mf->IssueMessage(
|
|
MessageType::FATAL_ERROR,
|
|
cmStrCat("Invalid policy max version value \"", version_max,
|
|
"\". "
|
|
"A numeric major.minor[.patch[.tweak]] must be given."));
|
|
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)) {
|
|
mf->IssueMessage(
|
|
MessageType::FATAL_ERROR,
|
|
cmStrCat("Policy VERSION range \"", version_min, "...", version_max,
|
|
"\" specifies a larger minimum than maximum."));
|
|
return false;
|
|
}
|
|
|
|
// Use the max version as the policy version.
|
|
polMajor = maxMajor;
|
|
polMinor = maxMinor;
|
|
polPatch = maxPatch;
|
|
}
|
|
|
|
return cmPolicies::ApplyPolicyVersion(mf, polMajor, polMinor, polPatch,
|
|
warnCompat);
|
|
}
|
|
|
|
bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer,
|
|
unsigned int minorVer,
|
|
unsigned int patchVer,
|
|
WarnCompat warnCompat)
|
|
{
|
|
// Warn about policy versions for which support will be removed.
|
|
if (warnCompat == WarnCompat::On &&
|
|
(majorVer < 3 || (majorVer == 3 && minorVer < 10)) &&
|
|
// Avoid warning on calls generated by install(EXPORT)
|
|
// in CMake versions prior to 3.18.
|
|
!(majorVer == 2 && minorVer == 6 && patchVer == 0 &&
|
|
mf->GetStateSnapshot().CanPopPolicyScope() &&
|
|
cmSystemTools::Strucmp(mf->GetBacktrace().Top().Name.c_str(),
|
|
"cmake_policy") == 0)) {
|
|
mf->IssueMessage(
|
|
MessageType::DEPRECATION_WARNING,
|
|
"Compatibility with CMake < 3.10 will be removed from "
|
|
"a future version of CMake.\n"
|
|
"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.");
|
|
}
|
|
|
|
// now loop over all the policies and set them as appropriate
|
|
std::vector<cmPolicies::PolicyID> ancientPolicies;
|
|
for (PolicyID pid = cmPolicies::CMP0000; pid != cmPolicies::CMPCOUNT;
|
|
pid = static_cast<PolicyID>(pid + 1)) {
|
|
if (isPolicyNewerThan(pid, majorVer, minorVer, patchVer)) {
|
|
if (cmPolicies::GetPolicyStatus(pid) == cmPolicies::REQUIRED_ALWAYS) {
|
|
ancientPolicies.push_back(pid);
|
|
} else {
|
|
cmPolicies::PolicyStatus status = cmPolicies::WARN;
|
|
if (!GetPolicyDefault(mf, idToString(pid), &status) ||
|
|
!mf->SetPolicy(pid, status)) {
|
|
return false;
|
|
}
|
|
if (pid == cmPolicies::CMP0001 &&
|
|
(status == cmPolicies::WARN || status == cmPolicies::OLD)) {
|
|
if (!(mf->GetState()->GetInitializedCacheValue(
|
|
"CMAKE_BACKWARDS_COMPATIBILITY"))) {
|
|
// Set it to 2.4 because that is the last version where the
|
|
// variable had meaning.
|
|
mf->AddCacheDefinition(
|
|
"CMAKE_BACKWARDS_COMPATIBILITY", "2.4",
|
|
"For backwards compatibility, what version of CMake "
|
|
"commands and "
|
|
"syntax should this version of CMake try to support.",
|
|
cmStateEnums::STRING);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (!mf->SetPolicy(pid, cmPolicies::NEW)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure the project does not use any ancient policies.
|
|
if (!ancientPolicies.empty()) {
|
|
DiagnoseAncientPolicies(ancientPolicies, majorVer, minorVer, patchVer, mf);
|
|
cmSystemTools::SetFatalErrorOccurred();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool cmPolicies::GetPolicyID(const char* id, cmPolicies::PolicyID& pid)
|
|
{
|
|
return stringToId(id, pid);
|
|
}
|
|
|
|
//! return a warning string for a given policy
|
|
std::string cmPolicies::GetPolicyWarning(cmPolicies::PolicyID id)
|
|
{
|
|
return cmStrCat("Policy ", idToString(id),
|
|
" is not set: ", idToShortDescription(id),
|
|
" "
|
|
"Run \"cmake --help-policy ",
|
|
idToString(id),
|
|
"\" for "
|
|
"policy details. "
|
|
"Use the cmake_policy command to set the policy "
|
|
"and suppress this warning.");
|
|
}
|
|
|
|
std::string cmPolicies::GetPolicyDeprecatedWarning(cmPolicies::PolicyID id)
|
|
{
|
|
return cmStrCat(
|
|
"The OLD behavior for policy ", idToString(id),
|
|
" "
|
|
"will be removed from a future version of CMake.\n"
|
|
"The cmake-policies(7) manual explains that the OLD behaviors of all "
|
|
"policies are deprecated and that a policy should be set to OLD only "
|
|
"under specific short-term circumstances. Projects should be ported "
|
|
"to the NEW behavior and not rely on setting a policy to OLD.");
|
|
}
|
|
|
|
//! return an error string for when a required policy is unspecified
|
|
std::string cmPolicies::GetRequiredPolicyError(cmPolicies::PolicyID id)
|
|
{
|
|
return cmStrCat(
|
|
"Policy ", idToString(id),
|
|
" is not set to NEW: ", idToShortDescription(id),
|
|
" "
|
|
"Run \"cmake --help-policy ",
|
|
idToString(id),
|
|
"\" for policy details. "
|
|
"CMake now requires this policy to be set to NEW by the project. "
|
|
"The policy may be set explicitly using the code\n"
|
|
" cmake_policy(SET ",
|
|
idToString(id),
|
|
" NEW)\n"
|
|
"or by upgrading all policies with the code\n"
|
|
" cmake_policy(VERSION ",
|
|
idToVersion(id),
|
|
") # or later\n"
|
|
"Run \"cmake --help-command cmake_policy\" for more information.");
|
|
}
|
|
|
|
//! Get the default status for a policy
|
|
cmPolicies::PolicyStatus cmPolicies::GetPolicyStatus(
|
|
cmPolicies::PolicyID /*unused*/)
|
|
{
|
|
return cmPolicies::WARN;
|
|
}
|
|
|
|
std::string cmPolicies::GetRequiredAlwaysPolicyError(cmPolicies::PolicyID id)
|
|
{
|
|
std::string pid = idToString(id);
|
|
return cmStrCat(
|
|
"Policy ", pid,
|
|
" may not be set to OLD behavior because this "
|
|
"version of CMake no longer supports it. "
|
|
"The policy was introduced in CMake version ",
|
|
idToVersion(id),
|
|
", and use of NEW behavior is now required."
|
|
"\n"
|
|
"Please either update your CMakeLists.txt files to conform to "
|
|
"the new behavior or use an older version of CMake that still "
|
|
"supports the old behavior. Run cmake --help-policy ",
|
|
pid, " for more information.");
|
|
}
|
|
|
|
cmPolicies::PolicyStatus cmPolicies::PolicyMap::Get(
|
|
cmPolicies::PolicyID id) const
|
|
{
|
|
PolicyStatus status = cmPolicies::WARN;
|
|
|
|
if (this->Status[(POLICY_STATUS_COUNT * id) + OLD]) {
|
|
status = cmPolicies::OLD;
|
|
} else if (this->Status[(POLICY_STATUS_COUNT * id) + NEW]) {
|
|
status = cmPolicies::NEW;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
void cmPolicies::PolicyMap::Set(cmPolicies::PolicyID id,
|
|
cmPolicies::PolicyStatus status)
|
|
{
|
|
this->Status[(POLICY_STATUS_COUNT * id) + OLD] = (status == OLD);
|
|
this->Status[(POLICY_STATUS_COUNT * id) + WARN] = (status == WARN);
|
|
this->Status[(POLICY_STATUS_COUNT * id) + NEW] = (status == NEW);
|
|
}
|
|
|
|
bool cmPolicies::PolicyMap::IsDefined(cmPolicies::PolicyID id) const
|
|
{
|
|
return this->Status[(POLICY_STATUS_COUNT * id) + OLD] ||
|
|
this->Status[(POLICY_STATUS_COUNT * id) + WARN] ||
|
|
this->Status[(POLICY_STATUS_COUNT * id) + NEW];
|
|
}
|
|
|
|
bool cmPolicies::PolicyMap::IsEmpty() const
|
|
{
|
|
return this->Status.none();
|
|
}
|