cmOrderDirectories.cxx 20 KB

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