瀏覽代碼

cmTarget: Allow transitive evaluation of SOURCES property.

Extend the cmGeneratorExpressionDAGChecker with an interface
returning the name of the top target.  Use that to determine
when there is a DAG violation, as required by the RunCMake.Languages
tests.
Stephen Kelly 11 年之前
父節點
當前提交
3676fb4963

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

@@ -152,6 +152,7 @@ Properties on Targets
    /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES
    /prop_tgt/INTERFACE_LINK_LIBRARIES
    /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE
+   /prop_tgt/INTERFACE_SOURCES
    /prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
    /prop_tgt/INTERPROCEDURAL_OPTIMIZATION_CONFIG
    /prop_tgt/INTERPROCEDURAL_OPTIMIZATION

+ 15 - 0
Help/prop_tgt/INTERFACE_SOURCES.rst

@@ -0,0 +1,15 @@
+INTERFACE_SOURCES
+-----------------
+
+List of interface sources to pass to the compiler.
+
+Targets may populate this property to publish the sources
+for consuming targets to compile.  Consuming
+targets can add entries to their own :prop_tgt:`SOURCES` property
+such as ``$<TARGET_PROPERTY:foo,INTERFACE_SOURCES>`` to use the
+sources specified in the interface of ``foo``.
+
+Contents of ``INTERFACE_SOURCES`` may use "generator expressions"
+with the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`
+manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
+manual for more on defining buildsystem properties.

+ 5 - 0
Help/release/dev/target-INTERFACE_SOURCES.rst

@@ -0,0 +1,5 @@
+target-INTERFACE_SOURCES
+------------------------
+
+* A new :prop_tgt:`INTERFACE_SOURCES` target property was introduced. This is
+  consumed by dependent targets, which compile and link the listed sources.

+ 12 - 0
Source/cmGeneratorExpressionDAGChecker.cxx

@@ -179,6 +179,18 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(const char *tgt)
        || strcmp(prop, "INTERFACE_LINK_LIBRARIES") == 0;
 }
 
+std::string cmGeneratorExpressionDAGChecker::TopTarget() const
+{
+  const cmGeneratorExpressionDAGChecker *top = this;
+  const cmGeneratorExpressionDAGChecker *parent = this->Parent;
+  while (parent)
+    {
+    top = parent;
+    parent = parent->Parent;
+    }
+  return top->Target;
+}
+
 enum TransitiveProperty {
 #define DEFINE_ENUM_ENTRY(NAME) NAME,
   CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(DEFINE_ENUM_ENTRY)

+ 4 - 1
Source/cmGeneratorExpressionDAGChecker.h

@@ -25,7 +25,8 @@
   SELECT(F, EvaluatingSystemIncludeDirectories, SYSTEM_INCLUDE_DIRECTORIES) \
   SELECT(F, EvaluatingCompileDefinitions,       COMPILE_DEFINITIONS) \
   SELECT(F, EvaluatingCompileOptions,           COMPILE_OPTIONS) \
-  SELECT(F, EvaluatingAutoUicOptions,           AUTOUIC_OPTIONS)
+  SELECT(F, EvaluatingAutoUicOptions,           AUTOUIC_OPTIONS) \
+  SELECT(F, EvaluatingSources,                  SOURCES)
 
 #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F) \
   CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)
@@ -70,6 +71,8 @@ struct cmGeneratorExpressionDAGChecker
   void SetTransitivePropertiesOnly()
     { this->TransitivePropertiesOnly = true; }
 
+  std::string TopTarget() const;
+
 private:
   Result CheckGraph() const;
 

+ 5 - 2
Source/cmGeneratorExpressionEvaluator.cxx

@@ -985,7 +985,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
     if (propertyName == "LINKER_LANGUAGE")
       {
       if (target->LinkLanguagePropagatesToDependents() &&
-          dagCheckerParent && dagCheckerParent->EvaluatingLinkLibraries())
+          dagCheckerParent && (dagCheckerParent->EvaluatingLinkLibraries()
+            || dagCheckerParent->EvaluatingSources()))
         {
         reportError(context, content->GetOriginalExpression(),
             "LINKER_LANGUAGE target property can not be used while evaluating "
@@ -1569,7 +1570,9 @@ struct TargetFilesystemArtifact : public cmGeneratorExpressionNode
                   "Target \"" + name + "\" is not an executable or library.");
       return std::string();
       }
-    if (dagChecker && dagChecker->EvaluatingLinkLibraries(name.c_str()))
+    if (dagChecker && (dagChecker->EvaluatingLinkLibraries(name.c_str())
+        || (dagChecker->EvaluatingSources()
+          && name == dagChecker->TopTarget())))
       {
       ::reportError(context, content->GetOriginalExpression(),
                     "Expressions which require the linker language may not "

+ 156 - 35
Source/cmTarget.cxx

@@ -159,10 +159,13 @@ public:
                                 CachedLinkInterfaceCompileOptionsEntries;
   mutable std::map<std::string, std::vector<TargetPropertyEntry*> >
                                 CachedLinkInterfaceCompileDefinitionsEntries;
+  mutable std::map<std::string, std::vector<TargetPropertyEntry*> >
+                                CachedLinkInterfaceSourcesEntries;
 
   mutable std::map<std::string, bool> CacheLinkInterfaceIncludeDirectoriesDone;
   mutable std::map<std::string, bool> CacheLinkInterfaceCompileDefinitionsDone;
   mutable std::map<std::string, bool> CacheLinkInterfaceCompileOptionsDone;
+  mutable std::map<std::string, bool> CacheLinkInterfaceSourcesDone;
 };
 
 //----------------------------------------------------------------------------
@@ -198,6 +201,7 @@ cmTargetInternals::~cmTargetInternals()
   deleteAndClear(this->CachedLinkInterfaceIncludeDirectoriesEntries);
   deleteAndClear(this->CachedLinkInterfaceCompileOptionsEntries);
   deleteAndClear(this->CachedLinkInterfaceCompileDefinitionsEntries);
+  deleteAndClear(this->CachedLinkInterfaceSourcesEntries);
 }
 
 //----------------------------------------------------------------------------
@@ -543,43 +547,157 @@ bool cmTarget::IsBundleOnApple() const
 }
 
 //----------------------------------------------------------------------------
-void cmTarget::GetSourceFiles(std::vector<std::string> &files,
-                              const std::string& config) const
+static void processSources(cmTarget const* tgt,
+      const std::vector<cmTargetInternals::TargetPropertyEntry*> &entries,
+      std::vector<std::string> &srcs,
+      std::set<std::string> &uniqueSrcs,
+      cmGeneratorExpressionDAGChecker *dagChecker,
+      cmTarget const* head,
+      std::string const& config)
 {
-  assert(this->GetType() != INTERFACE_LIBRARY);
-  for(std::vector<cmTargetInternals::TargetPropertyEntry*>::const_iterator
-      si = this->Internal->SourceEntries.begin();
-      si != this->Internal->SourceEntries.end(); ++si)
-    {
-    std::vector<std::string> srcs;
-    cmSystemTools::ExpandListArgument((*si)->ge->Evaluate(this->Makefile,
-                                        config,
-                                        false,
-                                        this),
-                                      srcs);
+  cmMakefile *mf = tgt->GetMakefile();
 
-    for(std::vector<std::string>::const_iterator i = srcs.begin();
-        i != srcs.end(); ++i)
+  for (std::vector<cmTargetInternals::TargetPropertyEntry*>::const_iterator
+      it = entries.begin(), end = entries.end(); it != end; ++it)
+    {
+    bool cacheSources = false;
+    std::vector<std::string> entrySources = (*it)->CachedEntries;
+    if(entrySources.empty())
       {
-      std::string src = *i;
-      cmSourceFile* sf = this->Makefile->GetOrCreateSource(src);
-      std::string e;
-      src = sf->GetFullPath(&e);
-      if(src.empty())
+      cmSystemTools::ExpandListArgument((*it)->ge->Evaluate(mf,
+                                                config,
+                                                false,
+                                                head ? head : tgt,
+                                                tgt,
+                                                dagChecker),
+                                      entrySources);
+      if (mf->IsGeneratingBuildSystem()
+          && !(*it)->ge->GetHadContextSensitiveCondition())
+        {
+        cacheSources = true;
+        }
+
+      for(std::vector<std::string>::iterator i = entrySources.begin();
+          i != entrySources.end(); ++i)
         {
-        if(!e.empty())
+        std::string& src = *i;
+
+        cmSourceFile* sf = mf->GetOrCreateSource(src);
+        std::string e;
+        src = sf->GetFullPath(&e);
+        if(src.empty())
           {
-          cmake* cm = this->Makefile->GetCMakeInstance();
-          cm->IssueMessage(cmake::FATAL_ERROR, e,
-                          this->GetBacktrace());
+          if(!e.empty())
+            {
+            cmake* cm = mf->GetCMakeInstance();
+            cm->IssueMessage(cmake::FATAL_ERROR, e,
+                            tgt->GetBacktrace());
+            }
+          return;
           }
-        return;
         }
-      files.push_back(src);
+      if (cacheSources)
+        {
+        (*it)->CachedEntries = entrySources;
+        }
+      }
+    for(std::vector<std::string>::iterator
+          li = entrySources.begin(); li != entrySources.end(); ++li)
+      {
+      std::string src = *li;
+
+      if(uniqueSrcs.insert(src).second)
+        {
+        srcs.push_back(src);
+        }
       }
     }
 }
 
+//----------------------------------------------------------------------------
+void cmTarget::GetSourceFiles(std::vector<std::string> &files,
+                              const std::string& config,
+                              cmTarget const* head) const
+{
+  assert(this->GetType() != INTERFACE_LIBRARY);
+
+
+  cmListFileBacktrace lfbt;
+
+  cmGeneratorExpressionDAGChecker dagChecker(lfbt,
+                                              this->GetName(),
+                                              "SOURCES", 0, 0);
+
+  std::set<std::string> uniqueSrcs;
+  processSources(this,
+                 this->Internal->SourceEntries,
+                 files,
+                 uniqueSrcs,
+                 &dagChecker,
+                 head,
+                 config);
+
+  if (!this->Internal->CacheLinkInterfaceSourcesDone[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, &dagChecker);
+      if (!this->Makefile->FindTargetToUse(targetResult))
+        {
+        continue;
+        }
+      }
+      std::string sourceGenex = "$<TARGET_PROPERTY:" +
+                              it->Value + ",INTERFACE_SOURCES>";
+      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.
+        sourceGenex = "$<$<BOOL:" + it->Value + ">:" + sourceGenex + ">";
+        }
+      cmGeneratorExpression ge(it->Backtrace);
+      cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(
+                                                                sourceGenex);
+
+      this->Internal
+        ->CachedLinkInterfaceSourcesEntries[config].push_back(
+                        new cmTargetInternals::TargetPropertyEntry(cge,
+                                                              it->Value));
+      }
+    }
+
+  processSources(this,
+    this->Internal->CachedLinkInterfaceSourcesEntries[config],
+                            files,
+                            uniqueSrcs,
+                            &dagChecker,
+                            head,
+                            config);
+
+  if (!this->Makefile->IsGeneratingBuildSystem())
+    {
+    deleteAndClear(this->Internal->CachedLinkInterfaceSourcesEntries);
+    }
+  else
+    {
+    this->Internal->CacheLinkInterfaceSourcesDone[config] = true;
+    }
+}
+
 //----------------------------------------------------------------------------
 bool
 cmTarget::GetConfigCommonSourceFiles(std::vector<cmSourceFile*>& files) const
@@ -639,10 +757,11 @@ cmTarget::GetConfigCommonSourceFiles(std::vector<cmSourceFile*>& files) const
 
 //----------------------------------------------------------------------------
 void cmTarget::GetSourceFiles(std::vector<cmSourceFile*> &files,
-                              const std::string& config) const
+                              const std::string& config,
+                              cmTarget const* head) const
 {
   std::vector<std::string> srcs;
-  this->GetSourceFiles(srcs, config);
+  this->GetSourceFiles(srcs, config, head);
 
   std::set<cmSourceFile*> emitted;
 
@@ -5053,10 +5172,11 @@ bool cmTarget::IsLinkInterfaceDependentNumberMaxProperty(const std::string &p,
 
 //----------------------------------------------------------------------------
 void cmTarget::GetLanguages(std::set<std::string>& languages,
-                            const std::string& config) const
+                            const std::string& config,
+                            cmTarget const* head) const
 {
   std::vector<cmSourceFile*> sourceFiles;
-  this->GetSourceFiles(sourceFiles, config);
+  this->GetSourceFiles(sourceFiles, config, head);
   for(std::vector<cmSourceFile*>::const_iterator
         i = sourceFiles.begin(); i != sourceFiles.end(); ++i)
     {
@@ -5111,7 +5231,7 @@ void cmTarget::GetLanguages(std::set<std::string>& languages,
   for(std::vector<cmTarget*>::const_iterator
       i = objectLibraries.begin(); i != objectLibraries.end(); ++i)
     {
-    (*i)->GetLanguages(languages, config);
+    (*i)->GetLanguages(languages, config, head);
     }
 }
 
@@ -6050,7 +6170,7 @@ cmTarget::GetLinkImplementation(const std::string& config,
     // Compute the link implementation for this configuration.
     LinkImplementation impl;
     this->ComputeLinkImplementation(config, impl, head);
-    this->ComputeLinkImplementationLanguages(config, impl);
+    this->ComputeLinkImplementationLanguages(config, impl, head);
 
     // Store the information for this configuration.
     cmTargetInternals::LinkImplMapType::value_type entry(key, impl);
@@ -6058,7 +6178,7 @@ cmTarget::GetLinkImplementation(const std::string& config,
     }
   else if (i->second.Languages.empty())
     {
-    this->ComputeLinkImplementationLanguages(config, i->second);
+    this->ComputeLinkImplementationLanguages(config, i->second, head);
     }
 
   return &i->second;
@@ -6172,12 +6292,13 @@ void cmTarget::ComputeLinkImplementation(const std::string& config,
 //----------------------------------------------------------------------------
 void
 cmTarget::ComputeLinkImplementationLanguages(const std::string& config,
-                                             LinkImplementation& impl) const
+                                             LinkImplementation& impl,
+                                             cmTarget const* head) const
 {
   // This target needs runtime libraries for its source languages.
   std::set<std::string> languages;
   // Get languages used in our source files.
-  this->GetLanguages(languages, config);
+  this->GetLanguages(languages, config, head);
   // Copy the set of langauges to the link implementation.
   for(std::set<std::string>::iterator li = languages.begin();
       li != languages.end(); ++li)

+ 8 - 4
Source/cmTarget.h

@@ -136,9 +136,11 @@ public:
    * Get the list of the source files used by this target
    */
   void GetSourceFiles(std::vector<std::string> &files,
-                      const std::string& config) const;
+                      const std::string& config,
+                      cmTarget const* head = 0) const;
   void GetSourceFiles(std::vector<cmSourceFile*> &files,
-                      const std::string& config) const;
+                      const std::string& config,
+                      cmTarget const* head = 0) const;
   bool GetConfigCommonSourceFiles(std::vector<cmSourceFile*>& files) const;
 
   /**
@@ -469,7 +471,8 @@ public:
   // information to forward these property changes to the targets
   // until we have per-target object file properties.
   void GetLanguages(std::set<std::string>& languages,
-                    const std::string& config) const;
+                    std::string const& config,
+                    cmTarget const* head = 0) const;
 
   /** Return whether this target is an executable with symbol exports
       enabled.  */
@@ -743,7 +746,8 @@ private:
                                  LinkImplementation& impl,
                                  cmTarget const* head) const;
   void ComputeLinkImplementationLanguages(const std::string& config,
-                                          LinkImplementation& impl) const;
+                                          LinkImplementation& impl,
+                                          cmTarget const* head) const;
   void ComputeLinkClosure(const std::string& config, LinkClosure& lc,
                           cmTarget const* head) const;
 

+ 1 - 0
Tests/CMakeLists.txt

@@ -282,6 +282,7 @@ if(BUILD_TESTING)
     set(ConfigSources_BUILD_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
     ADD_TEST_MACRO(ConfigSources ConfigSources)
   endif()
+  ADD_TEST_MACRO(SourcesProperty SourcesProperty)
   set_tests_properties(EmptyLibrary PROPERTIES
     PASS_REGULAR_EXPRESSION "CMake Error: CMake can not determine linker language for target: test")
   ADD_TEST_MACRO(CrossCompile CrossCompile)

+ 8 - 0
Tests/ConfigSources/CMakeLists.txt

@@ -3,7 +3,15 @@ cmake_minimum_required(VERSION 3.0)
 
 project(ConfigSources)
 
+add_library(iface INTERFACE)
+set_property(TARGET iface PROPERTY INTERFACE_SOURCES
+  iface_src.cpp
+  $<$<CONFIG:Debug>:iface_debug_src.cpp>
+  $<$<CONFIG:Release>:does_not_exist.cpp>
+)
+
 add_executable(ConfigSources
   $<$<CONFIG:Debug>:main.cpp>
   $<$<CONFIG:Release>:does_not_exist.cpp>
 )
+target_link_libraries(ConfigSources iface)

+ 4 - 0
Tests/ConfigSources/iface_debug.h

@@ -0,0 +1,4 @@
+
+int iface_src();
+
+int iface_debug();

+ 7 - 0
Tests/ConfigSources/iface_debug_src.cpp

@@ -0,0 +1,7 @@
+
+#include "iface_debug.h"
+
+int iface_debug()
+{
+  return 0;
+}

+ 5 - 0
Tests/ConfigSources/iface_src.cpp

@@ -0,0 +1,5 @@
+
+int iface_src()
+{
+  return 0;
+}

+ 3 - 1
Tests/ConfigSources/main.cpp

@@ -1,5 +1,7 @@
 
+#include "iface_debug.h"
+
 int main(int argc, char** argv)
 {
-  return 0;
+  return iface_src() + iface_debug();
 }

+ 10 - 0
Tests/SourcesProperty/CMakeLists.txt

@@ -0,0 +1,10 @@
+
+cmake_minimum_required(VERSION 3.0)
+
+project(SourcesProperty)
+
+add_library(iface INTERFACE)
+set_property(TARGET iface PROPERTY INTERFACE_SOURCES iface.cpp)
+
+add_executable(SourcesProperty main.cpp)
+target_link_libraries(SourcesProperty iface)

+ 5 - 0
Tests/SourcesProperty/iface.cpp

@@ -0,0 +1,5 @@
+
+int iface()
+{
+  return 0;
+}

+ 2 - 0
Tests/SourcesProperty/iface.h

@@ -0,0 +1,2 @@
+
+int iface();

+ 7 - 0
Tests/SourcesProperty/main.cpp

@@ -0,0 +1,7 @@
+
+#include "iface.h"
+
+int main(int argc, char** argv)
+{
+  return iface();
+}