cmOrderDirectories.cxx 18 KB

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