Commit fd37a6ec authored by Brad King's avatar Brad King 💬
Browse files

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
parent 9f2f456e
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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)
......@@ -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
......
......@@ -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);
}
}
//----------------------------------------------------------------------------
......
......@@ -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
......@@ -14,25 +14,34 @@
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef cmOrderRuntimeDirectories_h
#define cmOrderRuntimeDirectories_h
#ifndef cmOrderDirectories_h
#define cmOrderDirectories_h
#include "cmStandardIncludes.h"
#include <cmsys/RegularExpression.hxx>
class cmGlobalGenerator;
class cmOrderDirectoriesConstraint;
class cmOrderDirectoriesConstraintLibrary;
/** \class cmOrderRuntimeDirectories
/** \class cmOrderDirectories
* \brief Compute a safe runtime path order for a set of shared libraries.
*/
class cmOrderRuntimeDirectories
class cmOrderDirectories
{
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();
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;
......@@ -40,36 +49,24 @@ private:
bool Computed;
std::vector<std::string> RuntimeSearchPath;
// Runtime path computation.
struct LibraryRuntimeEntry
{
// The file name of the library.
std::string FileName;
std::vector<std::string> OrderedDirectories;
// 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;
bool OrderedDirectoriesComputed;
std::vector<cmOrderDirectoriesConstraint*> ConstraintEntries;
std::vector<std::string> UserDirectories;