cmOrderDirectories.cxx 19 KB

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