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
This commit is contained in:
Brad King 2008-02-21 11:41:11 -05:00
parent 9f2f456e7d
commit fd37a6ec3d
15 changed files with 847 additions and 507 deletions

View File

@ -12,6 +12,10 @@ IF(EXISTS /usr/include/dlfcn.h)
SET(CMAKE_EXE_EXPORTS_CXX_FLAG "-Wl,--export-dynamic") SET(CMAKE_EXE_EXPORTS_CXX_FLAG "-Wl,--export-dynamic")
ENDIF(EXISTS /usr/include/dlfcn.h) 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 # Initialize C link type selection flags. These flags are used when
# building a shared library, shared module, or executable that links # building a shared library, shared module, or executable that links
# to other libraries to select whether to use the static or shared # to other libraries to select whether to use the static or shared

View File

@ -8,6 +8,10 @@ SET(CMAKE_FIND_LIBRARY_SUFFIXES ".sl" ".so" ".a")
SET(CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH 1) SET(CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH 1)
SET(CMAKE_LINK_DEPENDENT_LIBRARY_DIRS 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 # fortran
IF(CMAKE_COMPILER_IS_GNUG77) IF(CMAKE_COMPILER_IS_GNUG77)
SET(CMAKE_SHARED_LIBRARY_Fortran_FLAGS "-fPIC") # -pic SET(CMAKE_SHARED_LIBRARY_Fortran_FLAGS "-fPIC") # -pic

View File

@ -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_C_FLAG "-Wl,--export-dynamic")
SET(CMAKE_EXE_EXPORTS_CXX_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 # Initialize C link type selection flags. These flags are used when
# building a shared library, shared module, or executable that links # building a shared library, shared module, or executable that links
# to other libraries to select whether to use the static or shared # to other libraries to select whether to use the static or shared

View File

@ -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_C_FLAG "-Wl,--export-dynamic")
SET(CMAKE_EXE_EXPORTS_CXX_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 # Initialize C link type selection flags. These flags are used when
# building a shared library, shared module, or executable that links # building a shared library, shared module, or executable that links
# to other libraries to select whether to use the static or shared # to other libraries to select whether to use the static or shared

View File

@ -103,4 +103,6 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX)
# in the -L path. # in the -L path.
SET(CMAKE_LINK_DEPENDENT_LIBRARY_DIRS 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)

View File

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

View File

@ -17,7 +17,7 @@
#include "cmComputeLinkInformation.h" #include "cmComputeLinkInformation.h"
#include "cmComputeLinkDepends.h" #include "cmComputeLinkDepends.h"
#include "cmOrderRuntimeDirectories.h" #include "cmOrderDirectories.h"
#include "cmGlobalGenerator.h" #include "cmGlobalGenerator.h"
#include "cmLocalGenerator.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 In our implementation we add all dependent libraries to the runtime
path computation. Then the auto-generated RPATH will find everything. 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; this->Config = config;
// Allocate internals. // Allocate internals.
this->OrderLinkerSearchPath =
new cmOrderDirectories(this->GlobalGenerator, target->GetName(),
"linker search path");
this->OrderRuntimeSearchPath = this->OrderRuntimeSearchPath =
new cmOrderRuntimeDirectories(this->GlobalGenerator, target->GetName(), new cmOrderDirectories(this->GlobalGenerator, target->GetName(),
"runtime path"); "runtime search path");
this->OrderDependentRPath = 0; this->OrderDependentRPath = 0;
// Get the language used for linking this target. // Get the language used for linking this target.
@ -298,6 +325,18 @@ cmComputeLinkInformation
this->RPathLinkFlag = this->Makefile->GetSafeDefinition(rlVar.c_str()); 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. // Get link type information.
this->ComputeLinkTypeInfo(); this->ComputeLinkTypeInfo();
@ -315,24 +354,16 @@ cmComputeLinkInformation
} }
else if(this->Makefile->IsOn("CMAKE_LINK_DEPENDENT_LIBRARY_DIRS")) else if(this->Makefile->IsOn("CMAKE_LINK_DEPENDENT_LIBRARY_DIRS"))
{ {
this->SharedDependencyMode = SharedDepModeDir; this->SharedDependencyMode = SharedDepModeLibDir;
} }
else if(!this->RPathLinkFlag.empty()) else if(!this->RPathLinkFlag.empty())
{ {
this->SharedDependencyMode = SharedDepModeDir; this->SharedDependencyMode = SharedDepModeDir;
}
if(this->SharedDependencyMode == SharedDepModeDir)
{
this->OrderDependentRPath = this->OrderDependentRPath =
new cmOrderRuntimeDirectories(this->GlobalGenerator, target->GetName(), new cmOrderDirectories(this->GlobalGenerator, target->GetName(),
"dependent library path"); "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. // Get the implicit link directories for this platform.
if(const char* implicitLinks = if(const char* implicitLinks =
(this->Makefile->GetDefinition (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. // Initial state.
this->HaveUserFlagItem = false; this->HaveUserFlagItem = false;
@ -374,6 +420,7 @@ cmComputeLinkInformation
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
cmComputeLinkInformation::~cmComputeLinkInformation() cmComputeLinkInformation::~cmComputeLinkInformation()
{ {
delete this->OrderLinkerSearchPath;
delete this->OrderRuntimeSearchPath; delete this->OrderRuntimeSearchPath;
delete this->OrderDependentRPath; delete this->OrderDependentRPath;
} }
@ -388,7 +435,7 @@ cmComputeLinkInformation::GetItems()
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
std::vector<std::string> const& cmComputeLinkInformation::GetDirectories() 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) // If there is no separate linker runtime search flag (-rpath-link)
// there is no reason to compute a string. // there is no reason to compute a string.
if(!this->OrderDependentRPath || this->RPathLinkFlag.empty()) if(!this->OrderDependentRPath)
{ {
return ""; return "";
} }
@ -405,7 +452,7 @@ std::string cmComputeLinkInformation::GetRPathLinkString()
std::string rpath_link; std::string rpath_link;
const char* sep = ""; const char* sep = "";
std::vector<std::string> const& dirs = std::vector<std::string> const& dirs =
this->OrderDependentRPath->GetRuntimePath(); this->OrderDependentRPath->GetOrderedDirectories();
for(std::vector<std::string>::const_iterator di = dirs.begin(); for(std::vector<std::string>::const_iterator di = dirs.begin();
di != dirs.end(); ++di) di != dirs.end(); ++di)
{ {
@ -487,8 +534,8 @@ bool cmComputeLinkInformation::Compute()
this->SetCurrentLinkType(this->StartLinkType); this->SetCurrentLinkType(this->StartLinkType);
} }
// Compute the linker search path. // Finish setting up linker search directories.
this->ComputeLinkerSearchDirectories(); this->FinishLinkerSearchDirectories();
return true; return true;
} }
@ -637,19 +684,31 @@ void cmComputeLinkInformation::AddSharedDepItem(std::string const& item,
this->AddLibraryRuntimeInfo(lib); this->AddLibraryRuntimeInfo(lib);
} }
// Add the item to the separate dependent library search path if // Check if we need to include the dependent shared library in other
// this platform wants one. // path ordering.
if(this->OrderDependentRPath) 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) if(tgt)
{ {
std::string soName = tgt->GetSOName(this->Config); std::string soName = tgt->GetSOName(this->Config);
const char* soname = soName.empty()? 0 : soName.c_str(); const char* soname = soName.empty()? 0 : soName.c_str();
this->OrderDependentRPath->AddLibrary(lib, soname); order->AddRuntimeLibrary(lib, soname);
} }
else else
{ {
this->OrderDependentRPath->AddLibrary(lib); order->AddRuntimeLibrary(lib);
} }
} }
} }
@ -747,7 +806,8 @@ void cmComputeLinkInformation::ComputeItemParserInfo()
// Create regex to remove any library extension. // Create regex to remove any library extension.
std::string reg("(.*)"); std::string reg("(.*)");
reg += libext; 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 // 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 // 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); 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. // Keep track of shared library targets linked.
if(target->GetType() == cmTarget::SHARED_LIBRARY) if(target->GetType() == cmTarget::SHARED_LIBRARY)
{ {
this->SharedLibrariesLinked.insert(target); 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. // Now add the full path to the library.
this->Items.push_back(Item(item, true)); this->Items.push_back(Item(item, true));
} }
@ -938,6 +1006,12 @@ void cmComputeLinkInformation::AddFullItem(std::string const& item)
return; 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. // 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 // If the target is not a static library make sure the link type is
// shared. This is because dynamic-mode linking can handle both // 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 // For compatibility with CMake 2.4 include the item's directory in
// 2.4 in below added these as -L paths. // the linker search path.
if(this->OldLinkDirMode) 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. // 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 platform will use the path to a library as its soname if the
this->DirectoriesEmmitted = this->ImplicitLinkDirs; // library is given via path and was not built with an soname. If
this->DirectoriesEmmitted.insert(""); // 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.
// Check if we need to include the runtime search path at link time. std::string file = cmSystemTools::GetFilenameName(item);
std::string var = "CMAKE_SHARED_LIBRARY_LINK_"; if(this->ExtractSharedLibraryName.find(file))
var += this->LinkLanguage;
var += "_WITH_RUNTIME_PATH";
if(this->Makefile->IsOn(var.c_str()))
{ {
// This platform requires the runtime library path for shared this->AddSharedLibNoSOName(item);
// libraries to be specified at link time as -L paths. It needs return true;
// them so that transitive dependencies of the libraries linked
// may be found by the linker.
this->AddLinkerSearchDirectories(this->GetRuntimeSearchPath());
}
// 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);
}
// 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());
} }
return false;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void cmComputeLinkInformation::AddSharedLibNoSOName(std::string const& item)
cmComputeLinkInformation
::AddLinkerSearchDirectories(std::vector<std::string> const& dirs)
{ {
for(std::vector<std::string>::const_iterator i = dirs.begin(); // We have a full path to a shared library with no soname. We need
i != dirs.end(); ++i) // 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);
// Make sure the link directory ordering will find the library.
this->OrderLinkerSearchPath->AddLinkLibrary(item);
}
//----------------------------------------------------------------------------
void cmComputeLinkInformation::FinishLinkerSearchDirectories()
{
// 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& std::vector<std::string> const&
cmComputeLinkInformation::GetRuntimeSearchPath() 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(); const char* soname = soName.empty()? 0 : soName.c_str();
// Include this library in the runtime path ordering. // 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. // Include this library in the runtime path ordering.
this->OrderRuntimeSearchPath->AddLibrary(fullPath); this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath);
if(this->LinkWithRuntimePath)
{
this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath);
}
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

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

View File

@ -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());
}

View File

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

View File

@ -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());
}

View File

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

View File

@ -2048,7 +2048,17 @@ std::string cmTarget::GetSOName(const char* config)
// Lookup the imported soname. // Lookup the imported soname.
if(cmTarget::ImportInfo const* info = this->GetImportInfo(config)) 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 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) 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 // properties. The "IMPORTED_" namespace is reserved for properties
// defined by the project exporting the target. // defined by the project exporting the target.
// Initialize members.
info.NoSOName = false;
// Track the configuration-specific property suffix. // Track the configuration-specific property suffix.
std::string suffix = "_"; std::string suffix = "_";
suffix += desired_config; 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. // Get the import library.
if(this->GetType() == cmTarget::SHARED_LIBRARY || if(this->GetType() == cmTarget::SHARED_LIBRARY ||
this->IsExecutableWithExports()) this->IsExecutableWithExports())

View File

@ -281,6 +281,10 @@ public:
/** Get the soname of the target. Allowed only for a shared library. */ /** Get the soname of the target. Allowed only for a shared library. */
std::string GetSOName(const char* config); 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 /** Get the full path to the target according to the settings in its
makefile and the configuration type. */ makefile and the configuration type. */
std::string GetFullPath(const char* config=0, bool implib = false, std::string GetFullPath(const char* config=0, bool implib = false,
@ -501,6 +505,7 @@ private:
// Cache import information from properties for each configuration. // Cache import information from properties for each configuration.
struct ImportInfo struct ImportInfo
{ {
bool NoSOName;
std::string Location; std::string Location;
std::string SOName; std::string SOName;
std::string ImportLibrary; std::string ImportLibrary;

View File

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