1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmOrderDirectories.cxx,v $
6 Date: $Date: 2008-12-26 18:28:20 $
7 Version: $Revision: 1.8 $
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"
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
35 //----------------------------------------------------------------------------
36 class cmOrderDirectoriesConstraint
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() {}
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
)
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.
89 w
<< " in " << this->Directory
<< " may be hidden by files in:\n";
91 w
<< " " << dir
<< "\n";
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.
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
;
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
138 cmOrderDirectoriesConstraintSOName(cmOrderDirectories
* od
,
139 std::string
const& file
,
141 cmOrderDirectoriesConstraint(od
, file
), SOName(soname
? soname
: "")
143 if(this->SOName
.empty())
145 // Try to guess the soname.
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())
168 virtual bool FindConflict(std::string
const& dir
);
170 // The soname of the shared library if it is known.
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
))
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
);
209 //----------------------------------------------------------------------------
210 class cmOrderDirectoriesConstraintLibrary
: public cmOrderDirectoriesConstraint
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
))
236 // Now check if the file exists with other extensions the linker
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
)
249 std::string fname
= lib
;
251 if(this->FileMayConflict(dir
, fname
.c_str()))
261 //----------------------------------------------------------------------------
262 cmOrderDirectories::cmOrderDirectories(cmGlobalGenerator
* gg
,
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
)
281 for(std::vector
<cmOrderDirectoriesConstraint
*>::iterator
282 i
= this->ImplicitDirEntries
.begin();
283 i
!= this->ImplicitDirEntries
.end(); ++i
)
289 //----------------------------------------------------------------------------
290 std::vector
<std::string
> const& cmOrderDirectories::GetOrderedDirectories()
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
,
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
));
322 // Construct the runtime information entry for this library.
323 this->ConstraintEntries
.push_back(
324 new cmOrderDirectoriesConstraintSOName(this, fullPath
, soname
));
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
));
357 // Construct the link library entry.
358 this->ConstraintEntries
.push_back(
359 new cmOrderDirectoriesConstraintLibrary(this, fullPath
));
363 //----------------------------------------------------------------------------
366 ::AddUserDirectories(std::vector
<std::string
> const& extra
)
368 this->UserDirectories
.insert(this->UserDirectories
.end(),
369 extra
.begin(), extra
.end());
372 //----------------------------------------------------------------------------
375 ::SetImplicitDirectories(std::set
<cmStdString
> const& implicitDirs
)
377 this->ImplicitDirectories
= implicitDirs
;
380 //----------------------------------------------------------------------------
383 ::SetLinkExtensionInfo(std::vector
<std::string
> const& linkExtensions
,
384 std::string
const& removeExtRegex
)
386 this->LinkExtensions
= linkExtensions
;
387 this->RemoveLibraryExtension
.compile(removeExtRegex
.c_str());
390 //----------------------------------------------------------------------------
391 void cmOrderDirectories::CollectOriginalDirectories()
393 // Add user directories specified for inclusion. These should be
394 // indexed first so their original order is preserved as much as
395 // possible subject to the constraints.
396 for(std::vector
<std::string
>::const_iterator
397 di
= this->UserDirectories
.begin();
398 di
!= this->UserDirectories
.end(); ++di
)
400 // We never explicitly specify implicit link directories.
401 if(this->ImplicitDirectories
.find(*di
) !=
402 this->ImplicitDirectories
.end())
407 // Skip the empty string.
413 // Add this directory.
414 this->AddOriginalDirectory(*di
);
417 // Add directories containing constraints.
418 for(unsigned int i
=0; i
< this->ConstraintEntries
.size(); ++i
)
420 this->ConstraintEntries
[i
]->AddDirectory();
424 //----------------------------------------------------------------------------
425 int cmOrderDirectories::AddOriginalDirectory(std::string
const& dir
)
427 // Add the runtime directory with a unique index.
428 std::map
<cmStdString
, int>::iterator i
=
429 this->DirectoryIndex
.find(dir
);
430 if(i
== this->DirectoryIndex
.end())
432 std::map
<cmStdString
, int>::value_type
433 entry(dir
, static_cast<int>(this->OriginalDirectories
.size()));
434 i
= this->DirectoryIndex
.insert(entry
).first
;
435 this->OriginalDirectories
.push_back(dir
);
441 //----------------------------------------------------------------------------
442 struct cmOrderDirectoriesCompare
444 typedef std::pair
<int, int> ConflictPair
;
446 // The conflict pair is unique based on just the directory
447 // (first). The second element is only used for displaying
448 // information about why the entry is present.
449 bool operator()(ConflictPair
const& l
,
450 ConflictPair
const& r
)
452 return l
.first
== r
.first
;
456 //----------------------------------------------------------------------------
457 void cmOrderDirectories::FindConflicts()
459 // Allocate the conflict graph.
460 this->ConflictGraph
.resize(this->OriginalDirectories
.size());
461 this->DirectoryVisited
.resize(this->OriginalDirectories
.size(), 0);
463 // Find directories conflicting with each entry.
464 for(unsigned int i
=0; i
< this->ConstraintEntries
.size(); ++i
)
466 this->ConstraintEntries
[i
]->FindConflicts(i
);
469 // Clean up the conflict graph representation.
470 for(std::vector
<ConflictList
>::iterator
471 i
= this->ConflictGraph
.begin();
472 i
!= this->ConflictGraph
.end(); ++i
)
474 // Sort the outgoing edges for each graph node so that the
475 // original order will be preserved as much as possible.
476 std::sort(i
->begin(), i
->end());
478 // Make the edge list unique so cycle detection will be reliable.
479 ConflictList::iterator last
=
480 std::unique(i
->begin(), i
->end(), cmOrderDirectoriesCompare());
481 i
->erase(last
, i
->end());
484 // Check items in implicit link directories.
485 this->FindImplicitConflicts();
488 //----------------------------------------------------------------------------
489 void cmOrderDirectories::FindImplicitConflicts()
491 // Check for items in implicit link directories that have conflicts
492 // in the explicit directories.
493 cmOStringStream conflicts
;
494 for(unsigned int i
=0; i
< this->ImplicitDirEntries
.size(); ++i
)
496 this->ImplicitDirEntries
[i
]->FindImplicitConflicts(conflicts
);
499 // Skip warning if there were no conflicts.
500 std::string text
= conflicts
.str();
506 // Warn about the conflicts.
508 w
<< "Cannot generate a safe " << this->Purpose
509 << " for target " << this->Target
->GetName()
510 << " because files in some directories may conflict with "
511 << " libraries in implicit directories:\n"
513 << "Some of these libraries may not be found correctly.";
514 this->GlobalGenerator
->GetCMakeInstance()
515 ->IssueMessage(cmake::WARNING
, w
.str(), this->Target
->GetBacktrace());
518 //----------------------------------------------------------------------------
519 void cmOrderDirectories::OrderDirectories()
521 // Allow a cycle to be diagnosed once.
522 this->CycleDiagnosed
= false;
525 // Iterate through the directories in the original order.
526 for(unsigned int i
=0; i
< this->OriginalDirectories
.size(); ++i
)
528 // Start a new DFS from this node.
530 this->VisitDirectory(i
);
534 //----------------------------------------------------------------------------
535 void cmOrderDirectories::VisitDirectory(unsigned int i
)
537 // Skip nodes already visited.
538 if(this->DirectoryVisited
[i
])
540 if(this->DirectoryVisited
[i
] == this->WalkId
)
542 // We have reached a node previously visited on this DFS.
544 this->DiagnoseCycle();
549 // We are now visiting this node so mark it.
550 this->DirectoryVisited
[i
] = this->WalkId
;
552 // Visit the neighbors of the node first.
553 ConflictList
const& clist
= this->ConflictGraph
[i
];
554 for(ConflictList::const_iterator j
= clist
.begin();
555 j
!= clist
.end(); ++j
)
557 this->VisitDirectory(j
->first
);
560 // Now that all directories required to come before this one have
561 // been emmitted, emit this directory.
562 this->OrderedDirectories
.push_back(this->OriginalDirectories
[i
]);
565 //----------------------------------------------------------------------------
566 void cmOrderDirectories::DiagnoseCycle()
568 // Report the cycle at most once.
569 if(this->CycleDiagnosed
)
573 this->CycleDiagnosed
= true;
575 // Construct the message.
577 e
<< "Cannot generate a safe " << this->Purpose
578 << " for target " << this->Target
->GetName()
579 << " because there is a cycle in the constraint graph:\n";
581 // Display the conflict graph.
582 for(unsigned int i
=0; i
< this->ConflictGraph
.size(); ++i
)
584 ConflictList
const& clist
= this->ConflictGraph
[i
];
585 e
<< " dir " << i
<< " is [" << this->OriginalDirectories
[i
] << "]\n";
586 for(ConflictList::const_iterator j
= clist
.begin();
587 j
!= clist
.end(); ++j
)
589 e
<< " dir " << j
->first
<< " must precede it due to ";
590 this->ConstraintEntries
[j
->second
]->Report(e
);
594 e
<< "Some of these libraries may not be found correctly.";
595 this->GlobalGenerator
->GetCMakeInstance()
596 ->IssueMessage(cmake::WARNING
, e
.str(), this->Target
->GetBacktrace());