From 8b4d32c18b44db48115c1d214560335628b6acc0 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 23 Aug 2023 16:12:35 -0400 Subject: cmStandardLevelResolver: add query for the effective standard level This allows for improved error messages. --- Source/cmStandardLevelResolver.cxx | 113 +++++++++++++++++++++++++++++++++++++ Source/cmStandardLevelResolver.h | 3 + 2 files changed, 116 insertions(+) diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx index 7204f72..de42bc6 100644 --- a/Source/cmStandardLevelResolver.cxx +++ b/Source/cmStandardLevelResolver.cxx @@ -73,6 +73,8 @@ struct StandardLevelComputer assert(this->Levels.size() == this->LevelsAsStrings.size()); } + // Note that the logic here is shadowed in `GetEffectiveStandard`; if one is + // changed, the other needs changed as well. std::string GetCompileOptionDef(cmMakefile* makefile, cmGeneratorTarget const* target, std::string const& config) const @@ -238,6 +240,105 @@ struct StandardLevelComputer return std::string{}; } + std::string GetEffectiveStandard(cmMakefile* makefile, + cmGeneratorTarget const* target, + std::string const& config) const + { + const auto& stds = this->Levels; + const auto& stdsStrings = this->LevelsAsStrings; + + cmValue defaultStd = makefile->GetDefinition( + cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT")); + if (!cmNonempty(defaultStd)) { + // this compiler has no notion of language standard levels + return std::string{}; + } + + cmPolicies::PolicyStatus const cmp0128{ makefile->GetPolicyStatus( + cmPolicies::CMP0128) }; + bool const defaultExt{ cmIsOn(*makefile->GetDefinition( + cmStrCat("CMAKE_", this->Language, "_EXTENSIONS_DEFAULT"))) }; + bool ext = true; + + if (cmp0128 == cmPolicies::NEW) { + ext = defaultExt; + } + + if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) { + ext = cmIsOn(*extPropValue); + } + + std::string const type{ ext ? "EXTENSION" : "STANDARD" }; + + cmValue standardProp = target->GetLanguageStandard(this->Language, config); + if (!standardProp) { + if (cmp0128 == cmPolicies::NEW) { + // Add extension flag if compiler's default doesn't match. + if (ext != defaultExt) { + return *defaultStd; + } + } else { + if (ext) { + return *defaultStd; + } + } + return std::string{}; + } + + if (target->GetLanguageStandardRequired(this->Language)) { + return *standardProp; + } + + // If the request matches the compiler's defaults we don't need to add + // anything. + if (*standardProp == *defaultStd && ext == defaultExt) { + if (cmp0128 == cmPolicies::NEW) { + return std::string{}; + } + } + + std::string standardStr(*standardProp); + if (this->Language == "CUDA"_s && standardStr == "98"_s) { + standardStr = "03"; + } + + auto stdIt = + std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr)); + if (stdIt == cm::cend(stds)) { + return std::string{}; + } + + auto defaultStdIt = + std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(*defaultStd)); + if (defaultStdIt == cm::cend(stds)) { + return std::string{}; + } + + // If the standard requested is older than the compiler's default or the + // extension mode doesn't match then we need to use a flag. + if ((cmp0128 != cmPolicies::NEW && stdIt <= defaultStdIt) || + (cmp0128 == cmPolicies::NEW && + (stdIt < defaultStdIt || ext != defaultExt))) { + auto offset = std::distance(cm::cbegin(stds), stdIt); + return stdsStrings[offset]; + } + + // The compiler's default is at least as new as the requested standard, + // and the requested standard is not required. Decay to the newest + // standard for which a flag is defined. + for (; defaultStdIt < stdIt; --stdIt) { + auto offset = std::distance(cm::cbegin(stds), stdIt); + std::string option_flag = + cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type, + "_COMPILE_OPTION"); + if (target->Target->GetMakefile()->GetDefinition(option_flag)) { + return stdsStrings[offset]; + } + } + + return std::string{}; + } + bool GetNewRequiredStandard(cmMakefile* makefile, std::string const& targetName, const std::string& feature, @@ -420,6 +521,18 @@ std::string cmStandardLevelResolver::GetCompileOptionDef( return mapping->second.GetCompileOptionDef(this->Makefile, target, config); } +std::string cmStandardLevelResolver::GetEffectiveStandard( + cmGeneratorTarget const* target, std::string const& lang, + std::string const& config) const +{ + const auto& mapping = StandardComputerMapping.find(lang); + if (mapping == cm::cend(StandardComputerMapping)) { + return std::string{}; + } + + return mapping->second.GetEffectiveStandard(this->Makefile, target, config); +} + bool cmStandardLevelResolver::AddRequiredTargetFeature( cmTarget* target, const std::string& feature, std::string* error) const { diff --git a/Source/cmStandardLevelResolver.h b/Source/cmStandardLevelResolver.h index 4226456..03adf3f 100644 --- a/Source/cmStandardLevelResolver.h +++ b/Source/cmStandardLevelResolver.h @@ -22,6 +22,9 @@ public: std::string GetCompileOptionDef(cmGeneratorTarget const* target, std::string const& lang, std::string const& config) const; + std::string GetEffectiveStandard(cmGeneratorTarget const* target, + std::string const& lang, + std::string const& config) const; bool AddRequiredTargetFeature(cmTarget* target, const std::string& feature, std::string* error = nullptr) const; -- cgit v0.12