summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Help/manual/cmake-generator-expressions.7.rst6
-rw-r--r--Help/release/dev/compile-language-features.rst4
-rw-r--r--Source/cmGeneratorExpression.cxx17
-rw-r--r--Source/cmGeneratorExpression.h5
-rw-r--r--Source/cmGeneratorExpressionEvaluator.cxx89
-rw-r--r--Source/cmGeneratorExpressionEvaluator.h2
-rw-r--r--Source/cmLocalGenerator.cxx25
-rw-r--r--Source/cmMakefile.cxx279
-rw-r--r--Source/cmMakefile.h23
-rw-r--r--Source/cmTarget.cxx1
-rw-r--r--Source/cmTarget.h7
-rw-r--r--Tests/CompileFeatures/CMakeLists.txt14
-rw-r--r--Tests/CompileFeatures/genex_test.cpp21
-rw-r--r--Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle-result.txt1
-rw-r--r--Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle-stderr.txt7
-rw-r--r--Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle.cmake15
-rw-r--r--Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved-result.txt1
-rw-r--r--Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved-stderr.txt1
-rw-r--r--Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved.cmake14
-rw-r--r--Tests/RunCMake/CompileFeatures/NonValidTarget1-result.txt1
-rw-r--r--Tests/RunCMake/CompileFeatures/NonValidTarget1-stderr.txt9
-rw-r--r--Tests/RunCMake/CompileFeatures/NonValidTarget1.cmake17
-rw-r--r--Tests/RunCMake/CompileFeatures/NonValidTarget2-result.txt1
-rw-r--r--Tests/RunCMake/CompileFeatures/NonValidTarget2-stderr.txt9
-rw-r--r--Tests/RunCMake/CompileFeatures/NonValidTarget2.cmake8
-rw-r--r--Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake10
26 files changed, 539 insertions, 48 deletions
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index dfda8dc..9184580 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -83,6 +83,12 @@ otherwise expands to nothing.
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
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
=========================
diff --git a/Help/release/dev/compile-language-features.rst b/Help/release/dev/compile-language-features.rst
index fe72e39..d10e22b 100644
--- a/Help/release/dev/compile-language-features.rst
+++ b/Help/release/dev/compile-language-features.rst
@@ -22,3 +22,7 @@ target-language-features
* New :command:`target_compile_features` command allows populating the
:prop_tgt:`COMPILE_FEATURES` target property, just like any other
build variable.
+
+* New ``COMPILE_FEATURES``
+ :manual:`generator expression <cmake-generator-expressions(7)>` allows
+ setting build properties based on available compiler features.
diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx
index d09e950..d53bdd7 100644
--- a/Source/cmGeneratorExpression.cxx
+++ b/Source/cmGeneratorExpression.cxx
@@ -110,6 +110,9 @@ const char *cmCompiledGeneratorExpression::Evaluate(
break;
}
}
+
+ this->MaxLanguageStandard = context.MaxLanguageStandard;
+
if (!context.HadError)
{
this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
@@ -465,3 +468,17 @@ bool cmGeneratorExpression::IsValidTargetName(const std::string &input)
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;
+ }
+}
diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h
index da64515..ef5360e 100644
--- a/Source/cmGeneratorExpression.h
+++ b/Source/cmGeneratorExpression.h
@@ -117,6 +117,9 @@ public:
this->EvaluateForBuildsystem = eval;
}
+ void GetMaxLanguageStandard(cmTarget const* tgt,
+ std::map<std::string, std::string>& mapping);
+
private:
cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace,
const std::string& input);
@@ -134,6 +137,8 @@ private:
mutable std::set<cmTarget*> DependTargets;
mutable std::set<cmTarget const*> AllTargetsSeen;
mutable std::set<std::string> SeenTargetProperties;
+ mutable std::map<cmTarget const*, std::map<std::string, std::string> >
+ MaxLanguageStandard;
mutable std::string Output;
mutable bool HadContextSensitiveCondition;
bool EvaluateForBuildsystem;
diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx
index a513921..0b357f6 100644
--- a/Source/cmGeneratorExpressionEvaluator.cxx
+++ b/Source/cmGeneratorExpressionEvaluator.cxx
@@ -1314,6 +1314,94 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode
} targetObjectsNode;
//----------------------------------------------------------------------------
+static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
+{
+ CompileFeaturesNode() {}
+
+ virtual int NumExpectedParameters() const { return OneOrMoreParameters; }
+
+ std::string Evaluate(const std::vector<std::string> &parameters,
+ 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[] = {
0
#define TARGET_POLICY_STRING(POLICY) \
@@ -1647,6 +1735,7 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier)
nodeMap["C_COMPILER_VERSION"] = &cCompilerVersionNode;
nodeMap["CXX_COMPILER_VERSION"] = &cxxCompilerVersionNode;
nodeMap["PLATFORM_ID"] = &platformIdNode;
+ nodeMap["COMPILE_FEATURES"] = &compileFeaturesNode;
nodeMap["CONFIGURATION"] = &configurationNode;
nodeMap["CONFIG"] = &configurationTestNode;
nodeMap["TARGET_FILE"] = &targetFileNode;
diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h
index 54a2548..eb76d7f 100644
--- a/Source/cmGeneratorExpressionEvaluator.h
+++ b/Source/cmGeneratorExpressionEvaluator.h
@@ -26,6 +26,8 @@ struct cmGeneratorExpressionContext
std::set<cmTarget*> DependTargets;
std::set<cmTarget const*> AllTargets;
std::set<std::string> SeenTargetProperties;
+ std::map<cmTarget const*, std::map<std::string, std::string> >
+ MaxLanguageStandard;
cmMakefile *Makefile;
std::string Config;
cmTarget const* HeadTarget; // The target whose property is being evaluated.
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index a6ad714..5d58265 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -1484,6 +1484,31 @@ void cmLocalGenerator::AddCompileOptions(
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);
}
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 42dedc9..9f33b92 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -5004,38 +5004,91 @@ AddRequiredTargetFeature(cmTarget *target, const std::string& feature,
target->AppendProperty("COMPILE_FEATURES", feature.c_str());
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,
cmArrayEnd(C_FEATURES), cmStrCmp(feature))
!= cmArrayEnd(C_FEATURES);
+ if (isCFeature)
+ {
+ lang = "C";
+ return true;
+ }
bool isCxxFeature = std::find_if(cmArrayBegin(CXX_FEATURES) + 1,
cmArrayEnd(CXX_FEATURES), cmStrCmp(feature))
!= cmArrayEnd(CXX_FEATURES);
- if (!isCFeature && !isCxxFeature)
+ if (isCxxFeature)
{
- cmOStringStream e;
- if (error)
- {
- e << "specified";
- }
- else
- {
- e << "Specified";
- }
- e << " unknown feature \"" << feature << "\" for "
- "target \"" << target->GetName() << "\".";
- if (error)
- {
- *error = e.str();
- }
- else
- {
- this->IssueMessage(cmake::FATAL_ERROR, e.str());
- }
- return false;
+ lang = "CXX";
+ return true;
}
+ cmOStringStream e;
+ if (error)
+ {
+ e << "specified";
+ }
+ else
+ {
+ e << "Specified";
+ }
+ e << " unknown feature \"" << feature << "\" for "
+ "target \"" << target->GetName() << "\".";
+ if (error)
+ {
+ *error = e.str();
+ }
+ else
+ {
+ this->IssueMessage(cmake::FATAL_ERROR, e.str());
+ }
+ return false;
+}
- std::string lang = isCFeature ? "C" : "CXX";
-
+//----------------------------------------------------------------------------
+const char* cmMakefile::
+CompileFeaturesAvailable(const std::string& lang, std::string *error) const
+{
const char* featuresKnown =
this->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
@@ -5062,40 +5115,150 @@ AddRequiredTargetFeature(cmTarget *target, const std::string& feature,
{
this->IssueMessage(cmake::FATAL_ERROR, e.str());
}
- return false;
+ return 0;
}
+ return featuresKnown;
+}
- std::vector<std::string> availableFeatures;
- cmSystemTools::ExpandListArgument(featuresKnown, availableFeatures);
- if (std::find(availableFeatures.begin(),
- availableFeatures.end(),
- feature) == availableFeatures.end())
+//----------------------------------------------------------------------------
+bool cmMakefile::HaveFeatureAvailable(cmTarget const* target,
+ std::string const& lang,
+ const std::string& feature) const
+{
+ return lang == "C"
+ ? this->HaveCFeatureAvailable(target, feature)
+ : this->HaveCxxFeatureAvailable(target, feature);
+}
+
+//----------------------------------------------------------------------------
+bool cmMakefile::
+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 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());
+ 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;
}
- target->AppendProperty("COMPILE_FEATURES", feature.c_str());
+ const char * const *existingCIt = existingCStandard
+ ? std::find_if(cmArrayBegin(C_STANDARDS),
+ cmArrayEnd(C_STANDARDS),
+ cmStrCmp(existingCStandard))
+ : cmArrayEnd(C_STANDARDS);
- return isCFeature
- ? this->AddRequiredTargetCFeature(target, feature)
- : this->AddRequiredTargetCxxFeature(target, feature);
+ 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::
-AddRequiredTargetCxxFeature(cmTarget *target,
- const std::string& feature) const
+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
{
bool needCxx98 = 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 =
this->GetDefinition("CMAKE_CXX98_COMPILE_FEATURES"))
{
@@ -5110,6 +5273,17 @@ AddRequiredTargetCxxFeature(cmTarget *target,
cmSystemTools::ExpandListArgument(propCxx11, props);
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");
if (existingCxxStandard)
@@ -5160,13 +5334,11 @@ AddRequiredTargetCxxFeature(cmTarget *target,
}
//----------------------------------------------------------------------------
-bool cmMakefile::
-AddRequiredTargetCFeature(cmTarget *target, const std::string& feature) const
+void cmMakefile::CheckNeededCLanguage(const std::string& feature,
+ bool& needC90,
+ bool& needC99,
+ bool& needC11) const
{
- bool needC90 = false;
- bool needC99 = false;
- bool needC11 = false;
-
if (const char *propC90 =
this->GetDefinition("CMAKE_C90_COMPILE_FEATURES"))
{
@@ -5188,6 +5360,17 @@ AddRequiredTargetCFeature(cmTarget *target, const std::string& feature) const
cmSystemTools::ExpandListArgument(propC11, props);
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");
if (existingCStandard)
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 90e2e19..11904a6 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -889,6 +889,19 @@ public:
const std::string& feature,
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 StoreMatches(cmsys::RegularExpression& re);
@@ -1104,6 +1117,16 @@ private:
bool AddRequiredTargetCxxFeature(cmTarget *target,
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;
};
//----------------------------------------------------------------------------
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 786021e..a961ddc 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -1229,6 +1229,7 @@ void cmTarget::GetDirectLinkLibraries(const std::string& config,
this->LinkImplicitNullProperties.insert(*it);
}
}
+ cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards);
}
}
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index bee6b34..45d1bd6 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -587,6 +587,12 @@ public:
const std::string &report,
const std::string &compatibilityType) const;
+ std::map<std::string, std::string> const&
+ GetMaxLanguageStandards() const
+ {
+ return this->MaxLanguageStandards;
+ }
+
private:
bool HandleLocationPropertyPolicy(cmMakefile* context) const;
@@ -718,6 +724,7 @@ private:
mutable bool DebugSourcesDone;
mutable bool DebugCompileFeaturesDone;
mutable std::set<std::string> LinkImplicitNullProperties;
+ mutable std::map<std::string, std::string> MaxLanguageStandards;
bool BuildInterfaceIncludesAppended;
// Cache target output paths for each configuration.
diff --git a/Tests/CompileFeatures/CMakeLists.txt b/Tests/CompileFeatures/CMakeLists.txt
index 0e1e6c9..7a8a975 100644
--- a/Tests/CompileFeatures/CMakeLists.txt
+++ b/Tests/CompileFeatures/CMakeLists.txt
@@ -83,3 +83,17 @@ set_property(TARGET iface
)
add_executable(IfaceCompileFeatures main.cpp)
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>)
diff --git a/Tests/CompileFeatures/genex_test.cpp b/Tests/CompileFeatures/genex_test.cpp
new file mode 100644
index 0000000..ca38883
--- /dev/null
+++ b/Tests/CompileFeatures/genex_test.cpp
@@ -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()
+{
+
+}
diff --git a/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle-result.txt b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle-stderr.txt b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle-stderr.txt
new file mode 100644
index 0000000..a584d7d
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle-stderr.txt
@@ -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.
diff --git a/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle.cmake b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle.cmake
new file mode 100644
index 0000000..9d56bc0
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycle.cmake
@@ -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
+)
diff --git a/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved-result.txt b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved-stderr.txt b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved-stderr.txt
new file mode 100644
index 0000000..10f3293
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved-stderr.txt
@@ -0,0 +1 @@
+^$
diff --git a/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved.cmake b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved.cmake
new file mode 100644
index 0000000..0df548b
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/LinkImplementationFeatureCycleSolved.cmake
@@ -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)
diff --git a/Tests/RunCMake/CompileFeatures/NonValidTarget1-result.txt b/Tests/RunCMake/CompileFeatures/NonValidTarget1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/NonValidTarget1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CompileFeatures/NonValidTarget1-stderr.txt b/Tests/RunCMake/CompileFeatures/NonValidTarget1-stderr.txt
new file mode 100644
index 0000000..7f3b43b
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/NonValidTarget1-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/CompileFeatures/NonValidTarget1.cmake b/Tests/RunCMake/CompileFeatures/NonValidTarget1.cmake
new file mode 100644
index 0000000..c6707c1
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/NonValidTarget1.cmake
@@ -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()
diff --git a/Tests/RunCMake/CompileFeatures/NonValidTarget2-result.txt b/Tests/RunCMake/CompileFeatures/NonValidTarget2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/NonValidTarget2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CompileFeatures/NonValidTarget2-stderr.txt b/Tests/RunCMake/CompileFeatures/NonValidTarget2-stderr.txt
new file mode 100644
index 0000000..635150c
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/NonValidTarget2-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/CompileFeatures/NonValidTarget2.cmake b/Tests/RunCMake/CompileFeatures/NonValidTarget2.cmake
new file mode 100644
index 0000000..eb84692
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/NonValidTarget2.cmake
@@ -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")
diff --git a/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake b/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake
index a23d44f..1892a5c 100644
--- a/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake
@@ -26,6 +26,16 @@ endif()
if (NOT CXX_FEATURES)
run_cmake(NoSupportedCxxFeatures)
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()
foreach(standard 98 11)