cmTarget: Add COMPILE_FEATURES target property.

Use the contents of it to upgrade the CXX_STANDARD target property,
if appropriate.  This will have the effect of adding the -std=c++11
compile flag or other language specification on GNU when that is
needed for the feature.
This commit is contained in:
Stephen Kelly 2014-04-02 15:57:15 +02:00
parent faeddf64f2
commit 03355d6b5b
7 changed files with 184 additions and 0 deletions

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -1459,6 +1459,19 @@ void cmLocalGenerator::AddCompileOptions(
this->AppendFlagEscape(flags, *i);
}
}
if (const char* featureProp = target->GetProperty("COMPILE_FEATURES"))
{
std::vector<std::string> features;
cmSystemTools::ExpandListArgument(featureProp, features);
for(std::vector<std::string>::const_iterator it = features.begin();
it != features.end(); ++it)
{
if (!this->Makefile->AddRequiredTargetFeature(target, *it))
{
return;
}
}
}
this->AddCompilerRequirementFlag(flags, target, lang);
}

View File

@ -41,6 +41,8 @@
#include <ctype.h> // for isspace
#include <assert.h>
#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<std::string> 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<std::string> 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;
}

View File

@ -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);

View File

@ -153,6 +153,7 @@ public:
};
std::vector<TargetPropertyEntry*> IncludeDirectoriesEntries;
std::vector<TargetPropertyEntry*> CompileOptionsEntries;
std::vector<TargetPropertyEntry*> CompileFeaturesEntries;
std::vector<TargetPropertyEntry*> CompileDefinitionsEntries;
std::vector<TargetPropertyEntry*> SourceEntries;
std::vector<cmValueWithOrigin> 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<cmCompiledGeneratorExpression> 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<TargetPropertyEntry*>::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;