Skip implicit link info for multiple OS X archs
[cmake.git] / Source / cmOrderDirectories.cxx
blob7b8a80e4783b96d7362c0841059d5fc4a736898d
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmOrderDirectories.cxx,v $
5 Language: C++
6 Date: $Date: 2009-07-27 16:43:16 $
7 Version: $Revision: 1.10 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmOrderDirectories.h"
19 #include "cmGlobalGenerator.h"
20 #include "cmSystemTools.h"
21 #include "cmake.h"
23 #include <assert.h>
25 #include <algorithm>
28 Directory ordering computation.
29 - Useful to compute a safe runtime library path order
30 - Need runtime path for supporting INSTALL_RPATH_USE_LINK_PATH
31 - Need runtime path at link time to pickup transitive link dependencies
32 for shared libraries.
35 //----------------------------------------------------------------------------
36 class cmOrderDirectoriesConstraint
38 public:
39 cmOrderDirectoriesConstraint(cmOrderDirectories* od,
40 std::string const& file):
41 OD(od), GlobalGenerator(od->GlobalGenerator)
43 this->FullPath = file;
44 this->Directory = cmSystemTools::GetFilenamePath(file);
45 this->FileName = cmSystemTools::GetFilenameName(file);
47 virtual ~cmOrderDirectoriesConstraint() {}
49 void AddDirectory()
51 this->DirectoryIndex = this->OD->AddOriginalDirectory(this->Directory);
54 virtual void Report(std::ostream& e) = 0;
56 void FindConflicts(unsigned int index)
58 for(unsigned int i=0; i < this->OD->OriginalDirectories.size(); ++i)
60 // Check if this directory conflicts with the entry.
61 std::string const& dir = this->OD->OriginalDirectories[i];
62 if(dir != this->Directory && this->FindConflict(dir))
64 // The library will be found in this directory but this is not
65 // the directory named for it. Add an entry to make sure the
66 // desired directory comes before this one.
67 cmOrderDirectories::ConflictPair p(this->DirectoryIndex, index);
68 this->OD->ConflictGraph[i].push_back(p);
73 void FindImplicitConflicts(cmOStringStream& w)
75 bool first = true;
76 for(unsigned int i=0; i < this->OD->OriginalDirectories.size(); ++i)
78 // Check if this directory conflicts with the entry.
79 std::string const& dir = this->OD->OriginalDirectories[i];
80 if(dir != this->Directory && this->FindConflict(dir))
82 // The library will be found in this directory but it is
83 // supposed to be found in an implicit search directory.
84 if(first)
86 first = false;
87 w << " ";
88 this->Report(w);
89 w << " in " << this->Directory << " may be hidden by files in:\n";
91 w << " " << dir << "\n";
95 protected:
96 virtual bool FindConflict(std::string const& dir) = 0;
98 bool FileMayConflict(std::string const& dir, std::string const& name);
100 cmOrderDirectories* OD;
101 cmGlobalGenerator* GlobalGenerator;
103 // The location in which the item is supposed to be found.
104 std::string FullPath;
105 std::string Directory;
106 std::string FileName;
108 // The index assigned to the directory.
109 int DirectoryIndex;
112 //----------------------------------------------------------------------------
113 bool cmOrderDirectoriesConstraint::FileMayConflict(std::string const& dir,
114 std::string const& name)
116 // Check if the file exists on disk.
117 std::string file = dir;
118 file += "/";
119 file += name;
120 if(cmSystemTools::FileExists(file.c_str(), true))
122 // The file conflicts only if it is not the same as the original
123 // file due to a symlink or hardlink.
124 return !cmSystemTools::SameFile(this->FullPath.c_str(), file.c_str());
127 // Check if the file will be built by cmake.
128 std::set<cmStdString> const& files =
129 (this->GlobalGenerator->GetDirectoryContent(dir, false));
130 std::set<cmStdString>::const_iterator fi = files.find(name);
131 return fi != files.end();
134 //----------------------------------------------------------------------------
135 class cmOrderDirectoriesConstraintSOName: public cmOrderDirectoriesConstraint
137 public:
138 cmOrderDirectoriesConstraintSOName(cmOrderDirectories* od,
139 std::string const& file,
140 const char* soname):
141 cmOrderDirectoriesConstraint(od, file), SOName(soname? soname : "")
143 if(this->SOName.empty())
145 // Try to guess the soname.
146 std::string soguess;
147 if(cmSystemTools::GuessLibrarySOName(file, soguess))
149 this->SOName = soguess;
154 virtual void Report(std::ostream& e)
156 e << "runtime library [";
157 if(this->SOName.empty())
159 e << this->FileName;
161 else
163 e << this->SOName;
165 e << "]";
168 virtual bool FindConflict(std::string const& dir);
169 private:
170 // The soname of the shared library if it is known.
171 std::string SOName;
174 //----------------------------------------------------------------------------
175 bool cmOrderDirectoriesConstraintSOName::FindConflict(std::string const& dir)
177 // Determine which type of check to do.
178 if(!this->SOName.empty())
180 // We have the library soname. Check if it will be found.
181 if(this->FileMayConflict(dir, this->SOName))
183 return true;
186 else
188 // We do not have the soname. Look for files in the directory
189 // that may conflict.
190 std::set<cmStdString> const& files =
191 (this->GlobalGenerator
192 ->GetDirectoryContent(dir, true));
194 // Get the set of files that might conflict. Since we do not
195 // know the soname just look at all files that start with the
196 // file name. Usually the soname starts with the library name.
197 std::string base = this->FileName;
198 std::set<cmStdString>::const_iterator first = files.lower_bound(base);
199 ++base[base.size()-1];
200 std::set<cmStdString>::const_iterator last = files.upper_bound(base);
201 if(first != last)
203 return true;
206 return false;
209 //----------------------------------------------------------------------------
210 class cmOrderDirectoriesConstraintLibrary: public cmOrderDirectoriesConstraint
212 public:
213 cmOrderDirectoriesConstraintLibrary(cmOrderDirectories* od,
214 std::string const& file):
215 cmOrderDirectoriesConstraint(od, file)
219 virtual void Report(std::ostream& e)
221 e << "link library [" << this->FileName << "]";
224 virtual bool FindConflict(std::string const& dir);
227 //----------------------------------------------------------------------------
228 bool cmOrderDirectoriesConstraintLibrary::FindConflict(std::string const& dir)
230 // We have the library file name. Check if it will be found.
231 if(this->FileMayConflict(dir, this->FileName))
233 return true;
236 // Now check if the file exists with other extensions the linker
237 // might consider.
238 if(!this->OD->LinkExtensions.empty() &&
239 this->OD->RemoveLibraryExtension.find(this->FileName))
241 cmStdString lib = this->OD->RemoveLibraryExtension.match(1);
242 cmStdString ext = this->OD->RemoveLibraryExtension.match(2);
243 for(std::vector<std::string>::iterator
244 i = this->OD->LinkExtensions.begin();
245 i != this->OD->LinkExtensions.end(); ++i)
247 if(*i != ext)
249 std::string fname = lib;
250 fname += *i;
251 if(this->FileMayConflict(dir, fname.c_str()))
253 return true;
258 return false;
261 //----------------------------------------------------------------------------
262 cmOrderDirectories::cmOrderDirectories(cmGlobalGenerator* gg,
263 cmTarget* target,
264 const char* purpose)
266 this->GlobalGenerator = gg;
267 this->Target = target;
268 this->Purpose = purpose;
269 this->Computed = false;
272 //----------------------------------------------------------------------------
273 cmOrderDirectories::~cmOrderDirectories()
275 for(std::vector<cmOrderDirectoriesConstraint*>::iterator
276 i = this->ConstraintEntries.begin();
277 i != this->ConstraintEntries.end(); ++i)
279 delete *i;
281 for(std::vector<cmOrderDirectoriesConstraint*>::iterator
282 i = this->ImplicitDirEntries.begin();
283 i != this->ImplicitDirEntries.end(); ++i)
285 delete *i;
289 //----------------------------------------------------------------------------
290 std::vector<std::string> const& cmOrderDirectories::GetOrderedDirectories()
292 if(!this->Computed)
294 this->Computed = true;
295 this->CollectOriginalDirectories();
296 this->FindConflicts();
297 this->OrderDirectories();
299 return this->OrderedDirectories;
302 //----------------------------------------------------------------------------
303 void cmOrderDirectories::AddRuntimeLibrary(std::string const& fullPath,
304 const char* soname)
306 // Add the runtime library at most once.
307 if(this->EmmittedConstraintSOName.insert(fullPath).second)
309 // Implicit link directories need special handling.
310 if(!this->ImplicitDirectories.empty())
312 std::string dir = cmSystemTools::GetFilenamePath(fullPath);
313 if(this->ImplicitDirectories.find(dir) !=
314 this->ImplicitDirectories.end())
316 this->ImplicitDirEntries.push_back(
317 new cmOrderDirectoriesConstraintSOName(this, fullPath, soname));
318 return;
322 // Construct the runtime information entry for this library.
323 this->ConstraintEntries.push_back(
324 new cmOrderDirectoriesConstraintSOName(this, fullPath, soname));
326 else
328 // This can happen if the same library is linked multiple times.
329 // In that case the runtime information check need be done only
330 // once anyway. For shared libs we could add a check in AddItem
331 // to not repeat them.
335 //----------------------------------------------------------------------------
336 void cmOrderDirectories::AddLinkLibrary(std::string const& fullPath)
338 // Link extension info is required for library constraints.
339 assert(!this->LinkExtensions.empty());
341 // Add the link library at most once.
342 if(this->EmmittedConstraintLibrary.insert(fullPath).second)
344 // Implicit link directories need special handling.
345 if(!this->ImplicitDirectories.empty())
347 std::string dir = cmSystemTools::GetFilenamePath(fullPath);
348 if(this->ImplicitDirectories.find(dir) !=
349 this->ImplicitDirectories.end())
351 this->ImplicitDirEntries.push_back(
352 new cmOrderDirectoriesConstraintLibrary(this, fullPath));
353 return;
357 // Construct the link library entry.
358 this->ConstraintEntries.push_back(
359 new cmOrderDirectoriesConstraintLibrary(this, fullPath));
363 //----------------------------------------------------------------------------
364 void
365 cmOrderDirectories
366 ::AddUserDirectories(std::vector<std::string> const& extra)
368 this->UserDirectories.insert(this->UserDirectories.end(),
369 extra.begin(), extra.end());
372 //----------------------------------------------------------------------------
373 void
374 cmOrderDirectories
375 ::AddLanguageDirectories(std::vector<std::string> const& dirs)
377 this->LanguageDirectories.insert(this->LanguageDirectories.end(),
378 dirs.begin(), dirs.end());
381 //----------------------------------------------------------------------------
382 void
383 cmOrderDirectories
384 ::SetImplicitDirectories(std::set<cmStdString> const& implicitDirs)
386 this->ImplicitDirectories = implicitDirs;
389 //----------------------------------------------------------------------------
390 void
391 cmOrderDirectories
392 ::SetLinkExtensionInfo(std::vector<std::string> const& linkExtensions,
393 std::string const& removeExtRegex)
395 this->LinkExtensions = linkExtensions;
396 this->RemoveLibraryExtension.compile(removeExtRegex.c_str());
399 //----------------------------------------------------------------------------
400 void cmOrderDirectories::CollectOriginalDirectories()
402 // Add user directories specified for inclusion. These should be
403 // indexed first so their original order is preserved as much as
404 // possible subject to the constraints.
405 this->AddOriginalDirectories(this->UserDirectories);
407 // Add directories containing constraints.
408 for(unsigned int i=0; i < this->ConstraintEntries.size(); ++i)
410 this->ConstraintEntries[i]->AddDirectory();
413 // Add language runtime directories last.
414 this->AddOriginalDirectories(this->LanguageDirectories);
417 //----------------------------------------------------------------------------
418 int cmOrderDirectories::AddOriginalDirectory(std::string const& dir)
420 // Add the runtime directory with a unique index.
421 std::map<cmStdString, int>::iterator i =
422 this->DirectoryIndex.find(dir);
423 if(i == this->DirectoryIndex.end())
425 std::map<cmStdString, int>::value_type
426 entry(dir, static_cast<int>(this->OriginalDirectories.size()));
427 i = this->DirectoryIndex.insert(entry).first;
428 this->OriginalDirectories.push_back(dir);
431 return i->second;
434 //----------------------------------------------------------------------------
435 void
436 cmOrderDirectories
437 ::AddOriginalDirectories(std::vector<std::string> const& dirs)
439 for(std::vector<std::string>::const_iterator di = dirs.begin();
440 di != dirs.end(); ++di)
442 // We never explicitly specify implicit link directories.
443 if(this->ImplicitDirectories.find(*di) !=
444 this->ImplicitDirectories.end())
446 continue;
449 // Skip the empty string.
450 if(di->empty())
452 continue;
455 // Add this directory.
456 this->AddOriginalDirectory(*di);
460 //----------------------------------------------------------------------------
461 struct cmOrderDirectoriesCompare
463 typedef std::pair<int, int> ConflictPair;
465 // The conflict pair is unique based on just the directory
466 // (first). The second element is only used for displaying
467 // information about why the entry is present.
468 bool operator()(ConflictPair const& l,
469 ConflictPair const& r)
471 return l.first == r.first;
475 //----------------------------------------------------------------------------
476 void cmOrderDirectories::FindConflicts()
478 // Allocate the conflict graph.
479 this->ConflictGraph.resize(this->OriginalDirectories.size());
480 this->DirectoryVisited.resize(this->OriginalDirectories.size(), 0);
482 // Find directories conflicting with each entry.
483 for(unsigned int i=0; i < this->ConstraintEntries.size(); ++i)
485 this->ConstraintEntries[i]->FindConflicts(i);
488 // Clean up the conflict graph representation.
489 for(std::vector<ConflictList>::iterator
490 i = this->ConflictGraph.begin();
491 i != this->ConflictGraph.end(); ++i)
493 // Sort the outgoing edges for each graph node so that the
494 // original order will be preserved as much as possible.
495 std::sort(i->begin(), i->end());
497 // Make the edge list unique so cycle detection will be reliable.
498 ConflictList::iterator last =
499 std::unique(i->begin(), i->end(), cmOrderDirectoriesCompare());
500 i->erase(last, i->end());
503 // Check items in implicit link directories.
504 this->FindImplicitConflicts();
507 //----------------------------------------------------------------------------
508 void cmOrderDirectories::FindImplicitConflicts()
510 // Check for items in implicit link directories that have conflicts
511 // in the explicit directories.
512 cmOStringStream conflicts;
513 for(unsigned int i=0; i < this->ImplicitDirEntries.size(); ++i)
515 this->ImplicitDirEntries[i]->FindImplicitConflicts(conflicts);
518 // Skip warning if there were no conflicts.
519 std::string text = conflicts.str();
520 if(text.empty())
522 return;
525 // Warn about the conflicts.
526 cmOStringStream w;
527 w << "Cannot generate a safe " << this->Purpose
528 << " for target " << this->Target->GetName()
529 << " because files in some directories may conflict with "
530 << " libraries in implicit directories:\n"
531 << text
532 << "Some of these libraries may not be found correctly.";
533 this->GlobalGenerator->GetCMakeInstance()
534 ->IssueMessage(cmake::WARNING, w.str(), this->Target->GetBacktrace());
537 //----------------------------------------------------------------------------
538 void cmOrderDirectories::OrderDirectories()
540 // Allow a cycle to be diagnosed once.
541 this->CycleDiagnosed = false;
542 this->WalkId = 0;
544 // Iterate through the directories in the original order.
545 for(unsigned int i=0; i < this->OriginalDirectories.size(); ++i)
547 // Start a new DFS from this node.
548 ++this->WalkId;
549 this->VisitDirectory(i);
553 //----------------------------------------------------------------------------
554 void cmOrderDirectories::VisitDirectory(unsigned int i)
556 // Skip nodes already visited.
557 if(this->DirectoryVisited[i])
559 if(this->DirectoryVisited[i] == this->WalkId)
561 // We have reached a node previously visited on this DFS.
562 // There is a cycle.
563 this->DiagnoseCycle();
565 return;
568 // We are now visiting this node so mark it.
569 this->DirectoryVisited[i] = this->WalkId;
571 // Visit the neighbors of the node first.
572 ConflictList const& clist = this->ConflictGraph[i];
573 for(ConflictList::const_iterator j = clist.begin();
574 j != clist.end(); ++j)
576 this->VisitDirectory(j->first);
579 // Now that all directories required to come before this one have
580 // been emmitted, emit this directory.
581 this->OrderedDirectories.push_back(this->OriginalDirectories[i]);
584 //----------------------------------------------------------------------------
585 void cmOrderDirectories::DiagnoseCycle()
587 // Report the cycle at most once.
588 if(this->CycleDiagnosed)
590 return;
592 this->CycleDiagnosed = true;
594 // Construct the message.
595 cmOStringStream e;
596 e << "Cannot generate a safe " << this->Purpose
597 << " for target " << this->Target->GetName()
598 << " because there is a cycle in the constraint graph:\n";
600 // Display the conflict graph.
601 for(unsigned int i=0; i < this->ConflictGraph.size(); ++i)
603 ConflictList const& clist = this->ConflictGraph[i];
604 e << " dir " << i << " is [" << this->OriginalDirectories[i] << "]\n";
605 for(ConflictList::const_iterator j = clist.begin();
606 j != clist.end(); ++j)
608 e << " dir " << j->first << " must precede it due to ";
609 this->ConstraintEntries[j->second]->Report(e);
610 e << "\n";
613 e << "Some of these libraries may not be found correctly.";
614 this->GlobalGenerator->GetCMakeInstance()
615 ->IssueMessage(cmake::WARNING, e.str(), this->Target->GetBacktrace());