summaryrefslogtreecommitdiffstats
path: root/Source/cmOrderRuntimeDirectories.cxx
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2008-02-01 13:56:00 (GMT)
committerBrad King <brad.king@kitware.com>2008-02-01 13:56:00 (GMT)
commit82fcaebe2804f88cfee258ab10003c34a9cee9da (patch)
tree8bb77511530f9da1e38e79ad85637cddeba77977 /Source/cmOrderRuntimeDirectories.cxx
parentf28f1585f663314648e608e5f156d22d7b1e5811 (diff)
downloadCMake-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.cxx325
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());
+}