#include "cmPolicies.h" #include #include #include #include #include #include #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(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 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 = "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 { std::ostringstream e; e << defaultVar << " has value \"" << defaultValue << R"(" but must be "OLD", "NEW", or "" (empty).)"; mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); 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) { std::ostringstream e; e << "Invalid policy version value \"" << version_min << "\". " << "A numeric major.minor[.patch[.tweak]] must be given."; mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); 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())) { std::ostringstream e; e << "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."; mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); 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(MessageType::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(MessageType::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, 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 < 5)) && // 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.5 will be removed from " "a future version of CMake.\n" "Update the VERSION argument value or use a ... suffix " "to tell CMake that the project does not need compatibility with " "older versions."); } // now loop over all the policies and set them as appropriate std::vector ancientPolicies; for (PolicyID pid = cmPolicies::CMP0000; pid != cmPolicies::CMPCOUNT; pid = static_cast(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) { 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(); } std::string cmPolicies::GetPolicyDeprecatedWarning(cmPolicies::PolicyID id) { std::ostringstream msg; /* clang-format off */ msg << "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." ; /* clang-format on */ 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 /*unused*/) { 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(); }