diff options
author | Brad King <brad.king@kitware.com> | 2008-02-01 13:56:00 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2008-02-01 13:56:00 (GMT) |
commit | 82fcaebe2804f88cfee258ab10003c34a9cee9da (patch) | |
tree | 8bb77511530f9da1e38e79ad85637cddeba77977 /Source/cmOrderRuntimeDirectories.cxx | |
parent | f28f1585f663314648e608e5f156d22d7b1e5811 (diff) | |
download | CMake-82fcaebe2804f88cfee258ab10003c34a9cee9da.zip CMake-82fcaebe2804f88cfee258ab10003c34a9cee9da.tar.gz CMake-82fcaebe2804f88cfee258ab10003c34a9cee9da.tar.bz2 |
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
Diffstat (limited to 'Source/cmOrderRuntimeDirectories.cxx')
-rw-r--r-- | Source/cmOrderRuntimeDirectories.cxx | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/Source/cmOrderRuntimeDirectories.cxx b/Source/cmOrderRuntimeDirectories.cxx new file mode 100644 index 0000000..b0fe337 --- /dev/null +++ b/Source/cmOrderRuntimeDirectories.cxx @@ -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()); +} |