mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-12 12:15:34 +00:00
Add and use a couple helper functions for issuing policy warnings. This improves consistency and allows some simplification of many call sites. (One or two instances in particular are greatly simplified.)
793 lines
26 KiB
C++
793 lines
26 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
|
|
#include "cmStandardLevelResolver.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <cm/iterator>
|
|
#include <cm/optional>
|
|
#include <cm/string_view>
|
|
#include <cmext/algorithm>
|
|
#include <cmext/string_view>
|
|
|
|
#include "cmGeneratorExpression.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmList.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMessageType.h"
|
|
#include "cmPolicies.h"
|
|
#include "cmStandardLevel.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmTarget.h"
|
|
#include "cmValue.h"
|
|
#include "cmake.h"
|
|
|
|
namespace {
|
|
|
|
#define FEATURE_STRING(F) , #F
|
|
char const* const C_FEATURES[] = { nullptr FOR_EACH_C_FEATURE(
|
|
FEATURE_STRING) };
|
|
|
|
char const* const CXX_FEATURES[] = { nullptr FOR_EACH_CXX_FEATURE(
|
|
FEATURE_STRING) };
|
|
|
|
char const* const CUDA_FEATURES[] = { nullptr FOR_EACH_CUDA_FEATURE(
|
|
FEATURE_STRING) };
|
|
|
|
char const* const HIP_FEATURES[] = { nullptr FOR_EACH_HIP_FEATURE(
|
|
FEATURE_STRING) };
|
|
#undef FEATURE_STRING
|
|
|
|
int ParseStd(std::string const& level)
|
|
{
|
|
try {
|
|
return std::stoi(level);
|
|
} catch (std::invalid_argument&) {
|
|
// Fall through to use an invalid value.
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
struct StandardLevelComputer
|
|
{
|
|
explicit StandardLevelComputer(std::string lang, std::vector<int> levels,
|
|
std::vector<std::string> levelsStr)
|
|
: Language(std::move(lang))
|
|
, Levels(std::move(levels))
|
|
, LevelsAsStrings(std::move(levelsStr))
|
|
{
|
|
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
|
|
{
|
|
|
|
auto const& stds = this->Levels;
|
|
auto const& 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{ makefile
|
|
->GetDefinition(cmStrCat("CMAKE_", this->Language,
|
|
"_EXTENSIONS_DEFAULT"))
|
|
.IsOn() };
|
|
bool ext = true;
|
|
|
|
if (cmp0128 == cmPolicies::NEW) {
|
|
ext = defaultExt;
|
|
}
|
|
|
|
if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) {
|
|
ext = extPropValue.IsOn();
|
|
}
|
|
|
|
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 cmStrCat("CMAKE_", this->Language, *defaultStd, '_', type,
|
|
"_COMPILE_OPTION");
|
|
}
|
|
} else {
|
|
if (cmp0128 == cmPolicies::WARN &&
|
|
makefile->PolicyOptionalWarningEnabled(
|
|
"CMAKE_POLICY_WARNING_CMP0128") &&
|
|
ext != defaultExt) {
|
|
char const* state{};
|
|
if (ext) {
|
|
if (!makefile->GetDefinition(cmStrCat(
|
|
"CMAKE_", this->Language, "_EXTENSION_COMPILE_OPTION"))) {
|
|
state = "enabled";
|
|
}
|
|
} else {
|
|
state = "disabled";
|
|
}
|
|
if (state) {
|
|
makefile->IssuePolicyWarning(
|
|
cmPolicies::CMP0128, {},
|
|
cmStrCat("For compatibility with older versions of CMake, "
|
|
"compiler extensions won't be "_s,
|
|
state, '.'));
|
|
}
|
|
}
|
|
|
|
if (ext) {
|
|
return cmStrCat("CMAKE_", this->Language,
|
|
"_EXTENSION_COMPILE_OPTION");
|
|
}
|
|
}
|
|
return std::string{};
|
|
}
|
|
|
|
if (target->GetLanguageStandardRequired(this->Language)) {
|
|
std::string option_flag = cmStrCat(
|
|
"CMAKE_", this->Language, *standardProp, '_', type, "_COMPILE_OPTION");
|
|
|
|
cmValue opt = target->Target->GetMakefile()->GetDefinition(option_flag);
|
|
if (!opt) {
|
|
std::ostringstream e;
|
|
e << "Target \"" << target->GetName()
|
|
<< "\" requires the language "
|
|
"dialect \""
|
|
<< this->Language << *standardProp << "\" "
|
|
<< (ext ? "(with compiler extensions)" : "")
|
|
<< ". But the current compiler \""
|
|
<< makefile->GetSafeDefinition(
|
|
cmStrCat("CMAKE_", this->Language, "_COMPILER_ID"))
|
|
<< "\" does not support this, or "
|
|
"CMake does not know the flags to enable it.";
|
|
|
|
makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
|
|
}
|
|
return option_flag;
|
|
}
|
|
|
|
// 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{};
|
|
}
|
|
|
|
if (cmp0128 == cmPolicies::WARN &&
|
|
makefile->PolicyOptionalWarningEnabled(
|
|
"CMAKE_POLICY_WARNING_CMP0128")) {
|
|
makefile->IssuePolicyWarning(
|
|
cmPolicies::CMP0128, {},
|
|
"For compatibility with older versions of CMake, unnecessary flags "
|
|
"for language standard or compiler extensions may be added."_s);
|
|
}
|
|
}
|
|
|
|
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)) {
|
|
std::string e =
|
|
cmStrCat(this->Language, "_STANDARD is set to invalid value '",
|
|
standardStr, '\'');
|
|
makefile->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
|
|
target->GetBacktrace());
|
|
return std::string{};
|
|
}
|
|
|
|
auto defaultStdIt =
|
|
std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(*defaultStd));
|
|
if (defaultStdIt == cm::cend(stds)) {
|
|
std::string e = cmStrCat("CMAKE_", this->Language,
|
|
"_STANDARD_DEFAULT is set to invalid value '",
|
|
*defaultStd, '\'');
|
|
makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
|
|
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 cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type,
|
|
"_COMPILE_OPTION");
|
|
}
|
|
|
|
// 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 option_flag;
|
|
}
|
|
}
|
|
|
|
return std::string{};
|
|
}
|
|
|
|
std::string GetEffectiveStandard(cmMakefile* makefile,
|
|
cmGeneratorTarget const* target,
|
|
std::string const& config) const
|
|
{
|
|
auto const& stds = this->Levels;
|
|
auto const& 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{ makefile
|
|
->GetDefinition(cmStrCat("CMAKE_", this->Language,
|
|
"_EXTENSIONS_DEFAULT"))
|
|
.IsOn() };
|
|
bool ext = true;
|
|
|
|
if (cmp0128 == cmPolicies::NEW) {
|
|
ext = defaultExt;
|
|
}
|
|
|
|
if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) {
|
|
ext = extPropValue.IsOn();
|
|
}
|
|
|
|
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,
|
|
cm::optional<cmStandardLevel> featureLevel,
|
|
cmValue currentLangStandardValue,
|
|
std::string& newRequiredStandard,
|
|
std::string* error) const
|
|
{
|
|
if (currentLangStandardValue) {
|
|
newRequiredStandard = *currentLangStandardValue;
|
|
} else {
|
|
newRequiredStandard.clear();
|
|
}
|
|
|
|
cmValue existingStandard = currentLangStandardValue;
|
|
if (!existingStandard) {
|
|
cmValue defaultStandard = makefile->GetDefinition(
|
|
cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
|
|
if (cmNonempty(defaultStandard)) {
|
|
existingStandard = defaultStandard;
|
|
}
|
|
}
|
|
|
|
auto existingLevelIter = cm::cend(this->Levels);
|
|
if (existingStandard) {
|
|
existingLevelIter =
|
|
std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
|
|
ParseStd(*existingStandard));
|
|
if (existingLevelIter == cm::cend(this->Levels)) {
|
|
std::string const e =
|
|
cmStrCat("The ", this->Language, "_STANDARD property on target \"",
|
|
targetName, "\" contained an invalid value: \"",
|
|
*existingStandard, "\".");
|
|
if (error) {
|
|
*error = e;
|
|
} else {
|
|
makefile->IssueMessage(MessageType::FATAL_ERROR, e);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (featureLevel) {
|
|
// Ensure the C++ language level is high enough to support
|
|
// the needed C++ features.
|
|
if (existingLevelIter == cm::cend(this->Levels) ||
|
|
existingLevelIter < this->Levels.begin() + featureLevel->Index()) {
|
|
newRequiredStandard = this->LevelsAsStrings[featureLevel->Index()];
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool HaveStandardAvailable(cmMakefile* makefile,
|
|
cmGeneratorTarget const* target,
|
|
std::string const& config,
|
|
std::string const& feature) const
|
|
{
|
|
cmValue defaultStandard = makefile->GetDefinition(
|
|
cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
|
|
if (!defaultStandard) {
|
|
makefile->IssueMessage(
|
|
MessageType::INTERNAL_ERROR,
|
|
cmStrCat("CMAKE_", this->Language,
|
|
"_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;
|
|
}
|
|
// convert defaultStandard to an integer
|
|
if (std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
|
|
ParseStd(*defaultStandard)) == cm::cend(this->Levels)) {
|
|
std::string const e = cmStrCat("The CMAKE_", this->Language,
|
|
"_STANDARD_DEFAULT variable contains an "
|
|
"invalid value: \"",
|
|
*defaultStandard, "\".");
|
|
makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
|
|
return false;
|
|
}
|
|
|
|
cmValue existingStandard =
|
|
target->GetLanguageStandard(this->Language, config);
|
|
if (!existingStandard) {
|
|
existingStandard = defaultStandard;
|
|
}
|
|
|
|
auto existingLevelIter =
|
|
std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
|
|
ParseStd(*existingStandard));
|
|
if (existingLevelIter == cm::cend(this->Levels)) {
|
|
std::string const e =
|
|
cmStrCat("The ", this->Language, "_STANDARD property on target \"",
|
|
target->GetName(), "\" contained an invalid value: \"",
|
|
*existingStandard, "\".");
|
|
makefile->IssueMessage(MessageType::FATAL_ERROR, e);
|
|
return false;
|
|
}
|
|
|
|
cm::optional<cmStandardLevel> needed =
|
|
this->CompileFeatureStandardLevel(makefile, feature);
|
|
|
|
return !needed ||
|
|
(this->Levels.begin() + needed->Index()) <= existingLevelIter;
|
|
}
|
|
|
|
cm::optional<cmStandardLevel> CompileFeatureStandardLevel(
|
|
cmMakefile* makefile, std::string const& feature) const
|
|
{
|
|
std::string prefix = cmStrCat("CMAKE_", this->Language);
|
|
cm::optional<cmStandardLevel> maxLevel;
|
|
for (size_t i = 0; i < this->Levels.size(); ++i) {
|
|
if (cmValue prop = makefile->GetDefinition(
|
|
cmStrCat(prefix, this->LevelsAsStrings[i], "_COMPILE_FEATURES"))) {
|
|
cmList props{ *prop };
|
|
if (cm::contains(props, feature)) {
|
|
maxLevel = cmStandardLevel(i);
|
|
}
|
|
}
|
|
}
|
|
return maxLevel;
|
|
}
|
|
|
|
cm::optional<cmStandardLevel> LanguageStandardLevel(
|
|
std::string const& standardStr) const
|
|
{
|
|
cm::optional<cmStandardLevel> langLevel;
|
|
auto const& stds = this->Levels;
|
|
auto stdIt =
|
|
std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr));
|
|
if (stdIt != cm::cend(stds)) {
|
|
langLevel = cmStandardLevel(std::distance(cm::cbegin(stds), stdIt));
|
|
}
|
|
return langLevel;
|
|
}
|
|
|
|
bool IsLaterStandard(int lhs, int rhs) const
|
|
{
|
|
auto rhsIt =
|
|
std::find(cm::cbegin(this->Levels), cm::cend(this->Levels), rhs);
|
|
|
|
return std::find(rhsIt, cm::cend(this->Levels), lhs) !=
|
|
cm::cend(this->Levels);
|
|
}
|
|
|
|
std::string Language;
|
|
std::vector<int> Levels;
|
|
std::vector<std::string> LevelsAsStrings;
|
|
};
|
|
|
|
std::unordered_map<std::string,
|
|
StandardLevelComputer> const StandardComputerMapping = {
|
|
{ "C",
|
|
StandardLevelComputer{
|
|
"C", std::vector<int>{ 90, 99, 11, 17, 23 },
|
|
std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
|
|
{ "CXX",
|
|
StandardLevelComputer{
|
|
"CXX", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
|
|
std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } },
|
|
{ "CUDA",
|
|
StandardLevelComputer{
|
|
"CUDA", std::vector<int>{ 03, 11, 14, 17, 20, 23, 26 },
|
|
std::vector<std::string>{ "03", "11", "14", "17", "20", "23", "26" } } },
|
|
{ "OBJC",
|
|
StandardLevelComputer{
|
|
"OBJC", std::vector<int>{ 90, 99, 11, 17, 23 },
|
|
std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
|
|
{ "OBJCXX",
|
|
StandardLevelComputer{
|
|
"OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
|
|
std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } },
|
|
{ "HIP",
|
|
StandardLevelComputer{
|
|
"HIP", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
|
|
std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } }
|
|
};
|
|
}
|
|
|
|
std::string cmStandardLevelResolver::GetCompileOptionDef(
|
|
cmGeneratorTarget const* target, std::string const& lang,
|
|
std::string const& config) const
|
|
{
|
|
auto const& mapping = StandardComputerMapping.find(lang);
|
|
if (mapping == cm::cend(StandardComputerMapping)) {
|
|
return std::string{};
|
|
}
|
|
|
|
return mapping->second.GetCompileOptionDef(this->Makefile, target, config);
|
|
}
|
|
|
|
std::string cmStandardLevelResolver::GetEffectiveStandard(
|
|
cmGeneratorTarget const* target, std::string const& lang,
|
|
std::string const& config) const
|
|
{
|
|
auto const& mapping = StandardComputerMapping.find(lang);
|
|
if (mapping == cm::cend(StandardComputerMapping)) {
|
|
return std::string{};
|
|
}
|
|
|
|
return mapping->second.GetEffectiveStandard(this->Makefile, target, config);
|
|
}
|
|
|
|
std::string cmStandardLevelResolver::GetLevelString(
|
|
std::string const& lang, cmStandardLevel level) const
|
|
{
|
|
auto mapping = StandardComputerMapping.find(lang);
|
|
if (mapping == StandardComputerMapping.end()) {
|
|
return {};
|
|
}
|
|
|
|
if (mapping->second.LevelsAsStrings.size() <= level.Index()) {
|
|
return {};
|
|
}
|
|
|
|
return mapping->second.LevelsAsStrings[level.Index()];
|
|
}
|
|
|
|
bool cmStandardLevelResolver::AddRequiredTargetFeature(
|
|
cmTarget* target, std::string const& feature, std::string* error) const
|
|
{
|
|
if (cmGeneratorExpression::Find(feature) != std::string::npos) {
|
|
target->AppendProperty("COMPILE_FEATURES", feature,
|
|
this->Makefile->GetBacktrace());
|
|
return true;
|
|
}
|
|
|
|
std::string lang;
|
|
if (!this->CheckCompileFeaturesAvailable(target->GetName(), feature, lang,
|
|
error)) {
|
|
return false;
|
|
}
|
|
|
|
target->AppendProperty("COMPILE_FEATURES", feature,
|
|
this->Makefile->GetBacktrace());
|
|
|
|
// 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.
|
|
cm::optional<cmStandardLevel> featureLevel;
|
|
std::string newRequiredStandard;
|
|
bool succeeded = this->GetNewRequiredStandard(
|
|
target->GetName(), feature,
|
|
target->GetProperty(cmStrCat(lang, "_STANDARD")), featureLevel,
|
|
newRequiredStandard, error);
|
|
if (!newRequiredStandard.empty()) {
|
|
target->SetProperty(cmStrCat(lang, "_STANDARD"), newRequiredStandard);
|
|
}
|
|
return succeeded;
|
|
}
|
|
|
|
bool cmStandardLevelResolver::CheckCompileFeaturesAvailable(
|
|
std::string const& targetName, std::string const& feature, std::string& lang,
|
|
std::string* error) const
|
|
{
|
|
if (!this->CompileFeatureKnown(targetName, feature, lang, error)) {
|
|
return false;
|
|
}
|
|
|
|
if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) {
|
|
return true;
|
|
}
|
|
|
|
cmValue features = this->CompileFeaturesAvailable(lang, error);
|
|
if (!features) {
|
|
return false;
|
|
}
|
|
|
|
cmList availableFeatures{ features };
|
|
if (!cm::contains(availableFeatures, feature)) {
|
|
std::ostringstream e;
|
|
e << "The compiler feature \"" << feature << "\" is not known to " << lang
|
|
<< " compiler\n\""
|
|
<< this->Makefile->GetSafeDefinition(
|
|
cmStrCat("CMAKE_", lang, "_COMPILER_ID"))
|
|
<< "\"\nversion "
|
|
<< this->Makefile->GetSafeDefinition(
|
|
cmStrCat("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(
|
|
std::string const& targetName, std::string const& 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;
|
|
}
|
|
bool isHIPFeature =
|
|
std::find_if(cm::cbegin(HIP_FEATURES) + 1, cm::cend(HIP_FEATURES),
|
|
cmStrCmp(feature)) != cm::cend(HIP_FEATURES);
|
|
if (isHIPFeature) {
|
|
lang = "HIP";
|
|
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;
|
|
}
|
|
|
|
cm::optional<cmStandardLevel>
|
|
cmStandardLevelResolver::CompileFeatureStandardLevel(
|
|
std::string const& lang, std::string const& feature) const
|
|
{
|
|
auto mapping = StandardComputerMapping.find(lang);
|
|
if (mapping == cm::cend(StandardComputerMapping)) {
|
|
return cm::nullopt;
|
|
}
|
|
return mapping->second.CompileFeatureStandardLevel(this->Makefile, feature);
|
|
}
|
|
|
|
cm::optional<cmStandardLevel> cmStandardLevelResolver::LanguageStandardLevel(
|
|
std::string const& lang, std::string const& standardStr) const
|
|
{
|
|
auto mapping = StandardComputerMapping.find(lang);
|
|
if (mapping == cm::cend(StandardComputerMapping)) {
|
|
return cm::nullopt;
|
|
}
|
|
return mapping->second.LanguageStandardLevel(standardStr);
|
|
}
|
|
|
|
cmValue cmStandardLevelResolver::CompileFeaturesAvailable(
|
|
std::string const& 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;
|
|
}
|
|
|
|
cmValue featuresKnown = this->Makefile->GetDefinition(
|
|
cmStrCat("CMAKE_", lang, "_COMPILE_FEATURES"));
|
|
|
|
if (!cmNonempty(featuresKnown)) {
|
|
std::ostringstream e;
|
|
if (error) {
|
|
e << "no";
|
|
} else {
|
|
e << "No";
|
|
}
|
|
e << " known features for " << lang << " compiler\n\""
|
|
<< this->Makefile->GetSafeDefinition(
|
|
cmStrCat("CMAKE_", lang, "_COMPILER_ID"))
|
|
<< "\"\nversion "
|
|
<< this->Makefile->GetSafeDefinition(
|
|
cmStrCat("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(
|
|
std::string const& targetName, std::string const& feature,
|
|
cmValue currentLangStandardValue,
|
|
cm::optional<cmStandardLevel>& featureLevel,
|
|
std::string& newRequiredStandard, std::string* error) const
|
|
{
|
|
std::string lang;
|
|
if (!this->CheckCompileFeaturesAvailable(targetName, feature, lang, error)) {
|
|
return false;
|
|
}
|
|
|
|
featureLevel = this->CompileFeatureStandardLevel(lang, feature);
|
|
|
|
auto mapping = StandardComputerMapping.find(lang);
|
|
if (mapping != cm::cend(StandardComputerMapping)) {
|
|
return mapping->second.GetNewRequiredStandard(
|
|
this->Makefile, targetName, featureLevel, currentLangStandardValue,
|
|
newRequiredStandard, error);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cmStandardLevelResolver::HaveStandardAvailable(
|
|
cmGeneratorTarget const* target, std::string const& lang,
|
|
std::string const& config, std::string const& feature) const
|
|
{
|
|
auto mapping = StandardComputerMapping.find(lang);
|
|
if (mapping != cm::cend(StandardComputerMapping)) {
|
|
return mapping->second.HaveStandardAvailable(this->Makefile, target,
|
|
config, feature);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cmStandardLevelResolver::IsLaterStandard(std::string const& lang,
|
|
std::string const& lhs,
|
|
std::string const& rhs) const
|
|
{
|
|
auto mapping = StandardComputerMapping.find(lang);
|
|
if (mapping != cm::cend(StandardComputerMapping)) {
|
|
return mapping->second.IsLaterStandard(std::stoi(lhs), std::stoi(rhs));
|
|
}
|
|
return false;
|
|
}
|