cmOrderDirectories.cxx 16 KB

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