ENH: add a new library path ordering algorithm to make sure -L paths will pick the correct libraries if possible

This commit is contained in:
Bill Hoffman 2005-02-24 13:16:41 -05:00
parent 9c4997bf06
commit ff812580eb
6 changed files with 549 additions and 93 deletions

View File

@ -44,6 +44,7 @@ SET(SRCS
cmMakeDepend.h
cmMakefile.cxx
cmMakefile.h
cmOrderLinkDirectories.cxx
cmSourceFile.cxx
cmSourceFile.h
cmSourceGroup.cxx

View File

@ -20,6 +20,7 @@
#include "cmMakefile.h"
#include "cmGeneratedFileStream.h"
#include "cmSourceFile.h"
#include "cmOrderLinkDirectories.h"
cmLocalGenerator::cmLocalGenerator()
{
@ -1077,6 +1078,16 @@ void cmLocalGenerator::GetTargetFlags(std::string& linkLibs,
}
void
cmLocalGenerator::DetermineLibraryPathOrder(const cmTarget& target,
std::vector<std::string>&
linkPaths,
std::vector<std::string>&
linkLibs)
{
}
/**
* Output the linking rules on a command line. For executables,
* targetLibrary should be a NULL pointer. For libraries, it should point
@ -1088,7 +1099,6 @@ void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout,
{
// Try to emit each search path once
std::set<cmStdString> emitted;
// Embed runtime search paths if possible and if required.
bool outputRuntime = true;
std::string runtimeFlag;
@ -1097,12 +1107,21 @@ void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout,
std::string buildType = m_Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
buildType = cmSystemTools::UpperCase(buildType);
cmTarget::LinkLibraryType cmakeBuildType = cmTarget::GENERAL;
if(buildType == "DEBUG")
{
cmakeBuildType = cmTarget::DEBUG;
}
if(buildType.size())
{
cmakeBuildType = cmTarget::OPTIMIZED;
}
const char* linkLanguage = tgt.GetLinkerLanguage(this->GetGlobalGenerator());
if(!linkLanguage)
{
cmSystemTools::Error("CMake can not determine linker language for target:",
tgt.GetName());
cmSystemTools::
Error("CMake can not determine linker language for target:",
tgt.GetName());
return;
}
std::string runTimeFlagVar = "CMAKE_SHARED_LIBRARY_RUNTIME_";
@ -1137,8 +1156,32 @@ void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout,
linkLibs += " ";
}
const std::vector<std::string>& libdirs = tgt.GetLinkDirectories();
for(std::vector<std::string>::const_iterator libDir = libdirs.begin();
cmOrderLinkDirectories orderLibs;
std::string ext =
m_Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_SUFFIX");
if(ext.size())
{
orderLibs.AddLinkExtension(ext.c_str());
}
ext =
m_Makefile->GetSafeDefinition("CMAKE_SHARED_LIBRARY_SUFFIX");
if(ext.size())
{
orderLibs.AddLinkExtension(ext.c_str());
}
ext =
m_Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX");
if(ext.size())
{
orderLibs.AddLinkExtension(ext.c_str());
}
// compute the correct order for -L paths
orderLibs.SetLinkInformation(tgt, cmakeBuildType, targetLibrary);
orderLibs.DetermineLibraryPathOrder();
std::vector<cmStdString> libdirs;
std::vector<cmStdString> linkItems;
orderLibs.GetLinkerInformation(libdirs, linkItems);
for(std::vector<cmStdString>::const_iterator libDir = libdirs.begin();
libDir != libdirs.end(); ++libDir)
{
std::string libpath = this->ConvertToOutputForExisting(libDir->c_str());
@ -1169,103 +1212,30 @@ void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout,
}
}
std::string linkSuffix = m_Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX");
std::string linkSuffix =
m_Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX");
std::string regexp = ".*\\";
regexp += linkSuffix;
regexp += "$";
cmsys::RegularExpression hasSuffix(regexp.c_str());
std::string librariesLinked;
const cmTarget::LinkLibraries& libs = tgt.GetLinkLibraries();
for(cmTarget::LinkLibraries::const_iterator lib = libs.begin();
lib != libs.end(); ++lib)
for(std::vector<cmStdString>::iterator lib = linkItems.begin();
lib != linkItems.end(); ++lib)
{
// Don't link the library against itself!
if(targetLibrary && (lib->first == targetLibrary)) continue;
// use the correct lib for the current configuration
if (lib->second == cmTarget::DEBUG && buildType != "DEBUG")
{
continue;
}
if (lib->second == cmTarget::OPTIMIZED && buildType == "DEBUG")
{
continue;
}
// skip zero size library entries, this may happen
// if a variable expands to nothing.
if (lib->first.size() == 0) continue;
// if it is a full path break it into -L and -l
cmStdString& linkItem = *lib;
// check to see if the link item has a -l already
cmsys::RegularExpression reg("^([ \t]*\\-[lWRB])|([ \t]*\\-framework)|(\\${)|([ \t]*\\-pthread)|([ \t]*`)");
if(lib->first.find('/') != std::string::npos
&& !reg.find(lib->first))
if(!reg.find(linkItem))
{
std::string dir, file;
cmSystemTools::SplitProgramPath(lib->first.c_str(),
dir, file);
std::string libpath = this->ConvertToOutputForExisting(dir.c_str());
if(emitted.insert(libpath).second)
{
linkLibs += libPathFlag;
linkLibs += libpath;
linkLibs += " ";
if(outputRuntime)
{
runtimeDirs.push_back( libpath );
}
}
cmsys::RegularExpression libname("^lib([^/]*)(\\.so|\\.lib|\\.dll|\\.sl|\\.a|\\.dylib).*");
cmsys::RegularExpression libname_noprefix("([^/]*)(\\.so|\\.lib|\\.dll|\\.sl|\\.a|\\.dylib).*");
if(libname.find(file))
{
// Library had "lib" prefix.
librariesLinked += libLinkFlag;
file = libname.match(1);
// if ignore libprefix is on,
// then add the lib prefix back into the name
if(m_IgnoreLibPrefix)
{
file = "lib" + file;
}
librariesLinked += file;
if(linkSuffix.size() && !hasSuffix.find(file))
{
librariesLinked += linkSuffix;
}
librariesLinked += " ";
}
else if(libname_noprefix.find(file))
{
// Library had no "lib" prefix.
librariesLinked += libLinkFlag;
file = libname_noprefix.match(1);
librariesLinked += file;
if(linkSuffix.size() && !hasSuffix.find(file))
{
librariesLinked += linkSuffix;
}
librariesLinked += " ";
}
else
{
// Error parsing the library name. Just use the full path.
// The linker will give an error if it is invalid.
librariesLinked += lib->first;
librariesLinked += " ";
}
librariesLinked += libLinkFlag;
}
// not a full path, so add -l name
else
librariesLinked += linkItem;
if(linkSuffix.size() && !hasSuffix.find(linkItem))
{
if(!reg.find(lib->first))
{
librariesLinked += libLinkFlag;
}
librariesLinked += lib->first;
if(linkSuffix.size() && !hasSuffix.find(lib->first))
{
librariesLinked += linkSuffix;
}
librariesLinked += " ";
librariesLinked += linkSuffix;
}
librariesLinked += " ";
}
linkLibs += librariesLinked;

View File

@ -103,7 +103,9 @@ public:
///! for existing files convert to output path and short path if spaces
std::string ConvertToOutputForExisting(const char* p);
void DetermineLibraryPathOrder(const cmTarget& target,
std::vector<std::string>& linkPaths,
std::vector<std::string>& linkLibs);
protected:
/** Construct a script from the given list of command lines. */
std::string ConstructScript(const cmCustomCommandLines& commandLines,

View File

@ -0,0 +1,356 @@
#include "cmOrderLinkDirectories.h"
#include "cmSystemTools.h"
#include "cmsys/RegularExpression.hxx"
inline void printv(std::vector<cmStdString>& v)
{
for(unsigned int i = 0; i < v.size(); ++i)
{
std::cerr << "[" << v[i] << "]" << " ";
}
std::cerr << "\n";
}
//-------------------------------------------------------------------
bool cmOrderLinkDirectories::LibraryInDirectory(const char* dir,
const char* lib)
{
cmStdString path = dir;
path += "/";
path += lib;
// first look for the library as given
if(cmSystemTools::FileExists(path.c_str()))
{
return true;
}
// next remove the extension (.a, .so ) and look for the library
// under a different name as the linker can do either
if(m_RemoveLibraryExtension.find(lib))
{
cmStdString lib = m_RemoveLibraryExtension.match(1);
cmStdString ext = m_RemoveLibraryExtension.match(2);
for(std::vector<cmStdString>::iterator i = m_LinkExtensions.begin();
i != m_LinkExtensions.end(); ++i)
{
if(ext != *i)
{
path = dir;
path += "/";
path += lib + *i;
if(cmSystemTools::FileExists(path.c_str()))
{
return true;
}
}
}
}
return false;
}
//-------------------------------------------------------------------
void cmOrderLinkDirectories::FindLibrariesInSeachPaths()
{
for(std::set<cmStdString>::iterator dir = m_LinkPathSet.begin();
dir != m_LinkPathSet.end(); ++dir)
{
for(std::map<cmStdString, Library>::iterator lib
= m_FullPathLibraries.begin();
lib != m_FullPathLibraries.end(); ++lib)
{
if(lib->second.Path != *dir)
{
if(LibraryInDirectory(dir->c_str(), lib->second.File.c_str()))
{
m_LibraryToDirectories[lib->second.FullPath].push_back(*dir);
}
}
}
}
}
//-------------------------------------------------------------------
void cmOrderLinkDirectories::FindIndividualLibraryOrders()
{
for(std::vector<Library>::iterator lib = m_MultiDirectoryLibraries.begin();
lib != m_MultiDirectoryLibraries.end(); ++lib)
{
std::vector<cmStdString>& dirs = m_LibraryToDirectories[lib->FullPath];
m_DirectoryToAfterList[lib->Path] = dirs;
}
}
//-------------------------------------------------------------------
void
cmOrderLinkDirectories::PrintMap(const char* name,
std::map<cmStdString, std::vector<cmStdString> >& m)
{
std::cerr << name << "\n";
for(std::map<cmStdString, std::vector<cmStdString> >::iterator i =
m.begin(); i != m.end();
++i)
{
std::cerr << i->first << ": ";
for(std::vector<cmStdString>::iterator l = i->second.begin();
l != i->second.end(); ++l)
{
std::cerr << *l << " ";
}
std::cerr << "\n";
}
}
//-------------------------------------------------------------------
void cmOrderLinkDirectories::CreateRegularExpressions()
{
cmStdString libext = "(";
bool first = true;
for(std::vector<cmStdString>::iterator i = m_LinkExtensions.begin();
i != m_LinkExtensions.end(); ++i)
{
if(!first)
{
libext += "|";
}
first = false;
libext += "\\";
libext += *i;
}
libext += ").*";
cmStdString reg("(.*)");
reg += libext;
m_RemoveLibraryExtension.compile(reg.c_str());
reg = "^lib([^/]*)";
reg += libext;
m_ExtractBaseLibraryName.compile(reg.c_str());
reg = "([^/]*)";
reg += libext;
m_ExtractBaseLibraryNameNoPrefix.compile(reg.c_str());
}
//-------------------------------------------------------------------
void cmOrderLinkDirectories::PrepareLinkTargets()
{
for(std::vector<cmStdString>::iterator i = m_LinkItems.begin();
i != m_LinkItems.end(); ++i)
{
// separate the library name from libfoo.a or foo.a
if(m_ExtractBaseLibraryName.find(*i))
{
*i = m_ExtractBaseLibraryName.match(1);
}
else if(m_ExtractBaseLibraryNameNoPrefix.find(*i))
{
*i = m_ExtractBaseLibraryNameNoPrefix.match(1);
}
}
}
//-------------------------------------------------------------------
bool cmOrderLinkDirectories::CanBeBefore(const cmStdString& d1,
const cmStdString& d2)
{
if(m_DirectoryToAfterList.count(d2) == 0)
{
return true;
}
std::vector<cmStdString>& d2dirs = m_DirectoryToAfterList[d2];
// is d1 in the d2's list of directories that d2 must be before
// if so, then d1 can not come before d2
for(std::vector<cmStdString>::iterator i = d2dirs.begin();
i != d2dirs.end(); ++i)
{
if(*i == d1)
{
return false;
}
}
return true;
}
// This is a stl function object used to sort
// the vector of library paths. It returns true
// if left directory can be before right directory (no swap).
// It also checks for the impossible case of two libraries and
// two directories that have both libraries.
struct cmOrderLinkDirectoriesCompare
: public std::binary_function <cmStdString, cmStdString, bool>
{
cmOrderLinkDirectoriesCompare()
{
This = 0;
}
bool operator()(
const cmStdString& left,
const cmStdString& right
) const
{
bool ret = This->CanBeBefore(left, right);
if(!ret)
{
// check for the case when both libraries have to come
// before each other
if(!This->CanBeBefore(right, left))
{
This->AddImpossible(right, left);
}
}
return ret;
}
cmOrderLinkDirectories* This;
};
//-------------------------------------------------------------------
void cmOrderLinkDirectories::AddImpossible(const cmStdString& d1,
const cmStdString& d2)
{
m_ImposibleDirectories.insert(d1);
m_ImposibleDirectories.insert(d2);
}
//-------------------------------------------------------------------
void cmOrderLinkDirectories::OrderPaths(std::vector<cmStdString>&
orderedPaths)
{
cmOrderLinkDirectoriesCompare comp;
comp.This = this;
std::sort(orderedPaths.begin(), orderedPaths.end(), comp);
}
//-------------------------------------------------------------------
void cmOrderLinkDirectories::SetLinkInformation(const cmTarget& target,
cmTarget::LinkLibraryType
linktype,
const char* targetLibrary)
{
// collect the search paths from the target into paths set
const std::vector<std::string>& searchPaths = target.GetLinkDirectories();
for(std::vector<std::string>::const_iterator p = searchPaths.begin();
p != searchPaths.end(); ++p)
{
m_LinkPathSet.insert(*p);
}
// collect the link items from the target and put it into libs
const cmTarget::LinkLibraries& tlibs = target.GetLinkLibraries();
std::vector<cmStdString> libs;
for(cmTarget::LinkLibraries::const_iterator lib = tlibs.begin();
lib != tlibs.end(); ++lib)
{
// skip zero size library entries, this may happen
// if a variable expands to nothing.
if (lib->first.size() == 0)
{
continue;
}
// Don't link the library against itself!
if(targetLibrary && (lib->first == targetLibrary))
{
continue;
}
// use the correct lib for the current configuration
if (lib->second == cmTarget::DEBUG && linktype != cmTarget::DEBUG)
{
continue;
}
if (lib->second == cmTarget::OPTIMIZED &&
linktype != cmTarget::OPTIMIZED)
{
continue;
}
m_RawLinkItems.push_back(lib->first);
}
}
//-------------------------------------------------------------------
bool cmOrderLinkDirectories::DetermineLibraryPathOrder()
{
// set up all the regular expressions
this->CreateRegularExpressions();
std::vector<cmStdString> finalOrderPaths;
// find all libs that are full paths
Library aLib;
cmStdString dir;
cmStdString file;
for(unsigned int i=0; i < m_RawLinkItems.size(); ++i)
{
if(cmSystemTools::FileIsFullPath(m_RawLinkItems[i].c_str()))
{
cmSystemTools::SplitProgramPath(m_RawLinkItems[i].c_str(),
dir, file);
m_LinkPathSet.insert(dir);
aLib.FullPath = m_RawLinkItems[i];
aLib.File = file;
aLib.Path = dir;
m_FullPathLibraries[aLib.FullPath] = aLib;
m_LinkItems.push_back(file);
}
else
{
m_LinkItems.push_back(m_RawLinkItems[i]);
}
}
this->FindLibrariesInSeachPaths();
for(std::map<cmStdString, std::vector<cmStdString> >::iterator lib =
m_LibraryToDirectories.begin(); lib!= m_LibraryToDirectories.end();
++lib)
{
if(lib->second.size() > 0)
{
m_MultiDirectoryLibraries.push_back(m_FullPathLibraries[lib->first]);
}
else
{
m_SingleDirectoryLibraries.push_back(m_FullPathLibraries[lib->first]);
}
}
this->FindIndividualLibraryOrders();
m_SortedSearchPaths.clear();
for(std::set<cmStdString>::iterator i = m_LinkPathSet.begin();
i != m_LinkPathSet.end(); ++i)
{
m_SortedSearchPaths.push_back(*i);
}
this->OrderPaths(m_SortedSearchPaths);
// now turn libfoo.a into foo and foo.a into foo
// This will prepare the link items for -litem
this->PrepareLinkTargets();
// this->PrintMap("m_DirectoryToAfterList", m_DirectoryToAfterList);
//this->PrintMap("m_LibraryToDirectories", m_LibraryToDirectories);
//std::cerr << "link objects: ";
//printv(m_LinkItems);
if(m_ImposibleDirectories.size())
{
return false;
}
return true;
}
std::string cmOrderLinkDirectories::GetWarnings()
{
std::string warning = "It is impossible to order the linker search path in such a way that libraries specified as full paths will be picked by the linker.\nDirectories and libraries involvied are:\n";
for(std::set<cmStdString>::iterator i = m_ImposibleDirectories.begin();
i != m_ImposibleDirectories.end(); ++i)
{
warning += "Directory: ";
warning += *i;
warning += " contains ";
std::map<cmStdString, std::vector<cmStdString> >::iterator j;
for(j = m_LibraryToDirectories.begin();
j != m_LibraryToDirectories.end(); ++j)
{
if(std::find(j->second.begin(), j->second.end(), *i)
!= j->second.end())
{
warning += "Library: ";
warning += j->first;
warning += "\n";
}
}
}
warning += "\n";
return warning;
}

View File

@ -0,0 +1,126 @@
/*=========================================================================
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 cmOrderLinkDirectories_h
#define cmOrderLinkDirectories_h
#include <cmStandardIncludes.h>
#include <map>
#include <vector>
#include "cmTarget.h"
#include "cmsys/RegularExpression.hxx"
/** \class cmOrderLinkDirectories
* \brief Compute the best -L path order
*
* This class computes the best order for -L paths.
* It tries to make sure full path specified libraries are
* used. For example if you have /usr/mylib/libfoo.a on as
* a link library for a target, and you also have /usr/lib/libbar.a
* and you also have /usr/lib/libfoo.a, then you would
* want -L/usr/mylib -L/usr/lib to make sure the correct libfoo.a is
* found by the linker. The algorithm is as follows:
* - foreach library create a vector of directories it exists in.
* - foreach directory create a vector of directories that must come
* after it, put this in a map<dir, vector<dir>> mapping from a directory
* to the vector of directories that it must be before.
* - put all directories into a vector
* - sort the vector with a compare function CanBeBefore
* CanBeBefore returns true if a directory is OK to be before
* another directory. This is determined by looking at the
* map<dir vector<dir>> and seeing if d1 is in the vector for d2.
*/
class cmOrderLinkDirectories
{
public:
///! set link information from the target
void SetLinkInformation(const cmTarget&, cmTarget::LinkLibraryType,
const char* targetLibrary);
///! Compute the best order for -L paths from GetLinkLibraries
bool DetermineLibraryPathOrder();
///! Get the results from DetermineLibraryPathOrder
void GetLinkerInformation(std::vector<cmStdString>& searchPaths,
std::vector<cmStdString>& linkItems)
{
linkItems = m_LinkItems;
searchPaths = m_SortedSearchPaths;
}
// should be set from CMAKE_STATIC_LIBRARY_SUFFIX,
// CMAKE_SHARED_LIBRARY_SUFFIX
// CMAKE_LINK_LIBRARY_SUFFIX
void AddLinkExtension(const char* e)
{
m_LinkExtensions.push_back(e);
}
// Return any warnings if the exist
std::string GetWarnings();
// structure to hold a full path library link item
struct Library
{
cmStdString FullPath;
cmStdString File;
cmStdString Path;
};
friend struct cmOrderLinkDirectoriesCompare;
private:
void CreateRegularExpressions();
void DetermineLibraryPathOrder(std::vector<cmStdString>& searchPaths,
std::vector<cmStdString>& libs,
std::vector<cmStdString>& sortedPaths);
void PrepareLinkTargets();
bool LibraryInDirectory(const char* dir, const char* lib);
void FindLibrariesInSeachPaths();
void FindIndividualLibraryOrders();
void PrintMap(const char* name,
std::map<cmStdString, std::vector<cmStdString> >& m);
void OrderPaths(std::vector<cmStdString>& paths);
bool CanBeBefore(const cmStdString& d1,
const cmStdString& d2);
void AddImpossible(const cmStdString& ,
const cmStdString& );
private:
// map from library to directories that it is in other than its full path
std::map<cmStdString, std::vector<cmStdString> > m_LibraryToDirectories;
// map from directory to vector of directories that must be after it
std::map<cmStdString, std::vector<cmStdString> > m_DirectoryToAfterList;
// map from full path to a Library struct
std::map<cmStdString, Library> m_FullPathLibraries;
// libraries that are found in multiple directories
std::vector<Library> m_MultiDirectoryLibraries;
// libraries that are only found in one directory
std::vector<Library> m_SingleDirectoryLibraries;
// This is a vector of all the link objects -lm or m
std::vector<cmStdString> m_LinkItems;
// Unprocessed link items
std::vector<cmStdString> m_RawLinkItems;
// This vector holds the sorted -L paths
std::vector<cmStdString> m_SortedSearchPaths;
// This is the set of -L paths unsorted, but unique
std::set<cmStdString> m_LinkPathSet;
// the names of link extensions
std::vector<cmStdString> m_LinkExtensions;
// set of directories that can not be put in the correct order
std::set<cmStdString> m_ImposibleDirectories;
// library regular expressions
cmsys::RegularExpression m_RemoveLibraryExtension;
cmsys::RegularExpression m_ExtractBaseLibraryName;
cmsys::RegularExpression m_ExtractBaseLibraryNameNoPrefix;
};
#endif

View File

@ -54,6 +54,7 @@ CMAKE_CXX_SOURCES="\
cmCustomCommand \
cmCacheManager \
cmListFileCache \
cmOrderLinkDirectories \
cmSourceGroup"
CMAKE_C_SOURCES="\