BUG: fix some bad changes in progress calc
[cmake.git] / Source / cmOrderDirectories.cxx
blob35d3e883dfdbb0fb311d93542df47de434d0af1d
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmOrderDirectories.cxx,v $
5 Language: C++
6 Date: $Date: 2008-02-22 14:44:11 $
7 Version: $Revision: 1.4 $
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"
22 #include <assert.h>
24 #include <algorithm>
27 Directory ordering computation.
28 - Useful to compute a safe runtime library path order
29 - Need runtime path for supporting INSTALL_RPATH_USE_LINK_PATH
30 - Need runtime path at link time to pickup transitive link dependencies
31 for shared libraries.
34 //----------------------------------------------------------------------------
35 class cmOrderDirectoriesConstraint
37 public:
38 cmOrderDirectoriesConstraint(cmOrderDirectories* od,
39 std::string const& file):
40 OD(od), GlobalGenerator(od->GlobalGenerator)
42 this->FullPath = file;
43 this->Directory = cmSystemTools::GetFilenamePath(file);
44 this->FileName = cmSystemTools::GetFilenameName(file);
46 virtual ~cmOrderDirectoriesConstraint() {}
48 void AddDirectory()
50 this->DirectoryIndex = this->OD->AddOriginalDirectory(this->Directory);
53 virtual void Report(std::ostream& e) = 0;
55 void FindConflicts(unsigned int index)
57 for(unsigned int i=0; i < this->OD->OriginalDirectories.size(); ++i)
59 // Check if this directory conflicts with they entry.
60 std::string const& dir = this->OD->OriginalDirectories[i];
61 if(dir != this->Directory && this->FindConflict(dir))
63 // The library will be found in this directory but this is not
64 // the directory named for it. Add an entry to make sure the
65 // desired directory comes before this one.
66 cmOrderDirectories::ConflictPair p(this->DirectoryIndex, index);
67 this->OD->ConflictGraph[i].push_back(p);
71 protected:
72 virtual bool FindConflict(std::string const& dir) = 0;
74 bool FileMayConflict(std::string const& dir, std::string const& name);
76 cmOrderDirectories* OD;
77 cmGlobalGenerator* GlobalGenerator;
79 // The location in which the item is supposed to be found.
80 std::string FullPath;
81 std::string Directory;
82 std::string FileName;
84 // The index assigned to the directory.
85 int DirectoryIndex;
88 //----------------------------------------------------------------------------
89 bool cmOrderDirectoriesConstraint::FileMayConflict(std::string const& dir,
90 std::string const& name)
92 // Check if the file will be built by cmake.
93 std::set<cmStdString> const& files =
94 (this->GlobalGenerator->GetDirectoryContent(dir, false));
95 if(std::set<cmStdString>::const_iterator(files.find(name)) != files.end())
97 return true;
100 // Check if the file exists on disk and is not a symlink back to the
101 // original file.
102 std::string file = dir;
103 file += "/";
104 file += name;
105 if(cmSystemTools::FileExists(file.c_str(), true) &&
106 !cmSystemTools::SameFile(this->FullPath.c_str(), file.c_str()))
108 return true;
110 return false;
113 //----------------------------------------------------------------------------
114 class cmOrderDirectoriesConstraintSOName: public cmOrderDirectoriesConstraint
116 public:
117 cmOrderDirectoriesConstraintSOName(cmOrderDirectories* od,
118 std::string const& file,
119 const char* soname):
120 cmOrderDirectoriesConstraint(od, file), SOName(soname? soname : "")
122 if(this->SOName.empty())
124 // Try to guess the soname.
125 std::string soguess;
126 if(cmSystemTools::GuessLibrarySOName(file, soguess))
128 this->SOName = soguess;
133 virtual void Report(std::ostream& e)
135 e << "runtime library [";
136 if(this->SOName.empty())
138 e << this->FileName;
140 else
142 e << this->SOName;
144 e << "]";
147 virtual bool FindConflict(std::string const& dir);
148 private:
149 // The soname of the shared library if it is known.
150 std::string SOName;
153 //----------------------------------------------------------------------------
154 bool cmOrderDirectoriesConstraintSOName::FindConflict(std::string const& dir)
156 // Determine which type of check to do.
157 if(!this->SOName.empty())
159 // We have the library soname. Check if it will be found.
160 if(this->FileMayConflict(dir, this->SOName))
162 return true;
165 else
167 // We do not have the soname. Look for files in the directory
168 // that may conflict.
169 std::set<cmStdString> const& files =
170 (this->GlobalGenerator
171 ->GetDirectoryContent(dir, true));
173 // Get the set of files that might conflict. Since we do not
174 // know the soname just look at all files that start with the
175 // file name. Usually the soname starts with the library name.
176 std::string base = this->FileName;
177 std::set<cmStdString>::const_iterator first = files.lower_bound(base);
178 ++base[base.size()-1];
179 std::set<cmStdString>::const_iterator last = files.upper_bound(base);
180 if(first != last)
182 return true;
185 return false;
188 //----------------------------------------------------------------------------
189 class cmOrderDirectoriesConstraintLibrary: public cmOrderDirectoriesConstraint
191 public:
192 cmOrderDirectoriesConstraintLibrary(cmOrderDirectories* od,
193 std::string const& file):
194 cmOrderDirectoriesConstraint(od, file)
198 virtual void Report(std::ostream& e)
200 e << "link library [" << this->FileName << "]";
203 virtual bool FindConflict(std::string const& dir);
206 //----------------------------------------------------------------------------
207 bool cmOrderDirectoriesConstraintLibrary::FindConflict(std::string const& dir)
209 // We have the library file name. Check if it will be found.
210 if(this->FileMayConflict(dir, this->FileName))
212 return true;
215 // Now check if the file exists with other extensions the linker
216 // might consider.
217 if(!this->OD->LinkExtensions.empty() &&
218 this->OD->RemoveLibraryExtension.find(this->FileName))
220 cmStdString lib = this->OD->RemoveLibraryExtension.match(1);
221 cmStdString ext = this->OD->RemoveLibraryExtension.match(2);
222 for(std::vector<std::string>::iterator
223 i = this->OD->LinkExtensions.begin();
224 i != this->OD->LinkExtensions.end(); ++i)
226 if(*i != ext)
228 std::string fname = lib;
229 fname += *i;
230 if(this->FileMayConflict(dir, fname.c_str()))
232 return true;
237 return false;
240 //----------------------------------------------------------------------------
241 cmOrderDirectories::cmOrderDirectories(cmGlobalGenerator* gg,
242 const char* name,
243 const char* purpose)
245 this->GlobalGenerator = gg;
246 this->Name = name;
247 this->Purpose = purpose;
248 this->Computed = false;
251 //----------------------------------------------------------------------------
252 cmOrderDirectories::~cmOrderDirectories()
254 for(std::vector<cmOrderDirectoriesConstraint*>::iterator
255 i = this->ConstraintEntries.begin();
256 i != this->ConstraintEntries.end(); ++i)
258 delete *i;
262 //----------------------------------------------------------------------------
263 std::vector<std::string> const& cmOrderDirectories::GetOrderedDirectories()
265 if(!this->Computed)
267 this->Computed = true;
268 this->CollectOriginalDirectories();
269 this->FindConflicts();
270 this->OrderDirectories();
272 return this->OrderedDirectories;
275 //----------------------------------------------------------------------------
276 void cmOrderDirectories::AddRuntimeLibrary(std::string const& fullPath,
277 const char* soname)
279 // Add the runtime library at most once.
280 if(this->EmmittedConstraintSOName.insert(fullPath).second)
282 // Avoid dealing with implicit directories.
283 if(!this->ImplicitDirectories.empty())
285 std::string dir = cmSystemTools::GetFilenamePath(fullPath);
286 if(this->ImplicitDirectories.find(dir) !=
287 this->ImplicitDirectories.end())
289 return;
293 // Construct the runtime information entry for this library.
294 this->ConstraintEntries.push_back(
295 new cmOrderDirectoriesConstraintSOName(this, fullPath, soname));
297 else
299 // This can happen if the same library is linked multiple times.
300 // In that case the runtime information check need be done only
301 // once anyway. For shared libs we could add a check in AddItem
302 // to not repeat them.
306 //----------------------------------------------------------------------------
307 void cmOrderDirectories::AddLinkLibrary(std::string const& fullPath)
309 // Link extension info is required for library constraints.
310 assert(!this->LinkExtensions.empty());
312 // Add the link library at most once.
313 if(this->EmmittedConstraintLibrary.insert(fullPath).second)
315 // Avoid dealing with implicit directories.
316 if(!this->ImplicitDirectories.empty())
318 std::string dir = cmSystemTools::GetFilenamePath(fullPath);
319 if(this->ImplicitDirectories.find(dir) !=
320 this->ImplicitDirectories.end())
322 return;
326 // Construct the link library entry.
327 this->ConstraintEntries.push_back(
328 new cmOrderDirectoriesConstraintLibrary(this, fullPath));
332 //----------------------------------------------------------------------------
333 void
334 cmOrderDirectories
335 ::AddUserDirectories(std::vector<std::string> const& extra)
337 this->UserDirectories.insert(this->UserDirectories.end(),
338 extra.begin(), extra.end());
341 //----------------------------------------------------------------------------
342 void
343 cmOrderDirectories
344 ::SetImplicitDirectories(std::set<cmStdString> const& implicitDirs)
346 this->ImplicitDirectories = implicitDirs;
349 //----------------------------------------------------------------------------
350 void
351 cmOrderDirectories
352 ::SetLinkExtensionInfo(std::vector<std::string> const& linkExtensions,
353 std::string const& removeExtRegex)
355 this->LinkExtensions = linkExtensions;
356 this->RemoveLibraryExtension.compile(removeExtRegex.c_str());
359 //----------------------------------------------------------------------------
360 void cmOrderDirectories::CollectOriginalDirectories()
362 // Add user directories specified for inclusion. These should be
363 // indexed first so their original order is preserved as much as
364 // possible subject to the constraints.
365 for(std::vector<std::string>::const_iterator
366 di = this->UserDirectories.begin();
367 di != this->UserDirectories.end(); ++di)
369 // Avoid dealing with implicit directories.
370 if(this->ImplicitDirectories.find(*di) !=
371 this->ImplicitDirectories.end())
373 continue;
376 // Skip the empty string.
377 if(di->empty())
379 continue;
382 // Add this directory.
383 this->AddOriginalDirectory(*di);
386 // Add directories containing constraints.
387 for(unsigned int i=0; i < this->ConstraintEntries.size(); ++i)
389 this->ConstraintEntries[i]->AddDirectory();
393 //----------------------------------------------------------------------------
394 int cmOrderDirectories::AddOriginalDirectory(std::string const& dir)
396 // Add the runtime directory with a unique index.
397 std::map<cmStdString, int>::iterator i =
398 this->DirectoryIndex.find(dir);
399 if(i == this->DirectoryIndex.end())
401 std::map<cmStdString, int>::value_type
402 entry(dir, static_cast<int>(this->OriginalDirectories.size()));
403 i = this->DirectoryIndex.insert(entry).first;
404 this->OriginalDirectories.push_back(dir);
407 return i->second;
410 //----------------------------------------------------------------------------
411 struct cmOrderDirectoriesCompare
413 typedef std::pair<int, int> ConflictPair;
415 // The conflict pair is unique based on just the directory
416 // (first). The second element is only used for displaying
417 // information about why the entry is present.
418 bool operator()(ConflictPair const& l,
419 ConflictPair const& r)
421 return l.first == r.first;
425 //----------------------------------------------------------------------------
426 void cmOrderDirectories::FindConflicts()
428 // Allocate the conflict graph.
429 this->ConflictGraph.resize(this->OriginalDirectories.size());
430 this->DirectoryVisited.resize(this->OriginalDirectories.size(), 0);
432 // Find directories conflicting with each entry.
433 for(unsigned int i=0; i < this->ConstraintEntries.size(); ++i)
435 this->ConstraintEntries[i]->FindConflicts(i);
438 // Clean up the conflict graph representation.
439 for(std::vector<ConflictList>::iterator
440 i = this->ConflictGraph.begin();
441 i != this->ConflictGraph.end(); ++i)
443 // Sort the outgoing edges for each graph node so that the
444 // original order will be preserved as much as possible.
445 std::sort(i->begin(), i->end());
447 // Make the edge list unique so cycle detection will be reliable.
448 ConflictList::iterator last =
449 std::unique(i->begin(), i->end(), cmOrderDirectoriesCompare());
450 i->erase(last, i->end());
454 //----------------------------------------------------------------------------
455 void cmOrderDirectories::OrderDirectories()
457 // Allow a cycle to be diagnosed once.
458 this->CycleDiagnosed = false;
459 this->WalkId = 0;
461 // Iterate through the directories in the original order.
462 for(unsigned int i=0; i < this->OriginalDirectories.size(); ++i)
464 // Start a new DFS from this node.
465 ++this->WalkId;
466 this->VisitDirectory(i);
470 //----------------------------------------------------------------------------
471 void cmOrderDirectories::VisitDirectory(unsigned int i)
473 // Skip nodes already visited.
474 if(this->DirectoryVisited[i])
476 if(this->DirectoryVisited[i] == this->WalkId)
478 // We have reached a node previously visited on this DFS.
479 // There is a cycle.
480 this->DiagnoseCycle();
482 return;
485 // We are now visiting this node so mark it.
486 this->DirectoryVisited[i] = this->WalkId;
488 // Visit the neighbors of the node first.
489 ConflictList const& clist = this->ConflictGraph[i];
490 for(ConflictList::const_iterator j = clist.begin();
491 j != clist.end(); ++j)
493 this->VisitDirectory(j->first);
496 // Now that all directories required to come before this one have
497 // been emmitted, emit this directory.
498 this->OrderedDirectories.push_back(this->OriginalDirectories[i]);
501 //----------------------------------------------------------------------------
502 void cmOrderDirectories::DiagnoseCycle()
504 // Report the cycle at most once.
505 if(this->CycleDiagnosed)
507 return;
509 this->CycleDiagnosed = true;
511 // Construct the message.
512 cmOStringStream e;
513 e << "WARNING: Cannot generate a safe " << this->Purpose
514 << " for target " << this->Name
515 << " because there is a cycle in the constraint graph:\n";
517 // Display the conflict graph.
518 for(unsigned int i=0; i < this->ConflictGraph.size(); ++i)
520 ConflictList const& clist = this->ConflictGraph[i];
521 e << "dir " << i << " is [" << this->OriginalDirectories[i] << "]\n";
522 for(ConflictList::const_iterator j = clist.begin();
523 j != clist.end(); ++j)
525 e << " dir " << j->first << " must precede it due to ";
526 this->ConstraintEntries[j->second]->Report(e);
527 e << "\n";
530 cmSystemTools::Message(e.str().c_str());