diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 9ea50de35..30413ae60 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -98,6 +98,7 @@ Properties on Targets /prop_tgt/COMPATIBLE_INTERFACE_STRING /prop_tgt/COMPILE_DEFINITIONS_CONFIG /prop_tgt/COMPILE_DEFINITIONS + /prop_tgt/COMPILE_FEATURES /prop_tgt/COMPILE_FLAGS /prop_tgt/COMPILE_OPTIONS /prop_tgt/COMPILE_PDB_NAME diff --git a/Help/prop_tgt/COMPILE_FEATURES.rst b/Help/prop_tgt/COMPILE_FEATURES.rst new file mode 100644 index 000000000..b2c614565 --- /dev/null +++ b/Help/prop_tgt/COMPILE_FEATURES.rst @@ -0,0 +1,7 @@ +COMPILE_FEATURES +---------------- + +Compiler features enabled for this target. + +The list of features in this property are a subset of the features listed +in the :variable:`CMAKE_CXX_COMPILE_FEATURES` variable. diff --git a/Help/release/dev/compile-language-features.rst b/Help/release/dev/compile-language-features.rst index f03e1a38e..74fe209fe 100644 --- a/Help/release/dev/compile-language-features.rst +++ b/Help/release/dev/compile-language-features.rst @@ -6,3 +6,9 @@ target-language-features compile options such as ``-std=c++11`` or ``-std=gnu++11``. The :variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_EXTENSIONS` variables may be set to initialize the target properties. + +* New :prop_tgt:`COMPILE_FEATURES` target property may contain a list + of features required to compile a target. CMake uses this + information to ensure that the compiler in use is capable of building + the target, and to add any necessary compile flags to support language + features. diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 779a2b8d9..0f8e7dcb4 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1459,6 +1459,19 @@ void cmLocalGenerator::AddCompileOptions( this->AppendFlagEscape(flags, *i); } } + if (const char* featureProp = target->GetProperty("COMPILE_FEATURES")) + { + std::vector features; + cmSystemTools::ExpandListArgument(featureProp, features); + for(std::vector::const_iterator it = features.begin(); + it != features.end(); ++it) + { + if (!this->Makefile->AddRequiredTargetFeature(target, *it)) + { + return; + } + } + } this->AddCompilerRequirementFlag(flags, target, lang); } diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 132897401..64163de17 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -41,6 +41,8 @@ #include // for isspace #include +#define FOR_EACH_CXX_FEATURE(F) + class cmMakefile::Internals { public: @@ -4494,3 +4496,114 @@ void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm) pm[pid] = this->GetPolicyStatus(pid); } } + +#define FEATURE_STRING(F) , #F + +static const char * const CXX_FEATURES[] = { + 0 + FOR_EACH_CXX_FEATURE(FEATURE_STRING) +}; + +static const char * const CXX_STANDARDS[] = { + "98" + , "11" +}; + +//---------------------------------------------------------------------------- +bool cmMakefile:: +AddRequiredTargetFeature(cmTarget *target, const std::string& feature, + std::string *error) const +{ + bool isCxxFeature = std::find_if(cmArrayBegin(CXX_FEATURES) + 1, + cmArrayEnd(CXX_FEATURES), cmStrCmp(feature)) + != cmArrayEnd(CXX_FEATURES); + if (!isCxxFeature) + { + cmOStringStream e; + e << "specified unknown feature \"" << feature << "\" specified for " + "target \"" << target->GetName() << "\"."; + if (error) + { + *error = e.str(); + } + else + { + this->IssueMessage(cmake::FATAL_ERROR, e.str()); + } + return false; + } + + std::string lang = "CXX"; + + const char* featuresKnown = + this->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES"); + + if (!featuresKnown || !*featuresKnown) + { + // We know of no features for the compiler at all. + return true; + } + + std::vector availableFeatures; + cmSystemTools::ExpandListArgument(featuresKnown, availableFeatures); + if (std::find(availableFeatures.begin(), + availableFeatures.end(), + feature) == availableFeatures.end()) + { + cmOStringStream e; + e << "The compiler feature \"" << feature + << "\" is not known to 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()); + + bool needCxx11 = false; + + if (const char *propCxx11 = + this->GetDefinition("CMAKE_CXX11_COMPILE_FEATURES")) + { + std::vector props; + cmSystemTools::ExpandListArgument(propCxx11, props); + needCxx11 = std::find(props.begin(), props.end(), feature) != props.end(); + } + + const char *existingCxxStandard = target->GetProperty("CXX_STANDARD"); + if (existingCxxStandard) + { + 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); + + bool setCxx11 = needCxx11 && !existingCxxStandard; + + if (needCxx11 && existingCxxStandard && existingCxxIt < + std::find_if(cmArrayBegin(CXX_STANDARDS), + cmArrayEnd(CXX_STANDARDS), + cmStrCmp("11"))) + { + setCxx11 = true; + } + + if (setCxx11) + { + target->SetProperty("CXX_STANDARD", "11"); + } + return true; +} diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 7695d6e28..3bccb63e6 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -885,6 +885,10 @@ public: bool PolicyOptionalWarningEnabled(std::string const& var); + bool AddRequiredTargetFeature(cmTarget *target, + const std::string& feature, + std::string *error = 0) const; + protected: // add link libraries and directories to the target void AddGlobalLinkInformation(const std::string& name, cmTarget& target); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 53c0205d2..6f53009bb 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -153,6 +153,7 @@ public: }; std::vector IncludeDirectoriesEntries; std::vector CompileOptionsEntries; + std::vector CompileFeaturesEntries; std::vector CompileDefinitionsEntries; std::vector SourceEntries; std::vector LinkImplementationPropertyEntries; @@ -1722,6 +1723,17 @@ void cmTarget::SetProperty(const std::string& prop, const char* value) new cmTargetInternals::TargetPropertyEntry(cge)); return; } + if(prop == "COMPILE_FEATURES") + { + cmListFileBacktrace lfbt; + this->Makefile->GetBacktrace(lfbt); + cmGeneratorExpression ge(lfbt); + deleteAndClear(this->Internal->CompileFeaturesEntries); + cmsys::auto_ptr cge = ge.Parse(value); + this->Internal->CompileFeaturesEntries.push_back( + new cmTargetInternals::TargetPropertyEntry(cge)); + return; + } if(prop == "COMPILE_DEFINITIONS") { cmListFileBacktrace lfbt; @@ -1812,6 +1824,15 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value, new cmTargetInternals::TargetPropertyEntry(ge.Parse(value))); return; } + if(prop == "COMPILE_FEATURES") + { + cmListFileBacktrace lfbt; + this->Makefile->GetBacktrace(lfbt); + cmGeneratorExpression ge(lfbt); + this->Internal->CompileFeaturesEntries.push_back( + new cmTargetInternals::TargetPropertyEntry(ge.Parse(value))); + return; + } if(prop == "COMPILE_DEFINITIONS") { cmListFileBacktrace lfbt; @@ -3109,6 +3130,24 @@ const char *cmTarget::GetProperty(const std::string& prop, } return output.c_str(); } + if(prop == "COMPILE_FEATURES") + { + static std::string output; + output = ""; + std::string sep; + typedef cmTargetInternals::TargetPropertyEntry + TargetPropertyEntry; + for (std::vector::const_iterator + it = this->Internal->CompileFeaturesEntries.begin(), + end = this->Internal->CompileFeaturesEntries.end(); + it != end; ++it) + { + output += sep; + output += (*it)->ge->GetInput(); + sep = ";"; + } + return output.c_str(); + } if(prop == "COMPILE_DEFINITIONS") { static std::string output; @@ -6886,6 +6925,7 @@ cmTargetInternalPointer::~cmTargetInternalPointer() { deleteAndClear(this->Pointer->IncludeDirectoriesEntries); deleteAndClear(this->Pointer->CompileOptionsEntries); + deleteAndClear(this->Pointer->CompileFeaturesEntries); deleteAndClear(this->Pointer->CompileDefinitionsEntries); deleteAndClear(this->Pointer->SourceEntries); delete this->Pointer;