398 lines
12 KiB
C++
398 lines
12 KiB
C++
#include "cmPolicies.h"
|
|
#include "cmake.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmVersion.h"
|
|
#include "cmVersionMacros.h"
|
|
#include "cmAlgorithms.h"
|
|
#include <ctype.h>
|
|
#include <map>
|
|
#include <set>
|
|
#include <queue>
|
|
#include <assert.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 (!cmSystemTools::StringToLong(input + 3, &id))
|
|
{
|
|
return false;
|
|
}
|
|
if (id >= cmPolicies::CMPCOUNT)
|
|
{
|
|
return false;
|
|
}
|
|
pid = 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 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
CM_FOR_EACH_POLICY_ID_VERSION(POLICY_CASE)
|
|
#undef POLICY_CASE
|
|
case cmPolicies::CMPCOUNT:
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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));
|
|
CM_FOR_EACH_POLICY_ID_VERSION(POLICY_CASE)
|
|
#undef POLICY_CASE
|
|
case cmPolicies::CMPCOUNT:
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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(std::vector<cmPolicies::PolicyID>::const_iterator
|
|
i = ancient.begin(); i != ancient.end(); ++i)
|
|
{
|
|
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(cmake::FATAL_ERROR, e.str());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool GetPolicyDefault(cmMakefile* mf, std::string const& policy,
|
|
cmPolicies::PolicyStatus* defaultSetting)
|
|
{
|
|
std::string defaultVar = "CMAKE_POLICY_DEFAULT_" + policy;
|
|
std::string defaultValue = mf->GetSafeDefinition(defaultVar);
|
|
if(defaultValue == "NEW")
|
|
{
|
|
*defaultSetting = cmPolicies::NEW;
|
|
}
|
|
else if(defaultValue == "OLD")
|
|
{
|
|
*defaultSetting = cmPolicies::OLD;
|
|
}
|
|
else if(defaultValue == "")
|
|
{
|
|
*defaultSetting = cmPolicies::WARN;
|
|
}
|
|
else
|
|
{
|
|
std::ostringstream e;
|
|
e << defaultVar << " has value \"" << defaultValue
|
|
<< "\" but must be \"OLD\", \"NEW\", or \"\" (empty).";
|
|
mf->IssueMessage(cmake::FATAL_ERROR, e.str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmPolicies::ApplyPolicyVersion(cmMakefile *mf,
|
|
const char *version)
|
|
{
|
|
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)
|
|
{
|
|
std::ostringstream e;
|
|
e << "Invalid policy version value \"" << ver << "\". "
|
|
<< "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))
|
|
{
|
|
mf->IssueMessage(cmake::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 (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()))
|
|
{
|
|
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. "
|
|
<< "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.";
|
|
mf->IssueMessage(cmake::FATAL_ERROR, e.str());
|
|
return false;
|
|
}
|
|
|
|
// 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 = 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.",
|
|
cmState::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::SetFatalErrorOccured();
|
|
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)
|
|
{
|
|
std::ostringstream msg;
|
|
msg <<
|
|
"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.";
|
|
return msg.str();
|
|
}
|
|
|
|
|
|
///! return an error string for when a required policy is unspecified
|
|
std::string cmPolicies::GetRequiredPolicyError(cmPolicies::PolicyID id)
|
|
{
|
|
std::ostringstream error;
|
|
error <<
|
|
"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.";
|
|
return error.str();
|
|
}
|
|
|
|
///! Get the default status for a policy
|
|
cmPolicies::PolicyStatus
|
|
cmPolicies::GetPolicyStatus(cmPolicies::PolicyID)
|
|
{
|
|
return cmPolicies::WARN;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string
|
|
cmPolicies::GetRequiredAlwaysPolicyError(cmPolicies::PolicyID id)
|
|
{
|
|
std::string pid = idToString(id);
|
|
std::ostringstream e;
|
|
e << "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.";
|
|
return e.str();
|
|
}
|
|
|
|
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();
|
|
}
|