ENH: Pass dependent library search path to linker on some platforms.

- Move runtime path ordering out of cmComputeLinkInformation
    into its own class cmOrderRuntimeDirectories.
  - Create an instance of cmOrderRuntimeDirectories for runtime
    path ordering and another instance for dependent library
    path ordering.
  - Replace CMAKE_DEPENDENT_SHARED_LIBRARY_MODE with explicit
    CMAKE_LINK_DEPENDENT_LIBRARY_FILES boolean.
  - Create CMAKE_LINK_DEPENDENT_LIBRARY_DIRS boolean.
  - Create variables to specify -rpath-link flags:
      CMAKE_SHARED_LIBRARY_RPATH_LINK_<LANG>_FLAG
      CMAKE_EXECUTABLE_RPATH_LINK_<LANG>_FLAG
  - Enable -rpath-link flag on Linux and QNX.
  - Documentation and error message updates
This commit is contained in:
Brad King 2008-02-01 08:56:00 -05:00
parent f28f1585f6
commit 82fcaebe28
22 changed files with 718 additions and 384 deletions

View File

@ -157,6 +157,10 @@ IF(NOT CMAKE_EXECUTABLE_RUNTIME_C_FLAG_SEP)
SET(CMAKE_EXECUTABLE_RUNTIME_C_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP}) SET(CMAKE_EXECUTABLE_RUNTIME_C_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP})
ENDIF(NOT CMAKE_EXECUTABLE_RUNTIME_C_FLAG_SEP) ENDIF(NOT CMAKE_EXECUTABLE_RUNTIME_C_FLAG_SEP)
IF(NOT CMAKE_EXECUTABLE_RPATH_LINK_C_FLAG)
SET(CMAKE_EXECUTABLE_RPATH_LINK_C_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG})
ENDIF(NOT CMAKE_EXECUTABLE_RPATH_LINK_C_FLAG)
MARK_AS_ADVANCED( MARK_AS_ADVANCED(
CMAKE_C_FLAGS CMAKE_C_FLAGS
CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_DEBUG

View File

@ -84,6 +84,10 @@ IF(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP)
SET(CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP}) SET(CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP})
ENDIF(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP) ENDIF(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP)
IF(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG)
SET(CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG})
ENDIF(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG)
IF(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG) IF(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG)
SET(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG}) SET(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG})
ENDIF(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG) ENDIF(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG)
@ -92,6 +96,10 @@ IF(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG_SEP)
SET(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP}) SET(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP})
ENDIF(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG_SEP) ENDIF(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG_SEP)
IF(NOT CMAKE_EXECUTABLE_RPATH_LINK_CXX_FLAG)
SET(CMAKE_EXECUTABLE_RPATH_LINK_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG})
ENDIF(NOT CMAKE_EXECUTABLE_RPATH_LINK_CXX_FLAG)
IF(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_CXX_WITH_RUNTIME_PATH) IF(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_CXX_WITH_RUNTIME_PATH)
SET(CMAKE_SHARED_LIBRARY_LINK_CXX_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH}) SET(CMAKE_SHARED_LIBRARY_LINK_CXX_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH})
ENDIF(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_CXX_WITH_RUNTIME_PATH) ENDIF(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_CXX_WITH_RUNTIME_PATH)

View File

@ -59,6 +59,10 @@ IF(NOT CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP)
SET(CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP}) SET(CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP})
ENDIF(NOT CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP) ENDIF(NOT CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP)
IF(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG)
SET(CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG})
ENDIF(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG)
# repeat for modules # repeat for modules
IF(NOT CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS) IF(NOT CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS)
SET(CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS ${CMAKE_SHARED_MODULE_CREATE_C_FLAGS}) SET(CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS ${CMAKE_SHARED_MODULE_CREATE_C_FLAGS})
@ -84,6 +88,10 @@ IF(NOT CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG_SEP)
SET(CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP}) SET(CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP})
ENDIF(NOT CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG_SEP) ENDIF(NOT CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG_SEP)
IF(NOT CMAKE_EXECUTABLE_RPATH_LINK_Fortran_FLAG)
SET(CMAKE_EXECUTABLE_RPATH_LINK_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG})
ENDIF(NOT CMAKE_EXECUTABLE_RPATH_LINK_Fortran_FLAG)
IF(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_Fortran_WITH_RUNTIME_PATH) IF(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_Fortran_WITH_RUNTIME_PATH)
SET(CMAKE_SHARED_LIBRARY_LINK_Fortran_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH}) SET(CMAKE_SHARED_LIBRARY_LINK_Fortran_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH})
ENDIF(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_Fortran_WITH_RUNTIME_PATH) ENDIF(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_Fortran_WITH_RUNTIME_PATH)

View File

@ -107,7 +107,7 @@ ENDIF(XCODE)
# with -isysroot (for universal binaries), the linker always looks for # with -isysroot (for universal binaries), the linker always looks for
# dependent libraries under the sysroot. Listing them on the link # dependent libraries under the sysroot. Listing them on the link
# line works around the problem. # line works around the problem.
SET(CMAKE_DEPENDENT_SHARED_LIBRARY_MODE "LINK") SET(CMAKE_LINK_DEPENDENT_LIBRARY_FILES 1)
SET(CMAKE_MacOSX_Content_COMPILE_OBJECT "\"${CMAKE_COMMAND}\" -E copy_if_different <SOURCE> <OBJECT>") SET(CMAKE_MacOSX_Content_COMPILE_OBJECT "\"${CMAKE_COMMAND}\" -E copy_if_different <SOURCE> <OBJECT>")

View File

@ -5,6 +5,7 @@ SET(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared")
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-rdynamic") SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-rdynamic")
SET(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") SET(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,")
SET(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":") SET(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":")
SET(CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG "-Wl,-rpath-link,")
SET(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,") SET(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,")
SET(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG "-Wl,-soname,") SET(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG "-Wl,-soname,")
SET(CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG "-Wl,-soname,") SET(CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG "-Wl,-soname,")

View File

@ -13,6 +13,7 @@ SET(CMAKE_SHARED_LIBRARY_CXX_FLAGS "")
SET(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared") SET(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared")
SET(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") SET(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,")
SET(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":") SET(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":")
SET(CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG "-Wl,-rpath-link,")
SET(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,") SET(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,")
SET(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG "-Wl,-soname,") 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")

View File

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

View File

@ -123,6 +123,14 @@ integer index with each link item. When the graph is built outgoing
edges are sorted by this index. This preserves the original link edges are sorted by this index. This preserves the original link
order as much as possible subject to the dependencies. order as much as possible subject to the dependencies.
After the initial exploration of the link interface tree, any
transitive (dependent) shared libraries that were encountered and not
included in the interface are processed in their own BFS. This BFS
follows only the dependent library lists and not the link interfaces.
They are added to the link items with a mark indicating that the are
transitive dependencies. Then cmComputeLinkInformation deals with
them on a per-platform basis.
*/ */
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -17,14 +17,13 @@
#include "cmComputeLinkInformation.h" #include "cmComputeLinkInformation.h"
#include "cmComputeLinkDepends.h" #include "cmComputeLinkDepends.h"
#include "cmOrderRuntimeDirectories.h"
#include "cmGlobalGenerator.h" #include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h" #include "cmLocalGenerator.h"
#include "cmMakefile.h" #include "cmMakefile.h"
#include "cmTarget.h" #include "cmTarget.h"
#include <algorithm>
#include <ctype.h> #include <ctype.h>
/* /*
@ -150,6 +149,68 @@ ld: 92453-07 linker ld HP Itanium(R) B.12.41 IPF/IPF
or SHLIB_PATH, in searching for shared libraries. This changes or SHLIB_PATH, in searching for shared libraries. This changes
the default search order of LD_LIBRARY_PATH, SHLIB_PATH, and the default search order of LD_LIBRARY_PATH, SHLIB_PATH, and
RPATH (embedded path). RPATH (embedded path).
------------------------------------------------------------------------------
Notes about dependent (transitive) shared libraries:
On non-Windows systems shared libraries may have transitive
dependencies. In order to support LINK_INTERFACE_LIBRARIES we must
support linking to a shared library without listing all the libraries
to which it links. Some linkers want to be able to find the
transitive dependencies (dependent libraries) of shared libraries
listed on the command line.
- On Windows, DLLs are not directly linked, and the import libraries
have no transitive dependencies.
- On Mac, we need to actually list the transitive dependencies.
Otherwise when using -isysroot for universal binaries it cannot
find the dependent libraries. Listing them on the command line
tells the linker where to find them, but unfortunately also links
the library.
- On HP-UX, the linker wants to find the transitive dependencies of
shared libraries in the -L paths even if the dependent libraries
are given on the link line.
- On AIX the transitive dependencies are not needed.
- On SGI, the linker wants to find the transitive dependencies of
shared libraries in the -L paths if they are not given on the link
line. Transitive linking can be disabled using the options
-no_transitive_link -Wl,-no_transitive_link
which disable it. Both options must be given when invoking the
linker through the compiler.
- On Sun, the linker wants to find the transitive dependencies of
shared libraries in the -L paths if they are not given on the link
line.
- On Linux, FreeBSD, and QNX:
The linker wants to find the transitive dependencies of shared
libraries in the "-rpath-link" paths option if they have not been
given on the link line. The option is like rpath but just for
link time:
-Wl,-rpath-link,"/path1:/path2"
For -rpath-link, we need a separate runtime path ordering pass
including just the dependent libraries that are not linked.
For -L paths on non-HP, we can do the same thing as with rpath-link
but put the results in -L paths. The paths should be listed at the
end to avoid conflicting with user search paths (?).
For -L paths on HP, we should do a runtime path ordering pass with
all libraries, both linked and non-linked. Even dependent
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.
*/ */
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -165,6 +226,12 @@ cmComputeLinkInformation
// The configuration being linked. // The configuration being linked.
this->Config = config; this->Config = config;
// Allocate internals.
this->OrderRuntimeSearchPath =
new cmOrderRuntimeDirectories(this->GlobalGenerator, target->GetName(),
"runtime path");
this->OrderDependentRPath = 0;
// Get the language used for linking this target. // Get the language used for linking this target.
this->LinkLanguage = this->LinkLanguage =
this->Target->GetLinkerLanguage(this->GlobalGenerator); this->Target->GetLinkerLanguage(this->GlobalGenerator);
@ -204,15 +271,11 @@ cmComputeLinkInformation
this->RuntimeUseChrpath = false; this->RuntimeUseChrpath = false;
if(this->Target->GetType() != cmTarget::STATIC_LIBRARY) if(this->Target->GetType() != cmTarget::STATIC_LIBRARY)
{ {
const char* tType =
((this->Target->GetType() == cmTarget::EXECUTABLE)?
"EXECUTABLE" : "SHARED_LIBRARY");
std::string rtVar = "CMAKE_"; std::string rtVar = "CMAKE_";
if(this->Target->GetType() == cmTarget::EXECUTABLE) rtVar += tType;
{
rtVar += "EXECUTABLE";
}
else
{
rtVar += "SHARED_LIBRARY";
}
rtVar += "_RUNTIME_"; rtVar += "_RUNTIME_";
rtVar += this->LinkLanguage; rtVar += this->LinkLanguage;
rtVar += "_FLAG"; rtVar += "_FLAG";
@ -223,6 +286,14 @@ cmComputeLinkInformation
(this->Makefile-> (this->Makefile->
GetSafeDefinition("CMAKE_PLATFORM_REQUIRED_RUNTIME_PATH")); GetSafeDefinition("CMAKE_PLATFORM_REQUIRED_RUNTIME_PATH"));
this->RuntimeUseChrpath = this->Target->IsChrpathUsed(); this->RuntimeUseChrpath = this->Target->IsChrpathUsed();
// Get options needed to help find dependent libraries.
std::string rlVar = "CMAKE_";
rlVar += tType;
rlVar += "_RPATH_LINK_";
rlVar += this->LinkLanguage;
rlVar += "_FLAG";
this->RPathLinkFlag = this->Makefile->GetSafeDefinition(rlVar.c_str());
} }
// Get link type information. // Get link type information.
@ -236,17 +307,23 @@ cmComputeLinkInformation
// Choose a mode for dealing with shared library dependencies. // Choose a mode for dealing with shared library dependencies.
this->SharedDependencyMode = SharedDepModeNone; this->SharedDependencyMode = SharedDepModeNone;
if(const char* mode = if(this->Makefile->IsOn("CMAKE_LINK_DEPENDENT_LIBRARY_FILES"))
this->Makefile->GetDefinition("CMAKE_DEPENDENT_SHARED_LIBRARY_MODE"))
{ {
if(strcmp(mode, "LINK") == 0) this->SharedDependencyMode = SharedDepModeLink;
{ }
this->SharedDependencyMode = SharedDepModeLink; else if(this->Makefile->IsOn("CMAKE_LINK_DEPENDENT_LIBRARY_DIRS"))
} {
else if(strcmp(mode, "DIR") == 0) this->SharedDependencyMode = SharedDepModeDir;
{ }
this->SharedDependencyMode = SharedDepModeDir; else if(!this->RPathLinkFlag.empty())
} {
this->SharedDependencyMode = SharedDepModeDir;
}
if(this->SharedDependencyMode == SharedDepModeDir)
{
this->OrderDependentRPath =
new cmOrderRuntimeDirectories(this->GlobalGenerator, target->GetName(),
"dependent library path");
} }
// Get the implicit link directories for this platform. // Get the implicit link directories for this platform.
@ -265,7 +342,6 @@ cmComputeLinkInformation
} }
// Initial state. // Initial state.
this->RuntimeSearchPathComputed = false;
this->HaveUserFlagItem = false; this->HaveUserFlagItem = false;
// Decide whether to enable compatible library search path mode. // Decide whether to enable compatible library search path mode.
@ -288,6 +364,13 @@ cmComputeLinkInformation
} }
} }
//----------------------------------------------------------------------------
cmComputeLinkInformation::~cmComputeLinkInformation()
{
delete this->OrderRuntimeSearchPath;
delete this->OrderDependentRPath;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
cmComputeLinkInformation::ItemVector const& cmComputeLinkInformation::ItemVector const&
cmComputeLinkInformation::GetItems() cmComputeLinkInformation::GetItems()
@ -301,6 +384,31 @@ std::vector<std::string> const& cmComputeLinkInformation::GetDirectories()
return this->Directories; return this->Directories;
} }
//----------------------------------------------------------------------------
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())
{
return "";
}
// Construct the linker runtime search path.
std::string rpath_link;
const char* sep = "";
std::vector<std::string> const& dirs =
this->OrderDependentRPath->GetRuntimePath();
for(std::vector<std::string>::const_iterator di = dirs.begin();
di != dirs.end(); ++di)
{
rpath_link += sep;
sep = ":";
rpath_link += *di;
}
return rpath_link;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
std::vector<std::string> const& cmComputeLinkInformation::GetDepends() std::vector<std::string> const& cmComputeLinkInformation::GetDepends()
{ {
@ -350,7 +458,14 @@ bool cmComputeLinkInformation::Compute()
lei = linkEntries.begin(); lei = linkEntries.begin();
lei != linkEntries.end(); ++lei) lei != linkEntries.end(); ++lei)
{ {
this->AddItem(lei->Item, lei->Target, lei->IsSharedDep); if(lei->IsSharedDep)
{
this->AddSharedDepItem(lei->Item, lei->Target);
}
else
{
this->AddItem(lei->Item, lei->Target);
}
} }
// Restore the target link type so the correct system runtime // Restore the target link type so the correct system runtime
@ -372,15 +487,8 @@ bool cmComputeLinkInformation::Compute()
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmComputeLinkInformation::AddItem(std::string const& item, void cmComputeLinkInformation::AddItem(std::string const& item, cmTarget* tgt)
cmTarget* tgt, bool isSharedDep)
{ {
// If dropping shared library dependencies, ignore them.
if(isSharedDep && this->SharedDependencyMode == SharedDepModeNone)
{
return;
}
// Compute the proper name to use to link this library. // Compute the proper name to use to link this library.
const char* config = this->Config; const char* config = this->Config;
bool impexe = (tgt && tgt->IsExecutableWithExports()); bool impexe = (tgt && tgt->IsExecutableWithExports());
@ -416,14 +524,6 @@ void cmComputeLinkInformation::AddItem(std::string const& item,
(this->UseImportLibrary && (this->UseImportLibrary &&
(impexe || tgt->GetType() == cmTarget::SHARED_LIBRARY)); (impexe || tgt->GetType() == cmTarget::SHARED_LIBRARY));
// Handle shared dependencies in directory mode.
if(isSharedDep && this->SharedDependencyMode == SharedDepModeDir)
{
std::string dir = tgt->GetDirectory(config, implib);
this->SharedDependencyDirectories.push_back(dir);
return;
}
// Pass the full path to the target file. // Pass the full path to the target file.
std::string lib = tgt->GetFullPath(config, implib); std::string lib = tgt->GetFullPath(config, implib);
this->Depends.push_back(lib); this->Depends.push_back(lib);
@ -434,7 +534,6 @@ void cmComputeLinkInformation::AddItem(std::string const& item,
// link. // link.
std::string fw = tgt->GetDirectory(config, implib); std::string fw = tgt->GetDirectory(config, implib);
this->AddFrameworkItem(fw); this->AddFrameworkItem(fw);
this->SharedLibrariesLinked.insert(tgt);
} }
else else
{ {
@ -469,6 +568,84 @@ void cmComputeLinkInformation::AddItem(std::string const& item,
} }
} }
//----------------------------------------------------------------------------
void cmComputeLinkInformation::AddSharedDepItem(std::string const& item,
cmTarget* tgt)
{
// If dropping shared library dependencies, ignore them.
if(this->SharedDependencyMode == SharedDepModeNone)
{
return;
}
// The user may have incorrectly named an item. Skip items that are
// not full paths to shared libraries.
if(tgt)
{
// The target will provide a full path. Make sure it is a shared
// library.
if(tgt->GetType() != cmTarget::SHARED_LIBRARY)
{
return;
}
}
else
{
// Skip items that are not full paths. We will not be able to
// reliably specify them.
if(!cmSystemTools::FileIsFullPath(item.c_str()))
{
return;
}
// Get the name of the library from the file name.
std::string file = cmSystemTools::GetFilenameName(item);
if(!this->ExtractSharedLibraryName.find(file.c_str()))
{
// This is not the name of a shared library.
return;
}
}
// If in linking mode, just link to the shared library.
if(this->SharedDependencyMode == SharedDepModeLink)
{
this->AddItem(item, tgt);
return;
}
// Get a full path to the dependent shared library.
// Add it to the runtime path computation so that the target being
// linked will be able to find it.
std::string lib;
if(tgt)
{
lib = tgt->GetFullPath(this->Config, this->UseImportLibrary);
this->AddLibraryRuntimeInfo(lib, tgt);
}
else
{
lib = item;
this->AddLibraryRuntimeInfo(lib);
}
// Add the item to the separate dependent library search path if
// this platform wants one.
if(this->OrderDependentRPath)
{
if(tgt)
{
std::string soName = tgt->GetSOName(this->Config);
const char* soname = soName.empty()? 0 : soName.c_str();
this->OrderDependentRPath->AddLibrary(lib, soname);
}
else
{
this->OrderDependentRPath->AddLibrary(lib);
}
}
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmComputeLinkInformation::ComputeLinkTypeInfo() void cmComputeLinkInformation::ComputeLinkTypeInfo()
{ {
@ -1022,17 +1199,14 @@ void cmComputeLinkInformation::ComputeLinkerSearchDirectories()
this->AddLinkerSearchDirectories(this->OldLinkDirs); this->AddLinkerSearchDirectories(this->OldLinkDirs);
} }
// Help the linker find dependent shared libraries. // If there is no separate linker runtime search flag (-rpath-link)
if(this->SharedDependencyMode == SharedDepModeDir) // and we have a search path for dependent libraries add it to the
// link directories.
if(this->OrderDependentRPath && this->RPathLinkFlag.empty())
{ {
// TODO: These directories should probably be added to the runtime this->AddLinkerSearchDirectories
// path ordering analysis. However they are a bit different. (this->OrderDependentRPath->GetRuntimePath());
// They should be placed both on the -L path and in the rpath.
// The link-with-runtime-path feature above should be replaced by
// this.
this->AddLinkerSearchDirectories(this->SharedDependencyDirectories);
} }
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -1054,23 +1228,9 @@ cmComputeLinkInformation
std::vector<std::string> const& std::vector<std::string> const&
cmComputeLinkInformation::GetRuntimeSearchPath() cmComputeLinkInformation::GetRuntimeSearchPath()
{ {
if(!this->RuntimeSearchPathComputed) return this->OrderRuntimeSearchPath->GetRuntimePath();
{
this->RuntimeSearchPathComputed = true;
this->CollectRuntimeDirectories();
this->FindConflictingLibraries();
this->OrderRuntimeSearchPath();
}
return this->RuntimeSearchPath;
} }
//============================================================================
// 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 (in future when we do not always add them).
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void
cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath, cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath,
@ -1104,262 +1264,8 @@ cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath,
return; return;
} }
// Add the runtime information at most once. // Include this library in the runtime path ordering.
if(this->LibraryRuntimeInfoEmmitted.insert(fullPath).second) this->OrderRuntimeSearchPath->AddLibrary(fullPath, soname);
{
// 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 cmComputeLinkInformation::CollectRuntimeDirectories()
{
// Get all directories that should be in the runtime search path.
// Add directories containing libraries linked with full path.
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 the target.
std::vector<std::string> const& dirs = this->Target->GetLinkDirectories();
for(std::vector<std::string>::const_iterator di = dirs.begin();
di != dirs.end(); ++di)
{
this->AddRuntimeDirectory(*di);
}
}
//----------------------------------------------------------------------------
int cmComputeLinkInformation::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 cmCLIRuntimeConflictCompare
{
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 cmComputeLinkInformation::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(), cmCLIRuntimeConflictCompare());
i->erase(last, i->end());
}
}
//----------------------------------------------------------------------------
void cmComputeLinkInformation::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 cmComputeLinkInformation::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 cmComputeLinkInformation::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 cmComputeLinkInformation::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 runtime path for target "
<< this->Target->GetName()
<< " 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

@ -25,6 +25,7 @@ class cmGlobalGenerator;
class cmLocalGenerator; class cmLocalGenerator;
class cmMakefile; class cmMakefile;
class cmTarget; class cmTarget;
class cmOrderRuntimeDirectories;
/** \class cmComputeLinkInformation /** \class cmComputeLinkInformation
* \brief Compute link information for a target in one configuration. * \brief Compute link information for a target in one configuration.
@ -33,6 +34,7 @@ class cmComputeLinkInformation
{ {
public: public:
cmComputeLinkInformation(cmTarget* target, const char* config); cmComputeLinkInformation(cmTarget* target, const char* config);
~cmComputeLinkInformation();
bool Compute(); bool Compute();
struct Item struct Item
@ -57,8 +59,12 @@ public:
std::string GetChrpathString(); std::string GetChrpathString();
std::string GetChrpathTool(); std::string GetChrpathTool();
std::set<cmTarget*> const& GetSharedLibrariesLinked(); std::set<cmTarget*> const& GetSharedLibrariesLinked();
std::string const& GetRPathLinkFlag() const { return this->RPathLinkFlag; }
std::string GetRPathLinkString();
private: private:
void AddItem(std::string const& item, cmTarget* tgt, bool isSharedDep); void AddItem(std::string const& item, cmTarget* tgt);
void AddSharedDepItem(std::string const& item, cmTarget* tgt);
// Output information. // Output information.
ItemVector Items; ItemVector Items;
@ -96,6 +102,7 @@ private:
std::string RuntimeSep; std::string RuntimeSep;
std::string RuntimeAlways; std::string RuntimeAlways;
bool RuntimeUseChrpath; bool RuntimeUseChrpath;
std::string RPathLinkFlag;
SharedDepMode SharedDependencyMode; SharedDepMode SharedDependencyMode;
// Link type adjustment. // Link type adjustment.
@ -143,7 +150,6 @@ private:
void AddLinkerSearchDirectories(std::vector<std::string> const& dirs); void AddLinkerSearchDirectories(std::vector<std::string> const& dirs);
std::set<cmStdString> DirectoriesEmmitted; std::set<cmStdString> DirectoriesEmmitted;
std::set<cmStdString> ImplicitLinkDirs; std::set<cmStdString> ImplicitLinkDirs;
std::vector<std::string> SharedDependencyDirectories;
// Linker search path compatibility mode. // Linker search path compatibility mode.
std::vector<std::string> OldLinkDirs; std::vector<std::string> OldLinkDirs;
@ -151,48 +157,13 @@ private:
bool HaveUserFlagItem; bool HaveUserFlagItem;
// Runtime path computation. // Runtime path computation.
struct LibraryRuntimeEntry cmOrderRuntimeDirectories* OrderRuntimeSearchPath;
{
// 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::set<cmStdString> LibraryRuntimeInfoEmmitted;
std::vector<std::string> RuntimeDirectories;
std::map<cmStdString, int> RuntimeDirectoryIndex;
std::vector<int> RuntimeDirectoryVisited;
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,
const char* soname = 0); const char* soname = 0);
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. // Dependent library path computation.
// This maps from directory to those that must come *before* it. cmOrderRuntimeDirectories* OrderDependentRPath;
// 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 #endif

View File

@ -1086,10 +1086,14 @@ void cmDocumentVariables::DefineVariables(cmake* cm)
cmProperty::VARIABLE,0,0); cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_SHARED_LIBRARY_RUNTIME_<LANG>_FLAG_SEP", cm->DefineProperty("CMAKE_SHARED_LIBRARY_RUNTIME_<LANG>_FLAG_SEP",
cmProperty::VARIABLE,0,0); cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_SHARED_LIBRARY_RPATH_LINK_<LANG>_FLAG",
cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_EXECUTABLE_RUNTIME_<LANG>_FLAG", cm->DefineProperty("CMAKE_EXECUTABLE_RUNTIME_<LANG>_FLAG",
cmProperty::VARIABLE,0,0); cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_EXECUTABLE_RUNTIME_<LANG>_FLAG_SEP", cm->DefineProperty("CMAKE_EXECUTABLE_RUNTIME_<LANG>_FLAG_SEP",
cmProperty::VARIABLE,0,0); cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_EXECUTABLE_RPATH_LINK_<LANG>_FLAG",
cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_PLATFORM_REQUIRED_RUNTIME_PATH", cm->DefineProperty("CMAKE_PLATFORM_REQUIRED_RUNTIME_PATH",
cmProperty::VARIABLE,0,0); cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_SHARED_MODULE_CREATE_<LANG>_FLAGS", cm->DefineProperty("CMAKE_SHARED_MODULE_CREATE_<LANG>_FLAGS",
@ -1104,7 +1108,8 @@ void cmDocumentVariables::DefineVariables(cmake* cm)
cmProperty::VARIABLE,0,0); cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_SHARED_MODULE_RUNTIME_<LANG>_FLAG_SEP", cm->DefineProperty("CMAKE_SHARED_MODULE_RUNTIME_<LANG>_FLAG_SEP",
cmProperty::VARIABLE,0,0); cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_DEPENDENT_SHARED_LIBRARY_MODE", cm->DefineProperty("CMAKE_LINK_DEPENDENT_LIBRARY_FILES",
cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_LINK_DEPENDENT_LIBRARY_DIRS",
cmProperty::VARIABLE,0,0); cmProperty::VARIABLE,0,0);
} }

View File

@ -122,7 +122,8 @@ cmExportBuildFileGenerator
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void
cmExportBuildFileGenerator cmExportBuildFileGenerator
::ComplainAboutMissingTarget(cmTarget* target, const char* dep) ::ComplainAboutMissingTarget(cmTarget* depender,
cmTarget* dependee)
{ {
if(!this->ExportCommand || !this->ExportCommand->ErrorMessage.empty()) if(!this->ExportCommand || !this->ExportCommand->ErrorMessage.empty())
{ {
@ -130,13 +131,10 @@ cmExportBuildFileGenerator
} }
cmOStringStream e; cmOStringStream e;
e << "called with target \"" << target->GetName() e << "called with target \"" << depender->GetName()
<< "\" which links to target \"" << dep << "\" which requires target \"" << dependee->GetName()
<< "\" that is not in the export list.\n" << "\" that is not in the export list.\n"
<< "If the link dependency is not part of the public interface " << "If the required target is not easy to reference in this call, "
<< "consider setting the LINK_INTERFACE_LIBRARIES property on \""
<< target->GetName() << "\". Otherwise add it to the export list. "
<< "If the link dependency is not easy to reference in this call, "
<< "consider using the APPEND option with multiple separate calls."; << "consider using the APPEND option with multiple separate calls.";
this->ExportCommand->ErrorMessage = e.str(); this->ExportCommand->ErrorMessage = e.str();
} }

View File

@ -50,7 +50,8 @@ protected:
virtual void GenerateImportTargetsConfig(std::ostream& os, virtual void GenerateImportTargetsConfig(std::ostream& os,
const char* config, const char* config,
std::string const& suffix); std::string const& suffix);
virtual void ComplainAboutMissingTarget(cmTarget* target, const char* dep); virtual void ComplainAboutMissingTarget(cmTarget* depender,
cmTarget* dependee);
/** Fill in properties indicating built file locations. */ /** Fill in properties indicating built file locations. */
void SetImportLocationProperty(const char* config, void SetImportLocationProperty(const char* config,

View File

@ -245,7 +245,7 @@ cmExportFileGenerator
{ {
// We are not appending, so all exported targets should be // We are not appending, so all exported targets should be
// known here. This is probably user-error. // known here. This is probably user-error.
this->ComplainAboutMissingTarget(target, li->c_str()); this->ComplainAboutMissingTarget(target, tgt);
} }
// Assume the target will be exported by another command. // Assume the target will be exported by another command.
// Append it with the export namespace. // Append it with the export namespace.

View File

@ -85,7 +85,8 @@ protected:
/** Each subclass knows how to complain about a target that is /** Each subclass knows how to complain about a target that is
missing from an export set. */ missing from an export set. */
virtual void ComplainAboutMissingTarget(cmTarget*, const char* dep) = 0; virtual void ComplainAboutMissingTarget(cmTarget* depender,
cmTarget* dependee) = 0;
// The namespace in which the exports are placed in the generated file. // The namespace in which the exports are placed in the generated file.
std::string Namespace; std::string Namespace;

View File

@ -264,16 +264,12 @@ cmExportInstallFileGenerator
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void
cmExportInstallFileGenerator cmExportInstallFileGenerator
::ComplainAboutMissingTarget(cmTarget* target, const char* dep) ::ComplainAboutMissingTarget(cmTarget* depender, cmTarget* dependee)
{ {
cmOStringStream e; cmOStringStream e;
e << "INSTALL(EXPORT \"" << this->Name << "\" ...) " e << "INSTALL(EXPORT \"" << this->Name << "\" ...) "
<< "includes target \"" << target->GetName() << "includes target \"" << depender->GetName()
<< "\" which links to target \"" << dep << "\" which requires target \"" << depender->GetName()
<< "\" that is not in the export set. " << "\" that is not in the export set.";
<< "If the link dependency is not part of the public interface "
<< "consider setting the LINK_INTERFACE_LIBRARIES property on "
<< "target \"" << target->GetName() << "\". "
<< "Otherwise add it to the export set.";
cmSystemTools::Error(e.str().c_str()); cmSystemTools::Error(e.str().c_str());
} }

View File

@ -66,7 +66,8 @@ protected:
virtual void GenerateImportTargetsConfig(std::ostream& os, virtual void GenerateImportTargetsConfig(std::ostream& os,
const char* config, const char* config,
std::string const& suffix); std::string const& suffix);
virtual void ComplainAboutMissingTarget(cmTarget* target, const char* dep); virtual void ComplainAboutMissingTarget(cmTarget* depender,
cmTarget* dependee);
/** Generate a per-configuration file for the targets. */ /** Generate a per-configuration file for the targets. */
bool GenerateImportFileConfig(const char* config); bool GenerateImportFileConfig(const char* config);

View File

@ -1605,6 +1605,15 @@ void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout,
} }
} }
// Add the linker runtime search path if any.
std::string rpath_link = cli.GetRPathLinkString();
if(!cli.GetRPathLinkFlag().empty() && !rpath_link.empty())
{
fout << cli.GetRPathLinkFlag();
fout << this->EscapeForShell(rpath_link.c_str(), true);
fout << " ";
}
// Add standard libraries for this language. // Add standard libraries for this language.
std::string standardLibsVar = "CMAKE_"; std::string standardLibsVar = "CMAKE_";
standardLibsVar += cli.GetLinkLanguage(); standardLibsVar += cli.GetLinkLanguage();

View File

@ -0,0 +1,325 @@
/*=========================================================================
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

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

@ -193,13 +193,13 @@ void cmTarget::DefineProperties(cmake *cm)
"Shared libraries may be linked to other shared libraries as part " "Shared libraries may be linked to other shared libraries as part "
"of their implementation. On some platforms the linker searches " "of their implementation. On some platforms the linker searches "
"for the dependent libraries of shared libraries they are including " "for the dependent libraries of shared libraries they are including "
"in the link. CMake gives the paths to these libraries to the linker " "in the link. This property lists "
"by listing them on the link line explicitly. This property lists "
"the dependent shared libraries of an imported library. The list " "the dependent shared libraries of an imported library. The list "
"should be disjoint from the list of interface libraries in the " "should be disjoint from the list of interface libraries in the "
"IMPORTED_LINK_INTERFACE_LIBRARIES property. On platforms requiring " "IMPORTED_LINK_INTERFACE_LIBRARIES property. On platforms requiring "
"dependent shared libraries to be found at link time CMake uses this " "dependent shared libraries to be found at link time CMake uses this "
"list to add the dependent libraries to the link command line."); "list to add appropriate files or paths to the link command line. "
"Ignored for non-imported targets.");
cm->DefineProperty cm->DefineProperty
("IMPORTED_LINK_DEPENDENT_LIBRARIES_<CONFIG>", cmProperty::TARGET, ("IMPORTED_LINK_DEPENDENT_LIBRARIES_<CONFIG>", cmProperty::TARGET,

View File

@ -169,6 +169,7 @@ CMAKE_CXX_SOURCES="\
cmListFileCache \ cmListFileCache \
cmComputeLinkDepends \ cmComputeLinkDepends \
cmComputeLinkInformation \ cmComputeLinkInformation \
cmOrderRuntimeDirectories \
" "
if ${cmake_system_mingw}; then if ${cmake_system_mingw}; then