CMake/Source/cmOrderLinkDirectories.cxx
Brad King 22c62c9e65 BUG: Sweeping changes to cleanup computation of target names. This should
fix many bugs related to target names being computed inconsistently.

- Centralized computation of a target's file name to a method in
  cmTarget.  Now that global knowledge is always available the
  *_CMAKE_PATH cache variables are no longer needed.

- Centralized computation of link library command lines and link
  directory search order.

- Moved computation of link directories needed to link CMake targets
  to be after evaluation of linking dependencies.

This also removed alot of duplicate code in which each version had its
own bugs.

This commit is surrounded by the tags

  CMake-TargetNameCentralization1-pre

and

  CMake-TargetNameCentralization1-post

so make the large set of changes easy to identify.
2006-01-13 18:18:32 -05:00

404 lines
12 KiB
C++

#include "cmOrderLinkDirectories.h"
#include "cmSystemTools.h"
#include "cmsys/RegularExpression.hxx"
#include <ctype.h>
//-------------------------------------------------------------------
cmOrderLinkDirectories::cmOrderLinkDirectories()
{
m_Debug = false;
}
//-------------------------------------------------------------------
bool cmOrderLinkDirectories::LibraryInDirectory(const char* dir,
const char* libIn)
{
cmStdString path = dir;
path += "/";
path += libIn;
// 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(libIn))
{
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(this->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;
}
}
//-------------------------------------------------------------------
std::string cmOrderLinkDirectories::NoCaseExpression(const char* str)
{
std::string ret;
const char* s = str;
while(*s)
{
if(*s == '.')
{
ret += *s;
}
else
{
ret += "[";
ret += tolower(*s);
ret += toupper(*s);
ret += "]";
}
s++;
}
return ret;
}
//-------------------------------------------------------------------
void cmOrderLinkDirectories::CreateRegularExpressions()
{
m_SplitFramework.compile("(.*)/(.*)\\.framework$");
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 += "\\";
#if defined(_WIN32) && !defined(__CYGWIN__)
libext += this->NoCaseExpression(i->c_str());
#else
libext += *i;
#endif
}
libext += ").*";
cmStdString reg("(.*)");
reg += libext;
m_RemoveLibraryExtension.compile(reg.c_str());
reg = "";
if(m_LinkPrefix.size())
{
reg = "^";
reg += m_LinkPrefix;
}
reg += "([^/]*)";
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::FindPathNotInDirectoryToAfterList(
cmStdString& path)
{
for(std::map<cmStdString, std::vector<cmStdString> >::iterator i
= m_DirectoryToAfterList.begin();
i != m_DirectoryToAfterList.end(); ++i)
{
const cmStdString& p = i->first;
bool found = false;
for(std::map<cmStdString, std::vector<cmStdString> >::iterator j
= m_DirectoryToAfterList.begin(); j != m_DirectoryToAfterList.end()
&& !found; ++j)
{
if(j != i)
{
found = (std::find(j->second.begin(), j->second.end(), p) != j->second.end());
}
}
if(!found)
{
path = p;
m_DirectoryToAfterList.erase(i);
return true;
}
}
path = "";
return false;
}
//-------------------------------------------------------------------
void cmOrderLinkDirectories::OrderPaths(std::vector<cmStdString>&
orderedPaths)
{
cmStdString path;
// This is a topological sort implementation
// One at a time find paths that are not in any other paths after list
// and put them into the orderedPaths vector in that order
// FindPathNotInDirectoryToAfterList removes the path from the
// m_DirectoryToAfterList once it is found
while(this->FindPathNotInDirectoryToAfterList(path))
{
orderedPaths.push_back(path);
}
// at this point if there are still paths in m_DirectoryToAfterList
// then there is a cycle and we are stuck
if(m_DirectoryToAfterList.size())
{
for(std::map<cmStdString, std::vector<cmStdString> >::iterator i
= m_DirectoryToAfterList.begin();
i != m_DirectoryToAfterList.end(); ++i)
{
m_ImposibleDirectories.insert(i->first);
// still put it in the path list in the order we find them
orderedPaths.push_back(i->first);
}
}
}
//-------------------------------------------------------------------
void cmOrderLinkDirectories::SetLinkInformation(
const char* targetName,
const std::vector<std::string>& linkLibraries,
const std::vector<std::string>& linkDirectories
)
{
// Save the target name.
m_TargetName = targetName;
// Merge the link directory search path given into our path set.
std::vector<cmStdString> empty;
for(std::vector<std::string>::const_iterator p = linkDirectories.begin();
p != linkDirectories.end(); ++p)
{
m_DirectoryToAfterList[*p] = empty;
m_LinkPathSet.insert(*p);
}
// Append the link library list into our raw list.
for(std::vector<std::string>::const_iterator l = linkLibraries.begin();
l != linkLibraries.end(); ++l)
{
m_RawLinkItems.push_back(*l);
}
}
//-------------------------------------------------------------------
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;
std::vector<cmStdString> empty;
bool framework = false;
for(unsigned int i=0; i < m_RawLinkItems.size(); ++i)
{
if(cmSystemTools::FileIsFullPath(m_RawLinkItems[i].c_str()))
{
if(cmSystemTools::FileIsDirectory(m_RawLinkItems[i].c_str()))
{
if(cmSystemTools::IsPathToFramework(m_RawLinkItems[i].c_str()))
{
m_SplitFramework.find(m_RawLinkItems[i]);
cmStdString path = m_SplitFramework.match(1);
// Add the -F path if we have not yet done so
if(m_EmittedFrameworkPaths.insert(path).second)
{
std::string fpath = "-F";
fpath += cmSystemTools::ConvertToOutputPath(path.c_str());
m_LinkItems.push_back(fpath);
}
// now add the -framework option
std::string frame = "-framework ";
frame += m_SplitFramework.match(2);
m_LinkItems.push_back(frame);
framework = true;
}
else
{
std::string message = "Warning: Ignoring path found in link libraries for target: ";
message += m_TargetName;
message += ", path is: ";
message += m_RawLinkItems[i];
message += ". Expected a library name or a full path to a library name.";
cmSystemTools::Message(message.c_str());
continue;
}
}
if(!framework)
{
cmSystemTools::SplitProgramPath(m_RawLinkItems[i].c_str(),
dir, file);
m_DirectoryToAfterList[dir] = empty;
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();
if(m_Debug)
{
this->PrintMap("m_LibraryToDirectories", m_LibraryToDirectories);
this->PrintMap("m_DirectoryToAfterList", m_DirectoryToAfterList);
}
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();
if(m_ImposibleDirectories.size())
{
cmSystemTools::Message(this->GetWarnings().c_str());
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 involved are:\n";
for(std::set<cmStdString>::iterator i = m_ImposibleDirectories.begin();
i != m_ImposibleDirectories.end(); ++i)
{
warning += "Directory: ";
warning += *i;
warning += " contains:\n";
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";
}
warning += "\n";
return warning;
}
//-------------------------------------------------------------------
void
cmOrderLinkDirectories::PrintMap(const char* name,
std::map<cmStdString, std::vector<cmStdString> >& m)
{
std::cout << name << "\n";
for(std::map<cmStdString, std::vector<cmStdString> >::iterator i =
m.begin(); i != m.end();
++i)
{
std::cout << i->first << ": ";
for(std::vector<cmStdString>::iterator l = i->second.begin();
l != i->second.end(); ++l)
{
std::cout << *l << " ";
}
std::cout << "\n";
}
}
void cmOrderLinkDirectories::GetFullPathLibraries(std::vector<cmStdString>&
libs)
{
for(std::map<cmStdString, Library>::iterator i = m_FullPathLibraries.begin();
i != m_FullPathLibraries.end(); ++i)
{
libs.push_back(i->first);
}
}