diff options
Diffstat (limited to 'Source/cmPolicies.cxx')
-rw-r--r-- | Source/cmPolicies.cxx | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx new file mode 100644 index 0000000..124fb6a --- /dev/null +++ b/Source/cmPolicies.cxx @@ -0,0 +1,349 @@ +#include "cmPolicies.h" + +#include "cmAlgorithms.h" +#include "cmMakefile.h" +#include "cmVersion.h" +#include "cmVersionMacros.h" +#include "cmake.h" +#include <assert.h> +#include <ctype.h> +#include <map> +#include <queue> +#include <set> + +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 CM_NULLPTR; + } + return CM_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; + CM_FOR_EACH_POLICY_ID_VERSION(POLICY_CASE) +#undef POLICY_CASE + case cmPolicies::CMPCOUNT: + return CM_NULLPTR; + } + return CM_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)); + 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 CM_NULLPTR; + } + return CM_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 (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(); +} |