浏览代码

BUG: Fix utility dependencies for static libraries in VS generators. This addresses bug#4789.

Brad King 18 年之前
父节点
当前提交
438a7e2fce

+ 5 - 7
Source/cmGlobalVisualStudio6Generator.cxx

@@ -177,7 +177,10 @@ void cmGlobalVisualStudio6Generator::Generate()
                                                "echo", "Build all projects");
       }
     }
-  
+
+  // Fix utility dependencies to avoid linking to libraries.
+  this->FixUtilityDepends();
+
   // first do the superclass method
   this->cmGlobalGenerator::Generate();
   
@@ -438,12 +441,7 @@ void cmGlobalVisualStudio6Generator::WriteProject(std::ostream& fout,
     {
     if(*i != dspname)
       {
-      std::string depName = *i;
-      if(strncmp(depName.c_str(), "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
-        {
-        depName.erase(depName.begin(), depName.begin() + 27);
-        }
-          
+      std::string depName = this->GetUtilityForTarget(target, i->c_str());
       fout << "Begin Project Dependency\n";
       fout << "Project_Dep_Name " << depName << "\n";
       fout << "End Project Dependency\n";

+ 1 - 11
Source/cmGlobalVisualStudio71Generator.cxx

@@ -349,17 +349,7 @@ cmGlobalVisualStudio71Generator
     {
     if(*i != dspname)
       {
-      std::string name = i->c_str();
-      if(strncmp(name.c_str(), "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
-        {
-        // kind of weird removing the first 27 letters.  my
-        // recommendatsions: use cmCustomCommand::GetCommand() to get the
-        // project name or get rid of the target name starting with
-        // "INCLUDE_EXTERNAL_MSPROJECT_" and use another indicator/flag
-        // somewhere.  These external project names shouldn't conflict
-        // with cmake target names anyways.
-        name.erase(name.begin(), name.begin() + 27);
-        }
+      std::string name = this->GetUtilityForTarget(target, i->c_str());
       std::string guid = this->GetGUID(name.c_str());
       if(guid.size() == 0)
         {

+ 4 - 11
Source/cmGlobalVisualStudio7Generator.cxx

@@ -235,6 +235,9 @@ void cmGlobalVisualStudio7Generator::Generate()
       }
     }
 
+  // Fix utility dependencies to avoid linking to libraries.
+  this->FixUtilityDepends();
+
   // first do the superclass method
   this->cmGlobalGenerator::Generate();
   
@@ -634,17 +637,7 @@ cmGlobalVisualStudio7Generator
     {
     if(*i != dspname)
       {
-      std::string name = *i;
-      if(strncmp(name.c_str(), "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
-        {
-        // kind of weird removing the first 27 letters.  my
-        // recommendatsions: use cmCustomCommand::GetCommand() to get the
-        // project name or get rid of the target name starting with
-        // "INCLUDE_EXTERNAL_MSPROJECT_" and use another indicator/flag
-        // somewhere.  These external project names shouldn't conflict
-        // with cmake target names anyways.
-        name.erase(name.begin(), name.begin() + 27);
-        }
+      std::string name = this->GetUtilityForTarget(target, i->c_str());
       std::string guid = this->GetGUID(name.c_str());
       if(guid.size() == 0)
         {

+ 4 - 0
Source/cmGlobalVisualStudio8Generator.h

@@ -50,6 +50,10 @@ public:
   virtual void Configure();
   virtual void Generate();
 protected:
+
+  // Utility target fix is not needed for VS8.
+  virtual void FixUtilityDepends() {}
+
   static cmVS7FlagTable const* GetExtraFlagTableVS8();
   virtual void AddPlatformDefinitions(cmMakefile* mf);
   virtual void WriteSLNFile(std::ostream& fout, cmLocalGenerator* root,

+ 168 - 0
Source/cmGlobalVisualStudioGenerator.cxx

@@ -16,6 +16,10 @@
 =========================================================================*/
 #include "cmGlobalVisualStudioGenerator.h"
 
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmTarget.h"
+
 //----------------------------------------------------------------------------
 cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator()
 {
@@ -25,3 +29,167 @@ cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator()
 cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator()
 {
 }
+
+//----------------------------------------------------------------------------
+void cmGlobalVisualStudioGenerator::FixUtilityDepends()
+{
+  // For VS versions before 8:
+  //
+  // When a target that links contains a project-level dependency on a
+  // library target that library is automatically linked.  In order to
+  // allow utility-style project-level dependencies that do not
+  // actually link we need to automatically insert an intermediate
+  // custom target.
+  //
+  // Here we edit the utility dependencies of a target to add the
+  // intermediate custom target when necessary.
+  for(unsigned i = 0; i < this->LocalGenerators.size(); ++i)
+    {
+    cmTargets* targets =
+      &(this->LocalGenerators[i]->GetMakefile()->GetTargets());
+    for(cmTargets::iterator tarIt = targets->begin();
+        tarIt != targets->end(); ++tarIt)
+      {
+      this->FixUtilityDependsForTarget(tarIt->second);
+      }
+    }
+}
+
+//----------------------------------------------------------------------------
+void
+cmGlobalVisualStudioGenerator::FixUtilityDependsForTarget(cmTarget& target)
+{
+  // Only targets that link need to be fixed.
+  if(target.GetType() != cmTarget::STATIC_LIBRARY &&
+     target.GetType() != cmTarget::SHARED_LIBRARY &&
+     target.GetType() != cmTarget::MODULE_LIBRARY &&
+     target.GetType() != cmTarget::EXECUTABLE)
+    {
+    return;
+    }
+
+  // Look at each utility dependency.
+  for(std::set<cmStdString>::const_iterator ui =
+        target.GetUtilities().begin();
+      ui != target.GetUtilities().end(); ++ui)
+    {
+    if(cmTarget* depTarget = this->FindTarget(0, ui->c_str()))
+      {
+      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, ui->c_str()))
+          {
+          this->CreateUtilityDependTarget(*depTarget);
+          }
+        }
+      }
+    }
+}
+
+//----------------------------------------------------------------------------
+void
+cmGlobalVisualStudioGenerator::CreateUtilityDependTarget(cmTarget& target)
+{
+  // This target is a library on which a utility dependency exists.
+  // We need to create an intermediate custom target to hook up the
+  // dependency without causing a link.
+  const char* altName = target.GetProperty("ALTERNATIVE_DEPENDENCY_NAME");
+  if(!altName)
+    {
+    // Create the intermediate utility target.
+    std::string altNameStr = target.GetName();
+    altNameStr += "_UTILITY";
+    const std::vector<std::string> no_depends;
+    cmCustomCommandLines no_commands;
+    const char* no_working_dir = 0;
+    const char* no_comment = 0;
+    target.GetMakefile()->AddUtilityCommand(altNameStr.c_str(), true,
+                                            no_working_dir, no_depends,
+                                            no_commands, false, no_comment);
+    target.SetProperty("ALTERNATIVE_DEPENDENCY_NAME", altNameStr.c_str());
+
+    // Most targets have a GUID created in ConfigureFinalPass.  Since
+    // that has already been called, create one for this target now.
+    this->CreateGUID(altNameStr.c_str());
+
+    // The intermediate target should depend on the original target.
+    if(cmTarget* alt = this->FindTarget(0, altNameStr.c_str()))
+      {
+      alt->AddUtility(target.GetName());
+      }
+    }
+}
+
+//----------------------------------------------------------------------------
+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)
+    {
+    // Static libraries never link to anything.
+    return false;
+    }
+  cmTarget::LinkLibraryVectorType const& libs = target.GetLinkLibraries();
+  for(cmTarget::LinkLibraryVectorType::const_iterator i = libs.begin();
+      i != libs.end(); ++i)
+    {
+    if(i->first == name)
+      {
+      return true;
+      }
+    }
+  return false;
+}
+
+//----------------------------------------------------------------------------
+const char*
+cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget& target,
+                                                   const char* name)
+{
+  // Handle the external MS project special case.
+  if(strncmp(name, "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
+    {
+    // Note from Ken:
+    // kind of weird removing the first 27 letters.  my
+    // recommendatsions: use cmCustomCommand::GetCommand() to get the
+    // project name or get rid of the target name starting with
+    // "INCLUDE_EXTERNAL_MSPROJECT_" and use another indicator/flag
+    // somewhere.  These external project names shouldn't conflict
+    // with cmake target names anyways.
+    return name+27;
+    }
+
+  // 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.  Lookup the dependee to
+    // see if it provides an alternative dependency name.
+    if(cmTarget* depTarget = this->FindTarget(0, name))
+      {
+      // Check for an alternative name created by FixUtilityDepends.
+      if(const char* altName =
+         depTarget->GetProperty("ALTERNATIVE_DEPENDENCY_NAME"))
+        {
+        // The alternative name is needed only if the depender does
+        // not really link to the dependee.
+        if(!this->CheckTargetLinks(target, name))
+          {
+          return altName;
+          }
+        }
+      }
+    }
+
+  // No special case.  Just use the original dependency name.
+  return name;
+}

+ 9 - 0
Source/cmGlobalVisualStudioGenerator.h

@@ -30,6 +30,15 @@ class cmGlobalVisualStudioGenerator : public cmGlobalGenerator
 public:
   cmGlobalVisualStudioGenerator();
   virtual ~cmGlobalVisualStudioGenerator();
+
+protected:
+  virtual void CreateGUID(const char*) {}
+  virtual void FixUtilityDepends();
+  const char* GetUtilityForTarget(cmTarget& target, const char*);
+private:
+  void FixUtilityDependsForTarget(cmTarget& target);
+  void CreateUtilityDependTarget(cmTarget& target);
+  bool CheckTargetLinks(cmTarget& target, const char* name);
 };
 
 #endif

+ 17 - 0
Tests/Dependency/Two/CMakeLists.txt

@@ -1,3 +1,20 @@
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
 ADD_LIBRARY( Two TwoSrc.c )
 TARGET_LINK_LIBRARIES( Two Three )
 
+# Setup a target to cause failure if Two does not depend on it or if
+# Two actually links to it.  This will test that a utility dependency
+# on a library target works properly.
+ADD_CUSTOM_COMMAND(
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/two-test.h
+  COMMAND ${CMAKE_COMMAND} -E copy_if_different
+  ${CMAKE_CURRENT_SOURCE_DIR}/two-test.h.in
+  ${CMAKE_CURRENT_BINARY_DIR}/two-test.h
+  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/two-test.h.in
+  )
+ADD_LIBRARY( TwoCustom TwoCustomSrc.c ${CMAKE_CURRENT_BINARY_DIR}/two-test.h)
+SET_TARGET_PROPERTIES(TwoCustom PROPERTIES EXCLUDE_FROM_ALL 1)
+TARGET_LINK_LIBRARIES(TwoCustom Three)
+
+# Add a utility dependency to make sure it works without linking.
+ADD_DEPENDENCIES(Two TwoCustom)

+ 10 - 0
Tests/Dependency/Two/TwoCustomSrc.c

@@ -0,0 +1,10 @@
+extern void NoFunction();
+
+/* Provide a function that is supposed to be found in the Three
+   library.  If Two links to TwoCustom then TwoCustom will come before
+   Three and this symbol will be used.  Since NoFunction is not
+   defined, that will cause a link failure.  */
+void ThreeFunction()
+{
+  NoFunction();
+}

+ 1 - 1
Tests/Dependency/Two/TwoSrc.c

@@ -1,4 +1,4 @@
-void ThreeFunction();
+#include <two-test.h>
 
 void TwoFunction()
 {

+ 1 - 0
Tests/Dependency/Two/two-test.h.in

@@ -0,0 +1 @@
+extern void ThreeFunction();