cmTarget: Transitively evaluate compiler features.

Extend the interface of the target_compile_features command with
PUBLIC and INTERFACE keywords. Populate the INTERFACE_COMPILER_FEATURES
target property if they are set. Consume the INTERFACE_COMPILER_FEATURES
target property from linked dependent targets to determine the final
required compiler features and the compile flag, if needed.

Use the same pattern of origin-debugging which is used for other
build properties.
This commit is contained in:
Stephen Kelly 2013-10-22 01:40:47 +02:00
parent baff44345c
commit 5412deded1
21 changed files with 203 additions and 14 deletions

View File

@ -151,6 +151,7 @@ Properties on Targets
/prop_tgt/INSTALL_RPATH_USE_LINK_PATH /prop_tgt/INSTALL_RPATH_USE_LINK_PATH
/prop_tgt/INTERFACE_AUTOUIC_OPTIONS /prop_tgt/INTERFACE_AUTOUIC_OPTIONS
/prop_tgt/INTERFACE_COMPILE_DEFINITIONS /prop_tgt/INTERFACE_COMPILE_DEFINITIONS
/prop_tgt/INTERFACE_COMPILE_FEATURES
/prop_tgt/INTERFACE_COMPILE_OPTIONS /prop_tgt/INTERFACE_COMPILE_OPTIONS
/prop_tgt/INTERFACE_INCLUDE_DIRECTORIES /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES
/prop_tgt/INTERFACE_LINK_LIBRARIES /prop_tgt/INTERFACE_LINK_LIBRARIES

View File

@ -0,0 +1,14 @@
INTERFACE_COMPILE_FEATURES
--------------------------
List of public compile requirements for a library.
Targets may populate this property to publish the compiler features
required to compile against the headers for the target. Consuming
targets can add entries to their own :prop_tgt:`COMPILE_FEATURES`
property such as ``$<TARGET_PROPERTY:foo,INTERFACE_COMPILE_FEATURES>``
to require the features specified in the interface of ``foo``.
Contents of ``INTERFACE_COMPILE_FEATURES`` may use "generator expressions"
with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)`
manual for available expressions.

View File

@ -7,7 +7,7 @@ This variable can be populated with a list of properties to generate
debug output for when evaluating target properties. Currently it can debug output for when evaluating target properties. Currently it can
only be used when evaluating the :prop_tgt:`INCLUDE_DIRECTORIES`, only be used when evaluating the :prop_tgt:`INCLUDE_DIRECTORIES`,
:prop_tgt:`COMPILE_DEFINITIONS`, :prop_tgt:`COMPILE_OPTIONS`, :prop_tgt:`COMPILE_DEFINITIONS`, :prop_tgt:`COMPILE_OPTIONS`,
:prop_tgt:`AUTOUIC_OPTIONS`, :prop_tgt:`SOURCES`, :prop_tgt:`AUTOUIC_OPTIONS`, :prop_tgt:`SOURCES`, :prop_tgt:`COMPILE_FEATURES`,
:prop_tgt:`POSITION_INDEPENDENT_CODE` target properties and any other property :prop_tgt:`POSITION_INDEPENDENT_CODE` target properties and any other property
listed in :prop_tgt:`COMPATIBLE_INTERFACE_STRING` and other ``COMPATIBLE_INTERFACE_`` listed in :prop_tgt:`COMPATIBLE_INTERFACE_STRING` and other ``COMPATIBLE_INTERFACE_``
properties. It outputs an origin for each entry in the target property. properties. It outputs an origin for each entry in the target property.

View File

@ -26,7 +26,8 @@
SELECT(F, EvaluatingCompileDefinitions, COMPILE_DEFINITIONS) \ SELECT(F, EvaluatingCompileDefinitions, COMPILE_DEFINITIONS) \
SELECT(F, EvaluatingCompileOptions, COMPILE_OPTIONS) \ SELECT(F, EvaluatingCompileOptions, COMPILE_OPTIONS) \
SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS) \ SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS) \
SELECT(F, EvaluatingSources, SOURCES) SELECT(F, EvaluatingSources, SOURCES) \
SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES)
#define CM_FOR_EACH_TRANSITIVE_PROPERTY(F) \ #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F) \
CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH) CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)

View File

@ -1460,7 +1460,7 @@ void cmLocalGenerator::AddCompileOptions(
} }
} }
std::vector<std::string> features; std::vector<std::string> features;
target->GetCompileFeatures(features); target->GetCompileFeatures(features, config);
for(std::vector<std::string>::const_iterator it = features.begin(); for(std::vector<std::string>::const_iterator it = features.begin();
it != features.end(); ++it) it != features.end(); ++it)
{ {

View File

@ -166,11 +166,14 @@ public:
CachedLinkInterfaceCompileDefinitionsEntries; CachedLinkInterfaceCompileDefinitionsEntries;
mutable std::map<std::string, std::vector<TargetPropertyEntry*> > mutable std::map<std::string, std::vector<TargetPropertyEntry*> >
CachedLinkInterfaceSourcesEntries; CachedLinkInterfaceSourcesEntries;
mutable std::map<std::string, std::vector<TargetPropertyEntry*> >
CachedLinkInterfaceCompileFeaturesEntries;
mutable std::map<std::string, bool> CacheLinkInterfaceIncludeDirectoriesDone; mutable std::map<std::string, bool> CacheLinkInterfaceIncludeDirectoriesDone;
mutable std::map<std::string, bool> CacheLinkInterfaceCompileDefinitionsDone; mutable std::map<std::string, bool> CacheLinkInterfaceCompileDefinitionsDone;
mutable std::map<std::string, bool> CacheLinkInterfaceCompileOptionsDone; mutable std::map<std::string, bool> CacheLinkInterfaceCompileOptionsDone;
mutable std::map<std::string, bool> CacheLinkInterfaceSourcesDone; mutable std::map<std::string, bool> CacheLinkInterfaceSourcesDone;
mutable std::map<std::string, bool> CacheLinkInterfaceCompileFeaturesDone;
}; };
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -205,6 +208,7 @@ cmTargetInternals::~cmTargetInternals()
{ {
deleteAndClear(this->CachedLinkInterfaceIncludeDirectoriesEntries); deleteAndClear(this->CachedLinkInterfaceIncludeDirectoriesEntries);
deleteAndClear(this->CachedLinkInterfaceCompileOptionsEntries); deleteAndClear(this->CachedLinkInterfaceCompileOptionsEntries);
deleteAndClear(this->CachedLinkInterfaceCompileFeaturesEntries);
deleteAndClear(this->CachedLinkInterfaceCompileDefinitionsEntries); deleteAndClear(this->CachedLinkInterfaceCompileDefinitionsEntries);
deleteAndClear(this->CachedLinkInterfaceSourcesEntries); deleteAndClear(this->CachedLinkInterfaceSourcesEntries);
} }
@ -228,6 +232,7 @@ cmTarget::cmTarget()
this->BuildInterfaceIncludesAppended = false; this->BuildInterfaceIncludesAppended = false;
this->DebugIncludesDone = false; this->DebugIncludesDone = false;
this->DebugCompileOptionsDone = false; this->DebugCompileOptionsDone = false;
this->DebugCompileFeaturesDone = false;
this->DebugCompileDefinitionsDone = false; this->DebugCompileDefinitionsDone = false;
this->DebugSourcesDone = false; this->DebugSourcesDone = false;
} }
@ -2617,18 +2622,114 @@ void cmTarget::GetCompileDefinitions(std::vector<std::string> &list,
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmTarget::GetCompileFeatures(std::vector<std::string> &features) const static void processCompileFeatures(cmTarget const* tgt,
const std::vector<cmTargetInternals::TargetPropertyEntry*> &entries,
std::vector<std::string> &options,
std::set<std::string> &uniqueOptions,
cmGeneratorExpressionDAGChecker *dagChecker,
const std::string& config, bool debugOptions)
{ {
assert(this->GetType() != INTERFACE_LIBRARY); processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
for(std::vector<cmTargetInternals::TargetPropertyEntry*>::const_iterator dagChecker, config, debugOptions, "features");
si = this->Internal->CompileFeaturesEntries.begin(); }
si != this->Internal->CompileFeaturesEntries.end(); ++si)
//----------------------------------------------------------------------------
void cmTarget::GetCompileFeatures(std::vector<std::string> &result,
const std::string& config) const
{
std::set<std::string> uniqueFeatures;
cmListFileBacktrace lfbt;
cmGeneratorExpressionDAGChecker dagChecker(lfbt,
this->GetName(),
"COMPILE_FEATURES",
0, 0);
std::vector<std::string> debugProperties;
const char *debugProp =
this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
if (debugProp)
{ {
cmSystemTools::ExpandListArgument((*si)->ge->Evaluate(this->Makefile, cmSystemTools::ExpandListArgument(debugProp, debugProperties);
"", }
false,
this), bool debugFeatures = !this->DebugCompileFeaturesDone
features); && std::find(debugProperties.begin(),
debugProperties.end(),
"COMPILE_FEATURES")
!= debugProperties.end();
if (this->Makefile->IsGeneratingBuildSystem())
{
this->DebugCompileFeaturesDone = true;
}
processCompileFeatures(this,
this->Internal->CompileFeaturesEntries,
result,
uniqueFeatures,
&dagChecker,
config,
debugFeatures);
if (!this->Internal->CacheLinkInterfaceCompileFeaturesDone[config])
{
for (std::vector<cmValueWithOrigin>::const_iterator
it = this->Internal->LinkImplementationPropertyEntries.begin(),
end = this->Internal->LinkImplementationPropertyEntries.end();
it != end; ++it)
{
if (!cmGeneratorExpression::IsValidTargetName(it->Value)
&& cmGeneratorExpression::Find(it->Value) == std::string::npos)
{
continue;
}
{
cmGeneratorExpression ge(lfbt);
cmsys::auto_ptr<cmCompiledGeneratorExpression> cge =
ge.Parse(it->Value);
std::string targetResult = cge->Evaluate(this->Makefile, config,
false, this, 0, 0);
if (!this->Makefile->FindTargetToUse(targetResult))
{
continue;
}
}
std::string featureGenex = "$<TARGET_PROPERTY:" +
it->Value + ",INTERFACE_COMPILE_FEATURES>";
if (cmGeneratorExpression::Find(it->Value) != std::string::npos)
{
// Because it->Value is a generator expression, ensure that it
// evaluates to the non-empty string before being used in the
// TARGET_PROPERTY expression.
featureGenex = "$<$<BOOL:" + it->Value + ">:" + featureGenex + ">";
}
cmGeneratorExpression ge(it->Backtrace);
cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(
featureGenex);
this->Internal
->CachedLinkInterfaceCompileFeaturesEntries[config].push_back(
new cmTargetInternals::TargetPropertyEntry(cge,
it->Value));
}
}
processCompileFeatures(this,
this->Internal->CachedLinkInterfaceCompileFeaturesEntries[config],
result,
uniqueFeatures,
&dagChecker,
config,
debugFeatures);
if (!this->Makefile->IsGeneratingBuildSystem())
{
deleteAndClear(this->Internal->CachedLinkInterfaceCompileFeaturesEntries);
}
else
{
this->Internal->CacheLinkInterfaceCompileFeaturesDone[config] = true;
} }
} }

View File

@ -545,7 +545,8 @@ public:
const std::string& config) const; const std::string& config) const;
void GetAutoUicOptions(std::vector<std::string> &result, void GetAutoUicOptions(std::vector<std::string> &result,
const std::string& config) const; const std::string& config) const;
void GetCompileFeatures(std::vector<std::string> &features) const; void GetCompileFeatures(std::vector<std::string> &features,
const std::string& config) const;
bool IsNullImpliedByLinkLibraries(const std::string &p) const; bool IsNullImpliedByLinkLibraries(const std::string &p) const;
bool IsLinkInterfaceDependentBoolProperty(const std::string &p, bool IsLinkInterfaceDependentBoolProperty(const std::string &p,
@ -712,6 +713,7 @@ private:
mutable bool DebugCompileOptionsDone; mutable bool DebugCompileOptionsDone;
mutable bool DebugCompileDefinitionsDone; mutable bool DebugCompileDefinitionsDone;
mutable bool DebugSourcesDone; mutable bool DebugSourcesDone;
mutable bool DebugCompileFeaturesDone;
mutable std::set<std::string> LinkImplicitNullProperties; mutable std::set<std::string> LinkImplicitNullProperties;
bool BuildInterfaceIncludesAppended; bool BuildInterfaceIncludesAppended;

View File

@ -27,3 +27,10 @@ add_executable(GenexCompileFeatures main.cpp)
set_property(TARGET GenexCompileFeatures set_property(TARGET GenexCompileFeatures
PROPERTY COMPILE_FEATURES "$<1:cxx_auto_type>;$<0:not_a_feature>" PROPERTY COMPILE_FEATURES "$<1:cxx_auto_type>;$<0:not_a_feature>"
) )
add_library(iface INTERFACE)
set_property(TARGET iface
PROPERTY INTERFACE_COMPILE_FEATURES "cxx_auto_type"
)
add_executable(IfaceCompileFeatures main.cpp)
target_link_libraries(IfaceCompileFeatures iface)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,2 @@
CMake Error in CMakeLists.txt:
Specified unknown feature "not_a_feature" for target "somelib".

View File

@ -0,0 +1,6 @@
add_library(iface INTERFACE)
set_property(TARGET iface PROPERTY INTERFACE_COMPILE_FEATURES "not_a_feature")
add_library(somelib STATIC empty.cpp)
target_link_libraries(somelib iface)

View File

@ -0,0 +1,11 @@
CMake Debug Log at NotAFeature_OriginDebug.cmake:4 \(set_property\):
Used compile features for target somelib:
\* not_a_feature
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
CMake Error in CMakeLists.txt:
Specified unknown feature "not_a_feature" for target "somelib".

View File

@ -0,0 +1,4 @@
set(CMAKE_DEBUG_TARGET_PROPERTIES COMPILE_FEATURES)
add_library(somelib STATIC empty.cpp)
set_property(TARGET somelib PROPERTY COMPILE_FEATURES "not_a_feature")

View File

@ -0,0 +1,11 @@
CMake Debug Log at NotAFeature_OriginDebugGenex.cmake:4 \(set_property\):
Used compile features for target somelib:
\* not_a_feature
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
CMake Error in CMakeLists.txt:
Specified unknown feature "not_a_feature" for target "somelib".

View File

@ -0,0 +1,4 @@
set(CMAKE_DEBUG_TARGET_PROPERTIES COMPILE_FEATURES)
add_library(somelib STATIC empty.cpp)
set_property(TARGET somelib PROPERTY COMPILE_FEATURES "$<1:not_a_feature>")

View File

@ -0,0 +1,11 @@
CMake Debug Log at NotAFeature_OriginDebugTransitive.cmake:6 \(target_link_libraries\):
Used compile features for target somelib:
\* not_a_feature
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
CMake Error in CMakeLists.txt:
Specified unknown feature "not_a_feature" for target "somelib".

View File

@ -0,0 +1,6 @@
set(CMAKE_DEBUG_TARGET_PROPERTIES COMPILE_FEATURES)
add_library(iface INTERFACE)
set_property(TARGET iface PROPERTY INTERFACE_COMPILE_FEATURES "not_a_feature")
add_library(somelib STATIC empty.cpp)
target_link_libraries(somelib iface)

View File

@ -2,3 +2,7 @@ include(RunCMake)
run_cmake(NotAFeature) run_cmake(NotAFeature)
run_cmake(NotAFeatureGenex) run_cmake(NotAFeatureGenex)
run_cmake(NotAFeatureTransitive)
run_cmake(NotAFeature_OriginDebug)
run_cmake(NotAFeature_OriginDebugGenex)
run_cmake(NotAFeature_OriginDebugTransitive)