浏览代码

ENH: Better linker search path computation.

  - Use linker search path -L.. -lfoo for lib w/out soname
    when platform sets CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME
  - Rename cmOrderRuntimeDirectories to cmOrderDirectories
    and generalize it for both soname constraints and link
    library constraints
  - Use cmOrderDirectories to order -L directories based
    on all needed constraints
  - Avoid processing implicit link directories
  - For CMAKE_OLD_LINK_PATHS add constraints from libs
    producing them to produce old ordering
Brad King 17 年之前
父节点
当前提交
fd37a6ec3d

+ 4 - 0
Modules/Platform/FreeBSD.cmake

@@ -12,6 +12,10 @@ IF(EXISTS /usr/include/dlfcn.h)
   SET(CMAKE_EXE_EXPORTS_CXX_FLAG "-Wl,--export-dynamic")
 ENDIF(EXISTS /usr/include/dlfcn.h)
 
+# Shared libraries with no builtin soname may not be linked safely by
+# specifying the file path.
+SET(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1)
+
 # Initialize C link type selection flags.  These flags are used when
 # building a shared library, shared module, or executable that links
 # to other libraries to select whether to use the static or shared

+ 4 - 0
Modules/Platform/HP-UX.cmake

@@ -8,6 +8,10 @@ SET(CMAKE_FIND_LIBRARY_SUFFIXES ".sl" ".so" ".a")
 SET(CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH 1)
 SET(CMAKE_LINK_DEPENDENT_LIBRARY_DIRS 1)
 
+# Shared libraries with no builtin soname may not be linked safely by
+# specifying the file path.
+SET(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1)
+
 # fortran
 IF(CMAKE_COMPILER_IS_GNUG77)
   SET(CMAKE_SHARED_LIBRARY_Fortran_FLAGS "-fPIC")            # -pic 

+ 4 - 0
Modules/Platform/Linux.cmake

@@ -12,6 +12,10 @@ SET(CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG "-Wl,-soname,")
 SET(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic")
 SET(CMAKE_EXE_EXPORTS_CXX_FLAG "-Wl,--export-dynamic")
 
+# Shared libraries with no builtin soname may not be linked safely by
+# specifying the file path.
+SET(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1)
+
 # Initialize C link type selection flags.  These flags are used when
 # building a shared library, shared module, or executable that links
 # to other libraries to select whether to use the static or shared

+ 4 - 0
Modules/Platform/QNX.cmake

@@ -19,6 +19,10 @@ SET(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG "-Wl,-soname,")
 SET(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic")
 SET(CMAKE_EXE_EXPORTS_CXX_FLAG "-Wl,--export-dynamic")
 
+# Shared libraries with no builtin soname may not be linked safely by
+# specifying the file path.
+SET(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1)
+
 # Initialize C link type selection flags.  These flags are used when
 # building a shared library, shared module, or executable that links
 # to other libraries to select whether to use the static or shared

+ 3 - 1
Modules/Platform/SunOS.cmake

@@ -103,4 +103,6 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX)
 # in the -L path.
 SET(CMAKE_LINK_DEPENDENT_LIBRARY_DIRS 1)
 
-
+# Shared libraries with no builtin soname may not be linked safely by
+# specifying the file path.
+SET(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1)

+ 2 - 2
Source/CMakeLists.txt

@@ -167,8 +167,8 @@ SET(SRCS
   cmMakefileExecutableTargetGenerator.cxx
   cmMakefileLibraryTargetGenerator.cxx
   cmMakefileUtilityTargetGenerator.cxx
-  cmOrderRuntimeDirectories.cxx
-  cmOrderRuntimeDirectories.h
+  cmOrderDirectories.cxx
+  cmOrderDirectories.h
   cmProperty.cxx
   cmProperty.h
   cmPropertyDefinition.cxx

+ 149 - 75
Source/cmComputeLinkInformation.cxx

@@ -17,7 +17,7 @@
 #include "cmComputeLinkInformation.h"
 
 #include "cmComputeLinkDepends.h"
-#include "cmOrderRuntimeDirectories.h"
+#include "cmOrderDirectories.h"
 
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
@@ -212,6 +212,30 @@ libraries that are also linked need to be listed in -L paths.
 In our implementation we add all dependent libraries to the runtime
 path computation.  Then the auto-generated RPATH will find everything.
 
+------------------------------------------------------------------------------
+Notes about shared libraries with not builtin soname:
+
+Some UNIX shared libraries may be created with no builtin soname.  On
+some platforms such libraries cannot be linked using the path to their
+location because the linker will copy the path into the field used to
+find the library at runtime.
+
+  Apple:    ../libfoo.dylib  ==>  libfoo.dylib  # ok, uses install_name
+  SGI:      ../libfoo.so     ==>  libfoo.so     # ok
+  AIX:      ../libfoo.so     ==>  libfoo.so     # ok
+  Linux:    ../libfoo.so     ==>  ../libfoo.so  # bad
+  HP-UX:    ../libfoo.so     ==>  ../libfoo.so  # bad
+  Sun:      ../libfoo.so     ==>  ../libfoo.so  # bad
+  FreeBSD:  ../libfoo.so     ==>  ../libfoo.so  # bad
+
+In order to link these libraries we need to use the old-style split
+into -L.. and -lfoo options.  This should be fairly safe because most
+problems with -lfoo options were related to selecting shared libraries
+instead of static but in this case we want the shared lib.  Link
+directory ordering needs to be done to make sure these shared
+libraries are found first.  There should be very few restrictions
+because this need be done only for shared libraries without soname-s.
+
 */
 
 //----------------------------------------------------------------------------
@@ -229,9 +253,12 @@ cmComputeLinkInformation
   this->Config = config;
 
   // Allocate internals.
+  this->OrderLinkerSearchPath =
+    new cmOrderDirectories(this->GlobalGenerator, target->GetName(),
+                           "linker search path");
   this->OrderRuntimeSearchPath =
-    new cmOrderRuntimeDirectories(this->GlobalGenerator, target->GetName(),
-                                  "runtime path");
+    new cmOrderDirectories(this->GlobalGenerator, target->GetName(),
+                           "runtime search path");
   this->OrderDependentRPath = 0;
 
   // Get the language used for linking this target.
@@ -298,6 +325,18 @@ cmComputeLinkInformation
     this->RPathLinkFlag = this->Makefile->GetSafeDefinition(rlVar.c_str());
     }
 
+  // Check if we need to include the runtime search path at link time.
+  {
+  std::string var = "CMAKE_SHARED_LIBRARY_LINK_";
+  var += this->LinkLanguage;
+  var += "_WITH_RUNTIME_PATH";
+  this->LinkWithRuntimePath = this->Makefile->IsOn(var.c_str());
+  }
+
+  // Check the platform policy for missing soname case.
+  this->NoSONameUsesPath =
+    this->Makefile->IsOn("CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME");
+
   // Get link type information.
   this->ComputeLinkTypeInfo();
 
@@ -315,24 +354,16 @@ cmComputeLinkInformation
     }
   else if(this->Makefile->IsOn("CMAKE_LINK_DEPENDENT_LIBRARY_DIRS"))
     {
-    this->SharedDependencyMode = SharedDepModeDir;
+    this->SharedDependencyMode = SharedDepModeLibDir;
     }
   else if(!this->RPathLinkFlag.empty())
     {
     this->SharedDependencyMode = SharedDepModeDir;
-    }
-  if(this->SharedDependencyMode == SharedDepModeDir)
-    {
     this->OrderDependentRPath =
-      new cmOrderRuntimeDirectories(this->GlobalGenerator, target->GetName(),
-                                    "dependent library path");
+      new cmOrderDirectories(this->GlobalGenerator, target->GetName(),
+                             "dependent library path");
     }
 
-  // Add the search path entries requested by the user to the runtime
-  // path computation.
-  this->OrderRuntimeSearchPath->AddDirectories(
-     this->Target->GetLinkDirectories());
-
   // Get the implicit link directories for this platform.
   if(const char* implicitLinks =
      (this->Makefile->GetDefinition
@@ -348,6 +379,21 @@ cmComputeLinkInformation
       }
     }
 
+  // Add the search path entries requested by the user to path ordering.
+  this->OrderLinkerSearchPath
+    ->AddUserDirectories(this->Target->GetLinkDirectories());
+  this->OrderRuntimeSearchPath
+    ->AddUserDirectories(this->Target->GetLinkDirectories());
+  this->OrderLinkerSearchPath
+    ->SetImplicitDirectories(this->ImplicitLinkDirs);
+  this->OrderRuntimeSearchPath
+    ->SetImplicitDirectories(this->ImplicitLinkDirs);
+  if(this->OrderDependentRPath)
+    {
+    this->OrderDependentRPath
+      ->SetImplicitDirectories(this->ImplicitLinkDirs);
+    }
+
   // Initial state.
   this->HaveUserFlagItem = false;
 
@@ -374,6 +420,7 @@ cmComputeLinkInformation
 //----------------------------------------------------------------------------
 cmComputeLinkInformation::~cmComputeLinkInformation()
 {
+  delete this->OrderLinkerSearchPath;
   delete this->OrderRuntimeSearchPath;
   delete this->OrderDependentRPath;
 }
@@ -388,7 +435,7 @@ cmComputeLinkInformation::GetItems()
 //----------------------------------------------------------------------------
 std::vector<std::string> const& cmComputeLinkInformation::GetDirectories()
 {
-  return this->Directories;
+  return this->OrderLinkerSearchPath->GetOrderedDirectories();
 }
 
 //----------------------------------------------------------------------------
@@ -396,7 +443,7 @@ std::string cmComputeLinkInformation::GetRPathLinkString()
 {
   // If there is no separate linker runtime search flag (-rpath-link)
   // there is no reason to compute a string.
-  if(!this->OrderDependentRPath || this->RPathLinkFlag.empty())
+  if(!this->OrderDependentRPath)
     {
     return "";
     }
@@ -405,7 +452,7 @@ std::string cmComputeLinkInformation::GetRPathLinkString()
   std::string rpath_link;
   const char* sep = "";
   std::vector<std::string> const& dirs =
-    this->OrderDependentRPath->GetRuntimePath();
+    this->OrderDependentRPath->GetOrderedDirectories();
   for(std::vector<std::string>::const_iterator di = dirs.begin();
       di != dirs.end(); ++di)
     {
@@ -487,8 +534,8 @@ bool cmComputeLinkInformation::Compute()
     this->SetCurrentLinkType(this->StartLinkType);
     }
 
-  // Compute the linker search path.
-  this->ComputeLinkerSearchDirectories();
+  // Finish setting up linker search directories.
+  this->FinishLinkerSearchDirectories();
 
   return true;
 }
@@ -637,19 +684,31 @@ void cmComputeLinkInformation::AddSharedDepItem(std::string const& item,
     this->AddLibraryRuntimeInfo(lib);
     }
 
-  // Add the item to the separate dependent library search path if
-  // this platform wants one.
-  if(this->OrderDependentRPath)
+  // Check if we need to include the dependent shared library in other
+  // path ordering.
+  cmOrderDirectories* order = 0;
+  if(this->SharedDependencyMode == SharedDepModeLibDir &&
+     !this->LinkWithRuntimePath /* AddLibraryRuntimeInfo adds it */)
+    {
+    // Add the item to the linker search path.
+    order = this->OrderLinkerSearchPath;
+    }
+  else if(this->SharedDependencyMode == SharedDepModeDir)
+    {
+    // Add the item to the separate dependent library search path.
+    order = this->OrderDependentRPath;
+    }
+  if(order)
     {
     if(tgt)
       {
       std::string soName = tgt->GetSOName(this->Config);
       const char* soname = soName.empty()? 0 : soName.c_str();
-      this->OrderDependentRPath->AddLibrary(lib, soname);
+      order->AddRuntimeLibrary(lib, soname);
       }
     else
       {
-      this->OrderDependentRPath->AddLibrary(lib);
+      order->AddRuntimeLibrary(lib);
       }
     }
 }
@@ -747,7 +806,8 @@ void cmComputeLinkInformation::ComputeItemParserInfo()
   // Create regex to remove any library extension.
   std::string reg("(.*)");
   reg += libext;
-  this->RemoveLibraryExtension.compile(reg.c_str());
+  this->OrderLinkerSearchPath->SetLinkExtensionInfo(this->LinkExtensions,
+                                                    reg);
 
   // Create a regex to match a library name.  Match index 1 will be
   // the prefix if it exists and empty otherwise.  Match index 2 will
@@ -913,18 +973,26 @@ void cmComputeLinkInformation::AddTargetItem(std::string const& item,
     this->SetCurrentLinkType(LinkShared);
     }
 
-  // If this platform wants a flag before the full path, add it.
-  if(!this->LibLinkFileFlag.empty())
-    {
-    this->Items.push_back(Item(this->LibLinkFileFlag, false));
-    }
-
   // Keep track of shared library targets linked.
   if(target->GetType() == cmTarget::SHARED_LIBRARY)
     {
     this->SharedLibrariesLinked.insert(target);
     }
 
+  // Handle case of an imported shared library with no soname.
+  if(this->NoSONameUsesPath &&
+     target->IsImportedSharedLibWithoutSOName(this->Config))
+    {
+    this->AddSharedLibNoSOName(item);
+    return;
+    }
+
+  // If this platform wants a flag before the full path, add it.
+  if(!this->LibLinkFileFlag.empty())
+    {
+    this->Items.push_back(Item(this->LibLinkFileFlag, false));
+    }
+
   // Now add the full path to the library.
   this->Items.push_back(Item(item, true));
 }
@@ -938,6 +1006,12 @@ void cmComputeLinkInformation::AddFullItem(std::string const& item)
     return;
     }
 
+  // Check for case of shared library with no builtin soname.
+  if(this->NoSONameUsesPath && this->CheckSharedLibNoSOName(item))
+    {
+    return;
+    }
+
   // This is called to handle a link item that is a full path.
   // If the target is not a static library make sure the link type is
   // shared.  This is because dynamic-mode linking can handle both
@@ -959,11 +1033,11 @@ void cmComputeLinkInformation::AddFullItem(std::string const& item)
       }
     }
 
-  // Record the directory in which the library appears because CMake
-  // 2.4 in below added these as -L paths.
+  // For compatibility with CMake 2.4 include the item's directory in
+  // the linker search path.
   if(this->OldLinkDirMode)
     {
-    this->OldLinkDirs.push_back(cmSystemTools::GetFilenamePath(item));
+    this->OldLinkDirItems.push_back(item);
     }
 
   // If this platform wants a flag before the full path, add it.
@@ -1184,55 +1258,47 @@ void cmComputeLinkInformation::AddFrameworkPath(std::string const& p)
 }
 
 //----------------------------------------------------------------------------
-void cmComputeLinkInformation::ComputeLinkerSearchDirectories()
+bool cmComputeLinkInformation::CheckSharedLibNoSOName(std::string const& item)
 {
-  // Some search paths should never be emitted.
-  this->DirectoriesEmmitted = this->ImplicitLinkDirs;
-  this->DirectoriesEmmitted.insert("");
-
-  // Check if we need to include the runtime search path at link time.
-  std::string var = "CMAKE_SHARED_LIBRARY_LINK_";
-  var += this->LinkLanguage;
-  var += "_WITH_RUNTIME_PATH";
-  if(this->Makefile->IsOn(var.c_str()))
+  // This platform will use the path to a library as its soname if the
+  // library is given via path and was not built with an soname.  If
+  // this is a shared library that might be the case.  TODO: Check if
+  // the lib is a symlink to detect that it actually has an soname.
+  std::string file = cmSystemTools::GetFilenameName(item);
+  if(this->ExtractSharedLibraryName.find(file))
     {
-    // This platform requires the runtime library path for shared
-    // libraries to be specified at link time as -L paths.  It needs
-    // them so that transitive dependencies of the libraries linked
-    // may be found by the linker.
-    this->AddLinkerSearchDirectories(this->GetRuntimeSearchPath());
+    this->AddSharedLibNoSOName(item);
+    return true;
     }
+  return false;
+}
 
-  // Get the search path entries requested by the user.
-  this->AddLinkerSearchDirectories(this->Target->GetLinkDirectories());
-
-  // Support broken projects if necessary.
-  if(this->HaveUserFlagItem && this->OldLinkDirMode)
-    {
-    this->AddLinkerSearchDirectories(this->OldLinkDirs);
-    }
+//----------------------------------------------------------------------------
+void cmComputeLinkInformation::AddSharedLibNoSOName(std::string const& item)
+{
+  // We have a full path to a shared library with no soname.  We need
+  // to ask the linker to locate the item because otherwise the path
+  // we give to it will be embedded in the target linked.  Then at
+  // runtime the dynamic linker will search for the library using the
+  // path instead of just the name.
+  std::string file = cmSystemTools::GetFilenameName(item);
+  this->AddUserItem(file);
 
-  // If there is no separate linker runtime search flag (-rpath-link)
-  // and we have a search path for dependent libraries add it to the
-  // link directories.
-  if(this->OrderDependentRPath && this->RPathLinkFlag.empty())
-    {
-    this->AddLinkerSearchDirectories
-      (this->OrderDependentRPath->GetRuntimePath());
-    }
+  // Make sure the link directory ordering will find the library.
+  this->OrderLinkerSearchPath->AddLinkLibrary(item);
 }
 
 //----------------------------------------------------------------------------
-void
-cmComputeLinkInformation
-::AddLinkerSearchDirectories(std::vector<std::string> const& dirs)
+void cmComputeLinkInformation::FinishLinkerSearchDirectories()
 {
-  for(std::vector<std::string>::const_iterator i = dirs.begin();
-      i != dirs.end(); ++i)
+  // Support broken projects if necessary.
+  if(this->HaveUserFlagItem && this->OldLinkDirMode)
     {
-    if(this->DirectoriesEmmitted.insert(*i).second)
+    for(std::vector<std::string>::const_iterator
+          i = this->OldLinkDirItems.begin();
+        i != this->OldLinkDirItems.end(); ++i)
       {
-      this->Directories.push_back(*i);
+      this->OrderLinkerSearchPath->AddLinkLibrary(*i);
       }
     }
 }
@@ -1241,7 +1307,7 @@ cmComputeLinkInformation
 std::vector<std::string> const&
 cmComputeLinkInformation::GetRuntimeSearchPath()
 {
-  return this->OrderRuntimeSearchPath->GetRuntimePath();
+  return this->OrderRuntimeSearchPath->GetOrderedDirectories();
 }
 
 //----------------------------------------------------------------------------
@@ -1261,7 +1327,11 @@ cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath,
   const char* soname = soName.empty()? 0 : soName.c_str();
 
   // Include this library in the runtime path ordering.
-  this->OrderRuntimeSearchPath->AddLibrary(fullPath, soname);
+  this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath, soname);
+  if(this->LinkWithRuntimePath)
+    {
+    this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath, soname);
+    }
 }
 
 //----------------------------------------------------------------------------
@@ -1289,7 +1359,11 @@ cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath)
     }
 
   // Include this library in the runtime path ordering.
-  this->OrderRuntimeSearchPath->AddLibrary(fullPath);
+  this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath);
+  if(this->LinkWithRuntimePath)
+    {
+    this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath);
+    }
 }
 
 //----------------------------------------------------------------------------

+ 15 - 12
Source/cmComputeLinkInformation.h

@@ -26,7 +26,7 @@ class cmGlobalGenerator;
 class cmLocalGenerator;
 class cmMakefile;
 class cmTarget;
-class cmOrderRuntimeDirectories;
+class cmOrderDirectories;
 
 /** \class cmComputeLinkInformation
  * \brief Compute link information for a target in one configuration.
@@ -89,9 +89,10 @@ private:
   // Modes for dealing with dependent shared libraries.
   enum SharedDepMode
   {
-    SharedDepModeNone, // Drop
-    SharedDepModeDir,  // Use in runtime information
-    SharedDepModeLink  // List file on link line
+    SharedDepModeNone,   // Drop
+    SharedDepModeDir,    // List dir in -rpath-link flag
+    SharedDepModeLibDir, // List dir in linker search path
+    SharedDepModeLink    // List file on link line
   };
 
   // System info.
@@ -104,6 +105,8 @@ private:
   std::string RuntimeSep;
   std::string RuntimeAlways;
   bool RuntimeUseChrpath;
+  bool NoSONameUsesPath;
+  bool LinkWithRuntimePath;
   std::string RPathLinkFlag;
   SharedDepMode SharedDependencyMode;
 
@@ -124,7 +127,6 @@ private:
   std::vector<std::string> SharedLinkExtensions;
   std::vector<std::string> LinkExtensions;
   std::set<cmStdString> LinkPrefixes;
-  cmsys::RegularExpression RemoveLibraryExtension;
   cmsys::RegularExpression ExtractStaticLibraryName;
   cmsys::RegularExpression ExtractSharedLibraryName;
   cmsys::RegularExpression ExtractAnyLibraryName;
@@ -133,7 +135,7 @@ private:
   std::string CreateExtensionRegex(std::vector<std::string> const& exts);
   std::string NoCaseExpression(const char* str);
 
-  // Handling of link items that are not targets or full file paths.
+  // Handling of link items.
   void AddTargetItem(std::string const& item, cmTarget* target);
   void AddFullItem(std::string const& item);
   bool CheckImplicitDirItem(std::string const& item);
@@ -141,6 +143,8 @@ private:
   void AddDirectoryItem(std::string const& item);
   void AddFrameworkItem(std::string const& item);
   void DropDirectoryItem(std::string const& item);
+  bool CheckSharedLibNoSOName(std::string const& item);
+  void AddSharedLibNoSOName(std::string const& item);
 
   // Framework info.
   void ComputeFrameworkInfo();
@@ -149,23 +153,22 @@ private:
   cmsys::RegularExpression SplitFramework;
 
   // Linker search path computation.
-  void ComputeLinkerSearchDirectories();
-  void AddLinkerSearchDirectories(std::vector<std::string> const& dirs);
-  std::set<cmStdString> DirectoriesEmmitted;
+  cmOrderDirectories* OrderLinkerSearchPath;
+  void FinishLinkerSearchDirectories();
   std::set<cmStdString> ImplicitLinkDirs;
 
   // Linker search path compatibility mode.
-  std::vector<std::string> OldLinkDirs;
+  std::vector<std::string> OldLinkDirItems;
   bool OldLinkDirMode;
   bool HaveUserFlagItem;
 
   // Runtime path computation.
-  cmOrderRuntimeDirectories* OrderRuntimeSearchPath;
+  cmOrderDirectories* OrderRuntimeSearchPath;
   void AddLibraryRuntimeInfo(std::string const& fullPath, cmTarget* target);
   void AddLibraryRuntimeInfo(std::string const& fullPath);
 
   // Dependent library path computation.
-  cmOrderRuntimeDirectories* OrderDependentRPath;
+  cmOrderDirectories* OrderDependentRPath;
 };
 
 #endif

+ 524 - 0
Source/cmOrderDirectories.cxx

@@ -0,0 +1,524 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#include "cmOrderDirectories.h"
+
+#include "cmGlobalGenerator.h"
+#include "cmSystemTools.h"
+
+#include <assert.h>
+
+#include <algorithm>
+
+/*
+Directory ordering computation.
+  - Useful to compute a safe runtime library path order
+  - Need runtime path for supporting INSTALL_RPATH_USE_LINK_PATH
+  - Need runtime path at link time to pickup transitive link dependencies
+    for shared libraries.
+*/
+
+//----------------------------------------------------------------------------
+class cmOrderDirectoriesConstraint
+{
+public:
+  cmOrderDirectoriesConstraint(cmOrderDirectories* od,
+                               std::string const& file):
+    OD(od), GlobalGenerator(od->GlobalGenerator)
+    {
+    this->FullPath = file;
+    this->Directory = cmSystemTools::GetFilenamePath(file);
+    this->FileName = cmSystemTools::GetFilenameName(file);
+    }
+  virtual ~cmOrderDirectoriesConstraint() {}
+
+  void AddDirectory()
+    {
+    this->DirectoryIndex = this->OD->AddOriginalDirectory(this->Directory);
+    }
+
+  virtual void Report(std::ostream& e) = 0;
+
+  void FindConflicts(unsigned int index)
+    {
+    for(unsigned int i=0; i < this->OD->OriginalDirectories.size(); ++i)
+      {
+      // Check if this directory conflicts with they entry.
+      std::string const& dir = this->OD->OriginalDirectories[i];
+      if(dir != this->Directory && this->FindConflict(dir))
+        {
+        // The library will be found in this directory but this is not
+        // the directory named for it.  Add an entry to make sure the
+        // desired directory comes before this one.
+        cmOrderDirectories::ConflictPair p(this->DirectoryIndex, index);
+        this->OD->ConflictGraph[i].push_back(p);
+        }
+      }
+    }
+protected:
+  virtual bool FindConflict(std::string const& dir) = 0;
+
+  bool FileMayConflict(std::string const& dir, std::string const& name);
+
+  cmOrderDirectories* OD;
+  cmGlobalGenerator* GlobalGenerator;
+
+  // The location in which the item is supposed to be found.
+  std::string FullPath;
+  std::string Directory;
+  std::string FileName;
+
+  // The index assigned to the directory.
+  int DirectoryIndex;
+};
+
+//----------------------------------------------------------------------------
+bool cmOrderDirectoriesConstraint::FileMayConflict(std::string const& dir,
+                                                   std::string const& name)
+{
+  // Check if the file will be built by cmake.
+  std::set<cmStdString> const& files =
+    (this->GlobalGenerator->GetDirectoryContent(dir, false));
+  if(std::set<cmStdString>::const_iterator(files.find(name)) != files.end())
+    {
+    return true;
+    }
+
+  // Check if the file exists on disk and is not a symlink back to the
+  // original file.
+  std::string file = dir;
+  file += "/";
+  file += name;
+  if(cmSystemTools::FileExists(file.c_str(), true) &&
+     !cmSystemTools::SameFile(this->FullPath.c_str(), file.c_str()))
+    {
+    return true;
+    }
+  return false;
+}
+
+//----------------------------------------------------------------------------
+class cmOrderDirectoriesConstraintSOName: public cmOrderDirectoriesConstraint
+{
+public:
+  cmOrderDirectoriesConstraintSOName(cmOrderDirectories* od,
+                                     std::string const& file,
+                                     const char* soname):
+    cmOrderDirectoriesConstraint(od, file), SOName(soname? soname : "")
+    {
+    }
+
+  virtual void Report(std::ostream& e)
+    {
+    e << "runtime library [";
+    if(this->SOName.empty())
+      {
+      e << this->FileName;
+      }
+    else
+      {
+      e << this->SOName;
+      }
+    e << "]";
+    }
+
+  virtual bool FindConflict(std::string const& dir);
+private:
+  // The soname of the shared library if it is known.
+  std::string SOName;
+};
+
+//----------------------------------------------------------------------------
+bool cmOrderDirectoriesConstraintSOName::FindConflict(std::string const& dir)
+{
+  // Determine which type of check to do.
+  if(!this->SOName.empty())
+    {
+    // We have the library soname.  Check if it will be found.
+    if(this->FileMayConflict(dir, this->SOName))
+      {
+      return true;
+      }
+    }
+  else
+    {
+    // We do not have the soname.  Look for files in the directory
+    // that may conflict.
+    std::set<cmStdString> const& files =
+      (this->GlobalGenerator
+       ->GetDirectoryContent(dir, true));
+
+    // Get the set of files that might conflict.  Since we do not
+    // know the soname just look at all files that start with the
+    // file name.  Usually the soname starts with the library name.
+    // TODO: Check if the library is a symlink and guess the soname.
+    std::string base = this->FileName;
+    std::set<cmStdString>::const_iterator first = files.lower_bound(base);
+    ++base[base.size()-1];
+    std::set<cmStdString>::const_iterator last = files.upper_bound(base);
+    bool found = false;
+    for(std::set<cmStdString>::const_iterator fi = first; fi != last; ++fi)
+      {
+      return true;
+      }
+    }
+  return false;
+}
+
+//----------------------------------------------------------------------------
+class cmOrderDirectoriesConstraintLibrary: public cmOrderDirectoriesConstraint
+{
+public:
+  cmOrderDirectoriesConstraintLibrary(cmOrderDirectories* od,
+                                      std::string const& file):
+    cmOrderDirectoriesConstraint(od, file)
+    {
+    }
+
+  virtual void Report(std::ostream& e)
+    {
+    e << "link library [" << this->FileName << "]";
+    }
+
+  virtual bool FindConflict(std::string const& dir);
+};
+
+//----------------------------------------------------------------------------
+bool cmOrderDirectoriesConstraintLibrary::FindConflict(std::string const& dir)
+{
+  // We have the library file name.  Check if it will be found.
+  if(this->FileMayConflict(dir, this->FileName))
+    {
+    return true;
+    }
+
+  // Now check if the file exists with other extensions the linker
+  // might consider.
+  if(!this->OD->LinkExtensions.empty() &&
+     this->OD->RemoveLibraryExtension.find(this->FileName))
+    {
+    cmStdString lib = this->OD->RemoveLibraryExtension.match(1);
+    cmStdString ext = this->OD->RemoveLibraryExtension.match(2);
+    for(std::vector<std::string>::iterator
+          i = this->OD->LinkExtensions.begin();
+        i != this->OD->LinkExtensions.end(); ++i)
+      {
+      if(*i != ext)
+        {
+        std::string fname = lib;
+        fname += *i;
+        if(this->FileMayConflict(dir, fname.c_str()))
+          {
+          return true;
+          }
+        }
+      }
+    }
+  return false;
+}
+
+//----------------------------------------------------------------------------
+cmOrderDirectories::cmOrderDirectories(cmGlobalGenerator* gg,
+                                       const char* name,
+                                       const char* purpose)
+{
+  this->GlobalGenerator = gg;
+  this->Name = name;
+  this->Purpose = purpose;
+  this->Computed = false;
+}
+
+//----------------------------------------------------------------------------
+cmOrderDirectories::~cmOrderDirectories()
+{
+  for(std::vector<cmOrderDirectoriesConstraint*>::iterator
+        i = this->ConstraintEntries.begin();
+      i != this->ConstraintEntries.end(); ++i)
+    {
+    delete *i;
+    }
+}
+
+//----------------------------------------------------------------------------
+std::vector<std::string> const& cmOrderDirectories::GetOrderedDirectories()
+{
+  if(!this->Computed)
+    {
+    this->Computed = true;
+    this->CollectOriginalDirectories();
+    this->FindConflicts();
+    this->OrderDirectories();
+    }
+  return this->OrderedDirectories;
+}
+
+//----------------------------------------------------------------------------
+void cmOrderDirectories::AddRuntimeLibrary(std::string const& fullPath,
+                                           const char* soname)
+{
+  // Add the runtime library at most once.
+  if(this->EmmittedConstraintSOName.insert(fullPath).second)
+    {
+    // Avoid dealing with implicit directories.
+    if(!this->ImplicitDirectories.empty())
+      {
+      std::string dir = cmSystemTools::GetFilenamePath(fullPath);
+      if(this->ImplicitDirectories.find(dir) !=
+         this->ImplicitDirectories.end())
+        {
+        return;
+        }
+      }
+
+    // Construct the runtime information entry for this library.
+    this->ConstraintEntries.push_back(
+      new cmOrderDirectoriesConstraintSOName(this, fullPath, soname));
+    }
+  else
+    {
+    // This can happen if the same library is linked multiple times.
+    // In that case the runtime information check need be done only
+    // once anyway.  For shared libs we could add a check in AddItem
+    // to not repeat them.
+    }
+}
+
+//----------------------------------------------------------------------------
+void cmOrderDirectories::AddLinkLibrary(std::string const& fullPath)
+{
+  // Link extension info is required for library constraints.
+  assert(!this->LinkExtensions.empty());
+
+  // Add the link library at most once.
+  if(this->EmmittedConstraintLibrary.insert(fullPath).second)
+    {
+    // Avoid dealing with implicit directories.
+    if(!this->ImplicitDirectories.empty())
+      {
+      std::string dir = cmSystemTools::GetFilenamePath(fullPath);
+      if(this->ImplicitDirectories.find(dir) !=
+         this->ImplicitDirectories.end())
+        {
+        return;
+        }
+      }
+
+    // Construct the link library entry.
+    this->ConstraintEntries.push_back(
+      new cmOrderDirectoriesConstraintLibrary(this, fullPath));
+    }
+}
+
+//----------------------------------------------------------------------------
+void
+cmOrderDirectories
+::AddUserDirectories(std::vector<std::string> const& extra)
+{
+  this->UserDirectories.insert(this->UserDirectories.end(),
+                               extra.begin(), extra.end());
+}
+
+//----------------------------------------------------------------------------
+void
+cmOrderDirectories
+::SetImplicitDirectories(std::set<cmStdString> const& implicitDirs)
+{
+  this->ImplicitDirectories = implicitDirs;
+}
+
+//----------------------------------------------------------------------------
+void
+cmOrderDirectories
+::SetLinkExtensionInfo(std::vector<std::string> const& linkExtensions,
+                       std::string const& removeExtRegex)
+{
+  this->LinkExtensions = linkExtensions;
+  this->RemoveLibraryExtension.compile(removeExtRegex.c_str());
+}
+
+//----------------------------------------------------------------------------
+void cmOrderDirectories::CollectOriginalDirectories()
+{
+  // Add user directories specified for inclusion.  These should be
+  // indexed first so their original order is preserved as much as
+  // possible subject to the constraints.
+  for(std::vector<std::string>::const_iterator
+        di = this->UserDirectories.begin();
+      di != this->UserDirectories.end(); ++di)
+    {
+    // Avoid dealing with implicit directories.
+    if(this->ImplicitDirectories.find(*di) !=
+       this->ImplicitDirectories.end())
+      {
+      continue;
+      }
+
+    // Skip the empty string.
+    if(di->empty())
+      {
+      continue;
+      }
+
+    // Add this directory.
+    this->AddOriginalDirectory(*di);
+    }
+
+  // Add directories containing constraints.
+  for(unsigned int i=0; i < this->ConstraintEntries.size(); ++i)
+    {
+    this->ConstraintEntries[i]->AddDirectory();
+    }
+}
+
+//----------------------------------------------------------------------------
+int cmOrderDirectories::AddOriginalDirectory(std::string const& dir)
+{
+  // Add the runtime directory with a unique index.
+  std::map<cmStdString, int>::iterator i =
+    this->DirectoryIndex.find(dir);
+  if(i == this->DirectoryIndex.end())
+    {
+    std::map<cmStdString, int>::value_type
+      entry(dir, static_cast<int>(this->OriginalDirectories.size()));
+    i = this->DirectoryIndex.insert(entry).first;
+    this->OriginalDirectories.push_back(dir);
+    }
+
+  return i->second;
+}
+
+//----------------------------------------------------------------------------
+struct cmOrderDirectoriesCompare
+{
+  typedef std::pair<int, int> ConflictPair;
+
+  // The conflict pair is unique based on just the directory
+  // (first).  The second element is only used for displaying
+  // information about why the entry is present.
+  bool operator()(ConflictPair const& l,
+                  ConflictPair const& r)
+    {
+    return l.first == r.first;
+    }
+};
+
+//----------------------------------------------------------------------------
+void cmOrderDirectories::FindConflicts()
+{
+  // Allocate the conflict graph.
+  this->ConflictGraph.resize(this->OriginalDirectories.size());
+  this->DirectoryVisited.resize(this->OriginalDirectories.size(), 0);
+
+  // Find directories conflicting with each entry.
+  for(unsigned int i=0; i < this->ConstraintEntries.size(); ++i)
+    {
+    this->ConstraintEntries[i]->FindConflicts(i);
+    }
+
+  // Clean up the conflict graph representation.
+  for(std::vector<ConflictList>::iterator
+        i = this->ConflictGraph.begin();
+      i != this->ConflictGraph.end(); ++i)
+    {
+    // Sort the outgoing edges for each graph node so that the
+    // original order will be preserved as much as possible.
+    std::sort(i->begin(), i->end());
+
+    // Make the edge list unique so cycle detection will be reliable.
+    ConflictList::iterator last =
+      std::unique(i->begin(), i->end(), cmOrderDirectoriesCompare());
+    i->erase(last, i->end());
+    }
+}
+
+//----------------------------------------------------------------------------
+void cmOrderDirectories::OrderDirectories()
+{
+  // Allow a cycle to be diagnosed once.
+  this->CycleDiagnosed = false;
+  this->WalkId = 0;
+
+  // Iterate through the directories in the original order.
+  for(unsigned int i=0; i < this->OriginalDirectories.size(); ++i)
+    {
+    // Start a new DFS from this node.
+    ++this->WalkId;
+    this->VisitDirectory(i);
+    }
+}
+
+//----------------------------------------------------------------------------
+void cmOrderDirectories::VisitDirectory(unsigned int i)
+{
+  // Skip nodes already visited.
+  if(this->DirectoryVisited[i])
+    {
+    if(this->DirectoryVisited[i] == this->WalkId)
+      {
+      // We have reached a node previously visited on this DFS.
+      // There is a cycle.
+      this->DiagnoseCycle();
+      }
+    return;
+    }
+
+  // We are now visiting this node so mark it.
+  this->DirectoryVisited[i] = this->WalkId;
+
+  // Visit the neighbors of the node first.
+  ConflictList const& clist = this->ConflictGraph[i];
+  for(ConflictList::const_iterator j = clist.begin();
+      j != clist.end(); ++j)
+    {
+    this->VisitDirectory(j->first);
+    }
+
+  // Now that all directories required to come before this one have
+  // been emmitted, emit this directory.
+  this->OrderedDirectories.push_back(this->OriginalDirectories[i]);
+}
+
+//----------------------------------------------------------------------------
+void cmOrderDirectories::DiagnoseCycle()
+{
+  // Report the cycle at most once.
+  if(this->CycleDiagnosed)
+    {
+    return;
+    }
+  this->CycleDiagnosed = true;
+
+  // Construct the message.
+  cmOStringStream e;
+  e << "WARNING: Cannot generate a safe " << this->Purpose
+    << " for target " << this->Name
+    << " because there is a cycle in the constraint graph:\n";
+
+  // Display the conflict graph.
+  for(unsigned int i=0; i < this->ConflictGraph.size(); ++i)
+    {
+    ConflictList const& clist = this->ConflictGraph[i];
+    e << "dir " << i << " is [" << this->OriginalDirectories[i] << "]\n";
+    for(ConflictList::const_iterator j = clist.begin();
+        j != clist.end(); ++j)
+      {
+      e << "  dir " << j->first << " must precede it due to ";
+      this->ConstraintEntries[j->second]->Report(e);
+      e << "\n";
+      }
+    }
+  cmSystemTools::Message(e.str().c_str());
+}

+ 88 - 0
Source/cmOrderDirectories.h

@@ -0,0 +1,88 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef cmOrderDirectories_h
+#define cmOrderDirectories_h
+
+#include "cmStandardIncludes.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+class cmGlobalGenerator;
+class cmOrderDirectoriesConstraint;
+class cmOrderDirectoriesConstraintLibrary;
+
+/** \class cmOrderDirectories
+ * \brief Compute a safe runtime path order for a set of shared libraries.
+ */
+class cmOrderDirectories
+{
+public:
+  cmOrderDirectories(cmGlobalGenerator* gg, const char* name,
+                     const char* purpose);
+  ~cmOrderDirectories();
+  void AddRuntimeLibrary(std::string const& fullPath, const char* soname = 0);
+  void AddLinkLibrary(std::string const& fullPath);
+  void AddUserDirectories(std::vector<std::string> const& extra);
+  void SetImplicitDirectories(std::set<cmStdString> const& implicitDirs);
+  void SetLinkExtensionInfo(std::vector<std::string> const& linkExtensions,
+                            std::string const& removeExtRegex);
+
+  std::vector<std::string> const& GetOrderedDirectories();
+private:
+  cmGlobalGenerator* GlobalGenerator;
+  std::string Name;
+  std::string Purpose;
+
+  bool Computed;
+
+  std::vector<std::string> OrderedDirectories;
+
+  bool OrderedDirectoriesComputed;
+  std::vector<cmOrderDirectoriesConstraint*> ConstraintEntries;
+  std::vector<std::string> UserDirectories;
+  cmsys::RegularExpression RemoveLibraryExtension;
+  std::vector<std::string> LinkExtensions;
+  std::set<cmStdString> ImplicitDirectories;
+  std::set<cmStdString> EmmittedConstraintSOName;
+  std::set<cmStdString> EmmittedConstraintLibrary;
+  std::vector<std::string> OriginalDirectories;
+  std::map<cmStdString, int> DirectoryIndex;
+  std::vector<int> DirectoryVisited;
+  void CollectOriginalDirectories();
+  int AddOriginalDirectory(std::string const& dir);
+  void FindConflicts();
+  void OrderDirectories();
+  void VisitDirectory(unsigned int i);
+  void DiagnoseCycle();
+  bool CycleDiagnosed;
+  int WalkId;
+
+  // Adjacency-list representation of runtime path ordering graph.
+  // This maps from directory to those that must come *before* it.
+  // Each entry that must come before is a pair.  The first element is
+  // the index of the directory that must come first.  The second
+  // element is the index of the runtime library that added the
+  // constraint.
+  typedef std::pair<int, int> ConflictPair;
+  struct ConflictList: public std::vector<ConflictPair> {};
+  std::vector<ConflictList> ConflictGraph;
+
+  friend class cmOrderDirectoriesConstraint;
+  friend class cmOrderDirectoriesConstraintLibrary;
+};
+
+#endif

+ 0 - 325
Source/cmOrderRuntimeDirectories.cxx

@@ -1,325 +0,0 @@
-/*=========================================================================
-
-  Program:   CMake - Cross-Platform Makefile Generator
-  Module:    $RCSfile$
-  Language:  C++
-  Date:      $Date$
-  Version:   $Revision$
-
-  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
-  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
-
-     This software is distributed WITHOUT ANY WARRANTY; without even
-     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-     PURPOSE.  See the above copyright notices for more information.
-
-=========================================================================*/
-#include "cmOrderRuntimeDirectories.h"
-
-#include "cmGlobalGenerator.h"
-#include "cmSystemTools.h"
-
-#include <algorithm>
-
-/*
-Directory ordering computation.
-  - Useful to compute a safe runtime library path order
-  - Need runtime path for supporting INSTALL_RPATH_USE_LINK_PATH
-  - Need runtime path at link time to pickup transitive link dependencies
-    for shared libraries.
-*/
-
-//----------------------------------------------------------------------------
-cmOrderRuntimeDirectories::cmOrderRuntimeDirectories(cmGlobalGenerator* gg,
-                                                     const char* name,
-                                                     const char* purpose)
-{
-  this->GlobalGenerator = gg;
-  this->Name = name;
-  this->Purpose = purpose;
-  this->Computed = false;
-}
-
-//----------------------------------------------------------------------------
-std::vector<std::string> const& cmOrderRuntimeDirectories::GetRuntimePath()
-{
-  if(!this->Computed)
-    {
-    this->Computed = true;
-    this->CollectRuntimeDirectories();
-    this->FindConflictingLibraries();
-    this->OrderRuntimeSearchPath();
-    }
-  return this->RuntimeSearchPath;
-}
-
-//----------------------------------------------------------------------------
-void cmOrderRuntimeDirectories::AddLibrary(std::string const& fullPath,
-                                           const char* soname)
-{
-  // Add the runtime information at most once.
-  if(this->LibraryRuntimeInfoEmmitted.insert(fullPath).second)
-    {
-    // Construct the runtime information entry for this library.
-    LibraryRuntimeEntry entry;
-    entry.FileName =  cmSystemTools::GetFilenameName(fullPath);
-    entry.SOName = soname? soname : "";
-    entry.Directory = cmSystemTools::GetFilenamePath(fullPath);
-    this->LibraryRuntimeInfo.push_back(entry);
-    }
-  else
-    {
-    // This can happen if the same library is linked multiple times.
-    // In that case the runtime information check need be done only
-    // once anyway.  For shared libs we could add a check in AddItem
-    // to not repeat them.
-    }
-}
-
-//----------------------------------------------------------------------------
-void
-cmOrderRuntimeDirectories
-::AddDirectories(std::vector<std::string> const& extra)
-{
-  this->UserDirectories.insert(this->UserDirectories.end(),
-                               extra.begin(), extra.end());
-}
-
-//----------------------------------------------------------------------------
-void cmOrderRuntimeDirectories::CollectRuntimeDirectories()
-{
-  // Get all directories that should be in the runtime search path.
-
-  // Add directories containing libraries.
-  for(std::vector<LibraryRuntimeEntry>::iterator
-        ei = this->LibraryRuntimeInfo.begin();
-      ei != this->LibraryRuntimeInfo.end(); ++ei)
-    {
-    ei->DirectoryIndex = this->AddRuntimeDirectory(ei->Directory);
-    }
-
-  // Add link directories specified for inclusion.
-  for(std::vector<std::string>::const_iterator
-        di = this->UserDirectories.begin();
-      di != this->UserDirectories.end(); ++di)
-    {
-    this->AddRuntimeDirectory(*di);
-    }
-}
-
-//----------------------------------------------------------------------------
-int cmOrderRuntimeDirectories::AddRuntimeDirectory(std::string const& dir)
-{
-  // Add the runtime directory with a unique index.
-  std::map<cmStdString, int>::iterator i =
-    this->RuntimeDirectoryIndex.find(dir);
-  if(i == this->RuntimeDirectoryIndex.end())
-    {
-    std::map<cmStdString, int>::value_type
-      entry(dir, static_cast<int>(this->RuntimeDirectories.size()));
-    i = this->RuntimeDirectoryIndex.insert(entry).first;
-    this->RuntimeDirectories.push_back(dir);
-    }
-
-  return i->second;
-}
-
-//----------------------------------------------------------------------------
-struct cmOrderRuntimeDirectoriesCompare
-{
-  typedef std::pair<int, int> RuntimeConflictPair;
-
-  // The conflict pair is unique based on just the directory
-  // (first).  The second element is only used for displaying
-  // information about why the entry is present.
-  bool operator()(RuntimeConflictPair const& l,
-                  RuntimeConflictPair const& r)
-    {
-    return l.first == r.first;
-    }
-};
-
-//----------------------------------------------------------------------------
-void cmOrderRuntimeDirectories::FindConflictingLibraries()
-{
-  // Allocate the conflict graph.
-  this->RuntimeConflictGraph.resize(this->RuntimeDirectories.size());
-  this->RuntimeDirectoryVisited.resize(this->RuntimeDirectories.size(), 0);
-
-  // Find all runtime directories providing each library.
-  for(unsigned int lri = 0; lri < this->LibraryRuntimeInfo.size(); ++lri)
-    {
-    this->FindDirectoriesForLib(lri);
-    }
-
-  // Clean up the conflict graph representation.
-  for(std::vector<RuntimeConflictList>::iterator
-        i = this->RuntimeConflictGraph.begin();
-      i != this->RuntimeConflictGraph.end(); ++i)
-    {
-    // Sort the outgoing edges for each graph node so that the
-    // original order will be preserved as much as possible.
-    std::sort(i->begin(), i->end());
-
-    // Make the edge list unique so cycle detection will be reliable.
-    RuntimeConflictList::iterator last =
-      std::unique(i->begin(), i->end(), cmOrderRuntimeDirectoriesCompare());
-    i->erase(last, i->end());
-    }
-}
-
-//----------------------------------------------------------------------------
-void cmOrderRuntimeDirectories::FindDirectoriesForLib(unsigned int lri)
-{
-  // Search through the runtime directories to find those providing
-  // this library.
-  LibraryRuntimeEntry& re = this->LibraryRuntimeInfo[lri];
-  for(unsigned int i = 0; i < this->RuntimeDirectories.size(); ++i)
-    {
-    // Skip the directory that is supposed to provide the library.
-    if(this->RuntimeDirectories[i] == re.Directory)
-      {
-      continue;
-      }
-
-    // Determine which type of check to do.
-    if(!re.SOName.empty())
-      {
-      // We have the library soname.  Check if it will be found.
-      std::string file = this->RuntimeDirectories[i];
-      file += "/";
-      file += re.SOName;
-      std::set<cmStdString> const& files =
-        (this->GlobalGenerator
-         ->GetDirectoryContent(this->RuntimeDirectories[i], false));
-      if((std::set<cmStdString>::const_iterator(files.find(re.SOName)) !=
-          files.end()) ||
-         cmSystemTools::FileExists(file.c_str(), true))
-        {
-        // The library will be found in this directory but this is not
-        // the directory named for it.  Add an entry to make sure the
-        // desired directory comes before this one.
-        RuntimeConflictPair p(re.DirectoryIndex, lri);
-        this->RuntimeConflictGraph[i].push_back(p);
-        }
-      }
-    else
-      {
-      // We do not have the soname.  Look for files in the directory
-      // that may conflict.
-      std::set<cmStdString> const& files =
-        (this->GlobalGenerator
-         ->GetDirectoryContent(this->RuntimeDirectories[i], true));
-
-      // Get the set of files that might conflict.  Since we do not
-      // know the soname just look at all files that start with the
-      // file name.  Usually the soname starts with the library name.
-      std::string base = re.FileName;
-      std::set<cmStdString>::const_iterator first = files.lower_bound(base);
-      ++base[base.size()-1];
-      std::set<cmStdString>::const_iterator last = files.upper_bound(base);
-      bool found = false;
-      for(std::set<cmStdString>::const_iterator fi = first;
-          !found && fi != last; ++fi)
-        {
-        found = true;
-        }
-
-      if(found)
-        {
-        // The library may be found in this directory but this is not
-        // the directory named for it.  Add an entry to make sure the
-        // desired directory comes before this one.
-        RuntimeConflictPair p(re.DirectoryIndex, lri);
-        this->RuntimeConflictGraph[i].push_back(p);
-        }
-      }
-    }
-}
-
-//----------------------------------------------------------------------------
-void cmOrderRuntimeDirectories::OrderRuntimeSearchPath()
-{
-  // Allow a cycle to be diagnosed once.
-  this->CycleDiagnosed = false;
-  this->WalkId = 0;
-
-  // Iterate through the directories in the original order.
-  for(unsigned int i=0; i < this->RuntimeDirectories.size(); ++i)
-    {
-    // Start a new DFS from this node.
-    ++this->WalkId;
-    this->VisitRuntimeDirectory(i);
-    }
-}
-
-//----------------------------------------------------------------------------
-void cmOrderRuntimeDirectories::VisitRuntimeDirectory(unsigned int i)
-{
-  // Skip nodes already visited.
-  if(this->RuntimeDirectoryVisited[i])
-    {
-    if(this->RuntimeDirectoryVisited[i] == this->WalkId)
-      {
-      // We have reached a node previously visited on this DFS.
-      // There is a cycle.
-      this->DiagnoseCycle();
-      }
-    return;
-    }
-
-  // We are now visiting this node so mark it.
-  this->RuntimeDirectoryVisited[i] = this->WalkId;
-
-  // Visit the neighbors of the node first.
-  RuntimeConflictList const& clist = this->RuntimeConflictGraph[i];
-  for(RuntimeConflictList::const_iterator j = clist.begin();
-      j != clist.end(); ++j)
-    {
-    this->VisitRuntimeDirectory(j->first);
-    }
-
-  // Now that all directories required to come before this one have
-  // been emmitted, emit this directory.
-  this->RuntimeSearchPath.push_back(this->RuntimeDirectories[i]);
-}
-
-//----------------------------------------------------------------------------
-void cmOrderRuntimeDirectories::DiagnoseCycle()
-{
-  // Report the cycle at most once.
-  if(this->CycleDiagnosed)
-    {
-    return;
-    }
-  this->CycleDiagnosed = true;
-
-  // Construct the message.
-  cmOStringStream e;
-  e << "WARNING: Cannot generate a safe " << this->Purpose
-    << " for target " << this->Name
-    << " because there is a cycle in the constraint graph:\n";
-
-  // Display the conflict graph.
-  for(unsigned int i=0; i < this->RuntimeConflictGraph.size(); ++i)
-    {
-    RuntimeConflictList const& clist = this->RuntimeConflictGraph[i];
-    e << "dir " << i << " is [" << this->RuntimeDirectories[i] << "]\n";
-    for(RuntimeConflictList::const_iterator j = clist.begin();
-        j != clist.end(); ++j)
-      {
-      e << "  dir " << j->first << " must precede it due to [";
-      LibraryRuntimeEntry const& re = this->LibraryRuntimeInfo[j->second];
-      if(re.SOName.empty())
-        {
-        e << re.FileName;
-        }
-      else
-        {
-        e << re.SOName;
-        }
-      e << "]\n";
-      }
-    }
-  cmSystemTools::Message(e.str().c_str());
-}

+ 0 - 88
Source/cmOrderRuntimeDirectories.h

@@ -1,88 +0,0 @@
-/*=========================================================================
-
-  Program:   CMake - Cross-Platform Makefile Generator
-  Module:    $RCSfile$
-  Language:  C++
-  Date:      $Date$
-  Version:   $Revision$
-
-  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
-  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
-
-     This software is distributed WITHOUT ANY WARRANTY; without even
-     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-     PURPOSE.  See the above copyright notices for more information.
-
-=========================================================================*/
-#ifndef cmOrderRuntimeDirectories_h
-#define cmOrderRuntimeDirectories_h
-
-#include "cmStandardIncludes.h"
-
-class cmGlobalGenerator;
-
-/** \class cmOrderRuntimeDirectories
- * \brief Compute a safe runtime path order for a set of shared libraries.
- */
-class cmOrderRuntimeDirectories
-{
-public:
-  cmOrderRuntimeDirectories(cmGlobalGenerator* gg, const char* name,
-                            const char* purpose);
-  void AddLibrary(std::string const& fullPath, const char* soname = 0);
-  void AddDirectories(std::vector<std::string> const& extra);
-
-  std::vector<std::string> const& GetRuntimePath();
-private:
-  cmGlobalGenerator* GlobalGenerator;
-  std::string Name;
-  std::string Purpose;
-
-  bool Computed;
-
-  std::vector<std::string> RuntimeSearchPath;
-
-  // Runtime path computation.
-  struct LibraryRuntimeEntry
-  {
-    // The file name of the library.
-    std::string FileName;
-
-    // The soname of the shared library if it is known.
-    std::string SOName;
-
-    // The directory in which the library is supposed to be found.
-    std::string Directory;
-
-    // The index assigned to the directory.
-    int DirectoryIndex;
-  };
-  bool RuntimeSearchPathComputed;
-  std::vector<LibraryRuntimeEntry> LibraryRuntimeInfo;
-  std::vector<std::string> UserDirectories;
-  std::set<cmStdString> LibraryRuntimeInfoEmmitted;
-  std::vector<std::string> RuntimeDirectories;
-  std::map<cmStdString, int> RuntimeDirectoryIndex;
-  std::vector<int> RuntimeDirectoryVisited;
-  void CollectRuntimeDirectories();
-  int AddRuntimeDirectory(std::string const& dir);
-  void FindConflictingLibraries();
-  void FindDirectoriesForLib(unsigned int lri);
-  void OrderRuntimeSearchPath();
-  void VisitRuntimeDirectory(unsigned int i);
-  void DiagnoseCycle();
-  bool CycleDiagnosed;
-  int WalkId;
-
-  // Adjacency-list representation of runtime path ordering graph.
-  // This maps from directory to those that must come *before* it.
-  // Each entry that must come before is a pair.  The first element is
-  // the index of the directory that must come first.  The second
-  // element is the index of the runtime library that added the
-  // constraint.
-  typedef std::pair<int, int> RuntimeConflictPair;
-  struct RuntimeConflictList: public std::vector<RuntimeConflictPair> {};
-  std::vector<RuntimeConflictList> RuntimeConflictGraph;
-};
-
-#endif

+ 42 - 1
Source/cmTarget.cxx

@@ -2048,7 +2048,17 @@ std::string cmTarget::GetSOName(const char* config)
     // Lookup the imported soname.
     if(cmTarget::ImportInfo const* info = this->GetImportInfo(config))
       {
-      return info->SOName;
+      if(info->NoSOName)
+        {
+        // The imported library has no builtin soname so the name
+        // searched at runtime will be just the filename.
+        return cmSystemTools::GetFilenameName(info->Location);
+        }
+      else
+        {
+        // Use the soname given if any.
+        return info->SOName;
+        }
       }
     else
       {
@@ -2068,6 +2078,19 @@ std::string cmTarget::GetSOName(const char* config)
     }
 }
 
+//----------------------------------------------------------------------------
+bool cmTarget::IsImportedSharedLibWithoutSOName(const char* config)
+{
+  if(this->IsImported() && this->GetType() == cmTarget::SHARED_LIBRARY)
+    {
+    if(cmTarget::ImportInfo const* info = this->GetImportInfo(config))
+      {
+      return info->NoSOName;
+      }
+    }
+  return false;
+}
+
 //----------------------------------------------------------------------------
 std::string cmTarget::NormalGetRealName(const char* config)
 {
@@ -3054,6 +3077,9 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
   // properties.  The "IMPORTED_" namespace is reserved for properties
   // defined by the project exporting the target.
 
+  // Initialize members.
+  info.NoSOName = false;
+
   // Track the configuration-specific property suffix.
   std::string suffix = "_";
   suffix += desired_config;
@@ -3164,6 +3190,21 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
       }
     }
 
+  // Get the "no-soname" mark.
+  if(this->GetType() == cmTarget::SHARED_LIBRARY)
+    {
+    std::string soProp = "IMPORTED_NO_SONAME";
+    soProp += suffix;
+    if(const char* config_no_soname = this->GetProperty(soProp.c_str()))
+      {
+      info.NoSOName = cmSystemTools::IsOn(config_no_soname);
+      }
+    else if(const char* no_soname = this->GetProperty("IMPORTED_NO_SONAME"))
+      {
+      info.NoSOName = cmSystemTools::IsOn(no_soname);
+      }
+    }
+
   // Get the import library.
   if(this->GetType() == cmTarget::SHARED_LIBRARY ||
      this->IsExecutableWithExports())

+ 5 - 0
Source/cmTarget.h

@@ -281,6 +281,10 @@ public:
   /** Get the soname of the target.  Allowed only for a shared library.  */
   std::string GetSOName(const char* config);
 
+  /** Test for special case of a third-party shared library that has
+      no soname at all.  */
+  bool IsImportedSharedLibWithoutSOName(const char* config);
+
   /** Get the full path to the target according to the settings in its
       makefile and the configuration type.  */
   std::string GetFullPath(const char* config=0, bool implib = false,
@@ -501,6 +505,7 @@ private:
   // Cache import information from properties for each configuration.
   struct ImportInfo
   {
+    bool NoSOName;
     std::string Location;
     std::string SOName;
     std::string ImportLibrary;

+ 1 - 1
bootstrap

@@ -175,7 +175,7 @@ CMAKE_CXX_SOURCES="\
   cmListFileCache \
   cmComputeLinkDepends \
   cmComputeLinkInformation \
-  cmOrderRuntimeDirectories \
+  cmOrderDirectories \
   cmComputeTargetDepends \
   cmComputeComponentGraph \
   cmExprLexer \