Browse Source

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.
Stephen Kelly 12 years ago
parent
commit
5412deded1

+ 1 - 0
Help/manual/cmake-properties.7.rst

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

+ 14 - 0
Help/prop_tgt/INTERFACE_COMPILE_FEATURES.rst

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

+ 1 - 1
Help/variable/CMAKE_DEBUG_TARGET_PROPERTIES.rst

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

+ 2 - 1
Source/cmGeneratorExpressionDAGChecker.h

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

+ 1 - 1
Source/cmLocalGenerator.cxx

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

+ 111 - 10
Source/cmTarget.cxx

@@ -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);
-  for(std::vector<cmTargetInternals::TargetPropertyEntry*>::const_iterator
-      si = this->Internal->CompileFeaturesEntries.begin();
-      si != this->Internal->CompileFeaturesEntries.end(); ++si)
+  processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
+                                dagChecker, config, debugOptions, "features");
+}
+
+//----------------------------------------------------------------------------
+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,
-                                        "",
-                                        false,
-                                        this),
-                                      features);
+    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+    }
+
+  bool debugFeatures = !this->DebugCompileFeaturesDone
+                    && 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;
     }
     }
 }
 }
 
 

+ 3 - 1
Source/cmTarget.h

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

+ 7 - 0
Tests/CompileFeatures/CMakeLists.txt

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

+ 1 - 0
Tests/RunCMake/CompileFeatures/NotAFeatureTransitive-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/CompileFeatures/NotAFeatureTransitive-stderr.txt

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

+ 6 - 0
Tests/RunCMake/CompileFeatures/NotAFeatureTransitive.cmake

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

+ 1 - 0
Tests/RunCMake/CompileFeatures/NotAFeature_OriginDebug-result.txt

@@ -0,0 +1 @@
+1

+ 11 - 0
Tests/RunCMake/CompileFeatures/NotAFeature_OriginDebug-stderr.txt

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

+ 4 - 0
Tests/RunCMake/CompileFeatures/NotAFeature_OriginDebug.cmake

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

+ 1 - 0
Tests/RunCMake/CompileFeatures/NotAFeature_OriginDebugGenex-result.txt

@@ -0,0 +1 @@
+1

+ 11 - 0
Tests/RunCMake/CompileFeatures/NotAFeature_OriginDebugGenex-stderr.txt

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

+ 4 - 0
Tests/RunCMake/CompileFeatures/NotAFeature_OriginDebugGenex.cmake

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

+ 1 - 0
Tests/RunCMake/CompileFeatures/NotAFeature_OriginDebugTransitive-result.txt

@@ -0,0 +1 @@
+1

+ 11 - 0
Tests/RunCMake/CompileFeatures/NotAFeature_OriginDebugTransitive-stderr.txt

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

+ 6 - 0
Tests/RunCMake/CompileFeatures/NotAFeature_OriginDebugTransitive.cmake

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

+ 4 - 0
Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake

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