From fd37a6ec3d31e65eed2dfa88246e86b8d0ab66ab Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 21 Feb 2008 11:41:11 -0500 Subject: 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 --- Modules/Platform/FreeBSD.cmake | 4 + Modules/Platform/HP-UX.cmake | 4 + Modules/Platform/Linux.cmake | 4 + Modules/Platform/QNX.cmake | 4 + Modules/Platform/SunOS.cmake | 4 +- Source/CMakeLists.txt | 4 +- Source/cmComputeLinkInformation.cxx | 224 ++++++++++----- Source/cmComputeLinkInformation.h | 27 +- Source/cmOrderDirectories.cxx | 524 +++++++++++++++++++++++++++++++++++ Source/cmOrderDirectories.h | 88 ++++++ Source/cmOrderRuntimeDirectories.cxx | 325 ---------------------- Source/cmOrderRuntimeDirectories.h | 88 ------ Source/cmTarget.cxx | 43 ++- Source/cmTarget.h | 5 + bootstrap | 2 +- 15 files changed, 845 insertions(+), 505 deletions(-) create mode 100644 Source/cmOrderDirectories.cxx create mode 100644 Source/cmOrderDirectories.h delete mode 100644 Source/cmOrderRuntimeDirectories.cxx delete mode 100644 Source/cmOrderRuntimeDirectories.h diff --git a/Modules/Platform/FreeBSD.cmake b/Modules/Platform/FreeBSD.cmake index 299847b..e7b26ac 100644 --- a/Modules/Platform/FreeBSD.cmake +++ b/Modules/Platform/FreeBSD.cmake @@ -12,6 +12,10 @@ IF(EXISTS /usr/include/dlfcn.h) SET(CMAKE_EXE_EXPORTS_CXX_FLAG "-Wl,--export-dynamic") ENDIF(EXISTS /usr/include/dlfcn.h) +# Shared libraries with no builtin soname may not be linked safely by +# specifying the file path. +SET(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1) + # Initialize C link type selection flags. These flags are used when # building a shared library, shared module, or executable that links # to other libraries to select whether to use the static or shared diff --git a/Modules/Platform/HP-UX.cmake b/Modules/Platform/HP-UX.cmake index ae036b9..50dedc3 100644 --- a/Modules/Platform/HP-UX.cmake +++ b/Modules/Platform/HP-UX.cmake @@ -8,6 +8,10 @@ SET(CMAKE_FIND_LIBRARY_SUFFIXES ".sl" ".so" ".a") SET(CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH 1) SET(CMAKE_LINK_DEPENDENT_LIBRARY_DIRS 1) +# Shared libraries with no builtin soname may not be linked safely by +# specifying the file path. +SET(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1) + # fortran IF(CMAKE_COMPILER_IS_GNUG77) SET(CMAKE_SHARED_LIBRARY_Fortran_FLAGS "-fPIC") # -pic diff --git a/Modules/Platform/Linux.cmake b/Modules/Platform/Linux.cmake index 095eac6..eba4bb3 100644 --- a/Modules/Platform/Linux.cmake +++ b/Modules/Platform/Linux.cmake @@ -12,6 +12,10 @@ SET(CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG "-Wl,-soname,") SET(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic") SET(CMAKE_EXE_EXPORTS_CXX_FLAG "-Wl,--export-dynamic") +# Shared libraries with no builtin soname may not be linked safely by +# specifying the file path. +SET(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1) + # Initialize C link type selection flags. These flags are used when # building a shared library, shared module, or executable that links # to other libraries to select whether to use the static or shared diff --git a/Modules/Platform/QNX.cmake b/Modules/Platform/QNX.cmake index f73997a..b48383a 100644 --- a/Modules/Platform/QNX.cmake +++ b/Modules/Platform/QNX.cmake @@ -19,6 +19,10 @@ SET(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG "-Wl,-soname,") SET(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic") SET(CMAKE_EXE_EXPORTS_CXX_FLAG "-Wl,--export-dynamic") +# Shared libraries with no builtin soname may not be linked safely by +# specifying the file path. +SET(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1) + # Initialize C link type selection flags. These flags are used when # building a shared library, shared module, or executable that links # to other libraries to select whether to use the static or shared diff --git a/Modules/Platform/SunOS.cmake b/Modules/Platform/SunOS.cmake index f6c9d2e..9bd8b09 100644 --- a/Modules/Platform/SunOS.cmake +++ b/Modules/Platform/SunOS.cmake @@ -103,4 +103,6 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX) # in the -L path. SET(CMAKE_LINK_DEPENDENT_LIBRARY_DIRS 1) - +# Shared libraries with no builtin soname may not be linked safely by +# specifying the file path. +SET(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 20b2a3b..987e908 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -167,8 +167,8 @@ SET(SRCS cmMakefileExecutableTargetGenerator.cxx cmMakefileLibraryTargetGenerator.cxx cmMakefileUtilityTargetGenerator.cxx - cmOrderRuntimeDirectories.cxx - cmOrderRuntimeDirectories.h + cmOrderDirectories.cxx + cmOrderDirectories.h cmProperty.cxx cmProperty.h cmPropertyDefinition.cxx diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index dea08bc..1da512c 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -17,7 +17,7 @@ #include "cmComputeLinkInformation.h" #include "cmComputeLinkDepends.h" -#include "cmOrderRuntimeDirectories.h" +#include "cmOrderDirectories.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" @@ -212,6 +212,30 @@ libraries that are also linked need to be listed in -L paths. In our implementation we add all dependent libraries to the runtime path computation. Then the auto-generated RPATH will find everything. +------------------------------------------------------------------------------ +Notes about shared libraries with not builtin soname: + +Some UNIX shared libraries may be created with no builtin soname. On +some platforms such libraries cannot be linked using the path to their +location because the linker will copy the path into the field used to +find the library at runtime. + + Apple: ../libfoo.dylib ==> libfoo.dylib # ok, uses install_name + SGI: ../libfoo.so ==> libfoo.so # ok + AIX: ../libfoo.so ==> libfoo.so # ok + Linux: ../libfoo.so ==> ../libfoo.so # bad + HP-UX: ../libfoo.so ==> ../libfoo.so # bad + Sun: ../libfoo.so ==> ../libfoo.so # bad + FreeBSD: ../libfoo.so ==> ../libfoo.so # bad + +In order to link these libraries we need to use the old-style split +into -L.. and -lfoo options. This should be fairly safe because most +problems with -lfoo options were related to selecting shared libraries +instead of static but in this case we want the shared lib. Link +directory ordering needs to be done to make sure these shared +libraries are found first. There should be very few restrictions +because this need be done only for shared libraries without soname-s. + */ //---------------------------------------------------------------------------- @@ -229,9 +253,12 @@ cmComputeLinkInformation this->Config = config; // Allocate internals. + this->OrderLinkerSearchPath = + new cmOrderDirectories(this->GlobalGenerator, target->GetName(), + "linker search path"); this->OrderRuntimeSearchPath = - new cmOrderRuntimeDirectories(this->GlobalGenerator, target->GetName(), - "runtime path"); + new cmOrderDirectories(this->GlobalGenerator, target->GetName(), + "runtime search path"); this->OrderDependentRPath = 0; // Get the language used for linking this target. @@ -298,6 +325,18 @@ cmComputeLinkInformation this->RPathLinkFlag = this->Makefile->GetSafeDefinition(rlVar.c_str()); } + // Check if we need to include the runtime search path at link time. + { + std::string var = "CMAKE_SHARED_LIBRARY_LINK_"; + var += this->LinkLanguage; + var += "_WITH_RUNTIME_PATH"; + this->LinkWithRuntimePath = this->Makefile->IsOn(var.c_str()); + } + + // Check the platform policy for missing soname case. + this->NoSONameUsesPath = + this->Makefile->IsOn("CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME"); + // Get link type information. this->ComputeLinkTypeInfo(); @@ -315,24 +354,16 @@ cmComputeLinkInformation } else if(this->Makefile->IsOn("CMAKE_LINK_DEPENDENT_LIBRARY_DIRS")) { - this->SharedDependencyMode = SharedDepModeDir; + this->SharedDependencyMode = SharedDepModeLibDir; } else if(!this->RPathLinkFlag.empty()) { this->SharedDependencyMode = SharedDepModeDir; - } - if(this->SharedDependencyMode == SharedDepModeDir) - { this->OrderDependentRPath = - new cmOrderRuntimeDirectories(this->GlobalGenerator, target->GetName(), - "dependent library path"); + new cmOrderDirectories(this->GlobalGenerator, target->GetName(), + "dependent library path"); } - // Add the search path entries requested by the user to the runtime - // path computation. - this->OrderRuntimeSearchPath->AddDirectories( - this->Target->GetLinkDirectories()); - // Get the implicit link directories for this platform. if(const char* implicitLinks = (this->Makefile->GetDefinition @@ -348,6 +379,21 @@ cmComputeLinkInformation } } + // Add the search path entries requested by the user to path ordering. + this->OrderLinkerSearchPath + ->AddUserDirectories(this->Target->GetLinkDirectories()); + this->OrderRuntimeSearchPath + ->AddUserDirectories(this->Target->GetLinkDirectories()); + this->OrderLinkerSearchPath + ->SetImplicitDirectories(this->ImplicitLinkDirs); + this->OrderRuntimeSearchPath + ->SetImplicitDirectories(this->ImplicitLinkDirs); + if(this->OrderDependentRPath) + { + this->OrderDependentRPath + ->SetImplicitDirectories(this->ImplicitLinkDirs); + } + // Initial state. this->HaveUserFlagItem = false; @@ -374,6 +420,7 @@ cmComputeLinkInformation //---------------------------------------------------------------------------- cmComputeLinkInformation::~cmComputeLinkInformation() { + delete this->OrderLinkerSearchPath; delete this->OrderRuntimeSearchPath; delete this->OrderDependentRPath; } @@ -388,7 +435,7 @@ cmComputeLinkInformation::GetItems() //---------------------------------------------------------------------------- std::vector 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 const& dirs = - this->OrderDependentRPath->GetRuntimePath(); + this->OrderDependentRPath->GetOrderedDirectories(); for(std::vector::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 const& dirs) +void cmComputeLinkInformation::FinishLinkerSearchDirectories() { - for(std::vector::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::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 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); + } } //---------------------------------------------------------------------------- diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h index 0145e78..c0f8b5c 100644 --- a/Source/cmComputeLinkInformation.h +++ b/Source/cmComputeLinkInformation.h @@ -26,7 +26,7 @@ class cmGlobalGenerator; class cmLocalGenerator; class cmMakefile; class cmTarget; -class cmOrderRuntimeDirectories; +class cmOrderDirectories; /** \class cmComputeLinkInformation * \brief Compute link information for a target in one configuration. @@ -89,9 +89,10 @@ private: // Modes for dealing with dependent shared libraries. enum SharedDepMode { - SharedDepModeNone, // Drop - SharedDepModeDir, // Use in runtime information - SharedDepModeLink // List file on link line + SharedDepModeNone, // Drop + SharedDepModeDir, // List dir in -rpath-link flag + SharedDepModeLibDir, // List dir in linker search path + SharedDepModeLink // List file on link line }; // System info. @@ -104,6 +105,8 @@ private: std::string RuntimeSep; std::string RuntimeAlways; bool RuntimeUseChrpath; + bool NoSONameUsesPath; + bool LinkWithRuntimePath; std::string RPathLinkFlag; SharedDepMode SharedDependencyMode; @@ -124,7 +127,6 @@ private: std::vector SharedLinkExtensions; std::vector LinkExtensions; std::set LinkPrefixes; - cmsys::RegularExpression RemoveLibraryExtension; cmsys::RegularExpression ExtractStaticLibraryName; cmsys::RegularExpression ExtractSharedLibraryName; cmsys::RegularExpression ExtractAnyLibraryName; @@ -133,7 +135,7 @@ private: std::string CreateExtensionRegex(std::vector 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 const& dirs); - std::set DirectoriesEmmitted; + cmOrderDirectories* OrderLinkerSearchPath; + void FinishLinkerSearchDirectories(); std::set ImplicitLinkDirs; // Linker search path compatibility mode. - std::vector OldLinkDirs; + std::vector 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 diff --git a/Source/cmOrderDirectories.cxx b/Source/cmOrderDirectories.cxx new file mode 100644 index 0000000..7bc5dd6 --- /dev/null +++ b/Source/cmOrderDirectories.cxx @@ -0,0 +1,524 @@ +/*========================================================================= + + Program: CMake - Cross-Platform Makefile Generator + Module: $RCSfile$ + Language: C++ + Date: $Date$ + Version: $Revision$ + + Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. + See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#include "cmOrderDirectories.h" + +#include "cmGlobalGenerator.h" +#include "cmSystemTools.h" + +#include + +#include + +/* +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 const& files = + (this->GlobalGenerator->GetDirectoryContent(dir, false)); + if(std::set::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 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::const_iterator first = files.lower_bound(base); + ++base[base.size()-1]; + std::set::const_iterator last = files.upper_bound(base); + bool found = false; + for(std::set::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::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::iterator + i = this->ConstraintEntries.begin(); + i != this->ConstraintEntries.end(); ++i) + { + delete *i; + } +} + +//---------------------------------------------------------------------------- +std::vector 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 const& extra) +{ + this->UserDirectories.insert(this->UserDirectories.end(), + extra.begin(), extra.end()); +} + +//---------------------------------------------------------------------------- +void +cmOrderDirectories +::SetImplicitDirectories(std::set const& implicitDirs) +{ + this->ImplicitDirectories = implicitDirs; +} + +//---------------------------------------------------------------------------- +void +cmOrderDirectories +::SetLinkExtensionInfo(std::vector 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::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::iterator i = + this->DirectoryIndex.find(dir); + if(i == this->DirectoryIndex.end()) + { + std::map::value_type + entry(dir, static_cast(this->OriginalDirectories.size())); + i = this->DirectoryIndex.insert(entry).first; + this->OriginalDirectories.push_back(dir); + } + + return i->second; +} + +//---------------------------------------------------------------------------- +struct cmOrderDirectoriesCompare +{ + typedef std::pair 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::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()); +} diff --git a/Source/cmOrderDirectories.h b/Source/cmOrderDirectories.h new file mode 100644 index 0000000..00a5955 --- /dev/null +++ b/Source/cmOrderDirectories.h @@ -0,0 +1,88 @@ +/*========================================================================= + + Program: CMake - Cross-Platform Makefile Generator + Module: $RCSfile$ + Language: C++ + Date: $Date$ + Version: $Revision$ + + Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. + See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef cmOrderDirectories_h +#define cmOrderDirectories_h + +#include "cmStandardIncludes.h" + +#include + +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 const& extra); + void SetImplicitDirectories(std::set const& implicitDirs); + void SetLinkExtensionInfo(std::vector const& linkExtensions, + std::string const& removeExtRegex); + + std::vector const& GetOrderedDirectories(); +private: + cmGlobalGenerator* GlobalGenerator; + std::string Name; + std::string Purpose; + + bool Computed; + + std::vector OrderedDirectories; + + bool OrderedDirectoriesComputed; + std::vector ConstraintEntries; + std::vector UserDirectories; + cmsys::RegularExpression RemoveLibraryExtension; + std::vector LinkExtensions; + std::set ImplicitDirectories; + std::set EmmittedConstraintSOName; + std::set EmmittedConstraintLibrary; + std::vector OriginalDirectories; + std::map DirectoryIndex; + std::vector 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 ConflictPair; + struct ConflictList: public std::vector {}; + std::vector ConflictGraph; + + friend class cmOrderDirectoriesConstraint; + friend class cmOrderDirectoriesConstraintLibrary; +}; + +#endif diff --git a/Source/cmOrderRuntimeDirectories.cxx b/Source/cmOrderRuntimeDirectories.cxx deleted file mode 100644 index b0fe337..0000000 --- a/Source/cmOrderRuntimeDirectories.cxx +++ /dev/null @@ -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 - -/* -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 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 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::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::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::iterator i = - this->RuntimeDirectoryIndex.find(dir); - if(i == this->RuntimeDirectoryIndex.end()) - { - std::map::value_type - entry(dir, static_cast(this->RuntimeDirectories.size())); - i = this->RuntimeDirectoryIndex.insert(entry).first; - this->RuntimeDirectories.push_back(dir); - } - - return i->second; -} - -//---------------------------------------------------------------------------- -struct cmOrderRuntimeDirectoriesCompare -{ - typedef std::pair 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::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 const& files = - (this->GlobalGenerator - ->GetDirectoryContent(this->RuntimeDirectories[i], false)); - if((std::set::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 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::const_iterator first = files.lower_bound(base); - ++base[base.size()-1]; - std::set::const_iterator last = files.upper_bound(base); - bool found = false; - for(std::set::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()); -} diff --git a/Source/cmOrderRuntimeDirectories.h b/Source/cmOrderRuntimeDirectories.h deleted file mode 100644 index d249f8f..0000000 --- a/Source/cmOrderRuntimeDirectories.h +++ /dev/null @@ -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 const& extra); - - std::vector const& GetRuntimePath(); -private: - cmGlobalGenerator* GlobalGenerator; - std::string Name; - std::string Purpose; - - bool Computed; - - std::vector 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 LibraryRuntimeInfo; - std::vector UserDirectories; - std::set LibraryRuntimeInfoEmmitted; - std::vector RuntimeDirectories; - std::map RuntimeDirectoryIndex; - std::vector 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 RuntimeConflictPair; - struct RuntimeConflictList: public std::vector {}; - std::vector RuntimeConflictGraph; -}; - -#endif diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 4e3a345..3da4627 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -2048,7 +2048,17 @@ std::string cmTarget::GetSOName(const char* config) // Lookup the imported soname. if(cmTarget::ImportInfo const* info = this->GetImportInfo(config)) { - return info->SOName; + if(info->NoSOName) + { + // The imported library has no builtin soname so the name + // searched at runtime will be just the filename. + return cmSystemTools::GetFilenameName(info->Location); + } + else + { + // Use the soname given if any. + return info->SOName; + } } else { @@ -2069,6 +2079,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) { // This should not be called for imported targets. @@ -3054,6 +3077,9 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config, // properties. The "IMPORTED_" namespace is reserved for properties // defined by the project exporting the target. + // Initialize members. + info.NoSOName = false; + // Track the configuration-specific property suffix. std::string suffix = "_"; suffix += desired_config; @@ -3164,6 +3190,21 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config, } } + // Get the "no-soname" mark. + if(this->GetType() == cmTarget::SHARED_LIBRARY) + { + std::string soProp = "IMPORTED_NO_SONAME"; + soProp += suffix; + if(const char* config_no_soname = this->GetProperty(soProp.c_str())) + { + info.NoSOName = cmSystemTools::IsOn(config_no_soname); + } + else if(const char* no_soname = this->GetProperty("IMPORTED_NO_SONAME")) + { + info.NoSOName = cmSystemTools::IsOn(no_soname); + } + } + // Get the import library. if(this->GetType() == cmTarget::SHARED_LIBRARY || this->IsExecutableWithExports()) diff --git a/Source/cmTarget.h b/Source/cmTarget.h index ad5a815..95c49da 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -281,6 +281,10 @@ public: /** Get the soname of the target. Allowed only for a shared library. */ std::string GetSOName(const char* config); + /** Test for special case of a third-party shared library that has + no soname at all. */ + bool IsImportedSharedLibWithoutSOName(const char* config); + /** Get the full path to the target according to the settings in its makefile and the configuration type. */ std::string GetFullPath(const char* config=0, bool implib = false, @@ -501,6 +505,7 @@ private: // Cache import information from properties for each configuration. struct ImportInfo { + bool NoSOName; std::string Location; std::string SOName; std::string ImportLibrary; diff --git a/bootstrap b/bootstrap index c14c4e0..40199ad 100755 --- a/bootstrap +++ b/bootstrap @@ -175,7 +175,7 @@ CMAKE_CXX_SOURCES="\ cmListFileCache \ cmComputeLinkDepends \ cmComputeLinkInformation \ - cmOrderRuntimeDirectories \ + cmOrderDirectories \ cmComputeTargetDepends \ cmComputeComponentGraph \ cmExprLexer \ -- cgit v0.12