1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
8 This software is distributed WITHOUT ANY WARRANTY; without even the
9 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License for more information.
11 ============================================================================*/
12 #include "cmOrderDirectories.h"
14 #include "cmGlobalGenerator.h"
15 #include "cmSystemTools.h"
23 Directory ordering computation.
24 - Useful to compute a safe runtime library path order
25 - Need runtime path for supporting INSTALL_RPATH_USE_LINK_PATH
26 - Need runtime path at link time to pickup transitive link dependencies
30 //----------------------------------------------------------------------------
31 class cmOrderDirectoriesConstraint
34 cmOrderDirectoriesConstraint(cmOrderDirectories* od,
35 std::string const& file):
36 OD(od), GlobalGenerator(od->GlobalGenerator)
38 this->FullPath = file;
40 if(file.rfind(".framework") != std::string::npos)
42 cmsys::RegularExpression splitFramework;
43 splitFramework.compile("^(.*)/(.*).framework/(.*)$");
44 if(splitFramework.find(file) &&
46 splitFramework.match(3).find(splitFramework.match(2))))
48 this->Directory = splitFramework.match(1);
50 std::string(file.begin() + this->Directory.size() + 1, file.end());
54 if(this->FileName.empty())
56 this->Directory = cmSystemTools::GetFilenamePath(file);
57 this->FileName = cmSystemTools::GetFilenameName(file);
60 virtual ~cmOrderDirectoriesConstraint() {}
64 this->DirectoryIndex = this->OD->AddOriginalDirectory(this->Directory);
67 virtual void Report(std::ostream& e) = 0;
69 void FindConflicts(unsigned int index)
71 for(unsigned int i=0; i < this->OD->OriginalDirectories.size(); ++i)
73 // Check if this directory conflicts with the entry.
74 std::string const& dir = this->OD->OriginalDirectories[i];
75 if(dir != this->Directory && this->FindConflict(dir))
77 // The library will be found in this directory but this is not
78 // the directory named for it. Add an entry to make sure the
79 // desired directory comes before this one.
80 cmOrderDirectories::ConflictPair p(this->DirectoryIndex, index);
81 this->OD->ConflictGraph[i].push_back(p);
86 void FindImplicitConflicts(cmOStringStream& w)
89 for(unsigned int i=0; i < this->OD->OriginalDirectories.size(); ++i)
91 // Check if this directory conflicts with the entry.
92 std::string const& dir = this->OD->OriginalDirectories[i];
93 if(dir != this->Directory && this->FindConflict(dir))
95 // The library will be found in this directory but it is
96 // supposed to be found in an implicit search directory.
102 w << " in " << this->Directory << " may be hidden by files in:\n";
104 w << " " << dir << "\n";
109 virtual bool FindConflict(std::string const& dir) = 0;
111 bool FileMayConflict(std::string const& dir, std::string const& name);
113 cmOrderDirectories* OD;
114 cmGlobalGenerator* GlobalGenerator;
116 // The location in which the item is supposed to be found.
117 std::string FullPath;
118 std::string Directory;
119 std::string FileName;
121 // The index assigned to the directory.
125 //----------------------------------------------------------------------------
126 bool cmOrderDirectoriesConstraint::FileMayConflict(std::string const& dir,
127 std::string const& name)
129 // Check if the file exists on disk.
130 std::string file = dir;
133 if(cmSystemTools::FileExists(file.c_str(), true))
135 // The file conflicts only if it is not the same as the original
136 // file due to a symlink or hardlink.
137 return !cmSystemTools::SameFile(this->FullPath.c_str(), file.c_str());
140 // Check if the file will be built by cmake.
141 std::set<cmStdString> const& files =
142 (this->GlobalGenerator->GetDirectoryContent(dir, false));
143 std::set<cmStdString>::const_iterator fi = files.find(name);
144 return fi != files.end();
147 //----------------------------------------------------------------------------
148 class cmOrderDirectoriesConstraintSOName: public cmOrderDirectoriesConstraint
151 cmOrderDirectoriesConstraintSOName(cmOrderDirectories* od,
152 std::string const& file,
154 cmOrderDirectoriesConstraint(od, file), SOName(soname? soname : "")
156 if(this->SOName.empty())
158 // Try to guess the soname.
160 if(cmSystemTools::GuessLibrarySOName(file, soguess))
162 this->SOName = soguess;
167 virtual void Report(std::ostream& e)
169 e << "runtime library [";
170 if(this->SOName.empty())
181 virtual bool FindConflict(std::string const& dir);
183 // The soname of the shared library if it is known.
187 //----------------------------------------------------------------------------
188 bool cmOrderDirectoriesConstraintSOName::FindConflict(std::string const& dir)
190 // Determine which type of check to do.
191 if(!this->SOName.empty())
193 // We have the library soname. Check if it will be found.
194 if(this->FileMayConflict(dir, this->SOName))
201 // We do not have the soname. Look for files in the directory
202 // that may conflict.
203 std::set<cmStdString> const& files =
204 (this->GlobalGenerator
205 ->GetDirectoryContent(dir, true));
207 // Get the set of files that might conflict. Since we do not
208 // know the soname just look at all files that start with the
209 // file name. Usually the soname starts with the library name.
210 std::string base = this->FileName;
211 std::set<cmStdString>::const_iterator first = files.lower_bound(base);
212 ++base[base.size()-1];
213 std::set<cmStdString>::const_iterator last = files.upper_bound(base);
222 //----------------------------------------------------------------------------
223 class cmOrderDirectoriesConstraintLibrary: public cmOrderDirectoriesConstraint
226 cmOrderDirectoriesConstraintLibrary(cmOrderDirectories* od,
227 std::string const& file):
228 cmOrderDirectoriesConstraint(od, file)
232 virtual void Report(std::ostream& e)
234 e << "link library [" << this->FileName << "]";
237 virtual bool FindConflict(std::string const& dir);
240 //----------------------------------------------------------------------------
241 bool cmOrderDirectoriesConstraintLibrary::FindConflict(std::string const& dir)
243 // We have the library file name. Check if it will be found.
244 if(this->FileMayConflict(dir, this->FileName))
249 // Now check if the file exists with other extensions the linker
251 if(!this->OD->LinkExtensions.empty() &&
252 this->OD->RemoveLibraryExtension.find(this->FileName))
254 cmStdString lib = this->OD->RemoveLibraryExtension.match(1);
255 cmStdString ext = this->OD->RemoveLibraryExtension.match(2);
256 for(std::vector<std::string>::iterator
257 i = this->OD->LinkExtensions.begin();
258 i != this->OD->LinkExtensions.end(); ++i)
262 std::string fname = lib;
264 if(this->FileMayConflict(dir, fname.c_str()))
274 //----------------------------------------------------------------------------
275 cmOrderDirectories::cmOrderDirectories(cmGlobalGenerator* gg,
279 this->GlobalGenerator = gg;
280 this->Target = target;
281 this->Purpose = purpose;
282 this->Computed = false;
285 //----------------------------------------------------------------------------
286 cmOrderDirectories::~cmOrderDirectories()
288 for(std::vector<cmOrderDirectoriesConstraint*>::iterator
289 i = this->ConstraintEntries.begin();
290 i != this->ConstraintEntries.end(); ++i)
294 for(std::vector<cmOrderDirectoriesConstraint*>::iterator
295 i = this->ImplicitDirEntries.begin();
296 i != this->ImplicitDirEntries.end(); ++i)
302 //----------------------------------------------------------------------------
303 std::vector<std::string> const& cmOrderDirectories::GetOrderedDirectories()
307 this->Computed = true;
308 this->CollectOriginalDirectories();
309 this->FindConflicts();
310 this->OrderDirectories();
312 return this->OrderedDirectories;
315 //----------------------------------------------------------------------------
316 void cmOrderDirectories::AddRuntimeLibrary(std::string const& fullPath,
319 // Add the runtime library at most once.
320 if(this->EmmittedConstraintSOName.insert(fullPath).second)
322 // Implicit link directories need special handling.
323 if(!this->ImplicitDirectories.empty())
325 std::string dir = cmSystemTools::GetFilenamePath(fullPath);
327 if(fullPath.rfind(".framework") != std::string::npos)
329 cmsys::RegularExpression splitFramework;
330 splitFramework.compile("^(.*)/(.*).framework/(.*)$");
331 if(splitFramework.find(fullPath) &&
332 (std::string::npos !=
333 splitFramework.match(3).find(splitFramework.match(2))))
335 dir = splitFramework.match(1);
339 if(this->ImplicitDirectories.find(dir) !=
340 this->ImplicitDirectories.end())
342 this->ImplicitDirEntries.push_back(
343 new cmOrderDirectoriesConstraintSOName(this, fullPath, soname));
348 // Construct the runtime information entry for this library.
349 this->ConstraintEntries.push_back(
350 new cmOrderDirectoriesConstraintSOName(this, fullPath, soname));
354 // This can happen if the same library is linked multiple times.
355 // In that case the runtime information check need be done only
356 // once anyway. For shared libs we could add a check in AddItem
357 // to not repeat them.
361 //----------------------------------------------------------------------------
362 void cmOrderDirectories::AddLinkLibrary(std::string const& fullPath)
364 // Link extension info is required for library constraints.
365 assert(!this->LinkExtensions.empty());
367 // Add the link library at most once.
368 if(this->EmmittedConstraintLibrary.insert(fullPath).second)
370 // Implicit link directories need special handling.
371 if(!this->ImplicitDirectories.empty())
373 std::string dir = cmSystemTools::GetFilenamePath(fullPath);
374 if(this->ImplicitDirectories.find(dir) !=
375 this->ImplicitDirectories.end())
377 this->ImplicitDirEntries.push_back(
378 new cmOrderDirectoriesConstraintLibrary(this, fullPath));
383 // Construct the link library entry.
384 this->ConstraintEntries.push_back(
385 new cmOrderDirectoriesConstraintLibrary(this, fullPath));
389 //----------------------------------------------------------------------------
392 ::AddUserDirectories(std::vector<std::string> const& extra)
394 this->UserDirectories.insert(this->UserDirectories.end(),
395 extra.begin(), extra.end());
398 //----------------------------------------------------------------------------
401 ::AddLanguageDirectories(std::vector<std::string> const& dirs)
403 this->LanguageDirectories.insert(this->LanguageDirectories.end(),
404 dirs.begin(), dirs.end());
407 //----------------------------------------------------------------------------
410 ::SetImplicitDirectories(std::set<cmStdString> const& implicitDirs)
412 this->ImplicitDirectories = implicitDirs;
415 //----------------------------------------------------------------------------
418 ::SetLinkExtensionInfo(std::vector<std::string> const& linkExtensions,
419 std::string const& removeExtRegex)
421 this->LinkExtensions = linkExtensions;
422 this->RemoveLibraryExtension.compile(removeExtRegex.c_str());
425 //----------------------------------------------------------------------------
426 void cmOrderDirectories::CollectOriginalDirectories()
428 // Add user directories specified for inclusion. These should be
429 // indexed first so their original order is preserved as much as
430 // possible subject to the constraints.
431 this->AddOriginalDirectories(this->UserDirectories);
433 // Add directories containing constraints.
434 for(unsigned int i=0; i < this->ConstraintEntries.size(); ++i)
436 this->ConstraintEntries[i]->AddDirectory();
439 // Add language runtime directories last.
440 this->AddOriginalDirectories(this->LanguageDirectories);
443 //----------------------------------------------------------------------------
444 int cmOrderDirectories::AddOriginalDirectory(std::string const& dir)
446 // Add the runtime directory with a unique index.
447 std::map<cmStdString, int>::iterator i =
448 this->DirectoryIndex.find(dir);
449 if(i == this->DirectoryIndex.end())
451 std::map<cmStdString, int>::value_type
452 entry(dir, static_cast<int>(this->OriginalDirectories.size()));
453 i = this->DirectoryIndex.insert(entry).first;
454 this->OriginalDirectories.push_back(dir);
460 //----------------------------------------------------------------------------
463 ::AddOriginalDirectories(std::vector<std::string> const& dirs)
465 for(std::vector<std::string>::const_iterator di = dirs.begin();
466 di != dirs.end(); ++di)
468 // We never explicitly specify implicit link directories.
469 if(this->ImplicitDirectories.find(*di) !=
470 this->ImplicitDirectories.end())
475 // Skip the empty string.
481 // Add this directory.
482 this->AddOriginalDirectory(*di);
486 //----------------------------------------------------------------------------
487 struct cmOrderDirectoriesCompare
489 typedef std::pair<int, int> ConflictPair;
491 // The conflict pair is unique based on just the directory
492 // (first). The second element is only used for displaying
493 // information about why the entry is present.
494 bool operator()(ConflictPair const& l,
495 ConflictPair const& r)
497 return l.first == r.first;
501 //----------------------------------------------------------------------------
502 void cmOrderDirectories::FindConflicts()
504 // Allocate the conflict graph.
505 this->ConflictGraph.resize(this->OriginalDirectories.size());
506 this->DirectoryVisited.resize(this->OriginalDirectories.size(), 0);
508 // Find directories conflicting with each entry.
509 for(unsigned int i=0; i < this->ConstraintEntries.size(); ++i)
511 this->ConstraintEntries[i]->FindConflicts(i);
514 // Clean up the conflict graph representation.
515 for(std::vector<ConflictList>::iterator
516 i = this->ConflictGraph.begin();
517 i != this->ConflictGraph.end(); ++i)
519 // Sort the outgoing edges for each graph node so that the
520 // original order will be preserved as much as possible.
521 std::sort(i->begin(), i->end());
523 // Make the edge list unique so cycle detection will be reliable.
524 ConflictList::iterator last =
525 std::unique(i->begin(), i->end(), cmOrderDirectoriesCompare());
526 i->erase(last, i->end());
529 // Check items in implicit link directories.
530 this->FindImplicitConflicts();
533 //----------------------------------------------------------------------------
534 void cmOrderDirectories::FindImplicitConflicts()
536 // Check for items in implicit link directories that have conflicts
537 // in the explicit directories.
538 cmOStringStream conflicts;
539 for(unsigned int i=0; i < this->ImplicitDirEntries.size(); ++i)
541 this->ImplicitDirEntries[i]->FindImplicitConflicts(conflicts);
544 // Skip warning if there were no conflicts.
545 std::string text = conflicts.str();
551 // Warn about the conflicts.
553 w << "Cannot generate a safe " << this->Purpose
554 << " for target " << this->Target->GetName()
555 << " because files in some directories may conflict with "
556 << " libraries in implicit directories:\n"
558 << "Some of these libraries may not be found correctly.";
559 this->GlobalGenerator->GetCMakeInstance()
560 ->IssueMessage(cmake::WARNING, w.str(), this->Target->GetBacktrace());
563 //----------------------------------------------------------------------------
564 void cmOrderDirectories::OrderDirectories()
566 // Allow a cycle to be diagnosed once.
567 this->CycleDiagnosed = false;
570 // Iterate through the directories in the original order.
571 for(unsigned int i=0; i < this->OriginalDirectories.size(); ++i)
573 // Start a new DFS from this node.
575 this->VisitDirectory(i);
579 //----------------------------------------------------------------------------
580 void cmOrderDirectories::VisitDirectory(unsigned int i)
582 // Skip nodes already visited.
583 if(this->DirectoryVisited[i])
585 if(this->DirectoryVisited[i] == this->WalkId)
587 // We have reached a node previously visited on this DFS.
589 this->DiagnoseCycle();
594 // We are now visiting this node so mark it.
595 this->DirectoryVisited[i] = this->WalkId;
597 // Visit the neighbors of the node first.
598 ConflictList const& clist = this->ConflictGraph[i];
599 for(ConflictList::const_iterator j = clist.begin();
600 j != clist.end(); ++j)
602 this->VisitDirectory(j->first);
605 // Now that all directories required to come before this one have
606 // been emmitted, emit this directory.
607 this->OrderedDirectories.push_back(this->OriginalDirectories[i]);
610 //----------------------------------------------------------------------------
611 void cmOrderDirectories::DiagnoseCycle()
613 // Report the cycle at most once.
614 if(this->CycleDiagnosed)
618 this->CycleDiagnosed = true;
620 // Construct the message.
622 e << "Cannot generate a safe " << this->Purpose
623 << " for target " << this->Target->GetName()
624 << " because there is a cycle in the constraint graph:\n";
626 // Display the conflict graph.
627 for(unsigned int i=0; i < this->ConflictGraph.size(); ++i)
629 ConflictList const& clist = this->ConflictGraph[i];
630 e << " dir " << i << " is [" << this->OriginalDirectories[i] << "]\n";
631 for(ConflictList::const_iterator j = clist.begin();
632 j != clist.end(); ++j)
634 e << " dir " << j->first << " must precede it due to ";
635 this->ConstraintEntries[j->second]->Report(e);
639 e << "Some of these libraries may not be found correctly.";
640 this->GlobalGenerator->GetCMakeInstance()
641 ->IssueMessage(cmake::WARNING, e.str(), this->Target->GetBacktrace());