Merge topic 'COMPILE_FEATURES-genex'
0dfe395e
Features: Add COMPILE_FEATURES generator expression.aa8a6fce
cmMakefile: Add methods for checking availability of a feature.b6dedf03
cmMakefile: Extract CheckNeeded{C,Cxx}Language methods.8dd129df
cmMakefile: Extract CompileFeaturesAvailable method.6b9b2fff
cmMakefile: Extract CompileFeatureKnown method.
This commit is contained in:
commit
5ce40619db
|
@ -83,6 +83,12 @@ otherwise expands to nothing.
|
||||||
else ``0``. If the policy was not set, the warning message for the policy
|
else ``0``. If the policy was not set, the warning message for the policy
|
||||||
will be emitted. This generator expression only works for a subset of
|
will be emitted. This generator expression only works for a subset of
|
||||||
policies.
|
policies.
|
||||||
|
``$<COMPILE_FEATURES:feature[,feature]...>``
|
||||||
|
``1`` if all of the ``feature`` features are available for the 'head'
|
||||||
|
target, and ``0`` otherwise. If this expression is used while evaluating
|
||||||
|
the link implementation of a target and if any dependency transitively
|
||||||
|
increases the required :prop_tgt:`C_STANDARD` or :prop_tgt:`CXX_STANDARD`
|
||||||
|
for the 'head' target, an error is reported.
|
||||||
|
|
||||||
Informational Expressions
|
Informational Expressions
|
||||||
=========================
|
=========================
|
||||||
|
|
|
@ -22,3 +22,7 @@ target-language-features
|
||||||
* New :command:`target_compile_features` command allows populating the
|
* New :command:`target_compile_features` command allows populating the
|
||||||
:prop_tgt:`COMPILE_FEATURES` target property, just like any other
|
:prop_tgt:`COMPILE_FEATURES` target property, just like any other
|
||||||
build variable.
|
build variable.
|
||||||
|
|
||||||
|
* New ``COMPILE_FEATURES``
|
||||||
|
:manual:`generator expression <cmake-generator-expressions(7)>` allows
|
||||||
|
setting build properties based on available compiler features.
|
||||||
|
|
|
@ -110,6 +110,9 @@ const char *cmCompiledGeneratorExpression::Evaluate(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->MaxLanguageStandard = context.MaxLanguageStandard;
|
||||||
|
|
||||||
if (!context.HadError)
|
if (!context.HadError)
|
||||||
{
|
{
|
||||||
this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
|
this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
|
||||||
|
@ -465,3 +468,17 @@ bool cmGeneratorExpression::IsValidTargetName(const std::string &input)
|
||||||
|
|
||||||
return targetNameValidator.find(input.c_str());
|
return targetNameValidator.find(input.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
cmCompiledGeneratorExpression::GetMaxLanguageStandard(cmTarget const* tgt,
|
||||||
|
std::map<std::string, std::string>& mapping)
|
||||||
|
{
|
||||||
|
typedef std::map<cmTarget const*,
|
||||||
|
std::map<std::string, std::string> > MapType;
|
||||||
|
MapType::const_iterator it = this->MaxLanguageStandard.find(tgt);
|
||||||
|
if (it != this->MaxLanguageStandard.end())
|
||||||
|
{
|
||||||
|
mapping = it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -117,6 +117,9 @@ public:
|
||||||
this->EvaluateForBuildsystem = eval;
|
this->EvaluateForBuildsystem = eval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetMaxLanguageStandard(cmTarget const* tgt,
|
||||||
|
std::map<std::string, std::string>& mapping);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace,
|
cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace,
|
||||||
const std::string& input);
|
const std::string& input);
|
||||||
|
@ -134,6 +137,8 @@ private:
|
||||||
mutable std::set<cmTarget*> DependTargets;
|
mutable std::set<cmTarget*> DependTargets;
|
||||||
mutable std::set<cmTarget const*> AllTargetsSeen;
|
mutable std::set<cmTarget const*> AllTargetsSeen;
|
||||||
mutable std::set<std::string> SeenTargetProperties;
|
mutable std::set<std::string> SeenTargetProperties;
|
||||||
|
mutable std::map<cmTarget const*, std::map<std::string, std::string> >
|
||||||
|
MaxLanguageStandard;
|
||||||
mutable std::string Output;
|
mutable std::string Output;
|
||||||
mutable bool HadContextSensitiveCondition;
|
mutable bool HadContextSensitiveCondition;
|
||||||
bool EvaluateForBuildsystem;
|
bool EvaluateForBuildsystem;
|
||||||
|
|
|
@ -1313,6 +1313,94 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode
|
||||||
}
|
}
|
||||||
} targetObjectsNode;
|
} targetObjectsNode;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
|
||||||
|
{
|
||||||
|
CompileFeaturesNode() {}
|
||||||
|
|
||||||
|
virtual int NumExpectedParameters() const { return OneOrMoreParameters; }
|
||||||
|
|
||||||
|
std::string Evaluate(const std::vector<std::string> ¶meters,
|
||||||
|
cmGeneratorExpressionContext *context,
|
||||||
|
const GeneratorExpressionContent *content,
|
||||||
|
cmGeneratorExpressionDAGChecker *dagChecker) const
|
||||||
|
{
|
||||||
|
cmTarget const* target = context->HeadTarget;
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
reportError(context, content->GetOriginalExpression(),
|
||||||
|
"$<COMPILE_FEATURE> may only be used with binary targets. It may "
|
||||||
|
"not be used with add_custom_command or add_custom_target.");
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef std::map<std::string, std::vector<std::string> > LangMap;
|
||||||
|
static LangMap availableFeatures;
|
||||||
|
|
||||||
|
LangMap testedFeatures;
|
||||||
|
|
||||||
|
for (std::vector<std::string>::const_iterator it = parameters.begin();
|
||||||
|
it != parameters.end(); ++it)
|
||||||
|
{
|
||||||
|
std::string error;
|
||||||
|
std::string lang;
|
||||||
|
if (!context->Makefile->CompileFeatureKnown(context->HeadTarget,
|
||||||
|
*it, lang, &error))
|
||||||
|
{
|
||||||
|
reportError(context, content->GetOriginalExpression(), error);
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
testedFeatures[lang].push_back(*it);
|
||||||
|
|
||||||
|
if (availableFeatures.find(lang) == availableFeatures.end())
|
||||||
|
{
|
||||||
|
const char* featuresKnown
|
||||||
|
= context->Makefile->CompileFeaturesAvailable(lang, &error);
|
||||||
|
if (!featuresKnown)
|
||||||
|
{
|
||||||
|
reportError(context, content->GetOriginalExpression(), error);
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
cmSystemTools::ExpandListArgument(featuresKnown,
|
||||||
|
availableFeatures[lang]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries();
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
for (LangMap::const_iterator lit = testedFeatures.begin();
|
||||||
|
lit != testedFeatures.end(); ++lit)
|
||||||
|
{
|
||||||
|
for (std::vector<std::string>::const_iterator it = lit->second.begin();
|
||||||
|
it != lit->second.end(); ++it)
|
||||||
|
{
|
||||||
|
if (!context->Makefile->HaveFeatureAvailable(target,
|
||||||
|
lit->first, *it))
|
||||||
|
{
|
||||||
|
if (evalLL)
|
||||||
|
{
|
||||||
|
const char* l = target->GetProperty(lit->first + "_STANDARD");
|
||||||
|
if (!l)
|
||||||
|
{
|
||||||
|
l = context->Makefile
|
||||||
|
->GetDefinition("CMAKE_" + lit->first + "_STANDARD_DEFAULT");
|
||||||
|
}
|
||||||
|
assert(l);
|
||||||
|
context->MaxLanguageStandard[target][lit->first] = l;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "1";
|
||||||
|
}
|
||||||
|
} compileFeaturesNode;
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
static const char* targetPolicyWhitelist[] = {
|
static const char* targetPolicyWhitelist[] = {
|
||||||
0
|
0
|
||||||
|
@ -1647,6 +1735,7 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier)
|
||||||
nodeMap["C_COMPILER_VERSION"] = &cCompilerVersionNode;
|
nodeMap["C_COMPILER_VERSION"] = &cCompilerVersionNode;
|
||||||
nodeMap["CXX_COMPILER_VERSION"] = &cxxCompilerVersionNode;
|
nodeMap["CXX_COMPILER_VERSION"] = &cxxCompilerVersionNode;
|
||||||
nodeMap["PLATFORM_ID"] = &platformIdNode;
|
nodeMap["PLATFORM_ID"] = &platformIdNode;
|
||||||
|
nodeMap["COMPILE_FEATURES"] = &compileFeaturesNode;
|
||||||
nodeMap["CONFIGURATION"] = &configurationNode;
|
nodeMap["CONFIGURATION"] = &configurationNode;
|
||||||
nodeMap["CONFIG"] = &configurationTestNode;
|
nodeMap["CONFIG"] = &configurationTestNode;
|
||||||
nodeMap["TARGET_FILE"] = &targetFileNode;
|
nodeMap["TARGET_FILE"] = &targetFileNode;
|
||||||
|
|
|
@ -26,6 +26,8 @@ struct cmGeneratorExpressionContext
|
||||||
std::set<cmTarget*> DependTargets;
|
std::set<cmTarget*> DependTargets;
|
||||||
std::set<cmTarget const*> AllTargets;
|
std::set<cmTarget const*> AllTargets;
|
||||||
std::set<std::string> SeenTargetProperties;
|
std::set<std::string> SeenTargetProperties;
|
||||||
|
std::map<cmTarget const*, std::map<std::string, std::string> >
|
||||||
|
MaxLanguageStandard;
|
||||||
cmMakefile *Makefile;
|
cmMakefile *Makefile;
|
||||||
std::string Config;
|
std::string Config;
|
||||||
cmTarget const* HeadTarget; // The target whose property is being evaluated.
|
cmTarget const* HeadTarget; // The target whose property is being evaluated.
|
||||||
|
|
|
@ -1484,6 +1484,31 @@ void cmLocalGenerator::AddCompileOptions(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(std::map<std::string, std::string>::const_iterator it
|
||||||
|
= target->GetMaxLanguageStandards().begin();
|
||||||
|
it != target->GetMaxLanguageStandards().end(); ++it)
|
||||||
|
{
|
||||||
|
const char* standard = target->GetProperty(it->first + "_STANDARD");
|
||||||
|
if(!standard)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (this->Makefile->IsLaterStandard(it->first, standard, it->second))
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "The COMPILE_FEATURES property of target \""
|
||||||
|
<< target->GetName() << "\" was evaluated when computing the link "
|
||||||
|
"implementation, and the \"" << it->first << "_STANDARD\" was \""
|
||||||
|
<< it->second << "\" for that computation. Computing the "
|
||||||
|
"COMPILE_FEATURES based on the link implementation resulted in a "
|
||||||
|
"higher \"" << it->first << "_STANDARD\" \"" << standard << "\". "
|
||||||
|
"This is not permitted. The COMPILE_FEATURES may not both depend on "
|
||||||
|
"and be depended on by the link implementation." << std::endl;
|
||||||
|
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
this->AddCompilerRequirementFlag(flags, target, lang);
|
this->AddCompilerRequirementFlag(flags, target, lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5004,14 +5004,65 @@ AddRequiredTargetFeature(cmTarget *target, const std::string& feature,
|
||||||
target->AppendProperty("COMPILE_FEATURES", feature.c_str());
|
target->AppendProperty("COMPILE_FEATURES", feature.c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string lang;
|
||||||
|
if (!this->CompileFeatureKnown(target, feature, lang, error))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* features = this->CompileFeaturesAvailable(lang, error);
|
||||||
|
if (!features)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> availableFeatures;
|
||||||
|
cmSystemTools::ExpandListArgument(features, availableFeatures);
|
||||||
|
if (std::find(availableFeatures.begin(),
|
||||||
|
availableFeatures.end(),
|
||||||
|
feature) == availableFeatures.end())
|
||||||
|
{
|
||||||
|
cmOStringStream 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") << ".";
|
||||||
|
this->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
target->AppendProperty("COMPILE_FEATURES", feature.c_str());
|
||||||
|
|
||||||
|
return lang == "C"
|
||||||
|
? this->AddRequiredTargetCFeature(target, feature)
|
||||||
|
: this->AddRequiredTargetCxxFeature(target, feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool cmMakefile::
|
||||||
|
CompileFeatureKnown(cmTarget const* target, const std::string& feature,
|
||||||
|
std::string& lang, std::string *error) const
|
||||||
|
{
|
||||||
|
assert(cmGeneratorExpression::Find(feature) == std::string::npos);
|
||||||
|
|
||||||
bool isCFeature = std::find_if(cmArrayBegin(C_FEATURES) + 1,
|
bool isCFeature = std::find_if(cmArrayBegin(C_FEATURES) + 1,
|
||||||
cmArrayEnd(C_FEATURES), cmStrCmp(feature))
|
cmArrayEnd(C_FEATURES), cmStrCmp(feature))
|
||||||
!= cmArrayEnd(C_FEATURES);
|
!= cmArrayEnd(C_FEATURES);
|
||||||
|
if (isCFeature)
|
||||||
|
{
|
||||||
|
lang = "C";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
bool isCxxFeature = std::find_if(cmArrayBegin(CXX_FEATURES) + 1,
|
bool isCxxFeature = std::find_if(cmArrayBegin(CXX_FEATURES) + 1,
|
||||||
cmArrayEnd(CXX_FEATURES), cmStrCmp(feature))
|
cmArrayEnd(CXX_FEATURES), cmStrCmp(feature))
|
||||||
!= cmArrayEnd(CXX_FEATURES);
|
!= cmArrayEnd(CXX_FEATURES);
|
||||||
if (!isCFeature && !isCxxFeature)
|
if (isCxxFeature)
|
||||||
{
|
{
|
||||||
|
lang = "CXX";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
cmOStringStream e;
|
cmOStringStream e;
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
|
@ -5032,10 +5083,12 @@ AddRequiredTargetFeature(cmTarget *target, const std::string& feature,
|
||||||
this->IssueMessage(cmake::FATAL_ERROR, e.str());
|
this->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string lang = isCFeature ? "C" : "CXX";
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
const char* cmMakefile::
|
||||||
|
CompileFeaturesAvailable(const std::string& lang, std::string *error) const
|
||||||
|
{
|
||||||
const char* featuresKnown =
|
const char* featuresKnown =
|
||||||
this->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
|
this->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
|
||||||
|
|
||||||
|
@ -5062,40 +5115,150 @@ AddRequiredTargetFeature(cmTarget *target, const std::string& feature,
|
||||||
{
|
{
|
||||||
this->IssueMessage(cmake::FATAL_ERROR, e.str());
|
this->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
}
|
}
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
return featuresKnown;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> availableFeatures;
|
//----------------------------------------------------------------------------
|
||||||
cmSystemTools::ExpandListArgument(featuresKnown, availableFeatures);
|
bool cmMakefile::HaveFeatureAvailable(cmTarget const* target,
|
||||||
if (std::find(availableFeatures.begin(),
|
std::string const& lang,
|
||||||
availableFeatures.end(),
|
const std::string& feature) const
|
||||||
feature) == availableFeatures.end())
|
{
|
||||||
{
|
return lang == "C"
|
||||||
cmOStringStream e;
|
? this->HaveCFeatureAvailable(target, feature)
|
||||||
e << "The compiler feature \"" << feature
|
: this->HaveCxxFeatureAvailable(target, feature);
|
||||||
<< "\" is not known to " << lang << " compiler\n\""
|
|
||||||
<< this->GetDefinition("CMAKE_" + lang + "_COMPILER_ID")
|
|
||||||
<< "\"\nversion "
|
|
||||||
<< this->GetDefinition("CMAKE_" + lang + "_COMPILER_VERSION") << ".";
|
|
||||||
this->IssueMessage(cmake::FATAL_ERROR, e.str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
target->AppendProperty("COMPILE_FEATURES", feature.c_str());
|
|
||||||
|
|
||||||
return isCFeature
|
|
||||||
? this->AddRequiredTargetCFeature(target, feature)
|
|
||||||
: this->AddRequiredTargetCxxFeature(target, feature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool cmMakefile::
|
bool cmMakefile::
|
||||||
AddRequiredTargetCxxFeature(cmTarget *target,
|
HaveCFeatureAvailable(cmTarget const* target, const std::string& feature) const
|
||||||
|
{
|
||||||
|
bool needC90 = false;
|
||||||
|
bool needC99 = false;
|
||||||
|
bool needC11 = false;
|
||||||
|
|
||||||
|
this->CheckNeededCLanguage(feature, needC90, needC99, needC11);
|
||||||
|
|
||||||
|
const char *existingCStandard = target->GetProperty("C_STANDARD");
|
||||||
|
if (!existingCStandard)
|
||||||
|
{
|
||||||
|
existingCStandard = this->GetDefinition("CMAKE_C_STANDARD_DEFAULT");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::find_if(cmArrayBegin(C_STANDARDS), cmArrayEnd(C_STANDARDS),
|
||||||
|
cmStrCmp(existingCStandard)) == cmArrayEnd(C_STANDARDS))
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "The C_STANDARD property on target \"" << target->GetName()
|
||||||
|
<< "\" contained an invalid value: \"" << existingCStandard << "\".";
|
||||||
|
this->IssueMessage(cmake::FATAL_ERROR, e.str().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * const *existingCIt = existingCStandard
|
||||||
|
? std::find_if(cmArrayBegin(C_STANDARDS),
|
||||||
|
cmArrayEnd(C_STANDARDS),
|
||||||
|
cmStrCmp(existingCStandard))
|
||||||
|
: cmArrayEnd(C_STANDARDS);
|
||||||
|
|
||||||
|
if (needC11 && existingCStandard && existingCIt <
|
||||||
|
std::find_if(cmArrayBegin(C_STANDARDS),
|
||||||
|
cmArrayEnd(C_STANDARDS),
|
||||||
|
cmStrCmp("11")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(needC99 && existingCStandard && existingCIt <
|
||||||
|
std::find_if(cmArrayBegin(C_STANDARDS),
|
||||||
|
cmArrayEnd(C_STANDARDS),
|
||||||
|
cmStrCmp("99")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(needC90 && existingCStandard && existingCIt <
|
||||||
|
std::find_if(cmArrayBegin(C_STANDARDS),
|
||||||
|
cmArrayEnd(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")
|
||||||
|
{
|
||||||
|
const char * const *rhsIt = std::find_if(cmArrayBegin(C_STANDARDS),
|
||||||
|
cmArrayEnd(C_STANDARDS),
|
||||||
|
cmStrCmp(rhs));
|
||||||
|
|
||||||
|
return std::find_if(rhsIt, cmArrayEnd(C_STANDARDS),
|
||||||
|
cmStrCmp(lhs)) != cmArrayEnd(C_STANDARDS);
|
||||||
|
}
|
||||||
|
const char * const *rhsIt = std::find_if(cmArrayBegin(CXX_STANDARDS),
|
||||||
|
cmArrayEnd(CXX_STANDARDS),
|
||||||
|
cmStrCmp(rhs));
|
||||||
|
|
||||||
|
return std::find_if(rhsIt, cmArrayEnd(CXX_STANDARDS),
|
||||||
|
cmStrCmp(lhs)) != cmArrayEnd(CXX_STANDARDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool cmMakefile::HaveCxxFeatureAvailable(cmTarget const* target,
|
||||||
const std::string& feature) const
|
const std::string& feature) const
|
||||||
{
|
{
|
||||||
bool needCxx98 = false;
|
bool needCxx98 = false;
|
||||||
bool needCxx11 = false;
|
bool needCxx11 = false;
|
||||||
|
this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11);
|
||||||
|
|
||||||
|
const char *existingCxxStandard = target->GetProperty("CXX_STANDARD");
|
||||||
|
if (!existingCxxStandard)
|
||||||
|
{
|
||||||
|
existingCxxStandard = this->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::find_if(cmArrayBegin(CXX_STANDARDS), cmArrayEnd(CXX_STANDARDS),
|
||||||
|
cmStrCmp(existingCxxStandard)) == cmArrayEnd(CXX_STANDARDS))
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "The CXX_STANDARD property on target \"" << target->GetName()
|
||||||
|
<< "\" contained an invalid value: \"" << existingCxxStandard << "\".";
|
||||||
|
this->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * const *existingCxxIt = existingCxxStandard
|
||||||
|
? std::find_if(cmArrayBegin(CXX_STANDARDS),
|
||||||
|
cmArrayEnd(CXX_STANDARDS),
|
||||||
|
cmStrCmp(existingCxxStandard))
|
||||||
|
: cmArrayEnd(CXX_STANDARDS);
|
||||||
|
|
||||||
|
if (needCxx11 && existingCxxIt < std::find_if(cmArrayBegin(CXX_STANDARDS),
|
||||||
|
cmArrayEnd(CXX_STANDARDS),
|
||||||
|
cmStrCmp("11")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(needCxx98 && existingCxxIt <
|
||||||
|
std::find_if(cmArrayBegin(CXX_STANDARDS),
|
||||||
|
cmArrayEnd(CXX_STANDARDS),
|
||||||
|
cmStrCmp("98")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void cmMakefile::CheckNeededCxxLanguage(const std::string& feature,
|
||||||
|
bool& needCxx98,
|
||||||
|
bool& needCxx11) const
|
||||||
|
{
|
||||||
if (const char *propCxx98 =
|
if (const char *propCxx98 =
|
||||||
this->GetDefinition("CMAKE_CXX98_COMPILE_FEATURES"))
|
this->GetDefinition("CMAKE_CXX98_COMPILE_FEATURES"))
|
||||||
{
|
{
|
||||||
|
@ -5110,6 +5273,17 @@ AddRequiredTargetCxxFeature(cmTarget *target,
|
||||||
cmSystemTools::ExpandListArgument(propCxx11, props);
|
cmSystemTools::ExpandListArgument(propCxx11, props);
|
||||||
needCxx11 = std::find(props.begin(), props.end(), feature) != props.end();
|
needCxx11 = std::find(props.begin(), props.end(), feature) != props.end();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool cmMakefile::
|
||||||
|
AddRequiredTargetCxxFeature(cmTarget *target,
|
||||||
|
const std::string& feature) const
|
||||||
|
{
|
||||||
|
bool needCxx98 = false;
|
||||||
|
bool needCxx11 = false;
|
||||||
|
|
||||||
|
this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11);
|
||||||
|
|
||||||
const char *existingCxxStandard = target->GetProperty("CXX_STANDARD");
|
const char *existingCxxStandard = target->GetProperty("CXX_STANDARD");
|
||||||
if (existingCxxStandard)
|
if (existingCxxStandard)
|
||||||
|
@ -5160,13 +5334,11 @@ AddRequiredTargetCxxFeature(cmTarget *target,
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool cmMakefile::
|
void cmMakefile::CheckNeededCLanguage(const std::string& feature,
|
||||||
AddRequiredTargetCFeature(cmTarget *target, const std::string& feature) const
|
bool& needC90,
|
||||||
|
bool& needC99,
|
||||||
|
bool& needC11) const
|
||||||
{
|
{
|
||||||
bool needC90 = false;
|
|
||||||
bool needC99 = false;
|
|
||||||
bool needC11 = false;
|
|
||||||
|
|
||||||
if (const char *propC90 =
|
if (const char *propC90 =
|
||||||
this->GetDefinition("CMAKE_C90_COMPILE_FEATURES"))
|
this->GetDefinition("CMAKE_C90_COMPILE_FEATURES"))
|
||||||
{
|
{
|
||||||
|
@ -5188,6 +5360,17 @@ AddRequiredTargetCFeature(cmTarget *target, const std::string& feature) const
|
||||||
cmSystemTools::ExpandListArgument(propC11, props);
|
cmSystemTools::ExpandListArgument(propC11, props);
|
||||||
needC11 = std::find(props.begin(), props.end(), feature) != props.end();
|
needC11 = std::find(props.begin(), props.end(), feature) != props.end();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool cmMakefile::
|
||||||
|
AddRequiredTargetCFeature(cmTarget *target, const std::string& feature) const
|
||||||
|
{
|
||||||
|
bool needC90 = false;
|
||||||
|
bool needC99 = false;
|
||||||
|
bool needC11 = false;
|
||||||
|
|
||||||
|
this->CheckNeededCLanguage(feature, needC90, needC99, needC11);
|
||||||
|
|
||||||
const char *existingCStandard = target->GetProperty("C_STANDARD");
|
const char *existingCStandard = target->GetProperty("C_STANDARD");
|
||||||
if (existingCStandard)
|
if (existingCStandard)
|
||||||
|
|
|
@ -889,6 +889,19 @@ public:
|
||||||
const std::string& feature,
|
const std::string& feature,
|
||||||
std::string *error = 0) const;
|
std::string *error = 0) const;
|
||||||
|
|
||||||
|
bool CompileFeatureKnown(cmTarget const* target, const std::string& feature,
|
||||||
|
std::string& lang, std::string *error) const;
|
||||||
|
|
||||||
|
const char* CompileFeaturesAvailable(const std::string& lang,
|
||||||
|
std::string *error) const;
|
||||||
|
|
||||||
|
bool HaveFeatureAvailable(cmTarget const* target, std::string const& lang,
|
||||||
|
const std::string& feature) const;
|
||||||
|
|
||||||
|
bool IsLaterStandard(std::string const& lang,
|
||||||
|
std::string const& lhs,
|
||||||
|
std::string const& rhs);
|
||||||
|
|
||||||
void ClearMatches();
|
void ClearMatches();
|
||||||
void StoreMatches(cmsys::RegularExpression& re);
|
void StoreMatches(cmsys::RegularExpression& re);
|
||||||
|
|
||||||
|
@ -1104,6 +1117,16 @@ private:
|
||||||
|
|
||||||
bool AddRequiredTargetCxxFeature(cmTarget *target,
|
bool AddRequiredTargetCxxFeature(cmTarget *target,
|
||||||
const std::string& feature) const;
|
const std::string& feature) const;
|
||||||
|
|
||||||
|
void CheckNeededCLanguage(const std::string& feature, bool& needC90,
|
||||||
|
bool& needC99, bool& needC11) const;
|
||||||
|
void CheckNeededCxxLanguage(const std::string& feature, bool& needCxx98,
|
||||||
|
bool& needCxx11) const;
|
||||||
|
|
||||||
|
bool HaveCFeatureAvailable(cmTarget const* target,
|
||||||
|
const std::string& feature) const;
|
||||||
|
bool HaveCxxFeatureAvailable(cmTarget const* target,
|
||||||
|
const std::string& feature) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
|
@ -1229,6 +1229,7 @@ void cmTarget::GetDirectLinkLibraries(const std::string& config,
|
||||||
this->LinkImplicitNullProperties.insert(*it);
|
this->LinkImplicitNullProperties.insert(*it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -587,6 +587,12 @@ public:
|
||||||
const std::string &report,
|
const std::string &report,
|
||||||
const std::string &compatibilityType) const;
|
const std::string &compatibilityType) const;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> const&
|
||||||
|
GetMaxLanguageStandards() const
|
||||||
|
{
|
||||||
|
return this->MaxLanguageStandards;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool HandleLocationPropertyPolicy(cmMakefile* context) const;
|
bool HandleLocationPropertyPolicy(cmMakefile* context) const;
|
||||||
|
|
||||||
|
@ -718,6 +724,7 @@ private:
|
||||||
mutable bool DebugSourcesDone;
|
mutable bool DebugSourcesDone;
|
||||||
mutable bool DebugCompileFeaturesDone;
|
mutable bool DebugCompileFeaturesDone;
|
||||||
mutable std::set<std::string> LinkImplicitNullProperties;
|
mutable std::set<std::string> LinkImplicitNullProperties;
|
||||||
|
mutable std::map<std::string, std::string> MaxLanguageStandards;
|
||||||
bool BuildInterfaceIncludesAppended;
|
bool BuildInterfaceIncludesAppended;
|
||||||
|
|
||||||
// Cache target output paths for each configuration.
|
// Cache target output paths for each configuration.
|
||||||
|
|
|
@ -83,3 +83,17 @@ set_property(TARGET iface
|
||||||
)
|
)
|
||||||
add_executable(IfaceCompileFeatures main.cpp)
|
add_executable(IfaceCompileFeatures main.cpp)
|
||||||
target_link_libraries(IfaceCompileFeatures iface)
|
target_link_libraries(IfaceCompileFeatures iface)
|
||||||
|
|
||||||
|
add_executable(CompileFeaturesGenex genex_test.cpp)
|
||||||
|
set_property(TARGET CompileFeaturesGenex PROPERTY CXX_STANDARD 11)
|
||||||
|
target_compile_definitions(CompileFeaturesGenex PRIVATE HAVE_OVERRIDE_CONTROL=$<COMPILE_FEATURES:cxx_final,cxx_override>)
|
||||||
|
|
||||||
|
add_executable(CompileFeaturesGenex2 genex_test.cpp)
|
||||||
|
target_compile_features(CompileFeaturesGenex2 PRIVATE cxx_constexpr)
|
||||||
|
target_compile_definitions(CompileFeaturesGenex2 PRIVATE HAVE_OVERRIDE_CONTROL=$<COMPILE_FEATURES:cxx_final,cxx_override>)
|
||||||
|
|
||||||
|
add_library(noexcept_iface INTERFACE)
|
||||||
|
target_compile_features(noexcept_iface INTERFACE cxx_noexcept)
|
||||||
|
add_executable(CompileFeaturesGenex3 genex_test.cpp)
|
||||||
|
target_link_libraries(CompileFeaturesGenex3 PRIVATE noexcept_iface)
|
||||||
|
target_compile_definitions(CompileFeaturesGenex3 PRIVATE HAVE_OVERRIDE_CONTROL=$<COMPILE_FEATURES:cxx_final,cxx_override>)
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
#if !HAVE_OVERRIDE_CONTROL
|
||||||
|
#error "Expect override control feature"
|
||||||
|
#else
|
||||||
|
|
||||||
|
struct A
|
||||||
|
{
|
||||||
|
virtual int getA() { return 7; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B final : A
|
||||||
|
{
|
||||||
|
int getA() override { return 42; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,7 @@
|
||||||
|
CMake Error in CMakeLists.txt:
|
||||||
|
The COMPILE_FEATURES property of target "empty1" was evaluated when
|
||||||
|
computing the link implementation, and the "CXX_STANDARD" was "98" for that
|
||||||
|
computation. Computing the COMPILE_FEATURES based on the link
|
||||||
|
implementation resulted in a higher "CXX_STANDARD" "11". This is not
|
||||||
|
permitted. The COMPILE_FEATURES may not both depend on and be depended on
|
||||||
|
by the link implementation.
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
add_library(empty1 empty.cpp)
|
||||||
|
|
||||||
|
add_library(empty2 INTERFACE)
|
||||||
|
add_library(empty3 INTERFACE)
|
||||||
|
target_compile_features(empty3 INTERFACE cxx_constexpr)
|
||||||
|
|
||||||
|
target_link_libraries(empty1
|
||||||
|
# When starting, $<COMPILE_FEATURES:cxx_final> is '0', so 'freeze' the
|
||||||
|
# CXX_STANDARD at 98 during computation.
|
||||||
|
$<$<COMPILE_FEATURES:cxx_final>:empty2>
|
||||||
|
# This would add cxx_constexpr, but that would require CXX_STANDARD = 11,
|
||||||
|
# which is not allowed after freeze. Report an error.
|
||||||
|
empty3
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
0
|
|
@ -0,0 +1 @@
|
||||||
|
^$
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
add_library(empty1 empty.cpp)
|
||||||
|
|
||||||
|
add_library(empty2 INTERFACE)
|
||||||
|
add_library(empty3 INTERFACE)
|
||||||
|
target_compile_features(empty3 INTERFACE cxx_constexpr)
|
||||||
|
|
||||||
|
target_link_libraries(empty1
|
||||||
|
$<$<COMPILE_FEATURES:cxx_final>:empty2>
|
||||||
|
empty3
|
||||||
|
)
|
||||||
|
# This, or populating the COMPILE_FEATURES property with a feature in the
|
||||||
|
# same standard as cxx_final, solves the cycle above.
|
||||||
|
set_property(TARGET empty1 PROPERTY CXX_STANDARD 11)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,9 @@
|
||||||
|
CMake Error at NonValidTarget1.cmake:[0-9]+ \(add_custom_command\):
|
||||||
|
Error evaluating generator expression:
|
||||||
|
|
||||||
|
\$<COMPILE_FEATURES:cxx_final>
|
||||||
|
|
||||||
|
\$<COMPILE_FEATURE> may only be used with binary targets. It may not be
|
||||||
|
used with add_custom_command or add_custom_target.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:3 \(include\)
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
set(genexvar $<COMPILE_FEATURES:cxx_final>)
|
||||||
|
|
||||||
|
if (HAVE_FINAL)
|
||||||
|
set(expected_result 1)
|
||||||
|
else()
|
||||||
|
set(expected_result 0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/copied_file${HAVE_FINAL}.cpp"
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/empty.cpp" "${CMAKE_CURRENT_BINARY_DIR}/copied_file${genexvar}.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(empty "${CMAKE_CURRENT_BINARY_DIR}/copied_file${genexvar}.cpp")
|
||||||
|
if (HAVE_FINAL)
|
||||||
|
target_compile_features(empty PRIVATE cxx_final)
|
||||||
|
endif()
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,9 @@
|
||||||
|
CMake Error at NonValidTarget2.cmake:4 \(add_custom_target\):
|
||||||
|
Error evaluating generator expression:
|
||||||
|
|
||||||
|
\$<COMPILE_FEATURES:cxx_final>
|
||||||
|
|
||||||
|
\$<COMPILE_FEATURE> may only be used with binary targets. It may not be
|
||||||
|
used with add_custom_command or add_custom_target.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:3 \(include\)
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
set(genexvar $<COMPILE_FEATURES:cxx_final>)
|
||||||
|
|
||||||
|
add_custom_target(copy_target
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/empty.cpp" "${CMAKE_CURRENT_BINARY_DIR}/copied_file${genexvar}.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(empty "${CMAKE_CURRENT_BINARY_DIR}/copied_file${genexvar}.cpp")
|
|
@ -26,6 +26,16 @@ endif()
|
||||||
if (NOT CXX_FEATURES)
|
if (NOT CXX_FEATURES)
|
||||||
run_cmake(NoSupportedCxxFeatures)
|
run_cmake(NoSupportedCxxFeatures)
|
||||||
run_cmake(NoSupportedCxxFeaturesGenex)
|
run_cmake(NoSupportedCxxFeaturesGenex)
|
||||||
|
else()
|
||||||
|
run_cmake(LinkImplementationFeatureCycle)
|
||||||
|
run_cmake(LinkImplementationFeatureCycleSolved)
|
||||||
|
|
||||||
|
if (";${CXX_FEATURES};" MATCHES ";cxx_final;")
|
||||||
|
set(RunCMake_TEST_OPTIONS "-DHAVE_FINAL=1")
|
||||||
|
endif()
|
||||||
|
run_cmake(NonValidTarget1)
|
||||||
|
run_cmake(NonValidTarget2)
|
||||||
|
unset(RunCMake_TEST_OPTIONS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
foreach(standard 98 11)
|
foreach(standard 98 11)
|
||||||
|
|
Loading…
Reference in New Issue