Browse Source

Merge branch 'vs-target-dependencies' into imported-target-dependencies

Brad King 15 years ago
parent
commit
bc7395c096

+ 7 - 5
Source/cmComputeTargetDepends.cxx

@@ -144,7 +144,7 @@ bool cmComputeTargetDepends::Compute()
 //----------------------------------------------------------------------------
 void
 cmComputeTargetDepends::GetTargetDirectDepends(cmTarget* t,
-                                               std::set<cmTarget*>& deps)
+                                               cmTargetDependSet& deps)
 {
   // Lookup the index for this target.  All targets should be known by
   // this point.
@@ -156,7 +156,9 @@ cmComputeTargetDepends::GetTargetDirectDepends(cmTarget* t,
   EdgeList const& nl = this->FinalGraph[i];
   for(EdgeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
     {
-    deps.insert(this->Targets[*ni]);
+    cmTarget* dep = this->Targets[*ni];
+    cmTargetDependSet::iterator di = deps.insert(dep).first;
+    di->SetType(ni->IsStrong());
     }
 }
 
@@ -445,7 +447,7 @@ cmComputeTargetDepends
       int j = *ei;
       if(cmap[j] == c && ei->IsStrong())
         {
-        this->FinalGraph[i].push_back(j);
+        this->FinalGraph[i].push_back(cmGraphEdge(j, true));
         if(!this->IntraComponent(cmap, c, j, head, emitted, visited))
           {
           return false;
@@ -456,7 +458,7 @@ cmComputeTargetDepends
     // Prepend to a linear linked-list of intra-component edges.
     if(*head >= 0)
       {
-      this->FinalGraph[i].push_back(*head);
+      this->FinalGraph[i].push_back(cmGraphEdge(*head, false));
       }
     else
       {
@@ -515,7 +517,7 @@ cmComputeTargetDepends
       int dependee_component = *ni;
       int dependee_component_head = this->ComponentHead[dependee_component];
       this->FinalGraph[depender_component_tail]
-        .push_back(dependee_component_head);
+        .push_back(cmGraphEdge(dependee_component_head, ni->IsStrong()));
       }
     }
   return true;

+ 2 - 1
Source/cmComputeTargetDepends.h

@@ -21,6 +21,7 @@
 class cmComputeComponentGraph;
 class cmGlobalGenerator;
 class cmTarget;
+class cmTargetDependSet;
 
 /** \class cmComputeTargetDepends
  * \brief Compute global interdependencies among targets.
@@ -38,7 +39,7 @@ public:
   bool Compute();
 
   std::vector<cmTarget*> const& GetTargets() const { return this->Targets; }
-  void GetTargetDirectDepends(cmTarget* t, std::set<cmTarget*>& deps);
+  void GetTargetDirectDepends(cmTarget* t, cmTargetDependSet& deps);
 private:
   void CollectTargets();
   void CollectDepends();

+ 2 - 1
Source/cmGlobalGenerator.h

@@ -16,6 +16,7 @@
 #include "cmStandardIncludes.h"
 
 #include "cmTarget.h" // For cmTargets
+#include "cmTargetDepend.h" // For cmTargetDependSet
 
 class cmake;
 class cmMakefile;
@@ -233,7 +234,7 @@ public:
   virtual const char* GetCleanTargetName()        { return 0; }
 
   // Class to track a set of dependencies.
-  class TargetDependSet: public std::set<cmTarget*> {};
+  typedef cmTargetDependSet TargetDependSet;
 
   // what targets does the specified target depend on directly
   // via a target_link_libraries or add_dependencies

+ 136 - 68
Source/cmGlobalVisualStudioGenerator.cxx

@@ -236,6 +236,59 @@ std::string cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
   return "";
 }
 
+//----------------------------------------------------------------------------
+void cmGlobalVisualStudioGenerator::FillLinkClosure(cmTarget* target,
+                                                    TargetSet& linked)
+{
+  if(linked.insert(target).second)
+    {
+    TargetDependSet const& depends = this->GetTargetDirectDepends(*target);
+    for(TargetDependSet::const_iterator di = depends.begin();
+        di != depends.end(); ++di)
+      {
+      if(di->IsLink())
+        {
+        this->FillLinkClosure(*di, linked);
+        }
+      }
+    }
+}
+
+//----------------------------------------------------------------------------
+cmGlobalVisualStudioGenerator::TargetSet const&
+cmGlobalVisualStudioGenerator::GetTargetLinkClosure(cmTarget* target)
+{
+  TargetSetMap::iterator i = this->TargetLinkClosure.find(target);
+  if(i == this->TargetLinkClosure.end())
+    {
+    TargetSetMap::value_type entry(target, TargetSet());
+    i = this->TargetLinkClosure.insert(entry).first;
+    this->FillLinkClosure(target, i->second);
+    }
+  return i->second;
+}
+
+//----------------------------------------------------------------------------
+void cmGlobalVisualStudioGenerator::FollowLinkDepends(
+  cmTarget* target, std::set<cmTarget*>& linked)
+{
+  if(linked.insert(target).second &&
+     target->GetType() == cmTarget::STATIC_LIBRARY)
+    {
+    // Static library targets do not list their link dependencies so
+    // we must follow them transitively now.
+    TargetDependSet const& depends = this->GetTargetDirectDepends(*target);
+    for(TargetDependSet::const_iterator di = depends.begin();
+        di != depends.end(); ++di)
+      {
+      if(di->IsLink())
+        {
+        this->FollowLinkDepends(*di, linked);
+        }
+      }
+    }
+}
+
 //----------------------------------------------------------------------------
 bool cmGlobalVisualStudioGenerator::ComputeTargetDepends()
 {
@@ -269,51 +322,94 @@ void cmGlobalVisualStudioGenerator::ComputeVSTargetDepends(cmTarget& target)
     return;
     }
   VSDependSet& vsTargetDepend = this->VSTargetDepends[&target];
+  // VS <= 7.1 has two behaviors that affect solution dependencies.
+  //
+  // (1) Solution-level dependencies between a linkable target and a
+  // library cause that library to be linked.  We use an intermedite
+  // empty utility target to express the dependency.  (VS 8 and above
+  // provide a project file "LinkLibraryDependencies" setting to
+  // choose whether to activate this behavior.  We disable it except
+  // when linking external project files.)
+  //
+  // (2) We cannot let static libraries depend directly on targets to
+  // which they "link" because the librarian tool will copy the
+  // targets into the static library.  While the work-around for
+  // behavior (1) would also avoid this, it would create a large
+  // number of extra utility targets for little gain.  Instead, use
+  // the above work-around only for dependencies explicitly added by
+  // the add_dependencies() command.  Approximate link dependencies by
+  // leaving them out for the static library itself but following them
+  // transitively for other targets.
+
+  bool allowLinkable = (target.GetType() != cmTarget::STATIC_LIBRARY &&
+                        target.GetType() != cmTarget::SHARED_LIBRARY &&
+                        target.GetType() != cmTarget::MODULE_LIBRARY &&
+                        target.GetType() != cmTarget::EXECUTABLE);
+
+  TargetDependSet const& depends = this->GetTargetDirectDepends(target);
+
+  // Collect implicit link dependencies (target_link_libraries).
+  // Static libraries cannot depend on their link implementation
+  // due to behavior (2), but they do not really need to.
+  std::set<cmTarget*> linkDepends;
   if(target.GetType() != cmTarget::STATIC_LIBRARY)
     {
-    cmTarget::LinkLibraryVectorType const& libs = target.GetLinkLibraries();
-    for(cmTarget::LinkLibraryVectorType::const_iterator j = libs.begin();
-        j != libs.end(); ++j)
+    for(TargetDependSet::const_iterator di = depends.begin();
+        di != depends.end(); ++di)
       {
-      if(j->first != target.GetName() &&
-         this->FindTarget(0, j->first.c_str()))
+      cmTargetDepend dep = *di;
+      if(dep.IsLink())
         {
-        vsTargetDepend.insert(j->first);
+        this->FollowLinkDepends(dep, linkDepends);
         }
       }
     }
-  std::set<cmStdString> const& utils = target.GetUtilities();
-  for(std::set<cmStdString>::const_iterator i = utils.begin();
-      i != utils.end(); ++i)
+
+  // Collext explicit util dependencies (add_dependencies).
+  std::set<cmTarget*> utilDepends;
+  for(TargetDependSet::const_iterator di = depends.begin();
+      di != depends.end(); ++di)
     {
-    if(*i != target.GetName())
+    cmTargetDepend dep = *di;
+    if(dep.IsUtil())
       {
-      std::string name = this->GetUtilityForTarget(target, i->c_str());
-      vsTargetDepend.insert(name);
+      this->FollowLinkDepends(dep, utilDepends);
       }
     }
-}
 
-//----------------------------------------------------------------------------
-bool cmGlobalVisualStudioGenerator::CheckTargetLinks(cmTarget& target,
-                                                     const char* name)
-{
-  // Return whether the given target links to a target with the given name.
-  if(target.GetType() == cmTarget::STATIC_LIBRARY)
+  // Collect all targets linked by this target so we can avoid
+  // intermediate targets below.
+  TargetSet linked;
+  if(target.GetType() != cmTarget::STATIC_LIBRARY)
     {
-    // Static libraries never link to anything.
-    return false;
+    linked = this->GetTargetLinkClosure(&target);
     }
-  cmTarget::LinkLibraryVectorType const& libs = target.GetLinkLibraries();
-  for(cmTarget::LinkLibraryVectorType::const_iterator i = libs.begin();
-      i != libs.end(); ++i)
+
+  // Emit link dependencies.
+  for(std::set<cmTarget*>::iterator di = linkDepends.begin();
+      di != linkDepends.end(); ++di)
+    {
+    cmTarget* dep = *di;
+    vsTargetDepend.insert(dep->GetName());
+    }
+
+  // Emit util dependencies.  Possibly use intermediate targets.
+  for(std::set<cmTarget*>::iterator di = utilDepends.begin();
+      di != utilDepends.end(); ++di)
     {
-    if(i->first == name)
+    cmTarget* dep = *di;
+    if(allowLinkable || !dep->IsLinkable() || linked.count(dep))
       {
-      return true;
+      // Direct dependency allowed.
+      vsTargetDepend.insert(dep->GetName());
+      }
+    else
+      {
+      // Direct dependency on linkable target not allowed.
+      // Use an intermediate utility target.
+      vsTargetDepend.insert(this->GetUtilityDepend(dep));
       }
     }
-  return false;
 }
 
 //----------------------------------------------------------------------------
@@ -329,45 +425,6 @@ std::string cmGlobalVisualStudioGenerator::GetUtilityDepend(cmTarget* target)
   return i->second;
 }
 
-//----------------------------------------------------------------------------
-std::string
-cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget& target,
-                                                   const char* name)
-{
-  if(!this->VSLinksDependencies())
-    {
-    return name;
-    }
-
-  // Possibly depend on an intermediate utility target to avoid
-  // linking.
-  if(target.GetType() == cmTarget::STATIC_LIBRARY ||
-     target.GetType() == cmTarget::SHARED_LIBRARY ||
-     target.GetType() == cmTarget::MODULE_LIBRARY ||
-     target.GetType() == cmTarget::EXECUTABLE)
-    {
-    // The depender is a target that links.
-    if(cmTarget* depTarget = this->FindTarget(0, name))
-      {
-      if(depTarget->GetType() == cmTarget::STATIC_LIBRARY ||
-         depTarget->GetType() == cmTarget::SHARED_LIBRARY ||
-         depTarget->GetType() == cmTarget::MODULE_LIBRARY)
-        {
-        // This utility dependency will cause an attempt to link.  If
-        // the depender does not already link the dependee we need an
-        // intermediate target.
-        if(!this->CheckTargetLinks(target, name))
-          {
-          return this->GetUtilityDepend(depTarget);
-          }
-        }
-      }
-    }
-
-  // No special case.  Just use the original dependency name.
-  return name;
-}
-
 //----------------------------------------------------------------------------
 #include <windows.h>
 
@@ -706,11 +763,22 @@ cmGlobalVisualStudioGenerator::TargetCompare
 
 //----------------------------------------------------------------------------
 cmGlobalVisualStudioGenerator::OrderedTargetDependSet
-::OrderedTargetDependSet(cmGlobalGenerator::TargetDependSet const& targets)
+::OrderedTargetDependSet(TargetDependSet const& targets)
 {
-  for(cmGlobalGenerator::TargetDependSet::const_iterator ti =
+  for(TargetDependSet::const_iterator ti =
         targets.begin(); ti != targets.end(); ++ti)
     {
     this->insert(*ti);
     }
 }
+
+//----------------------------------------------------------------------------
+cmGlobalVisualStudioGenerator::OrderedTargetDependSet
+::OrderedTargetDependSet(TargetSet const& targets)
+{
+  for(TargetSet::const_iterator ti = targets.begin();
+      ti != targets.end(); ++ti)
+    {
+    this->insert(*ti);
+    }
+}

+ 20 - 5
Source/cmGlobalVisualStudioGenerator.h

@@ -69,15 +69,12 @@ public:
       i.e. "Can I build Debug and Release in the same tree?" */
   virtual bool IsMultiConfig() { return true; }
 
+  class TargetSet: public std::set<cmTarget*> {};
   struct TargetCompare
   {
     bool operator()(cmTarget const* l, cmTarget const* r) const;
   };
-  class OrderedTargetDependSet: public std::multiset<cmTarget*, TargetCompare>
-  {
-  public:
-    OrderedTargetDependSet(cmGlobalGenerator::TargetDependSet const&);
-  };
+  class OrderedTargetDependSet;
 
 protected:
   // Does this VS version link targets to each other if there are
@@ -99,6 +96,24 @@ protected:
   std::string GetUtilityDepend(cmTarget* target);
   typedef std::map<cmTarget*, cmStdString> UtilityDependsMap;
   UtilityDependsMap UtilityDepends;
+private:
+  void FollowLinkDepends(cmTarget* target, std::set<cmTarget*>& linked);
+
+  class TargetSetMap: public std::map<cmTarget*, TargetSet> {};
+  TargetSetMap TargetLinkClosure;
+  void FillLinkClosure(cmTarget* target, TargetSet& linked);
+  TargetSet const& GetTargetLinkClosure(cmTarget* target);
+};
+
+class cmGlobalVisualStudioGenerator::OrderedTargetDependSet:
+  public std::multiset<cmTargetDepend,
+                       cmGlobalVisualStudioGenerator::TargetCompare>
+{
+public:
+  typedef cmGlobalGenerator::TargetDependSet TargetDependSet;
+  typedef cmGlobalVisualStudioGenerator::TargetSet TargetSet;
+  OrderedTargetDependSet(TargetDependSet const&);
+  OrderedTargetDependSet(TargetSet const&);
 };
 
 #endif

+ 48 - 0
Source/cmTargetDepend.h

@@ -0,0 +1,48 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2000-2010 Kitware, Inc., Insight Software Consortium
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  This software is distributed WITHOUT ANY WARRANTY; without even the
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the License for more information.
+============================================================================*/
+#ifndef cmTargetDepend_h
+#define cmTargetDepend_h
+
+#include "cmStandardIncludes.h"
+
+class cmTarget;
+
+/** One edge in the global target dependency graph.
+    It may be marked as a 'link' or 'util' edge or both.  */
+class cmTargetDepend
+{
+  cmTarget* Target;
+
+  // The set order depends only on the Target, so we use
+  // mutable members to acheive a map with set syntax.
+  mutable bool Link;
+  mutable bool Util;
+public:
+  cmTargetDepend(cmTarget* t): Target(t), Link(false), Util(false) {}
+  operator cmTarget*() const { return this->Target; }
+  cmTarget* operator->() const { return this->Target; }
+  cmTarget& operator*() const { return *this->Target; }
+  friend bool operator < (cmTargetDepend const& l, cmTargetDepend const& r)
+    { return l.Target < r.Target; }
+  void SetType(bool strong) const
+    {
+    if(strong) { this->Util = true; }
+    else { this->Link = true; }
+    }
+  bool IsLink() const { return this->Link; }
+  bool IsUtil() const { return this->Util; }
+};
+
+/** Unordered set of (direct) dependencies of a target. */
+class cmTargetDependSet: public std::set<cmTargetDepend> {};
+
+#endif