#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); } }