/src/CMake/Source/cmOrderDirectories.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmOrderDirectories.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <cassert> |
7 | | #include <functional> |
8 | | #include <sstream> |
9 | | #include <vector> |
10 | | |
11 | | #include <cm/memory> |
12 | | #include <cmext/algorithm> |
13 | | |
14 | | #include "cmGeneratorTarget.h" |
15 | | #include "cmGlobalGenerator.h" |
16 | | #include "cmMessageType.h" |
17 | | #include "cmStringAlgorithms.h" |
18 | | #include "cmSystemTools.h" |
19 | | #include "cmake.h" |
20 | | |
21 | | /* |
22 | | Directory ordering computation. |
23 | | - Useful to compute a safe runtime library path order |
24 | | - Need runtime path for supporting INSTALL_RPATH_USE_LINK_PATH |
25 | | - Need runtime path at link time to pickup transitive link dependencies |
26 | | for shared libraries. |
27 | | */ |
28 | | |
29 | | class cmOrderDirectoriesConstraint |
30 | | { |
31 | | public: |
32 | | cmOrderDirectoriesConstraint(cmOrderDirectories* od, std::string const& file) |
33 | 0 | : OD(od) |
34 | 0 | , GlobalGenerator(od->GlobalGenerator) |
35 | 0 | { |
36 | 0 | this->FullPath = file; |
37 | |
|
38 | 0 | if (file.rfind(".framework") != std::string::npos) { |
39 | 0 | static cmsys::RegularExpression splitFramework( |
40 | 0 | "^(.*)/(.*).framework/(.*)$"); |
41 | 0 | if (splitFramework.find(file) && |
42 | 0 | (std::string::npos != |
43 | 0 | splitFramework.match(3).find(splitFramework.match(2)))) { |
44 | 0 | this->Directory = splitFramework.match(1); |
45 | 0 | this->FileName = |
46 | 0 | std::string(file.begin() + this->Directory.size() + 1, file.end()); |
47 | 0 | } |
48 | 0 | } |
49 | |
|
50 | 0 | if (this->FileName.empty()) { |
51 | 0 | this->Directory = cmSystemTools::GetFilenamePath(file); |
52 | 0 | this->FileName = cmSystemTools::GetFilenameName(file); |
53 | 0 | } |
54 | 0 | } |
55 | 0 | virtual ~cmOrderDirectoriesConstraint() = default; |
56 | | |
57 | | void AddDirectory() |
58 | 0 | { |
59 | 0 | this->DirectoryIndex = this->OD->AddOriginalDirectory(this->Directory); |
60 | 0 | } |
61 | | |
62 | | virtual void Report(std::ostream& e) = 0; |
63 | | |
64 | | void FindConflicts(unsigned int index) |
65 | 0 | { |
66 | 0 | for (unsigned int i = 0; i < this->OD->OriginalDirectories.size(); ++i) { |
67 | | // Check if this directory conflicts with the entry. |
68 | 0 | std::string const& dir = this->OD->OriginalDirectories[i]; |
69 | 0 | if (!this->OD->IsSameDirectory(dir, this->Directory) && |
70 | 0 | this->FindConflict(dir)) { |
71 | | // The library will be found in this directory but this is not |
72 | | // the directory named for it. Add an entry to make sure the |
73 | | // desired directory comes before this one. |
74 | 0 | cmOrderDirectories::ConflictPair p(this->DirectoryIndex, index); |
75 | 0 | this->OD->ConflictGraph[i].push_back(p); |
76 | 0 | } |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | void FindImplicitConflicts(std::ostringstream& w) |
81 | 0 | { |
82 | 0 | bool first = true; |
83 | 0 | for (std::string const& dir : this->OD->OriginalDirectories) { |
84 | | // Check if this directory conflicts with the entry. |
85 | 0 | if (dir != this->Directory && |
86 | 0 | cmSystemTools::GetRealPath(dir) != |
87 | 0 | cmSystemTools::GetRealPath(this->Directory) && |
88 | 0 | this->FindConflict(dir)) { |
89 | | // The library will be found in this directory but it is |
90 | | // supposed to be found in an implicit search directory. |
91 | 0 | if (first) { |
92 | 0 | first = false; |
93 | 0 | w << " "; |
94 | 0 | this->Report(w); |
95 | 0 | w << " in " << this->Directory << " may be hidden by files in:\n"; |
96 | 0 | } |
97 | 0 | w << " " << dir << "\n"; |
98 | 0 | } |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | protected: |
103 | | virtual bool FindConflict(std::string const& dir) = 0; |
104 | | |
105 | | bool FileMayConflict(std::string const& dir, std::string const& name); |
106 | | |
107 | | cmOrderDirectories* OD; |
108 | | cmGlobalGenerator* GlobalGenerator; |
109 | | |
110 | | // The location in which the item is supposed to be found. |
111 | | std::string FullPath; |
112 | | std::string Directory; |
113 | | std::string FileName; |
114 | | |
115 | | // The index assigned to the directory. |
116 | | int DirectoryIndex; |
117 | | }; |
118 | | |
119 | | bool cmOrderDirectoriesConstraint::FileMayConflict(std::string const& dir, |
120 | | std::string const& name) |
121 | 0 | { |
122 | | // Check if the file exists on disk. |
123 | 0 | std::string file = cmStrCat(dir, '/', name); |
124 | 0 | if (cmSystemTools::FileExists(file, true)) { |
125 | | // The file conflicts only if it is not the same as the original |
126 | | // file due to a symlink or hardlink. |
127 | 0 | return !cmSystemTools::SameFile(this->FullPath, file); |
128 | 0 | } |
129 | | |
130 | | // Check if the file will be built by cmake. |
131 | 0 | std::set<std::string> const& files = |
132 | 0 | (this->GlobalGenerator->GetDirectoryContent(dir, false)); |
133 | 0 | auto fi = files.find(name); |
134 | 0 | return fi != files.end(); |
135 | 0 | } |
136 | | |
137 | | class cmOrderDirectoriesConstraintSOName : public cmOrderDirectoriesConstraint |
138 | | { |
139 | | public: |
140 | | cmOrderDirectoriesConstraintSOName(cmOrderDirectories* od, |
141 | | std::string const& file, |
142 | | char const* soname) |
143 | 0 | : cmOrderDirectoriesConstraint(od, file) |
144 | 0 | , SOName(soname ? soname : "") |
145 | 0 | { |
146 | 0 | if (this->SOName.empty()) { |
147 | | // Try to guess the soname. |
148 | 0 | std::string soguess; |
149 | 0 | if (cmSystemTools::GuessLibrarySOName(file, soguess)) { |
150 | 0 | this->SOName = soguess; |
151 | 0 | } |
152 | 0 | } |
153 | 0 | } |
154 | | |
155 | | void Report(std::ostream& e) override |
156 | 0 | { |
157 | 0 | e << "runtime library ["; |
158 | 0 | if (this->SOName.empty()) { |
159 | 0 | e << this->FileName; |
160 | 0 | } else { |
161 | 0 | e << this->SOName; |
162 | 0 | } |
163 | 0 | e << "]"; |
164 | 0 | } |
165 | | |
166 | | bool FindConflict(std::string const& dir) override; |
167 | | |
168 | | private: |
169 | | // The soname of the shared library if it is known. |
170 | | std::string SOName; |
171 | | }; |
172 | | |
173 | | bool cmOrderDirectoriesConstraintSOName::FindConflict(std::string const& dir) |
174 | 0 | { |
175 | | // Determine which type of check to do. |
176 | 0 | if (!this->SOName.empty()) { |
177 | | // We have the library soname. Check if it will be found. |
178 | 0 | if (this->FileMayConflict(dir, this->SOName)) { |
179 | 0 | return true; |
180 | 0 | } |
181 | 0 | } else { |
182 | | // We do not have the soname. Look for files in the directory |
183 | | // that may conflict. |
184 | 0 | std::set<std::string> const& files = |
185 | 0 | (this->GlobalGenerator->GetDirectoryContent(dir, true)); |
186 | | |
187 | | // Get the set of files that might conflict. Since we do not |
188 | | // know the soname just look at all files that start with the |
189 | | // file name. Usually the soname starts with the library name. |
190 | 0 | std::string base = this->FileName; |
191 | 0 | auto first = files.lower_bound(base); |
192 | 0 | ++base.back(); |
193 | 0 | auto last = files.upper_bound(base); |
194 | 0 | if (first != last) { |
195 | 0 | return true; |
196 | 0 | } |
197 | 0 | } |
198 | 0 | return false; |
199 | 0 | } |
200 | | |
201 | | class cmOrderDirectoriesConstraintLibrary : public cmOrderDirectoriesConstraint |
202 | | { |
203 | | public: |
204 | | cmOrderDirectoriesConstraintLibrary(cmOrderDirectories* od, |
205 | | std::string const& file) |
206 | 0 | : cmOrderDirectoriesConstraint(od, file) |
207 | 0 | { |
208 | 0 | } |
209 | | |
210 | | void Report(std::ostream& e) override |
211 | 0 | { |
212 | 0 | e << "link library [" << this->FileName << "]"; |
213 | 0 | } |
214 | | |
215 | | bool FindConflict(std::string const& dir) override; |
216 | | }; |
217 | | |
218 | | bool cmOrderDirectoriesConstraintLibrary::FindConflict(std::string const& dir) |
219 | 0 | { |
220 | | // We have the library file name. Check if it will be found. |
221 | 0 | if (this->FileMayConflict(dir, this->FileName)) { |
222 | 0 | return true; |
223 | 0 | } |
224 | | |
225 | | // Now check if the file exists with other extensions the linker |
226 | | // might consider. |
227 | 0 | if (!this->OD->LinkExtensions.empty() && |
228 | 0 | this->OD->RemoveLibraryExtension.find(this->FileName)) { |
229 | 0 | std::string lib = this->OD->RemoveLibraryExtension.match(1); |
230 | 0 | std::string ext = this->OD->RemoveLibraryExtension.match(2); |
231 | 0 | for (std::string const& LinkExtension : this->OD->LinkExtensions) { |
232 | 0 | if (LinkExtension != ext) { |
233 | 0 | std::string fname = cmStrCat(lib, LinkExtension); |
234 | 0 | if (this->FileMayConflict(dir, fname)) { |
235 | 0 | return true; |
236 | 0 | } |
237 | 0 | } |
238 | 0 | } |
239 | 0 | } |
240 | 0 | return false; |
241 | 0 | } |
242 | | |
243 | | cmOrderDirectories::cmOrderDirectories(cmGlobalGenerator* gg, |
244 | | cmGeneratorTarget const* target, |
245 | | char const* purpose) |
246 | 0 | { |
247 | 0 | this->GlobalGenerator = gg; |
248 | 0 | this->Target = target; |
249 | 0 | this->Purpose = purpose; |
250 | 0 | this->Computed = false; |
251 | 0 | } |
252 | | |
253 | 0 | cmOrderDirectories::~cmOrderDirectories() = default; |
254 | | |
255 | | std::vector<std::string> const& cmOrderDirectories::GetOrderedDirectories() |
256 | 0 | { |
257 | 0 | if (!this->Computed) { |
258 | 0 | this->Computed = true; |
259 | 0 | this->CollectOriginalDirectories(); |
260 | 0 | this->FindConflicts(); |
261 | 0 | this->OrderDirectories(); |
262 | 0 | } |
263 | 0 | return this->OrderedDirectories; |
264 | 0 | } |
265 | | |
266 | | void cmOrderDirectories::AddRuntimeLibrary(std::string const& fullPath, |
267 | | char const* soname) |
268 | 0 | { |
269 | | // Add the runtime library at most once. |
270 | 0 | if (this->EmittedConstraintSOName.insert(fullPath).second) { |
271 | | // Implicit link directories need special handling. |
272 | 0 | if (!this->ImplicitDirectories.empty()) { |
273 | 0 | std::string dir = cmSystemTools::GetFilenamePath(fullPath); |
274 | |
|
275 | 0 | if (fullPath.rfind(".framework") != std::string::npos) { |
276 | 0 | static cmsys::RegularExpression splitFramework( |
277 | 0 | "^(.*)/(.*).framework/(.*)$"); |
278 | 0 | if (splitFramework.find(fullPath) && |
279 | 0 | (std::string::npos != |
280 | 0 | splitFramework.match(3).find(splitFramework.match(2)))) { |
281 | 0 | dir = splitFramework.match(1); |
282 | 0 | } |
283 | 0 | } |
284 | |
|
285 | 0 | if (this->IsImplicitDirectory(dir)) { |
286 | 0 | this->ImplicitDirEntries.push_back( |
287 | 0 | cm::make_unique<cmOrderDirectoriesConstraintSOName>(this, fullPath, |
288 | 0 | soname)); |
289 | 0 | return; |
290 | 0 | } |
291 | 0 | } |
292 | | |
293 | | // Construct the runtime information entry for this library. |
294 | 0 | this->ConstraintEntries.push_back( |
295 | 0 | cm::make_unique<cmOrderDirectoriesConstraintSOName>(this, fullPath, |
296 | 0 | soname)); |
297 | 0 | } else { |
298 | | // This can happen if the same library is linked multiple times. |
299 | | // In that case the runtime information check need be done only |
300 | | // once anyway. For shared libs we could add a check in AddItem |
301 | | // to not repeat them. |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | | void cmOrderDirectories::AddLinkLibrary(std::string const& fullPath) |
306 | 0 | { |
307 | | // Link extension info is required for library constraints. |
308 | 0 | assert(!this->LinkExtensions.empty()); |
309 | | |
310 | | // Add the link library at most once. |
311 | 0 | if (this->EmittedConstraintLibrary.insert(fullPath).second) { |
312 | | // Implicit link directories need special handling. |
313 | 0 | if (!this->ImplicitDirectories.empty()) { |
314 | 0 | std::string dir = cmSystemTools::GetFilenamePath(fullPath); |
315 | 0 | if (this->IsImplicitDirectory(dir)) { |
316 | 0 | this->ImplicitDirEntries.push_back( |
317 | 0 | cm::make_unique<cmOrderDirectoriesConstraintLibrary>(this, |
318 | 0 | fullPath)); |
319 | 0 | return; |
320 | 0 | } |
321 | 0 | } |
322 | | |
323 | | // Construct the link library entry. |
324 | 0 | this->ConstraintEntries.push_back( |
325 | 0 | cm::make_unique<cmOrderDirectoriesConstraintLibrary>(this, fullPath)); |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | | void cmOrderDirectories::AddUserDirectories( |
330 | | std::vector<std::string> const& extra) |
331 | 0 | { |
332 | 0 | cm::append(this->UserDirectories, extra); |
333 | 0 | } |
334 | | |
335 | | void cmOrderDirectories::AddLanguageDirectories( |
336 | | std::vector<std::string> const& dirs) |
337 | 0 | { |
338 | 0 | cm::append(this->LanguageDirectories, dirs); |
339 | 0 | } |
340 | | |
341 | | void cmOrderDirectories::SetImplicitDirectories( |
342 | | std::set<std::string> const& implicitDirs) |
343 | 0 | { |
344 | 0 | this->ImplicitDirectories.clear(); |
345 | 0 | for (std::string const& implicitDir : implicitDirs) { |
346 | 0 | this->ImplicitDirectories.insert(this->GetRealPath(implicitDir)); |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | | bool cmOrderDirectories::IsImplicitDirectory(std::string const& dir) |
351 | 0 | { |
352 | 0 | std::string const& real = this->GetRealPath(dir); |
353 | 0 | return this->ImplicitDirectories.find(real) != |
354 | 0 | this->ImplicitDirectories.end(); |
355 | 0 | } |
356 | | |
357 | | void cmOrderDirectories::SetLinkExtensionInfo( |
358 | | std::vector<std::string> const& linkExtensions, |
359 | | std::string const& removeExtRegex) |
360 | 0 | { |
361 | 0 | this->LinkExtensions = linkExtensions; |
362 | 0 | this->RemoveLibraryExtension.compile(removeExtRegex); |
363 | 0 | } |
364 | | |
365 | | void cmOrderDirectories::CollectOriginalDirectories() |
366 | 0 | { |
367 | | // Add user directories specified for inclusion. These should be |
368 | | // indexed first so their original order is preserved as much as |
369 | | // possible subject to the constraints. |
370 | 0 | this->AddOriginalDirectories(this->UserDirectories); |
371 | | |
372 | | // Add directories containing constraints. |
373 | 0 | for (auto const& entry : this->ConstraintEntries) { |
374 | 0 | entry->AddDirectory(); |
375 | 0 | } |
376 | | |
377 | | // Add language runtime directories last. |
378 | 0 | this->AddOriginalDirectories(this->LanguageDirectories); |
379 | 0 | } |
380 | | |
381 | | int cmOrderDirectories::AddOriginalDirectory(std::string const& dir) |
382 | 0 | { |
383 | | // Add the runtime directory with a unique index. |
384 | 0 | auto i = this->DirectoryIndex.find(dir); |
385 | 0 | if (i == this->DirectoryIndex.end()) { |
386 | 0 | std::map<std::string, int>::value_type entry( |
387 | 0 | dir, static_cast<int>(this->OriginalDirectories.size())); |
388 | 0 | i = this->DirectoryIndex.insert(entry).first; |
389 | 0 | this->OriginalDirectories.push_back(dir); |
390 | 0 | } |
391 | |
|
392 | 0 | return i->second; |
393 | 0 | } |
394 | | |
395 | | void cmOrderDirectories::AddOriginalDirectories( |
396 | | std::vector<std::string> const& dirs) |
397 | 0 | { |
398 | 0 | for (std::string const& dir : dirs) { |
399 | | // We never explicitly specify implicit link directories. |
400 | 0 | if (this->IsImplicitDirectory(dir)) { |
401 | 0 | continue; |
402 | 0 | } |
403 | | |
404 | | // Skip the empty string. |
405 | 0 | if (dir.empty()) { |
406 | 0 | continue; |
407 | 0 | } |
408 | | |
409 | | // Add this directory. |
410 | 0 | this->AddOriginalDirectory(dir); |
411 | 0 | } |
412 | 0 | } |
413 | | |
414 | | struct cmOrderDirectoriesCompare |
415 | | { |
416 | | using ConflictPair = std::pair<int, int>; |
417 | | |
418 | | // The conflict pair is unique based on just the directory |
419 | | // (first). The second element is only used for displaying |
420 | | // information about why the entry is present. |
421 | | bool operator()(ConflictPair l, ConflictPair r) |
422 | 0 | { |
423 | 0 | return l.first == r.first; |
424 | 0 | } |
425 | | }; |
426 | | |
427 | | void cmOrderDirectories::FindConflicts() |
428 | 0 | { |
429 | | // Allocate the conflict graph. |
430 | 0 | this->ConflictGraph.resize(this->OriginalDirectories.size()); |
431 | 0 | this->DirectoryVisited.resize(this->OriginalDirectories.size(), 0); |
432 | | |
433 | | // Find directories conflicting with each entry. |
434 | 0 | for (unsigned int i = 0; i < this->ConstraintEntries.size(); ++i) { |
435 | 0 | this->ConstraintEntries[i]->FindConflicts(i); |
436 | 0 | } |
437 | | |
438 | | // Clean up the conflict graph representation. |
439 | 0 | for (ConflictList& cl : this->ConflictGraph) { |
440 | | // Sort the outgoing edges for each graph node so that the |
441 | | // original order will be preserved as much as possible. |
442 | 0 | std::sort(cl.begin(), cl.end()); |
443 | | |
444 | | // Make the edge list unique so cycle detection will be reliable. |
445 | 0 | auto last = std::unique(cl.begin(), cl.end(), cmOrderDirectoriesCompare()); |
446 | 0 | cl.erase(last, cl.end()); |
447 | 0 | } |
448 | | |
449 | | // Check items in implicit link directories. |
450 | 0 | this->FindImplicitConflicts(); |
451 | 0 | } |
452 | | |
453 | | void cmOrderDirectories::FindImplicitConflicts() |
454 | 0 | { |
455 | | // Check for items in implicit link directories that have conflicts |
456 | | // in the explicit directories. |
457 | 0 | std::ostringstream conflicts; |
458 | 0 | for (auto const& entry : this->ImplicitDirEntries) { |
459 | 0 | entry->FindImplicitConflicts(conflicts); |
460 | 0 | } |
461 | | |
462 | | // Skip warning if there were no conflicts. |
463 | 0 | std::string const text = conflicts.str(); |
464 | 0 | if (text.empty()) { |
465 | 0 | return; |
466 | 0 | } |
467 | | |
468 | | // Warn about the conflicts. |
469 | 0 | this->GlobalGenerator->GetCMakeInstance()->IssueMessage( |
470 | 0 | MessageType::WARNING, |
471 | 0 | cmStrCat("Cannot generate a safe ", this->Purpose, " for target ", |
472 | 0 | this->Target->GetName(), |
473 | 0 | " because files in some directories may " |
474 | 0 | "conflict with libraries in implicit directories:\n", |
475 | 0 | text, "Some of these libraries may not be found correctly."), |
476 | 0 | this->Target->GetBacktrace()); |
477 | 0 | } |
478 | | |
479 | | void cmOrderDirectories::OrderDirectories() |
480 | 0 | { |
481 | | // Allow a cycle to be diagnosed once. |
482 | 0 | this->CycleDiagnosed = false; |
483 | 0 | this->WalkId = 0; |
484 | | |
485 | | // Iterate through the directories in the original order. |
486 | 0 | for (unsigned int i = 0; i < this->OriginalDirectories.size(); ++i) { |
487 | | // Start a new DFS from this node. |
488 | 0 | ++this->WalkId; |
489 | 0 | this->VisitDirectory(i); |
490 | 0 | } |
491 | 0 | } |
492 | | |
493 | | void cmOrderDirectories::VisitDirectory(unsigned int i) |
494 | 0 | { |
495 | | // Skip nodes already visited. |
496 | 0 | if (this->DirectoryVisited[i]) { |
497 | 0 | if (this->DirectoryVisited[i] == this->WalkId) { |
498 | | // We have reached a node previously visited on this DFS. |
499 | | // There is a cycle. |
500 | 0 | this->DiagnoseCycle(); |
501 | 0 | } |
502 | 0 | return; |
503 | 0 | } |
504 | | |
505 | | // We are now visiting this node so mark it. |
506 | 0 | this->DirectoryVisited[i] = this->WalkId; |
507 | | |
508 | | // Visit the neighbors of the node first. |
509 | 0 | ConflictList const& clist = this->ConflictGraph[i]; |
510 | 0 | for (ConflictPair const& j : clist) { |
511 | 0 | this->VisitDirectory(j.first); |
512 | 0 | } |
513 | | |
514 | | // Now that all directories required to come before this one have |
515 | | // been emitted, emit this directory. |
516 | 0 | this->OrderedDirectories.push_back(this->OriginalDirectories[i]); |
517 | 0 | } |
518 | | |
519 | | void cmOrderDirectories::DiagnoseCycle() |
520 | 0 | { |
521 | | // Report the cycle at most once. |
522 | 0 | if (this->CycleDiagnosed) { |
523 | 0 | return; |
524 | 0 | } |
525 | 0 | this->CycleDiagnosed = true; |
526 | | |
527 | | // Construct the message. |
528 | 0 | std::ostringstream e; |
529 | 0 | e << "Cannot generate a safe " << this->Purpose << " for target " |
530 | 0 | << this->Target->GetName() |
531 | 0 | << " because there is a cycle in the constraint graph:\n"; |
532 | | |
533 | | // Display the conflict graph. |
534 | 0 | for (unsigned int i = 0; i < this->ConflictGraph.size(); ++i) { |
535 | 0 | ConflictList const& clist = this->ConflictGraph[i]; |
536 | 0 | e << " dir " << i << " is [" << this->OriginalDirectories[i] << "]\n"; |
537 | 0 | for (ConflictPair const& j : clist) { |
538 | 0 | e << " dir " << j.first << " must precede it due to "; |
539 | 0 | this->ConstraintEntries[j.second]->Report(e); |
540 | 0 | e << "\n"; |
541 | 0 | } |
542 | 0 | } |
543 | 0 | e << "Some of these libraries may not be found correctly."; |
544 | 0 | this->GlobalGenerator->GetCMakeInstance()->IssueMessage( |
545 | 0 | MessageType::WARNING, e.str(), this->Target->GetBacktrace()); |
546 | 0 | } |
547 | | |
548 | | bool cmOrderDirectories::IsSameDirectory(std::string const& l, |
549 | | std::string const& r) |
550 | 0 | { |
551 | 0 | return this->GetRealPath(l) == this->GetRealPath(r); |
552 | 0 | } |
553 | | |
554 | | std::string const& cmOrderDirectories::GetRealPath(std::string const& dir) |
555 | 0 | { |
556 | 0 | auto i = this->RealPaths.lower_bound(dir); |
557 | 0 | if (i == this->RealPaths.end() || |
558 | 0 | this->RealPaths.key_comp()(dir, i->first)) { |
559 | 0 | using value_type = std::map<std::string, std::string>::value_type; |
560 | 0 | i = this->RealPaths.insert( |
561 | 0 | i, value_type(dir, cmSystemTools::GetRealPath(dir))); |
562 | 0 | } |
563 | 0 | return i->second; |
564 | 0 | } |