Sfoglia il codice sorgente

cmStandardLevelResolver: Added to handle standard level queries

Refactored out of cmMakefile
Robert Maynard 5 anni fa
parent
commit
dc94b0249e

+ 2 - 0
Source/CMakeLists.txt

@@ -412,6 +412,8 @@ set(SRCS
   cmSourceFileLocationKind.h
   cmSourceGroup.cxx
   cmSourceGroup.h
+  cmStandardLevelResolver.cxx
+  cmStandardLevelResolver.h
   cmState.cxx
   cmState.h
   cmStateDirectory.cxx

+ 6 - 5
Source/cmGeneratorExpressionNode.cxx

@@ -37,6 +37,7 @@
 #include "cmPolicies.h"
 #include "cmProperty.h"
 #include "cmRange.h"
+#include "cmStandardLevelResolver.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
@@ -1707,11 +1708,11 @@ static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
     static LangMap availableFeatures;
 
     LangMap testedFeatures;
-
+    cmStandardLevelResolver standardResolver(context->LG->GetMakefile());
     for (std::string const& p : parameters) {
       std::string error;
       std::string lang;
-      if (!context->LG->GetMakefile()->CompileFeatureKnown(
+      if (!standardResolver.CompileFeatureKnown(
             context->HeadTarget->Target->GetName(), p, lang, &error)) {
         reportError(context, content->GetOriginalExpression(), error);
         return std::string();
@@ -1720,7 +1721,7 @@ static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
 
       if (availableFeatures.find(lang) == availableFeatures.end()) {
         const char* featuresKnown =
-          context->LG->GetMakefile()->CompileFeaturesAvailable(lang, &error);
+          standardResolver.CompileFeaturesAvailable(lang, &error);
         if (!featuresKnown) {
           reportError(context, content->GetOriginalExpression(), error);
           return std::string();
@@ -1745,8 +1746,8 @@ static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
           // All features known for the language are always available.
           continue;
         }
-        if (!context->LG->GetMakefile()->HaveStandardAvailable(
-              target, lit.first, context->Config, it)) {
+        if (!standardResolver.HaveStandardAvailable(target, lit.first,
+                                                    context->Config, it)) {
           if (evalLL) {
             cmProp l = target->GetLanguageStandard(lit.first, context->Config);
             if (!l) {

+ 5 - 3
Source/cmGeneratorTarget.cxx

@@ -43,6 +43,7 @@
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
 #include "cmSourceFileLocationKind.h"
+#include "cmStandardLevelResolver.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
@@ -4464,11 +4465,12 @@ void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
 bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) const
 {
   // Compute the language standard based on the compile features.
+  cmStandardLevelResolver standardResolver(this->Makefile);
   std::vector<BT<std::string>> features = this->GetCompileFeatures(config);
   for (BT<std::string> const& f : features) {
     std::string lang;
-    if (!this->Makefile->CompileFeatureKnown(this->Target->GetName(), f.Value,
-                                             lang, nullptr)) {
+    if (!standardResolver.CompileFeatureKnown(this->Target->GetName(), f.Value,
+                                              lang, nullptr)) {
       return false;
     }
 
@@ -4476,7 +4478,7 @@ bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) const
     cmProp currentLanguageStandard = this->GetLanguageStandard(lang, config);
 
     std::string newRequiredStandard;
-    if (!this->Makefile->GetNewRequiredStandard(
+    if (!standardResolver.GetNewRequiredStandard(
           this->Target->GetName(), f.Value, currentLanguageStandard,
           newRequiredStandard)) {
       return false;

+ 3 - 1
Source/cmLocalGenerator.cxx

@@ -39,6 +39,7 @@
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
 #include "cmSourceFileLocationKind.h"
+#include "cmStandardLevelResolver.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
@@ -994,12 +995,13 @@ void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags,
     this->AppendCompileOptions(flags, targetCompileOpts);
   }
 
+  cmStandardLevelResolver standardResolver(this->Makefile);
   for (auto const& it : target->GetMaxLanguageStandards()) {
     cmProp standard = target->GetLanguageStandard(it.first, config);
     if (!standard) {
       continue;
     }
-    if (this->Makefile->IsLaterStandard(it.first, *standard, it.second)) {
+    if (standardResolver.IsLaterStandard(it.first, *standard, it.second)) {
       std::ostringstream e;
       e << "The COMPILE_FEATURES property of target \"" << target->GetName()
         << "\" was evaluated when computing the link "

+ 0 - 781
Source/cmMakefile.cxx

@@ -40,7 +40,6 @@
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionEvaluationFile.h"
-#include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstallGenerator.h" // IWYU pragma: keep
 #include "cmInstallSubdirectoryGenerator.h"
@@ -4634,786 +4633,6 @@ bool cmMakefile::IgnoreErrorsCMP0061() const
   return ignoreErrors;
 }
 
-#define FEATURE_STRING(F) , #F
-static const char* const C_FEATURES[] = { nullptr FOR_EACH_C_FEATURE(
-  FEATURE_STRING) };
-
-static const char* const CXX_FEATURES[] = { nullptr FOR_EACH_CXX_FEATURE(
-  FEATURE_STRING) };
-
-static const char* const CUDA_FEATURES[] = { nullptr FOR_EACH_CUDA_FEATURE(
-  FEATURE_STRING) };
-#undef FEATURE_STRING
-
-static const char* const C_STANDARDS[] = { "90", "99", "11" };
-static const char* const CXX_STANDARDS[] = { "98", "11", "14", "17", "20" };
-static const char* const CUDA_STANDARDS[] = { "03", "11", "14", "17", "20" };
-
-bool cmMakefile::AddRequiredTargetFeature(cmTarget* target,
-                                          const std::string& feature,
-                                          std::string* error) const
-{
-  if (cmGeneratorExpression::Find(feature) != std::string::npos) {
-    target->AppendProperty("COMPILE_FEATURES", feature);
-    return true;
-  }
-
-  std::string lang;
-  if (!this->CheckCompileFeaturesAvailable(target->GetName(), feature, lang,
-                                           error)) {
-    return false;
-  }
-
-  target->AppendProperty("COMPILE_FEATURES", feature);
-
-  // FIXME: Add a policy to avoid updating the <LANG>_STANDARD target
-  // property due to COMPILE_FEATURES.  The language standard selection
-  // should be done purely at generate time based on whatever the project
-  // code put in these properties explicitly.  That is mostly true now,
-  // but for compatibility we need to continue updating the property here.
-  if (lang == "C" || lang == "OBJC") {
-    return this->AddRequiredTargetCFeature(target, feature, lang, error);
-  }
-  if (lang == "CUDA") {
-    return this->AddRequiredTargetCudaFeature(target, feature, lang, error);
-  }
-  return this->AddRequiredTargetCxxFeature(target, feature, lang, error);
-}
-
-bool cmMakefile::CheckCompileFeaturesAvailable(const std::string& targetName,
-                                               const std::string& feature,
-                                               std::string& lang,
-                                               std::string* error) const
-{
-  if (!this->CompileFeatureKnown(targetName, feature, lang, error)) {
-    return false;
-  }
-
-  const char* features = this->CompileFeaturesAvailable(lang, error);
-  if (!features) {
-    return false;
-  }
-
-  std::vector<std::string> availableFeatures = cmExpandedList(features);
-  if (!cm::contains(availableFeatures, feature)) {
-    std::ostringstream e;
-    e << "The compiler feature \"" << feature << "\" is not known to " << lang
-      << " compiler\n\""
-      << this->GetDefinition("CMAKE_" + lang + "_COMPILER_ID")
-      << "\"\nversion "
-      << this->GetDefinition("CMAKE_" + lang + "_COMPILER_VERSION") << ".";
-    if (error) {
-      *error = e.str();
-    } else {
-      this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(),
-                                             this->Backtrace);
-    }
-    return false;
-  }
-
-  return true;
-}
-
-bool cmMakefile::CompileFeatureKnown(const std::string& targetName,
-                                     const std::string& feature,
-                                     std::string& lang,
-                                     std::string* error) const
-{
-  assert(cmGeneratorExpression::Find(feature) == std::string::npos);
-
-  bool isCFeature =
-    std::find_if(cm::cbegin(C_FEATURES) + 1, cm::cend(C_FEATURES),
-                 cmStrCmp(feature)) != cm::cend(C_FEATURES);
-  if (isCFeature) {
-    lang = "C";
-    return true;
-  }
-  bool isCxxFeature =
-    std::find_if(cm::cbegin(CXX_FEATURES) + 1, cm::cend(CXX_FEATURES),
-                 cmStrCmp(feature)) != cm::cend(CXX_FEATURES);
-  if (isCxxFeature) {
-    lang = "CXX";
-    return true;
-  }
-  bool isCudaFeature =
-    std::find_if(cm::cbegin(CUDA_FEATURES) + 1, cm::cend(CUDA_FEATURES),
-                 cmStrCmp(feature)) != cm::cend(CUDA_FEATURES);
-  if (isCudaFeature) {
-    lang = "CUDA";
-    return true;
-  }
-  std::ostringstream e;
-  if (error) {
-    e << "specified";
-  } else {
-    e << "Specified";
-  }
-  e << " unknown feature \"" << feature
-    << "\" for "
-       "target \""
-    << targetName << "\".";
-  if (error) {
-    *error = e.str();
-  } else {
-    this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(),
-                                           this->Backtrace);
-  }
-  return false;
-}
-
-const char* cmMakefile::CompileFeaturesAvailable(const std::string& lang,
-                                                 std::string* error) const
-{
-  if (!this->GlobalGenerator->GetLanguageEnabled(lang)) {
-    std::ostringstream e;
-    if (error) {
-      e << "cannot";
-    } else {
-      e << "Cannot";
-    }
-    e << " use features from non-enabled language " << lang;
-    if (error) {
-      *error = e.str();
-    } else {
-      this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(),
-                                             this->Backtrace);
-    }
-    return nullptr;
-  }
-
-  const char* featuresKnown =
-    this->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
-
-  if (!featuresKnown || !*featuresKnown) {
-    std::ostringstream e;
-    if (error) {
-      e << "no";
-    } else {
-      e << "No";
-    }
-    e << " known features for " << lang << " compiler\n\""
-      << this->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID")
-      << "\"\nversion "
-      << this->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_VERSION") << ".";
-    if (error) {
-      *error = e.str();
-    } else {
-      this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(),
-                                             this->Backtrace);
-    }
-    return nullptr;
-  }
-  return featuresKnown;
-}
-
-bool cmMakefile::GetNewRequiredStandard(const std::string& targetName,
-                                        const std::string& feature,
-                                        cmProp currentLangStandardValue,
-                                        std::string& newRequiredStandard,
-                                        std::string* error) const
-{
-  std::string lang;
-  if (!this->CheckCompileFeaturesAvailable(targetName, feature, lang, error)) {
-    return false;
-  }
-
-  if (lang == "C" || lang == "OBJC") {
-    return this->GetNewRequiredCStandard(targetName, feature, lang,
-                                         currentLangStandardValue,
-                                         newRequiredStandard, error);
-  }
-  if (lang == "CUDA") {
-    return this->GetNewRequiredCudaStandard(targetName, feature, lang,
-                                            currentLangStandardValue,
-                                            newRequiredStandard, error);
-  }
-  return this->GetNewRequiredCxxStandard(targetName, feature, lang,
-                                         currentLangStandardValue,
-                                         newRequiredStandard, error);
-}
-
-bool cmMakefile::HaveStandardAvailable(cmGeneratorTarget const* target,
-                                       std::string const& lang,
-                                       std::string const& config,
-                                       const std::string& feature) const
-{
-  if (lang == "C" || lang == "OBJC") {
-    return this->HaveCStandardAvailable(target, lang, config, feature);
-  }
-  if (lang == "CUDA") {
-    return this->HaveCudaStandardAvailable(target, lang, config, feature);
-  }
-  return this->HaveCxxStandardAvailable(target, lang, config, feature);
-}
-
-bool cmMakefile::HaveCStandardAvailable(cmGeneratorTarget const* target,
-                                        std::string const& lang,
-                                        std::string const& config,
-                                        const std::string& feature) const
-{
-  cmProp defaultCStandard =
-    this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
-  if (!defaultCStandard) {
-    this->IssueMessage(
-      MessageType::INTERNAL_ERROR,
-      cmStrCat("CMAKE_", lang,
-               "_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
-               "not fully configured for this compiler."));
-    // Return true so the caller does not try to lookup the default standard.
-    return true;
-  }
-  if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
-                   cmStrCmp(*defaultCStandard)) == cm::cend(C_STANDARDS)) {
-    const std::string e = cmStrCat("The CMAKE_", lang,
-                                   "_STANDARD_DEFAULT variable contains an "
-                                   "invalid value: \"",
-                                   *defaultCStandard, "\".");
-    this->IssueMessage(MessageType::INTERNAL_ERROR, e);
-    return false;
-  }
-
-  bool needC90 = false;
-  bool needC99 = false;
-  bool needC11 = false;
-
-  this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11);
-
-  cmProp existingCStandard = target->GetLanguageStandard(lang, config);
-  if (!existingCStandard) {
-    existingCStandard = defaultCStandard;
-  }
-
-  if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
-                   cmStrCmp(*existingCStandard)) == cm::cend(C_STANDARDS)) {
-    const std::string e = cmStrCat(
-      "The ", lang, "_STANDARD property on target \"", target->GetName(),
-      "\" contained an invalid value: \"", *existingCStandard, "\".");
-    this->IssueMessage(MessageType::FATAL_ERROR, e);
-    return false;
-  }
-
-  const char* const* existingCIt = existingCStandard
-    ? std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
-                   cmStrCmp(*existingCStandard))
-    : cm::cend(C_STANDARDS);
-
-  if (needC11 && existingCStandard &&
-      existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
-                                 cm::cend(C_STANDARDS), cmStrCmp("11"))) {
-    return false;
-  }
-  if (needC99 && existingCStandard &&
-      existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
-                                 cm::cend(C_STANDARDS), cmStrCmp("99"))) {
-    return false;
-  }
-  if (needC90 && existingCStandard &&
-      existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
-                                 cm::cend(C_STANDARDS), cmStrCmp("90"))) {
-    return false;
-  }
-  return true;
-}
-
-bool cmMakefile::IsLaterStandard(std::string const& lang,
-                                 std::string const& lhs,
-                                 std::string const& rhs)
-{
-  if (lang == "C" || lang == "OBJC") {
-    const char* const* rhsIt = std::find_if(
-      cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), cmStrCmp(rhs));
-
-    return std::find_if(rhsIt, cm::cend(C_STANDARDS), cmStrCmp(lhs)) !=
-      cm::cend(C_STANDARDS);
-  }
-  if (lang == "CUDA") {
-    const char* const* rhsIt = std::find_if(
-      cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS), cmStrCmp(rhs));
-
-    return std::find_if(rhsIt, cm::cend(CUDA_STANDARDS), cmStrCmp(lhs)) !=
-      cm::cend(CUDA_STANDARDS);
-  }
-
-  const char* const* rhsIt = std::find_if(
-    cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS), cmStrCmp(rhs));
-
-  return std::find_if(rhsIt, cm::cend(CXX_STANDARDS), cmStrCmp(lhs)) !=
-    cm::cend(CXX_STANDARDS);
-}
-
-bool cmMakefile::HaveCxxStandardAvailable(cmGeneratorTarget const* target,
-                                          std::string const& lang,
-                                          std::string const& config,
-                                          const std::string& feature) const
-{
-  cmProp defaultCxxStandard =
-    this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
-  if (!defaultCxxStandard) {
-    this->IssueMessage(
-      MessageType::INTERNAL_ERROR,
-      cmStrCat("CMAKE_", lang,
-               "_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
-               "not fully configured for this compiler."));
-    // Return true so the caller does not try to lookup the default standard.
-    return true;
-  }
-  if (std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
-                   cmStrCmp(*defaultCxxStandard)) == cm::cend(CXX_STANDARDS)) {
-    const std::string e =
-      cmStrCat("The CMAKE_", lang, "_STANDARD_DEFAULT variable contains an ",
-               "invalid value: \"", *defaultCxxStandard, "\".");
-    this->IssueMessage(MessageType::INTERNAL_ERROR, e);
-    return false;
-  }
-
-  bool needCxx98 = false;
-  bool needCxx11 = false;
-  bool needCxx14 = false;
-  bool needCxx17 = false;
-  bool needCxx20 = false;
-  this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14,
-                               needCxx17, needCxx20);
-
-  cmProp existingCxxStandard = target->GetLanguageStandard(lang, config);
-  if (!existingCxxStandard) {
-    existingCxxStandard = defaultCxxStandard;
-  }
-
-  const char* const* existingCxxLevel =
-    std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
-                 cmStrCmp(*existingCxxStandard));
-  if (existingCxxLevel == cm::cend(CXX_STANDARDS)) {
-    const std::string e = cmStrCat(
-      "The ", lang, "_STANDARD property on target \"", target->GetName(),
-      "\" contained an invalid value: \"", *existingCxxStandard, "\".");
-    this->IssueMessage(MessageType::FATAL_ERROR, e);
-    return false;
-  }
-
-  /* clang-format off */
-  const char* const* needCxxLevel =
-    needCxx20 ? &CXX_STANDARDS[4]
-    : needCxx17 ? &CXX_STANDARDS[3]
-    : needCxx14 ? &CXX_STANDARDS[2]
-    : needCxx11 ? &CXX_STANDARDS[1]
-    : needCxx98 ? &CXX_STANDARDS[0]
-    : nullptr;
-  /* clang-format on */
-
-  return !needCxxLevel || needCxxLevel <= existingCxxLevel;
-}
-
-void cmMakefile::CheckNeededCxxLanguage(const std::string& feature,
-                                        std::string const& lang,
-                                        bool& needCxx98, bool& needCxx11,
-                                        bool& needCxx14, bool& needCxx17,
-                                        bool& needCxx20) const
-{
-  if (const char* propCxx98 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "98_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propCxx98);
-    needCxx98 = cm::contains(props, feature);
-  }
-  if (const char* propCxx11 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propCxx11);
-    needCxx11 = cm::contains(props, feature);
-  }
-  if (const char* propCxx14 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "14_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propCxx14);
-    needCxx14 = cm::contains(props, feature);
-  }
-  if (const char* propCxx17 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "17_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propCxx17);
-    needCxx17 = cm::contains(props, feature);
-  }
-  if (const char* propCxx20 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "20_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propCxx20);
-    needCxx20 = cm::contains(props, feature);
-  }
-}
-
-bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target,
-                                             const std::string& feature,
-                                             std::string const& lang,
-                                             std::string* error) const
-{
-  std::string newRequiredStandard;
-  if (this->GetNewRequiredCxxStandard(
-        target->GetName(), feature, lang,
-        target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
-        error)) {
-    if (!newRequiredStandard.empty()) {
-      target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
-    }
-    return true;
-  }
-
-  return false;
-}
-
-bool cmMakefile::GetNewRequiredCxxStandard(const std::string& targetName,
-                                           const std::string& feature,
-                                           std::string const& lang,
-                                           cmProp currentLangStandardValue,
-                                           std::string& newRequiredStandard,
-                                           std::string* error) const
-{
-  newRequiredStandard.clear();
-
-  bool needCxx98 = false;
-  bool needCxx11 = false;
-  bool needCxx14 = false;
-  bool needCxx17 = false;
-  bool needCxx20 = false;
-
-  this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14,
-                               needCxx17, needCxx20);
-
-  cmProp existingCxxStandard = currentLangStandardValue;
-  if (existingCxxStandard == nullptr) {
-    cmProp defaultCxxStandard =
-      this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
-    if (defaultCxxStandard && !defaultCxxStandard->empty()) {
-      existingCxxStandard = defaultCxxStandard;
-    }
-  }
-  const char* const* existingCxxLevel = nullptr;
-  if (existingCxxStandard) {
-    existingCxxLevel =
-      std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
-                   cmStrCmp(*existingCxxStandard));
-    if (existingCxxLevel == cm::cend(CXX_STANDARDS)) {
-      const std::string e = cmStrCat(
-        "The ", lang, "_STANDARD property on target \"", targetName,
-        "\" contained an invalid value: \"", *existingCxxStandard, "\".");
-      if (error) {
-        *error = e;
-      } else {
-        this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
-                                               this->Backtrace);
-      }
-      return false;
-    }
-  }
-
-  /* clang-format off */
-  const char* const* needCxxLevel =
-    needCxx20 ? &CXX_STANDARDS[4]
-    : needCxx17 ? &CXX_STANDARDS[3]
-    : needCxx14 ? &CXX_STANDARDS[2]
-    : needCxx11 ? &CXX_STANDARDS[1]
-    : needCxx98 ? &CXX_STANDARDS[0]
-    : nullptr;
-  /* clang-format on */
-
-  if (needCxxLevel) {
-    // Ensure the C++ language level is high enough to support
-    // the needed C++ features.
-    if (!existingCxxLevel || existingCxxLevel < needCxxLevel) {
-      newRequiredStandard = *needCxxLevel;
-    }
-  }
-
-  return true;
-}
-
-bool cmMakefile::HaveCudaStandardAvailable(cmGeneratorTarget const* target,
-                                           std::string const& lang,
-                                           std::string const& config,
-                                           const std::string& feature) const
-{
-  cmProp defaultCudaStandard =
-    this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
-  if (!defaultCudaStandard) {
-    this->IssueMessage(
-      MessageType::INTERNAL_ERROR,
-      cmStrCat("CMAKE_", lang,
-               "_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
-               "not fully configured for this compiler."));
-    // Return true so the caller does not try to lookup the default standard.
-    return true;
-  }
-  if (std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS),
-                   cmStrCmp(*defaultCudaStandard)) ==
-      cm::cend(CUDA_STANDARDS)) {
-    const std::string e =
-      cmStrCat("The CMAKE_", lang, "_STANDARD_DEFAULT variable contains an ",
-               "invalid value: \"", *defaultCudaStandard, "\".");
-    this->IssueMessage(MessageType::INTERNAL_ERROR, e);
-    return false;
-  }
-
-  bool needCuda03 = false;
-  bool needCuda11 = false;
-  bool needCuda14 = false;
-  bool needCuda17 = false;
-  bool needCuda20 = false;
-  this->CheckNeededCudaLanguage(feature, lang, needCuda03, needCuda11,
-                                needCuda14, needCuda17, needCuda20);
-
-  cmProp existingCudaStandard = target->GetLanguageStandard(lang, config);
-  if (!existingCudaStandard) {
-    existingCudaStandard = defaultCudaStandard;
-  }
-
-  const char* const* existingCudaLevel =
-    std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS),
-                 cmStrCmp(*existingCudaStandard));
-  if (existingCudaLevel == cm::cend(CUDA_STANDARDS)) {
-    const std::string e = cmStrCat(
-      "The ", lang, "_STANDARD property on target \"", target->GetName(),
-      "\" contained an invalid value: \"", *existingCudaStandard, "\".");
-    this->IssueMessage(MessageType::FATAL_ERROR, e);
-    return false;
-  }
-
-  /* clang-format off */
-  const char* const* needCudaLevel =
-    needCuda20 ? &CUDA_STANDARDS[4]
-    : needCuda17 ? &CUDA_STANDARDS[3]
-    : needCuda14 ? &CUDA_STANDARDS[2]
-    : needCuda11 ? &CUDA_STANDARDS[1]
-    : needCuda03 ? &CUDA_STANDARDS[0]
-    : nullptr;
-  /* clang-format on */
-
-  return !needCudaLevel || needCudaLevel <= existingCudaLevel;
-}
-
-void cmMakefile::CheckNeededCudaLanguage(const std::string& feature,
-                                         std::string const& lang,
-                                         bool& needCuda03, bool& needCuda11,
-                                         bool& needCuda14, bool& needCuda17,
-                                         bool& needCuda20) const
-{
-  if (const char* propCuda03 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "03_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propCuda03);
-    needCuda03 = cm::contains(props, feature);
-  }
-  if (const char* propCuda11 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propCuda11);
-    needCuda11 = cm::contains(props, feature);
-  }
-  if (const char* propCuda14 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "14_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propCuda14);
-    needCuda14 = cm::contains(props, feature);
-  }
-  if (const char* propCuda17 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "17_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propCuda17);
-    needCuda17 = cm::contains(props, feature);
-  }
-  if (const char* propCuda20 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "20_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propCuda20);
-    needCuda20 = cm::contains(props, feature);
-  }
-}
-
-bool cmMakefile::AddRequiredTargetCudaFeature(cmTarget* target,
-                                              const std::string& feature,
-                                              std::string const& lang,
-                                              std::string* error) const
-{
-  std::string newRequiredStandard;
-  if (this->GetNewRequiredCudaStandard(
-        target->GetName(), feature, lang,
-        target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
-        error)) {
-    if (!newRequiredStandard.empty()) {
-      target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
-    }
-    return true;
-  }
-  return false;
-}
-
-bool cmMakefile::GetNewRequiredCudaStandard(const std::string& targetName,
-                                            const std::string& feature,
-                                            std::string const& lang,
-                                            cmProp currentLangStandardValue,
-                                            std::string& newRequiredStandard,
-                                            std::string* error) const
-{
-  newRequiredStandard.clear();
-
-  bool needCuda03 = false;
-  bool needCuda11 = false;
-  bool needCuda14 = false;
-  bool needCuda17 = false;
-  bool needCuda20 = false;
-
-  this->CheckNeededCudaLanguage(feature, lang, needCuda03, needCuda11,
-                                needCuda14, needCuda17, needCuda20);
-
-  cmProp existingCudaStandard = currentLangStandardValue;
-  if (existingCudaStandard == nullptr) {
-    cmProp defaultCudaStandard =
-      this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
-    if (defaultCudaStandard && !defaultCudaStandard->empty()) {
-      existingCudaStandard = defaultCudaStandard;
-    }
-  }
-  const char* const* existingCudaLevel = nullptr;
-  if (existingCudaStandard) {
-    existingCudaLevel =
-      std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS),
-                   cmStrCmp(*existingCudaStandard));
-    if (existingCudaLevel == cm::cend(CUDA_STANDARDS)) {
-      const std::string e = cmStrCat(
-        "The ", lang, "_STANDARD property on target \"", targetName,
-        "\" contained an invalid value: \"", *existingCudaStandard, "\".");
-      if (error) {
-        *error = e;
-      } else {
-        this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
-                                               this->Backtrace);
-      }
-      return false;
-    }
-  }
-
-  /* clang-format off */
-  const char* const* needCudaLevel =
-    needCuda20 ? &CUDA_STANDARDS[4]
-    : needCuda17 ? &CUDA_STANDARDS[3]
-    : needCuda14 ? &CUDA_STANDARDS[2]
-    : needCuda11 ? &CUDA_STANDARDS[1]
-    : needCuda03 ? &CUDA_STANDARDS[0]
-    : nullptr;
-  /* clang-format on */
-
-  if (needCudaLevel) {
-    // Ensure the CUDA language level is high enough to support
-    // the needed CUDA features.
-    if (!existingCudaLevel || existingCudaLevel < needCudaLevel) {
-      newRequiredStandard = *needCudaLevel;
-    }
-  }
-
-  return true;
-}
-
-void cmMakefile::CheckNeededCLanguage(const std::string& feature,
-                                      std::string const& lang, bool& needC90,
-                                      bool& needC99, bool& needC11) const
-{
-  if (const char* propC90 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "90_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propC90);
-    needC90 = cm::contains(props, feature);
-  }
-  if (const char* propC99 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "99_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propC99);
-    needC99 = cm::contains(props, feature);
-  }
-  if (const char* propC11 =
-        this->GetDefinition(cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
-    std::vector<std::string> props = cmExpandedList(propC11);
-    needC11 = cm::contains(props, feature);
-  }
-}
-
-bool cmMakefile::AddRequiredTargetCFeature(cmTarget* target,
-                                           const std::string& feature,
-                                           std::string const& lang,
-                                           std::string* error) const
-{
-  std::string newRequiredStandard;
-  if (this->GetNewRequiredCStandard(
-        target->GetName(), feature, lang,
-        target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
-        error)) {
-    if (!newRequiredStandard.empty()) {
-      target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
-    }
-    return true;
-  }
-
-  return false;
-}
-
-bool cmMakefile::GetNewRequiredCStandard(const std::string& targetName,
-                                         const std::string& feature,
-                                         std::string const& lang,
-                                         cmProp currentLangStandardValue,
-                                         std::string& newRequiredStandard,
-                                         std::string* error) const
-{
-  newRequiredStandard.clear();
-
-  bool needC90 = false;
-  bool needC99 = false;
-  bool needC11 = false;
-
-  this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11);
-
-  cmProp existingCStandard = currentLangStandardValue;
-  if (existingCStandard == nullptr) {
-    cmProp defaultCStandard =
-      this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
-    if (defaultCStandard && !defaultCStandard->empty()) {
-      existingCStandard = defaultCStandard;
-    }
-  }
-  if (existingCStandard) {
-    if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
-                     cmStrCmp(*existingCStandard)) == cm::cend(C_STANDARDS)) {
-      const std::string e = cmStrCat(
-        "The ", lang, "_STANDARD property on target \"", targetName,
-        "\" contained an invalid value: \"", *existingCStandard, "\".");
-      if (error) {
-        *error = e;
-      } else {
-        this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
-                                               this->Backtrace);
-      }
-      return false;
-    }
-  }
-  const char* const* existingCIt = existingCStandard
-    ? std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
-                   cmStrCmp(*existingCStandard))
-    : cm::cend(C_STANDARDS);
-
-  bool setC90 = needC90 && !existingCStandard;
-  bool setC99 = needC99 && !existingCStandard;
-  bool setC11 = needC11 && !existingCStandard;
-
-  if (needC11 && existingCStandard &&
-      existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
-                                 cm::cend(C_STANDARDS), cmStrCmp("11"))) {
-    setC11 = true;
-  } else if (needC99 && existingCStandard &&
-             existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
-                                        cm::cend(C_STANDARDS),
-                                        cmStrCmp("99"))) {
-    setC99 = true;
-  } else if (needC90 && existingCStandard &&
-             existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
-                                        cm::cend(C_STANDARDS),
-                                        cmStrCmp("90"))) {
-    setC90 = true;
-  }
-
-  if (setC11) {
-    newRequiredStandard = "11";
-  } else if (setC99) {
-    newRequiredStandard = "99";
-  } else if (setC90) {
-    newRequiredStandard = "90";
-  }
-  return true;
-}
-
 cmMakefile::FunctionPushPop::FunctionPushPop(cmMakefile* mf,
                                              const std::string& fileName,
                                              cmPolicies::PolicyMap const& pm)

+ 0 - 86
Source/cmMakefile.h

@@ -47,7 +47,6 @@ class cmExpandedCommandArgument;
 class cmExportBuildFileGenerator;
 class cmFunctionBlocker;
 class cmGeneratorExpressionEvaluationFile;
-class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmImplicitDependsList;
 class cmInstallGenerator;
@@ -927,30 +926,6 @@ public:
 
   bool PolicyOptionalWarningEnabled(std::string const& var);
 
-  bool AddRequiredTargetFeature(cmTarget* target, const std::string& feature,
-                                std::string* error = nullptr) const;
-
-  bool CompileFeatureKnown(const std::string& targetName,
-                           const std::string& feature, std::string& lang,
-                           std::string* error) const;
-
-  const char* CompileFeaturesAvailable(const std::string& lang,
-                                       std::string* error) const;
-
-  bool GetNewRequiredStandard(const std::string& targetName,
-                              const std::string& feature,
-                              cmProp currentLangStandardValue,
-                              std::string& newRequiredStandard,
-                              std::string* error = nullptr) const;
-
-  bool HaveStandardAvailable(cmGeneratorTarget const* target,
-                             std::string const& lang,
-                             std::string const& config,
-                             const std::string& feature) const;
-
-  bool IsLaterStandard(std::string const& lang, std::string const& lhs,
-                       std::string const& rhs);
-
   void PushLoopBlock();
   void PopLoopBlock();
   bool IsLoopBlock() const;
@@ -1170,67 +1145,6 @@ private:
    */
   bool MightHaveCustomCommand(const std::string& name) const;
 
-  bool AddRequiredTargetCFeature(cmTarget* target, const std::string& feature,
-                                 std::string const& lang,
-                                 std::string* error = nullptr) const;
-  bool AddRequiredTargetCxxFeature(cmTarget* target,
-                                   const std::string& feature,
-                                   std::string const& lang,
-                                   std::string* error = nullptr) const;
-  bool AddRequiredTargetCudaFeature(cmTarget* target,
-                                    const std::string& feature,
-                                    std::string const& lang,
-                                    std::string* error = nullptr) const;
-
-  bool CheckCompileFeaturesAvailable(const std::string& targetName,
-                                     const std::string& feature,
-                                     std::string& lang,
-                                     std::string* error) const;
-
-  void CheckNeededCLanguage(const std::string& feature,
-                            std::string const& lang, bool& needC90,
-                            bool& needC99, bool& needC11) const;
-  void CheckNeededCxxLanguage(const std::string& feature,
-                              std::string const& lang, bool& needCxx98,
-                              bool& needCxx11, bool& needCxx14,
-                              bool& needCxx17, bool& needCxx20) const;
-  void CheckNeededCudaLanguage(const std::string& feature,
-                               std::string const& lang, bool& needCuda03,
-                               bool& needCuda11, bool& needCuda14,
-                               bool& needCuda17, bool& needCuda20) const;
-
-  bool GetNewRequiredCStandard(const std::string& targetName,
-                               const std::string& feature,
-                               std::string const& lang,
-                               cmProp currentLangStandardValue,
-                               std::string& newRequiredStandard,
-                               std::string* error = nullptr) const;
-  bool GetNewRequiredCxxStandard(const std::string& targetName,
-                                 const std::string& feature,
-                                 std::string const& lang,
-                                 cmProp currentLangStandardValue,
-                                 std::string& newRequiredStandard,
-                                 std::string* error = nullptr) const;
-  bool GetNewRequiredCudaStandard(const std::string& targetName,
-                                  const std::string& feature,
-                                  std::string const& lang,
-                                  cmProp currentLangStandardValue,
-                                  std::string& newRequiredStandard,
-                                  std::string* error = nullptr) const;
-
-  bool HaveCStandardAvailable(cmGeneratorTarget const* target,
-                              std::string const& lang,
-                              std::string const& config,
-                              const std::string& feature) const;
-  bool HaveCxxStandardAvailable(cmGeneratorTarget const* target,
-                                std::string const& lang,
-                                std::string const& config,
-                                const std::string& feature) const;
-  bool HaveCudaStandardAvailable(cmGeneratorTarget const* target,
-                                 std::string const& lang,
-                                 std::string const& config,
-                                 const std::string& feature) const;
-
   bool CheckSystemVars;
   bool CheckCMP0000;
   std::set<std::string> WarnedCMP0074;

+ 779 - 0
Source/cmStandardLevelResolver.cxx

@@ -0,0 +1,779 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmStandardLevelResolver.h"
+
+#include <algorithm>
+#include <cassert>
+#include <sstream>
+#include <vector>
+
+#include <cm/iterator>
+#include <cmext/algorithm>
+
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorTarget.h"
+#include "cmGlobalGenerator.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmProperty.h"
+#include "cmStringAlgorithms.h"
+#include "cmTarget.h"
+#include "cmake.h"
+
+#define FEATURE_STRING(F) , #F
+static const char* const C_FEATURES[] = { nullptr FOR_EACH_C_FEATURE(
+  FEATURE_STRING) };
+
+static const char* const CXX_FEATURES[] = { nullptr FOR_EACH_CXX_FEATURE(
+  FEATURE_STRING) };
+
+static const char* const CUDA_FEATURES[] = { nullptr FOR_EACH_CUDA_FEATURE(
+  FEATURE_STRING) };
+#undef FEATURE_STRING
+
+static const char* const C_STANDARDS[] = { "90", "99", "11" };
+static const char* const CXX_STANDARDS[] = { "98", "11", "14", "17", "20" };
+static const char* const CUDA_STANDARDS[] = { "03", "11", "14", "17", "20" };
+
+bool cmStandardLevelResolver::AddRequiredTargetFeature(
+  cmTarget* target, const std::string& feature, std::string* error) const
+{
+  if (cmGeneratorExpression::Find(feature) != std::string::npos) {
+    target->AppendProperty("COMPILE_FEATURES", feature);
+    return true;
+  }
+
+  std::string lang;
+  if (!this->CheckCompileFeaturesAvailable(target->GetName(), feature, lang,
+                                           error)) {
+    return false;
+  }
+
+  target->AppendProperty("COMPILE_FEATURES", feature);
+
+  // FIXME: Add a policy to avoid updating the <LANG>_STANDARD target
+  // property due to COMPILE_FEATURES.  The language standard selection
+  // should be done purely at generate time based on whatever the project
+  // code put in these properties explicitly.  That is mostly true now,
+  // but for compatibility we need to continue updating the property here.
+  if (lang == "C" || lang == "OBJC") {
+    return this->AddRequiredTargetCFeature(target, feature, lang, error);
+  }
+  if (lang == "CUDA") {
+    return this->AddRequiredTargetCudaFeature(target, feature, lang, error);
+  }
+  return this->AddRequiredTargetCxxFeature(target, feature, lang, error);
+}
+
+bool cmStandardLevelResolver::CheckCompileFeaturesAvailable(
+  const std::string& targetName, const std::string& feature, std::string& lang,
+  std::string* error) const
+{
+  if (!this->CompileFeatureKnown(targetName, feature, lang, error)) {
+    return false;
+  }
+
+  const char* features = this->CompileFeaturesAvailable(lang, error);
+  if (!features) {
+    return false;
+  }
+
+  std::vector<std::string> availableFeatures = cmExpandedList(features);
+  if (!cm::contains(availableFeatures, feature)) {
+    std::ostringstream e;
+    e << "The compiler feature \"" << feature << "\" is not known to " << lang
+      << " compiler\n\""
+      << this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILER_ID")
+      << "\"\nversion "
+      << this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILER_VERSION")
+      << ".";
+    if (error) {
+      *error = e.str();
+    } else {
+      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    }
+    return false;
+  }
+
+  return true;
+}
+
+bool cmStandardLevelResolver::CompileFeatureKnown(
+  const std::string& targetName, const std::string& feature, std::string& lang,
+  std::string* error) const
+{
+  assert(cmGeneratorExpression::Find(feature) == std::string::npos);
+
+  bool isCFeature =
+    std::find_if(cm::cbegin(C_FEATURES) + 1, cm::cend(C_FEATURES),
+                 cmStrCmp(feature)) != cm::cend(C_FEATURES);
+  if (isCFeature) {
+    lang = "C";
+    return true;
+  }
+  bool isCxxFeature =
+    std::find_if(cm::cbegin(CXX_FEATURES) + 1, cm::cend(CXX_FEATURES),
+                 cmStrCmp(feature)) != cm::cend(CXX_FEATURES);
+  if (isCxxFeature) {
+    lang = "CXX";
+    return true;
+  }
+  bool isCudaFeature =
+    std::find_if(cm::cbegin(CUDA_FEATURES) + 1, cm::cend(CUDA_FEATURES),
+                 cmStrCmp(feature)) != cm::cend(CUDA_FEATURES);
+  if (isCudaFeature) {
+    lang = "CUDA";
+    return true;
+  }
+  std::ostringstream e;
+  if (error) {
+    e << "specified";
+  } else {
+    e << "Specified";
+  }
+  e << " unknown feature \"" << feature
+    << "\" for "
+       "target \""
+    << targetName << "\".";
+  if (error) {
+    *error = e.str();
+  } else {
+    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  }
+  return false;
+}
+
+const char* cmStandardLevelResolver::CompileFeaturesAvailable(
+  const std::string& lang, std::string* error) const
+{
+  if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) {
+    std::ostringstream e;
+    if (error) {
+      e << "cannot";
+    } else {
+      e << "Cannot";
+    }
+    e << " use features from non-enabled language " << lang;
+    if (error) {
+      *error = e.str();
+    } else {
+      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    }
+    return nullptr;
+  }
+
+  const char* featuresKnown =
+    this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
+
+  if (!featuresKnown || !*featuresKnown) {
+    std::ostringstream e;
+    if (error) {
+      e << "no";
+    } else {
+      e << "No";
+    }
+    e << " known features for " << lang << " compiler\n\""
+      << this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID")
+      << "\"\nversion "
+      << this->Makefile->GetSafeDefinition("CMAKE_" + lang +
+                                           "_COMPILER_VERSION")
+      << ".";
+    if (error) {
+      *error = e.str();
+    } else {
+      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    }
+    return nullptr;
+  }
+  return featuresKnown;
+}
+
+bool cmStandardLevelResolver::GetNewRequiredStandard(
+  const std::string& targetName, const std::string& feature,
+  cmProp currentLangStandardValue, std::string& newRequiredStandard,
+  std::string* error) const
+{
+  std::string lang;
+  if (!this->CheckCompileFeaturesAvailable(targetName, feature, lang, error)) {
+    return false;
+  }
+
+  if (lang == "C" || lang == "OBJC") {
+    return this->GetNewRequiredCStandard(targetName, feature, lang,
+                                         currentLangStandardValue,
+                                         newRequiredStandard, error);
+  }
+  if (lang == "CUDA") {
+    return this->GetNewRequiredCudaStandard(targetName, feature, lang,
+                                            currentLangStandardValue,
+                                            newRequiredStandard, error);
+  }
+  return this->GetNewRequiredCxxStandard(targetName, feature, lang,
+                                         currentLangStandardValue,
+                                         newRequiredStandard, error);
+}
+
+bool cmStandardLevelResolver::HaveStandardAvailable(
+  cmGeneratorTarget const* target, std::string const& lang,
+  std::string const& config, const std::string& feature) const
+{
+  if (lang == "C" || lang == "OBJC") {
+    return this->HaveCStandardAvailable(target, lang, config, feature);
+  }
+  if (lang == "CUDA") {
+    return this->HaveCudaStandardAvailable(target, lang, config, feature);
+  }
+  return this->HaveCxxStandardAvailable(target, lang, config, feature);
+}
+
+bool cmStandardLevelResolver::HaveCStandardAvailable(
+  cmGeneratorTarget const* target, std::string const& lang,
+  std::string const& config, const std::string& feature) const
+{
+  cmProp defaultCStandard =
+    this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
+  if (!defaultCStandard) {
+    this->Makefile->IssueMessage(
+      MessageType::INTERNAL_ERROR,
+      cmStrCat("CMAKE_", lang,
+               "_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
+               "not fully configured for this compiler."));
+    // Return true so the caller does not try to lookup the default standard.
+    return true;
+  }
+  if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
+                   cmStrCmp(*defaultCStandard)) == cm::cend(C_STANDARDS)) {
+    const std::string e = cmStrCat("The CMAKE_", lang,
+                                   "_STANDARD_DEFAULT variable contains an "
+                                   "invalid value: \"",
+                                   *defaultCStandard, "\".");
+    this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
+    return false;
+  }
+
+  bool needC90 = false;
+  bool needC99 = false;
+  bool needC11 = false;
+
+  this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11);
+
+  cmProp existingCStandard = target->GetLanguageStandard(lang, config);
+  if (!existingCStandard) {
+    existingCStandard = defaultCStandard;
+  }
+
+  if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
+                   cmStrCmp(*existingCStandard)) == cm::cend(C_STANDARDS)) {
+    const std::string e = cmStrCat(
+      "The ", lang, "_STANDARD property on target \"", target->GetName(),
+      "\" contained an invalid value: \"", *existingCStandard, "\".");
+    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+    return false;
+  }
+
+  const char* const* existingCIt = existingCStandard
+    ? std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
+                   cmStrCmp(*existingCStandard))
+    : cm::cend(C_STANDARDS);
+
+  if (needC11 && existingCStandard &&
+      existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
+                                 cm::cend(C_STANDARDS), cmStrCmp("11"))) {
+    return false;
+  }
+  if (needC99 && existingCStandard &&
+      existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
+                                 cm::cend(C_STANDARDS), cmStrCmp("99"))) {
+    return false;
+  }
+  if (needC90 && existingCStandard &&
+      existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
+                                 cm::cend(C_STANDARDS), cmStrCmp("90"))) {
+    return false;
+  }
+  return true;
+}
+
+bool cmStandardLevelResolver::IsLaterStandard(std::string const& lang,
+                                              std::string const& lhs,
+                                              std::string const& rhs) const
+{
+  if (lang == "C" || lang == "OBJC") {
+    const char* const* rhsIt = std::find_if(
+      cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), cmStrCmp(rhs));
+
+    return std::find_if(rhsIt, cm::cend(C_STANDARDS), cmStrCmp(lhs)) !=
+      cm::cend(C_STANDARDS);
+  }
+  if (lang == "CUDA") {
+    const char* const* rhsIt = std::find_if(
+      cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS), cmStrCmp(rhs));
+
+    return std::find_if(rhsIt, cm::cend(CUDA_STANDARDS), cmStrCmp(lhs)) !=
+      cm::cend(CUDA_STANDARDS);
+  }
+
+  const char* const* rhsIt = std::find_if(
+    cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS), cmStrCmp(rhs));
+
+  return std::find_if(rhsIt, cm::cend(CXX_STANDARDS), cmStrCmp(lhs)) !=
+    cm::cend(CXX_STANDARDS);
+}
+
+bool cmStandardLevelResolver::HaveCxxStandardAvailable(
+  cmGeneratorTarget const* target, std::string const& lang,
+  std::string const& config, const std::string& feature) const
+{
+  cmProp defaultCxxStandard =
+    this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
+  if (!defaultCxxStandard) {
+    this->Makefile->IssueMessage(
+      MessageType::INTERNAL_ERROR,
+      cmStrCat("CMAKE_", lang,
+               "_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
+               "not fully configured for this compiler."));
+    // Return true so the caller does not try to lookup the default standard.
+    return true;
+  }
+  if (std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
+                   cmStrCmp(*defaultCxxStandard)) == cm::cend(CXX_STANDARDS)) {
+    const std::string e =
+      cmStrCat("The CMAKE_", lang, "_STANDARD_DEFAULT variable contains an ",
+               "invalid value: \"", *defaultCxxStandard, "\".");
+    this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
+    return false;
+  }
+
+  bool needCxx98 = false;
+  bool needCxx11 = false;
+  bool needCxx14 = false;
+  bool needCxx17 = false;
+  bool needCxx20 = false;
+  this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14,
+                               needCxx17, needCxx20);
+
+  cmProp existingCxxStandard = target->GetLanguageStandard(lang, config);
+  if (!existingCxxStandard) {
+    existingCxxStandard = defaultCxxStandard;
+  }
+
+  const char* const* existingCxxLevel =
+    std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
+                 cmStrCmp(*existingCxxStandard));
+  if (existingCxxLevel == cm::cend(CXX_STANDARDS)) {
+    const std::string e = cmStrCat(
+      "The ", lang, "_STANDARD property on target \"", target->GetName(),
+      "\" contained an invalid value: \"", *existingCxxStandard, "\".");
+    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+    return false;
+  }
+
+  /* clang-format off */
+  const char* const* needCxxLevel =
+    needCxx20 ? &CXX_STANDARDS[4]
+    : needCxx17 ? &CXX_STANDARDS[3]
+    : needCxx14 ? &CXX_STANDARDS[2]
+    : needCxx11 ? &CXX_STANDARDS[1]
+    : needCxx98 ? &CXX_STANDARDS[0]
+    : nullptr;
+  /* clang-format on */
+
+  return !needCxxLevel || needCxxLevel <= existingCxxLevel;
+}
+
+void cmStandardLevelResolver::CheckNeededCxxLanguage(
+  const std::string& feature, std::string const& lang, bool& needCxx98,
+  bool& needCxx11, bool& needCxx14, bool& needCxx17, bool& needCxx20) const
+{
+  if (const char* propCxx98 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "98_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCxx98);
+    needCxx98 = cm::contains(props, feature);
+  }
+  if (const char* propCxx11 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCxx11);
+    needCxx11 = cm::contains(props, feature);
+  }
+  if (const char* propCxx14 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "14_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCxx14);
+    needCxx14 = cm::contains(props, feature);
+  }
+  if (const char* propCxx17 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "17_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCxx17);
+    needCxx17 = cm::contains(props, feature);
+  }
+  if (const char* propCxx20 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "20_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCxx20);
+    needCxx20 = cm::contains(props, feature);
+  }
+}
+
+bool cmStandardLevelResolver::AddRequiredTargetCxxFeature(
+  cmTarget* target, const std::string& feature, std::string const& lang,
+  std::string* error) const
+{
+  std::string newRequiredStandard;
+  if (this->GetNewRequiredCxxStandard(
+        target->GetName(), feature, lang,
+        target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
+        error)) {
+    if (!newRequiredStandard.empty()) {
+      target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
+    }
+    return true;
+  }
+
+  return false;
+}
+
+bool cmStandardLevelResolver::GetNewRequiredCxxStandard(
+  const std::string& targetName, const std::string& feature,
+  std::string const& lang, cmProp currentLangStandardValue,
+  std::string& newRequiredStandard, std::string* error) const
+{
+  newRequiredStandard.clear();
+
+  bool needCxx98 = false;
+  bool needCxx11 = false;
+  bool needCxx14 = false;
+  bool needCxx17 = false;
+  bool needCxx20 = false;
+
+  this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14,
+                               needCxx17, needCxx20);
+
+  cmProp existingCxxStandard = currentLangStandardValue;
+  if (existingCxxStandard == nullptr) {
+    cmProp defaultCxxStandard =
+      this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
+    if (defaultCxxStandard && !defaultCxxStandard->empty()) {
+      existingCxxStandard = defaultCxxStandard;
+    }
+  }
+  const char* const* existingCxxLevel = nullptr;
+  if (existingCxxStandard) {
+    existingCxxLevel =
+      std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
+                   cmStrCmp(*existingCxxStandard));
+    if (existingCxxLevel == cm::cend(CXX_STANDARDS)) {
+      const std::string e = cmStrCat(
+        "The ", lang, "_STANDARD property on target \"", targetName,
+        "\" contained an invalid value: \"", *existingCxxStandard, "\".");
+      if (error) {
+        *error = e;
+      } else {
+        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+      }
+      return false;
+    }
+  }
+
+  /* clang-format off */
+  const char* const* needCxxLevel =
+    needCxx20 ? &CXX_STANDARDS[4]
+    : needCxx17 ? &CXX_STANDARDS[3]
+    : needCxx14 ? &CXX_STANDARDS[2]
+    : needCxx11 ? &CXX_STANDARDS[1]
+    : needCxx98 ? &CXX_STANDARDS[0]
+    : nullptr;
+  /* clang-format on */
+
+  if (needCxxLevel) {
+    // Ensure the C++ language level is high enough to support
+    // the needed C++ features.
+    if (!existingCxxLevel || existingCxxLevel < needCxxLevel) {
+      newRequiredStandard = *needCxxLevel;
+    }
+  }
+
+  return true;
+}
+
+bool cmStandardLevelResolver::HaveCudaStandardAvailable(
+  cmGeneratorTarget const* target, std::string const& lang,
+  std::string const& config, const std::string& feature) const
+{
+  cmProp defaultCudaStandard =
+    this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
+  if (!defaultCudaStandard) {
+    this->Makefile->IssueMessage(
+      MessageType::INTERNAL_ERROR,
+      cmStrCat("CMAKE_", lang,
+               "_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
+               "not fully configured for this compiler."));
+    // Return true so the caller does not try to lookup the default standard.
+    return true;
+  }
+  if (std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS),
+                   cmStrCmp(*defaultCudaStandard)) ==
+      cm::cend(CUDA_STANDARDS)) {
+    const std::string e =
+      cmStrCat("The CMAKE_", lang, "_STANDARD_DEFAULT variable contains an ",
+               "invalid value: \"", *defaultCudaStandard, "\".");
+    this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
+    return false;
+  }
+
+  bool needCuda03 = false;
+  bool needCuda11 = false;
+  bool needCuda14 = false;
+  bool needCuda17 = false;
+  bool needCuda20 = false;
+  this->CheckNeededCudaLanguage(feature, lang, needCuda03, needCuda11,
+                                needCuda14, needCuda17, needCuda20);
+
+  cmProp existingCudaStandard = target->GetLanguageStandard(lang, config);
+  if (!existingCudaStandard) {
+    existingCudaStandard = defaultCudaStandard;
+  }
+
+  const char* const* existingCudaLevel =
+    std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS),
+                 cmStrCmp(*existingCudaStandard));
+  if (existingCudaLevel == cm::cend(CUDA_STANDARDS)) {
+    const std::string e = cmStrCat(
+      "The ", lang, "_STANDARD property on target \"", target->GetName(),
+      "\" contained an invalid value: \"", *existingCudaStandard, "\".");
+    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+    return false;
+  }
+
+  /* clang-format off */
+  const char* const* needCudaLevel =
+    needCuda20 ? &CUDA_STANDARDS[4]
+    : needCuda17 ? &CUDA_STANDARDS[3]
+    : needCuda14 ? &CUDA_STANDARDS[2]
+    : needCuda11 ? &CUDA_STANDARDS[1]
+    : needCuda03 ? &CUDA_STANDARDS[0]
+    : nullptr;
+  /* clang-format on */
+
+  return !needCudaLevel || needCudaLevel <= existingCudaLevel;
+}
+
+void cmStandardLevelResolver::CheckNeededCudaLanguage(
+  const std::string& feature, std::string const& lang, bool& needCuda03,
+  bool& needCuda11, bool& needCuda14, bool& needCuda17, bool& needCuda20) const
+{
+  if (const char* propCuda03 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "03_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCuda03);
+    needCuda03 = cm::contains(props, feature);
+  }
+  if (const char* propCuda11 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCuda11);
+    needCuda11 = cm::contains(props, feature);
+  }
+  if (const char* propCuda14 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "14_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCuda14);
+    needCuda14 = cm::contains(props, feature);
+  }
+  if (const char* propCuda17 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "17_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCuda17);
+    needCuda17 = cm::contains(props, feature);
+  }
+  if (const char* propCuda20 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "20_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCuda20);
+    needCuda20 = cm::contains(props, feature);
+  }
+}
+
+bool cmStandardLevelResolver::AddRequiredTargetCudaFeature(
+  cmTarget* target, const std::string& feature, std::string const& lang,
+  std::string* error) const
+{
+  std::string newRequiredStandard;
+  if (this->GetNewRequiredCudaStandard(
+        target->GetName(), feature, lang,
+        target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
+        error)) {
+    if (!newRequiredStandard.empty()) {
+      target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
+    }
+    return true;
+  }
+  return false;
+}
+
+bool cmStandardLevelResolver::GetNewRequiredCudaStandard(
+  const std::string& targetName, const std::string& feature,
+  std::string const& lang, cmProp currentLangStandardValue,
+  std::string& newRequiredStandard, std::string* error) const
+{
+  newRequiredStandard.clear();
+
+  bool needCuda03 = false;
+  bool needCuda11 = false;
+  bool needCuda14 = false;
+  bool needCuda17 = false;
+  bool needCuda20 = false;
+
+  this->CheckNeededCudaLanguage(feature, lang, needCuda03, needCuda11,
+                                needCuda14, needCuda17, needCuda20);
+
+  cmProp existingCudaStandard = currentLangStandardValue;
+  if (existingCudaStandard == nullptr) {
+    cmProp defaultCudaStandard =
+      this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
+    if (defaultCudaStandard && !defaultCudaStandard->empty()) {
+      existingCudaStandard = defaultCudaStandard;
+    }
+  }
+  const char* const* existingCudaLevel = nullptr;
+  if (existingCudaStandard) {
+    existingCudaLevel =
+      std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS),
+                   cmStrCmp(*existingCudaStandard));
+    if (existingCudaLevel == cm::cend(CUDA_STANDARDS)) {
+      const std::string e = cmStrCat(
+        "The ", lang, "_STANDARD property on target \"", targetName,
+        "\" contained an invalid value: \"", *existingCudaStandard, "\".");
+      if (error) {
+        *error = e;
+      } else {
+        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+      }
+      return false;
+    }
+  }
+
+  /* clang-format off */
+  const char* const* needCudaLevel =
+    needCuda20 ? &CUDA_STANDARDS[4]
+    : needCuda17 ? &CUDA_STANDARDS[3]
+    : needCuda14 ? &CUDA_STANDARDS[2]
+    : needCuda11 ? &CUDA_STANDARDS[1]
+    : needCuda03 ? &CUDA_STANDARDS[0]
+    : nullptr;
+  /* clang-format on */
+
+  if (needCudaLevel) {
+    // Ensure the CUDA language level is high enough to support
+    // the needed CUDA features.
+    if (!existingCudaLevel || existingCudaLevel < needCudaLevel) {
+      newRequiredStandard = *needCudaLevel;
+    }
+  }
+
+  return true;
+}
+
+void cmStandardLevelResolver::CheckNeededCLanguage(const std::string& feature,
+                                                   std::string const& lang,
+                                                   bool& needC90,
+                                                   bool& needC99,
+                                                   bool& needC11) const
+{
+  if (const char* propC90 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "90_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propC90);
+    needC90 = cm::contains(props, feature);
+  }
+  if (const char* propC99 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "99_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propC99);
+    needC99 = cm::contains(props, feature);
+  }
+  if (const char* propC11 = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propC11);
+    needC11 = cm::contains(props, feature);
+  }
+}
+
+bool cmStandardLevelResolver::AddRequiredTargetCFeature(
+  cmTarget* target, const std::string& feature, std::string const& lang,
+  std::string* error) const
+{
+  std::string newRequiredStandard;
+  if (this->GetNewRequiredCStandard(
+        target->GetName(), feature, lang,
+        target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
+        error)) {
+    if (!newRequiredStandard.empty()) {
+      target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
+    }
+    return true;
+  }
+
+  return false;
+}
+
+bool cmStandardLevelResolver::GetNewRequiredCStandard(
+  const std::string& targetName, const std::string& feature,
+  std::string const& lang, cmProp currentLangStandardValue,
+  std::string& newRequiredStandard, std::string* error) const
+{
+  newRequiredStandard.clear();
+
+  bool needC90 = false;
+  bool needC99 = false;
+  bool needC11 = false;
+
+  this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11);
+
+  cmProp existingCStandard = currentLangStandardValue;
+  if (existingCStandard == nullptr) {
+    cmProp defaultCStandard =
+      this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
+    if (defaultCStandard && !defaultCStandard->empty()) {
+      existingCStandard = defaultCStandard;
+    }
+  }
+  if (existingCStandard) {
+    if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
+                     cmStrCmp(*existingCStandard)) == cm::cend(C_STANDARDS)) {
+      const std::string e = cmStrCat(
+        "The ", lang, "_STANDARD property on target \"", targetName,
+        "\" contained an invalid value: \"", *existingCStandard, "\".");
+      if (error) {
+        *error = e;
+      } else {
+        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+      }
+      return false;
+    }
+  }
+  const char* const* existingCIt = existingCStandard
+    ? std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
+                   cmStrCmp(*existingCStandard))
+    : cm::cend(C_STANDARDS);
+
+  bool setC90 = needC90 && !existingCStandard;
+  bool setC99 = needC99 && !existingCStandard;
+  bool setC11 = needC11 && !existingCStandard;
+
+  if (needC11 && existingCStandard &&
+      existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
+                                 cm::cend(C_STANDARDS), cmStrCmp("11"))) {
+    setC11 = true;
+  } else if (needC99 && existingCStandard &&
+             existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
+                                        cm::cend(C_STANDARDS),
+                                        cmStrCmp("99"))) {
+    setC99 = true;
+  } else if (needC90 && existingCStandard &&
+             existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
+                                        cm::cend(C_STANDARDS),
+                                        cmStrCmp("90"))) {
+    setC90 = true;
+  }
+
+  if (setC11) {
+    newRequiredStandard = "11";
+  } else if (setC99) {
+    newRequiredStandard = "99";
+  } else if (setC90) {
+    newRequiredStandard = "90";
+  }
+  return true;
+}

+ 111 - 0
Source/cmStandardLevelResolver.h

@@ -0,0 +1,111 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmStandardLevelResolver_h
+#define cmStandardLevelResolver_h
+
+#include <string>
+
+#include "cmProperty.h"
+
+class cmMakefile;
+class cmGeneratorTarget;
+class cmTarget;
+
+class cmStandardLevelResolver
+{
+
+public:
+  explicit cmStandardLevelResolver(cmMakefile* makefile)
+    : Makefile(makefile)
+  {
+  }
+
+  bool AddRequiredTargetFeature(cmTarget* target, const std::string& feature,
+                                std::string* error = nullptr) const;
+
+  bool CompileFeatureKnown(const std::string& targetName,
+                           const std::string& feature, std::string& lang,
+                           std::string* error) const;
+
+  const char* CompileFeaturesAvailable(const std::string& lang,
+                                       std::string* error) const;
+
+  bool GetNewRequiredStandard(const std::string& targetName,
+                              const std::string& feature,
+                              cmProp currentLangStandardValue,
+                              std::string& newRequiredStandard,
+                              std::string* error = nullptr) const;
+
+  bool HaveStandardAvailable(cmGeneratorTarget const* target,
+                             std::string const& lang,
+                             std::string const& config,
+                             const std::string& feature) const;
+
+  bool IsLaterStandard(std::string const& lang, std::string const& lhs,
+                       std::string const& rhs) const;
+
+private:
+  bool AddRequiredTargetCFeature(cmTarget* target, const std::string& feature,
+                                 std::string const& lang,
+                                 std::string* error = nullptr) const;
+  bool AddRequiredTargetCxxFeature(cmTarget* target,
+                                   const std::string& feature,
+                                   std::string const& lang,
+                                   std::string* error = nullptr) const;
+  bool AddRequiredTargetCudaFeature(cmTarget* target,
+                                    const std::string& feature,
+                                    std::string const& lang,
+                                    std::string* error = nullptr) const;
+
+  bool CheckCompileFeaturesAvailable(const std::string& targetName,
+                                     const std::string& feature,
+                                     std::string& lang,
+                                     std::string* error) const;
+
+  void CheckNeededCLanguage(const std::string& feature,
+                            std::string const& lang, bool& needC90,
+                            bool& needC99, bool& needC11) const;
+  void CheckNeededCxxLanguage(const std::string& feature,
+                              std::string const& lang, bool& needCxx98,
+                              bool& needCxx11, bool& needCxx14,
+                              bool& needCxx17, bool& needCxx20) const;
+  void CheckNeededCudaLanguage(const std::string& feature,
+                               std::string const& lang, bool& needCuda03,
+                               bool& needCuda11, bool& needCuda14,
+                               bool& needCuda17, bool& needCuda20) const;
+
+  bool GetNewRequiredCStandard(const std::string& targetName,
+                               const std::string& feature,
+                               std::string const& lang,
+                               cmProp currentLangStandardValue,
+                               std::string& newRequiredStandard,
+                               std::string* error = nullptr) const;
+  bool GetNewRequiredCxxStandard(const std::string& targetName,
+                                 const std::string& feature,
+                                 std::string const& lang,
+                                 cmProp currentLangStandardValue,
+                                 std::string& newRequiredStandard,
+                                 std::string* error = nullptr) const;
+  bool GetNewRequiredCudaStandard(const std::string& targetName,
+                                  const std::string& feature,
+                                  std::string const& lang,
+                                  cmProp currentLangStandardValue,
+                                  std::string& newRequiredStandard,
+                                  std::string* error = nullptr) const;
+
+  bool HaveCStandardAvailable(cmGeneratorTarget const* target,
+                              std::string const& lang,
+                              std::string const& config,
+                              const std::string& feature) const;
+  bool HaveCxxStandardAvailable(cmGeneratorTarget const* target,
+                                std::string const& lang,
+                                std::string const& config,
+                                const std::string& feature) const;
+  bool HaveCudaStandardAvailable(cmGeneratorTarget const* target,
+                                 std::string const& lang,
+                                 std::string const& config,
+                                 const std::string& feature) const;
+
+  cmMakefile* Makefile;
+};
+#endif

+ 3 - 1
Source/cmTargetCompileFeaturesCommand.cxx

@@ -4,6 +4,7 @@
 
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStandardLevelResolver.h"
 #include "cmStringAlgorithms.h"
 #include "cmTargetPropCommandBase.h"
 
@@ -29,9 +30,10 @@ private:
                            const std::vector<std::string>& content,
                            bool /*prepend*/, bool /*system*/) override
   {
+    cmStandardLevelResolver standardResolver(this->Makefile);
     for (std::string const& it : content) {
       std::string error;
-      if (!this->Makefile->AddRequiredTargetFeature(tgt, it, &error)) {
+      if (!standardResolver.AddRequiredTargetFeature(tgt, it, &error)) {
         this->SetError(error);
         return false; // Not (successfully) handled.
       }

+ 1 - 0
bootstrap

@@ -432,6 +432,7 @@ CMAKE_CXX_SOURCES="\
   cmSiteNameCommand \
   cmSourceFile \
   cmSourceFileLocation \
+  cmStandardLevelResolver \
   cmState \
   cmStateDirectory \
   cmStateSnapshot \