mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-02 11:49:55 -06:00
Move AddCompilerRequirementFlag to cmStandardLevelResolver
The cmLocalGenerator::AddCompilerRequirementFlag was another source of truth on what standard levels existed per language, so fold that into cmStandardLevelResolver to make updating CMake easier
This commit is contained in:
@@ -2156,153 +2156,18 @@ void cmLocalGenerator::AddCompilerRequirementFlag(
|
||||
std::string& flags, cmGeneratorTarget const* target, const std::string& lang,
|
||||
const std::string& config)
|
||||
{
|
||||
if (lang.empty()) {
|
||||
return;
|
||||
}
|
||||
const char* defaultStd =
|
||||
this->Makefile->GetDefinition("CMAKE_" + lang + "_STANDARD_DEFAULT");
|
||||
if (!defaultStd || !*defaultStd) {
|
||||
// This compiler has no notion of language standard levels.
|
||||
return;
|
||||
}
|
||||
bool ext = true;
|
||||
if (cmProp extPropValue = target->GetLanguageExtensions(lang)) {
|
||||
if (cmIsOff(*extPropValue)) {
|
||||
ext = false;
|
||||
}
|
||||
}
|
||||
cmProp standardProp = target->GetLanguageStandard(lang, config);
|
||||
if (!standardProp) {
|
||||
if (ext) {
|
||||
// No language standard is specified and extensions are not disabled.
|
||||
// Check if this compiler needs a flag to enable extensions.
|
||||
std::string const option_flag =
|
||||
"CMAKE_" + lang + "_EXTENSION_COMPILE_OPTION";
|
||||
if (const char* opt =
|
||||
target->Target->GetMakefile()->GetDefinition(option_flag)) {
|
||||
std::vector<std::string> optVec = cmExpandedList(opt);
|
||||
for (std::string const& i : optVec) {
|
||||
this->AppendFlagEscape(flags, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
cmStandardLevelResolver standardResolver(this->Makefile);
|
||||
|
||||
std::string const type = ext ? "EXTENSION" : "STANDARD";
|
||||
|
||||
if (target->GetLanguageStandardRequired(lang)) {
|
||||
std::string option_flag =
|
||||
"CMAKE_" + lang + *standardProp + "_" + type + "_COMPILE_OPTION";
|
||||
|
||||
const char* opt =
|
||||
target->Target->GetMakefile()->GetDefinition(option_flag);
|
||||
if (!opt) {
|
||||
std::ostringstream e;
|
||||
e << "Target \"" << target->GetName()
|
||||
<< "\" requires the language "
|
||||
"dialect \""
|
||||
<< lang << *standardProp << "\" "
|
||||
<< (ext ? "(with compiler extensions)" : "")
|
||||
<< ", but CMake "
|
||||
"does not know the compile flags to use to enable it.";
|
||||
this->IssueMessage(MessageType::FATAL_ERROR, e.str());
|
||||
} else {
|
||||
std::string const& optionFlagDef =
|
||||
standardResolver.GetCompileOptionDef(target, lang, config);
|
||||
if (!optionFlagDef.empty()) {
|
||||
auto opt = target->Target->GetMakefile()->GetDefinition(optionFlagDef);
|
||||
if (opt) {
|
||||
std::vector<std::string> optVec = cmExpandedList(opt);
|
||||
for (std::string const& i : optVec) {
|
||||
this->AppendFlagEscape(flags, i);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> langStdMap;
|
||||
if (langStdMap.empty()) {
|
||||
// Maintain sorted order, most recent first.
|
||||
langStdMap["CXX"].emplace_back("20");
|
||||
langStdMap["CXX"].emplace_back("17");
|
||||
langStdMap["CXX"].emplace_back("14");
|
||||
langStdMap["CXX"].emplace_back("11");
|
||||
langStdMap["CXX"].emplace_back("98");
|
||||
|
||||
langStdMap["OBJCXX"].emplace_back("20");
|
||||
langStdMap["OBJCXX"].emplace_back("17");
|
||||
langStdMap["OBJCXX"].emplace_back("14");
|
||||
langStdMap["OBJCXX"].emplace_back("11");
|
||||
langStdMap["OBJCXX"].emplace_back("98");
|
||||
|
||||
langStdMap["C"].emplace_back("11");
|
||||
langStdMap["C"].emplace_back("99");
|
||||
langStdMap["C"].emplace_back("90");
|
||||
|
||||
langStdMap["OBJC"].emplace_back("11");
|
||||
langStdMap["OBJC"].emplace_back("99");
|
||||
langStdMap["OBJC"].emplace_back("90");
|
||||
|
||||
langStdMap["CUDA"].emplace_back("20");
|
||||
langStdMap["CUDA"].emplace_back("17");
|
||||
langStdMap["CUDA"].emplace_back("14");
|
||||
langStdMap["CUDA"].emplace_back("11");
|
||||
langStdMap["CUDA"].emplace_back("03");
|
||||
}
|
||||
|
||||
std::string standard(*standardProp);
|
||||
if (lang == "CUDA" && standard == "98") {
|
||||
standard = "03";
|
||||
}
|
||||
std::vector<std::string>& stds = langStdMap[lang];
|
||||
|
||||
auto stdIt = std::find(stds.begin(), stds.end(), standard);
|
||||
if (stdIt == stds.end()) {
|
||||
|
||||
std::string e =
|
||||
lang + "_STANDARD is set to invalid value '" + standard + "'";
|
||||
this->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
|
||||
MessageType::FATAL_ERROR, e, target->GetBacktrace());
|
||||
return;
|
||||
}
|
||||
|
||||
auto defaultStdIt = std::find(stds.begin(), stds.end(), defaultStd);
|
||||
if (defaultStdIt == stds.end()) {
|
||||
std::string e = "CMAKE_" + lang +
|
||||
"_STANDARD_DEFAULT is set to invalid value '" + std::string(defaultStd) +
|
||||
"'";
|
||||
this->IssueMessage(MessageType::INTERNAL_ERROR, e);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the standard requested is older than the compiler's default
|
||||
// then we need to use a flag to change it. The comparison is
|
||||
// greater-or-equal because the standards are stored in backward
|
||||
// chronological order.
|
||||
if (stdIt >= defaultStdIt) {
|
||||
std::string option_flag =
|
||||
"CMAKE_" + lang + *stdIt + "_" + type + "_COMPILE_OPTION";
|
||||
|
||||
std::string const& opt =
|
||||
target->Target->GetMakefile()->GetRequiredDefinition(option_flag);
|
||||
std::vector<std::string> optVec = cmExpandedList(opt);
|
||||
for (std::string const& i : optVec) {
|
||||
this->AppendFlagEscape(flags, i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// The standard requested is at least as new as the compiler's default,
|
||||
// and the standard request is not required. Decay to the newest standard
|
||||
// for which a flag is defined.
|
||||
for (; stdIt < defaultStdIt; ++stdIt) {
|
||||
std::string option_flag =
|
||||
cmStrCat("CMAKE_", lang, *stdIt, "_", type, "_COMPILE_OPTION");
|
||||
|
||||
if (const char* opt =
|
||||
target->Target->GetMakefile()->GetDefinition(option_flag)) {
|
||||
std::vector<std::string> optVec = cmExpandedList(opt);
|
||||
for (std::string const& i : optVec) {
|
||||
this->AppendFlagEscape(flags, i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -45,10 +46,126 @@ struct StandardNeeded
|
||||
|
||||
struct StanardLevelComputer
|
||||
{
|
||||
explicit StanardLevelComputer(std::string lang, std::vector<int> levels)
|
||||
explicit StanardLevelComputer(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(levels.size() == levelsStr.size());
|
||||
}
|
||||
|
||||
std::string GetCompileOptionDef(cmMakefile* makefile,
|
||||
cmGeneratorTarget const* target,
|
||||
std::string const& config) const
|
||||
{
|
||||
|
||||
const auto& stds = this->Levels;
|
||||
const auto& stdsStrings = this->LevelsAsStrings;
|
||||
|
||||
const char* defaultStd = makefile->GetDefinition(
|
||||
cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
|
||||
if (!defaultStd || !*defaultStd) {
|
||||
// this compiler has no notion of language standard levels
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
bool ext = true;
|
||||
if (cmProp extPropValue = target->GetLanguageExtensions(this->Language)) {
|
||||
if (cmIsOff(*extPropValue)) {
|
||||
ext = false;
|
||||
}
|
||||
}
|
||||
|
||||
cmProp standardProp = target->GetLanguageStandard(this->Language, config);
|
||||
if (!standardProp) {
|
||||
if (ext) {
|
||||
// No language standard is specified and extensions are not disabled.
|
||||
// Check if this compiler needs a flag to enable extensions.
|
||||
return cmStrCat("CMAKE_", this->Language, "_EXTENSION_COMPILE_OPTION");
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
std::string const type = ext ? "EXTENSION" : "STANDARD";
|
||||
|
||||
if (target->GetLanguageStandardRequired(this->Language)) {
|
||||
std::string option_flag = cmStrCat(
|
||||
"CMAKE_", this->Language, *standardProp, "_", type, "_COMPILE_OPTION");
|
||||
|
||||
const char* 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 CMake "
|
||||
"does not know the compile flags to use to enable it.";
|
||||
makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
|
||||
}
|
||||
return option_flag;
|
||||
}
|
||||
|
||||
std::string standardStr(*standardProp);
|
||||
if (this->Language == "CUDA" && standardStr == "98") {
|
||||
standardStr = "03";
|
||||
}
|
||||
|
||||
int standardValue = -1;
|
||||
int defaultValue = -1;
|
||||
try {
|
||||
standardValue = std::stoi(standardStr);
|
||||
defaultValue = std::stoi(defaultStd);
|
||||
} catch (std::invalid_argument&) {
|
||||
// fall through as we want an error
|
||||
// when we can't find the bad value in the `stds` vector
|
||||
}
|
||||
|
||||
auto stdIt = std::find(cm::cbegin(stds), cm::cend(stds), standardValue);
|
||||
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), defaultValue);
|
||||
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
|
||||
// then we need to use a flag to change it.
|
||||
if (stdIt <= defaultStdIt) {
|
||||
auto offset = std::distance(cm::cbegin(stds), stdIt);
|
||||
return cmStrCat("CMAKE_", this->Language, stdsStrings[offset], "_", type,
|
||||
"_COMPILE_OPTION");
|
||||
}
|
||||
|
||||
// The standard requested is at least as new as the compiler's default,
|
||||
// and the standard request 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{};
|
||||
}
|
||||
|
||||
bool GetNewRequiredStandard(cmMakefile* makefile,
|
||||
@@ -99,7 +216,7 @@ struct StanardLevelComputer
|
||||
// the needed C++ features.
|
||||
if (existingLevelIter == cm::cend(this->Levels) ||
|
||||
existingLevelIter < this->Levels.begin() + needed.index) {
|
||||
newRequiredStandard = std::to_string(this->Levels[needed.index]);
|
||||
newRequiredStandard = this->LevelsAsStrings[needed.index];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,8 +280,8 @@ struct StanardLevelComputer
|
||||
std::string prefix = cmStrCat("CMAKE_", this->Language);
|
||||
StandardNeeded maxLevel = { -1, -1 };
|
||||
for (size_t i = 0; i < this->Levels.size(); ++i) {
|
||||
if (const char* prop = makefile->GetDefinition(cmStrCat(
|
||||
prefix, std::to_string(this->Levels[i]), "_COMPILE_FEATURES"))) {
|
||||
if (const char* prop = makefile->GetDefinition(
|
||||
cmStrCat(prefix, this->LevelsAsStrings[i], "_COMPILE_FEATURES"))) {
|
||||
std::vector<std::string> props = cmExpandedList(prop);
|
||||
if (cm::contains(props, feature)) {
|
||||
maxLevel = { static_cast<int>(i), this->Levels[i] };
|
||||
@@ -185,22 +302,44 @@ struct StanardLevelComputer
|
||||
|
||||
std::string Language;
|
||||
std::vector<int> Levels;
|
||||
std::vector<std::string> LevelsAsStrings;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, StanardLevelComputer> StandardComputerMapping =
|
||||
{
|
||||
{ "C", StanardLevelComputer{ "C", std::vector<int>{ 90, 99, 11 } } },
|
||||
{ "C",
|
||||
StanardLevelComputer{ "C", std::vector<int>{ 90, 99, 11 },
|
||||
std::vector<std::string>{ "90", "99", "11" } } },
|
||||
{ "CXX",
|
||||
StanardLevelComputer{ "CXX", std::vector<int>{ 98, 11, 14, 17, 20 } } },
|
||||
StanardLevelComputer{
|
||||
"CXX", std::vector<int>{ 98, 11, 14, 17, 20 },
|
||||
std::vector<std::string>{ "98", "11", "14", "17", "20" } } },
|
||||
{ "CUDA",
|
||||
StanardLevelComputer{ "CUDA", std::vector<int>{ 03, 11, 14, 17, 20 } } },
|
||||
{ "OBJC", StanardLevelComputer{ "OBJC", std::vector<int>{ 90, 99, 11 } } },
|
||||
StanardLevelComputer{
|
||||
"CUDA", std::vector<int>{ 03, 11, 14, 17, 20 },
|
||||
std::vector<std::string>{ "03", "11", "14", "17", "20" } } },
|
||||
{ "OBJC",
|
||||
StanardLevelComputer{ "OBJC", std::vector<int>{ 90, 99, 11 },
|
||||
std::vector<std::string>{ "90", "99", "11" } } },
|
||||
{ "OBJCXX",
|
||||
StanardLevelComputer{ "OBJCXX",
|
||||
std::vector<int>{ 98, 11, 14, 17, 20 } } },
|
||||
StanardLevelComputer{
|
||||
"OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20 },
|
||||
std::vector<std::string>{ "98", "11", "14", "17", "20" } } },
|
||||
};
|
||||
}
|
||||
|
||||
std::string cmStandardLevelResolver::GetCompileOptionDef(
|
||||
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.GetCompileOptionDef(this->Makefile, target, config);
|
||||
}
|
||||
|
||||
bool cmStandardLevelResolver::AddRequiredTargetFeature(
|
||||
cmTarget* target, const std::string& feature, std::string* error) const
|
||||
{
|
||||
|
||||
@@ -20,6 +20,10 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
std::string GetCompileOptionDef(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;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
^CMake Error at .*/Tests/RunCMake/try_compile/CudaStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\):
|
||||
CUDA_STANDARD is set to invalid value '3'
|
||||
CUDA_STANDARD is set to invalid value '4'
|
||||
+
|
||||
CMake Error at CudaStandard.cmake:[0-9]+ \(try_compile\):
|
||||
Failed to generate test project build system.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
enable_language(CUDA)
|
||||
try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
|
||||
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.cu
|
||||
CUDA_STANDARD 3
|
||||
CUDA_STANDARD 4
|
||||
OUTPUT_VARIABLE out
|
||||
)
|
||||
message("try_compile output:\n${out}")
|
||||
|
||||
Reference in New Issue
Block a user