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;
39 this->Directory = cmSystemTools::GetFilenamePath(file);
40 this->FileName = cmSystemTools::GetFilenameName(file);
42 virtual ~cmOrderDirectoriesConstraint() {}
46 this->DirectoryIndex = this->OD->AddOriginalDirectory(this->Directory);
49 virtual void Report(std::ostream& e) = 0;
51 void FindConflicts(unsigned int index)
53 for(unsigned int i=0; i < this->OD->OriginalDirectories.size(); ++i)
55 // Check if this directory conflicts with the entry.
56 std::string const& dir = this->OD->OriginalDirectories[i];
57 if(dir != this->Directory && this->FindConflict(dir))
59 // The library will be found in this directory but this is not
60 // the directory named for it. Add an entry to make sure the
61 // desired directory comes before this one.
62 cmOrderDirectories::ConflictPair p(this->DirectoryIndex, index);
63 this->OD->ConflictGraph[i].push_back(p);
68 void FindImplicitConflicts(cmOStringStream& w)
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 it is
78 // supposed to be found in an implicit search directory.
84 w << " in " << this->Directory << " may be hidden by files in:\n";
86 w << " " << dir << "\n";
91 virtual bool FindConflict(std::string const& dir) = 0;
93 bool FileMayConflict(std::string const& dir, std::string const& name);
95 cmOrderDirectories* OD;
96 cmGlobalGenerator* GlobalGenerator;
98 // The location in which the item is supposed to be found.
100 std::string Directory;
101 std::string FileName;
103 // The index assigned to the directory.
107 //----------------------------------------------------------------------------
108 bool cmOrderDirectoriesConstraint::FileMayConflict(std::string const& dir,
109 std::string const& name)
111 // Check if the file exists on disk.
112 std::string file = dir;
115 if(cmSystemTools::FileExists(file.c_str(), true))
117 // The file conflicts only if it is not the same as the original
118 // file due to a symlink or hardlink.
119 return !cmSystemTools::SameFile(this->FullPath.c_str(), file.c_str());
122 // Check if the file will be built by cmake.
123 std::set<cmStdString> const& files =
124 (this->GlobalGenerator->GetDirectoryContent(dir, false));
125 std::set<cmStdString>::const_iterator fi = files.find(name);
126 return fi != files.end();
129 //----------------------------------------------------------------------------
130 class cmOrderDirectoriesConstraintSOName: public cmOrderDirectoriesConstraint
133 cmOrderDirectoriesConstraintSOName(cmOrderDirectories* od,
134 std::string const& file,
136 cmOrderDirectoriesConstraint(od, file), SOName(soname? soname : "")
138 if(this->SOName.empty())
140 // Try to guess the soname.
142 if(cmSystemTools::GuessLibrarySOName(file, soguess))
144 this->SOName = soguess;
149 virtual void Report(std::ostream& e)
151 e << "runtime library [";
152 if(this->SOName.empty())
163 virtual bool FindConflict(std::string const& dir);
165 // The soname of the shared library if it is known.
169 //----------------------------------------------------------------------------
170 bool cmOrderDirectoriesConstraintSOName::FindConflict(std::string const& dir)
172 // Determine which type of check to do.
173 if(!this->SOName.empty())
175 // We have the library soname. Check if it will be found.
176 if(this->FileMayConflict(dir, this->SOName))
183 // We do not have the soname. Look for files in the directory
184 // that may conflict.
185 std::set<cmStdString> const& files =
186 (this->GlobalGenerator
187 ->GetDirectoryContent(dir, true));
189 // Get the set of files that might conflict. Since we do not
190 // know the soname just look at all files that start with the
191 // file name. Usually the soname starts with the library name.
192 std::string base = this->FileName;
193 std::set<cmStdString>::const_iterator first = files.lower_bound(base);
194 ++base[base.size()-1];
195 std::set<cmStdString>::const_iterator last = files.upper_bound(base);
204 //----------------------------------------------------------------------------
205 class cmOrderDirectoriesConstraintLibrary: public cmOrderDirectoriesConstraint
208 cmOrderDirectoriesConstraintLibrary(cmOrderDirectories* od,
209 std::string const& file):
210 cmOrderDirectoriesConstraint(od, file)
214 virtual void Report(std::ostream& e)
216 e << "link library [" << this->FileName << "]";
219 virtual bool FindConflict(std::string const& dir);
222 //----------------------------------------------------------------------------
223 bool cmOrderDirectoriesConstraintLibrary::FindConflict(std::string const& dir)
225 // We have the library file name. Check if it will be found.
226 if(this->FileMayConflict(dir, this->FileName))
231 // Now check if the file exists with other extensions the linker
233 if(!this->OD->LinkExtensions.empty() &&
234 this->OD->RemoveLibraryExtension.find(this->FileName))
236 cmStdString lib = this->OD->RemoveLibraryExtension.match(1);
237 cmStdString ext = this->OD->RemoveLibraryExtension.match(2);
238 for(std::vector<std::string>::iterator
239 i = this->OD->LinkExtensions.begin();
240 i != this->OD->LinkExtensions.end(); ++i)
244 std::string fname = lib;
246 if(this->FileMayConflict(dir, fname.c_str()))
256 //----------------------------------------------------------------------------
257 cmOrderDirectories::cmOrderDirectories(cmGlobalGenerator* gg,
261 this->GlobalGenerator = gg;
262 this->Target = target;
263 this->Purpose = purpose;
264 this->Computed = false;
267 //----------------------------------------------------------------------------
268 cmOrderDirectories::~cmOrderDirectories()
270 for(std::vector<cmOrderDirectoriesConstraint*>::iterator
271 i = this->ConstraintEntries.begin();
272 i != this->ConstraintEntries.end(); ++i)
276 for(std::vector<cmOrderDirectoriesConstraint*>::iterator
277 i = this->ImplicitDirEntries.begin();
278 i != this->ImplicitDirEntries.end(); ++i)
284 //----------------------------------------------------------------------------
285 std::vector<std::string> const& cmOrderDirectories::GetOrderedDirectories()
289 this->Computed = true;
290 this->CollectOriginalDirectories();
291 this->FindConflicts();
292 this->OrderDirectories();
294 return this->OrderedDirectories;
297 //----------------------------------------------------------------------------
298 void cmOrderDirectories::AddRuntimeLibrary(std::string const& fullPath,
301 // Add the runtime library at most once.
302 if(this->EmmittedConstraintSOName.insert(fullPath).second)
304 // Implicit link directories need special handling.
305 if(!this->ImplicitDirectories.empty())
307 std::string dir = cmSystemTools::GetFilenamePath(fullPath);
308 if(this->ImplicitDirectories.find(dir) !=
309 this->ImplicitDirectories.end())
311 this->ImplicitDirEntries.push_back(
312 new cmOrderDirectoriesConstraintSOName(this, fullPath, soname));
317 // Construct the runtime information entry for this library.
318 this->ConstraintEntries.push_back(
319 new cmOrderDirectoriesConstraintSOName(this, fullPath, soname));
323 // This can happen if the same library is linked multiple times.
324 // In that case the runtime information check need be done only
325 // once anyway. For shared libs we could add a check in AddItem
326 // to not repeat them.
330 //----------------------------------------------------------------------------
331 void cmOrderDirectories::AddLinkLibrary(std::string const& fullPath)
333 // Link extension info is required for library constraints.
334 assert(!this->LinkExtensions.empty());
336 // Add the link library at most once.
337 if(this->EmmittedConstraintLibrary.insert(fullPath).second)
339 // Implicit link directories need special handling.
340 if(!this->ImplicitDirectories.empty())
342 std::string dir = cmSystemTools::GetFilenamePath(fullPath);
343 if(this->ImplicitDirectories.find(dir) !=
344 this->ImplicitDirectories.end())
346 this->ImplicitDirEntries.push_back(
347 new cmOrderDirectoriesConstraintLibrary(this, fullPath));
352 // Construct the link library entry.
353 this->ConstraintEntries.push_back(
354 new cmOrderDirectoriesConstraintLibrary(this, fullPath));
358 //----------------------------------------------------------------------------
361 ::AddUserDirectories(std::vector<std::string> const& extra)
363 this->UserDirectories.insert(this->UserDirectories.end(),
364 extra.begin(), extra.end());
367 //----------------------------------------------------------------------------
370 ::AddLanguageDirectories(std::vector<std::string> const& dirs)
372 this->LanguageDirectories.insert(this->LanguageDirectories.end(),
373 dirs.begin(), dirs.end());
376 //----------------------------------------------------------------------------
379 ::SetImplicitDirectories(std::set<cmStdString> const& implicitDirs)
381 this->ImplicitDirectories = implicitDirs;
384 //----------------------------------------------------------------------------
387 ::SetLinkExtensionInfo(std::vector<std::string> const& linkExtensions,
388 std::string const& removeExtRegex)
390 this->LinkExtensions = linkExtensions;
391 this->RemoveLibraryExtension.compile(removeExtRegex.c_str());
394 //----------------------------------------------------------------------------
395 void cmOrderDirectories::CollectOriginalDirectories()
397 // Add user directories specified for inclusion. These should be
398 // indexed first so their original order is preserved as much as
399 // possible subject to the constraints.
400 this->AddOriginalDirectories(this->UserDirectories);
402 // Add directories containing constraints.
403 for(unsigned int i=0; i < this->ConstraintEntries.size(); ++i)
405 this->ConstraintEntries[i]->AddDirectory();
408 // Add language runtime directories last.
409 this->AddOriginalDirectories(this->LanguageDirectories);
412 //----------------------------------------------------------------------------
413 int cmOrderDirectories::AddOriginalDirectory(std::string const& dir)
415 // Add the runtime directory with a unique index.
416 std::map<cmStdString, int>::iterator i =
417 this->DirectoryIndex.find(dir);
418 if(i == this->DirectoryIndex.end())
420 std::map<cmStdString, int>::value_type
421 entry(dir, static_cast<int>(this->OriginalDirectories.size()));
422 i = this->DirectoryIndex.insert(entry).first;
423 this->OriginalDirectories.push_back(dir);
429 //----------------------------------------------------------------------------
432 ::AddOriginalDirectories(std::vector<std::string> const& dirs)
434 for(std::vector<std::string>::const_iterator di = dirs.begin();
435 di != dirs.end(); ++di)
437 // We never explicitly specify implicit link directories.
438 if(this->ImplicitDirectories.find(*di) !=
439 this->ImplicitDirectories.end())
444 // Skip the empty string.
450 // Add this directory.
451 this->AddOriginalDirectory(*di);
455 //----------------------------------------------------------------------------
456 struct cmOrderDirectoriesCompare
458 typedef std::pair<int, int> ConflictPair;
460 // The conflict pair is unique based on just the directory
461 // (first). The second element is only used for displaying
462 // information about why the entry is present.
463 bool operator()(ConflictPair const& l,
464 ConflictPair const& r)
466 return l.first == r.first;
470 //----------------------------------------------------------------------------
471 void cmOrderDirectories::FindConflicts()
473 // Allocate the conflict graph.
474 this->ConflictGraph.resize(this->OriginalDirectories.size());
475 this->DirectoryVisited.resize(this->OriginalDirectories.size(), 0);
477 // Find directories conflicting with each entry.
478 for(unsigned int i=0; i < this->ConstraintEntries.size(); ++i)
480 this->ConstraintEntries[i]->FindConflicts(i);
483 // Clean up the conflict graph representation.
484 for(std::vector<ConflictList>::iterator
485 i = this->ConflictGraph.begin();
486 i != this->ConflictGraph.end(); ++i)
488 // Sort the outgoing edges for each graph node so that the
489 // original order will be preserved as much as possible.
490 std::sort(i->begin(), i->end());
492 // Make the edge list unique so cycle detection will be reliable.
493 ConflictList::iterator last =
494 std::unique(i->begin(), i->end(), cmOrderDirectoriesCompare());
495 i->erase(last, i->end());
498 // Check items in implicit link directories.
499 this->FindImplicitConflicts();
502 //----------------------------------------------------------------------------
503 void cmOrderDirectories::FindImplicitConflicts()
505 // Check for items in implicit link directories that have conflicts
506 // in the explicit directories.
507 cmOStringStream conflicts;
508 for(unsigned int i=0; i < this->ImplicitDirEntries.size(); ++i)
510 this->ImplicitDirEntries[i]->FindImplicitConflicts(conflicts);
513 // Skip warning if there were no conflicts.
514 std::string text = conflicts.str();
520 // Warn about the conflicts.
522 w << "Cannot generate a safe " << this->Purpose
523 << " for target " << this->Target->GetName()
524 << " because files in some directories may conflict with "
525 << " libraries in implicit directories:\n"
527 << "Some of these libraries may not be found correctly.";
528 this->GlobalGenerator->GetCMakeInstance()
529 ->IssueMessage(cmake::WARNING, w.str(), this->Target->GetBacktrace());
532 //----------------------------------------------------------------------------
533 void cmOrderDirectories::OrderDirectories()
535 // Allow a cycle to be diagnosed once.
536 this->CycleDiagnosed = false;
539 // Iterate through the directories in the original order.
540 for(unsigned int i=0; i < this->OriginalDirectories.size(); ++i)
542 // Start a new DFS from this node.
544 this->VisitDirectory(i);
548 //----------------------------------------------------------------------------
549 void cmOrderDirectories::VisitDirectory(unsigned int i)
551 // Skip nodes already visited.
552 if(this->DirectoryVisited[i])
554 if(this->DirectoryVisited[i] == this->WalkId)
556 // We have reached a node previously visited on this DFS.
558 this->DiagnoseCycle();
563 // We are now visiting this node so mark it.
564 this->DirectoryVisited[i] = this->WalkId;
566 // Visit the neighbors of the node first.
567 ConflictList const& clist = this->ConflictGraph[i];
568 for(ConflictList::const_iterator j = clist.begin();
569 j != clist.end(); ++j)
571 this->VisitDirectory(j->first);
574 // Now that all directories required to come before this one have
575 // been emmitted, emit this directory.
576 this->OrderedDirectories.push_back(this->OriginalDirectories[i]);
579 //----------------------------------------------------------------------------
580 void cmOrderDirectories::DiagnoseCycle()
582 // Report the cycle at most once.
583 if(this->CycleDiagnosed)
587 this->CycleDiagnosed = true;
589 // Construct the message.
591 e << "Cannot generate a safe " << this->Purpose
592 << " for target " << this->Target->GetName()
593 << " because there is a cycle in the constraint graph:\n";
595 // Display the conflict graph.
596 for(unsigned int i=0; i < this->ConflictGraph.size(); ++i)
598 ConflictList const& clist = this->ConflictGraph[i];
599 e << " dir " << i << " is [" << this->OriginalDirectories[i] << "]\n";
600 for(ConflictList::const_iterator j = clist.begin();
601 j != clist.end(); ++j)
603 e << " dir " << j->first << " must precede it due to ";
604 this->ConstraintEntries[j->second]->Report(e);
608 e << "Some of these libraries may not be found correctly.";
609 this->GlobalGenerator->GetCMakeInstance()
610 ->IssueMessage(cmake::WARNING, e.str(), this->Target->GetBacktrace());