cmOrderDirectories.cxx 20 KB

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