cmGraphVizWriter.cxx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  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 "cmGraphVizWriter.h"
  11. #include "cmGeneratedFileStream.h"
  12. #include "cmGlobalGenerator.h"
  13. #include "cmLocalGenerator.h"
  14. #include "cmMakefile.h"
  15. static const char* getShapeForTarget(const cmGeneratorTarget* target)
  16. {
  17. if (!target) {
  18. return "ellipse";
  19. }
  20. switch (target->GetType()) {
  21. case cmState::EXECUTABLE:
  22. return "house";
  23. case cmState::STATIC_LIBRARY:
  24. return "diamond";
  25. case cmState::SHARED_LIBRARY:
  26. return "polygon";
  27. case cmState::MODULE_LIBRARY:
  28. return "octagon";
  29. default:
  30. break;
  31. }
  32. return "box";
  33. }
  34. cmGraphVizWriter::cmGraphVizWriter(
  35. const std::vector<cmLocalGenerator*>& localGenerators)
  36. : GraphType("digraph")
  37. , GraphName("GG")
  38. , GraphHeader("node [\n fontsize = \"12\"\n];")
  39. , GraphNodePrefix("node")
  40. , LocalGenerators(localGenerators)
  41. , GenerateForExecutables(true)
  42. , GenerateForStaticLibs(true)
  43. , GenerateForSharedLibs(true)
  44. , GenerateForModuleLibs(true)
  45. , GenerateForExternals(true)
  46. , GeneratePerTarget(true)
  47. , GenerateDependers(true)
  48. , HaveTargetsAndLibs(false)
  49. {
  50. }
  51. void cmGraphVizWriter::ReadSettings(const char* settingsFileName,
  52. const char* fallbackSettingsFileName)
  53. {
  54. cmake cm;
  55. cm.SetHomeDirectory("");
  56. cm.SetHomeOutputDirectory("");
  57. cm.GetCurrentSnapshot().SetDefaultDefinitions();
  58. cmGlobalGenerator ggi(&cm);
  59. CM_AUTO_PTR<cmMakefile> mf(new cmMakefile(&ggi, cm.GetCurrentSnapshot()));
  60. CM_AUTO_PTR<cmLocalGenerator> lg(ggi.CreateLocalGenerator(mf.get()));
  61. const char* inFileName = settingsFileName;
  62. if (!cmSystemTools::FileExists(inFileName)) {
  63. inFileName = fallbackSettingsFileName;
  64. if (!cmSystemTools::FileExists(inFileName)) {
  65. return;
  66. }
  67. }
  68. if (!mf->ReadListFile(inFileName)) {
  69. cmSystemTools::Error("Problem opening GraphViz options file: ",
  70. inFileName);
  71. return;
  72. }
  73. std::cout << "Reading GraphViz options file: " << inFileName << std::endl;
  74. #define __set_if_set(var, cmakeDefinition) \
  75. { \
  76. const char* value = mf->GetDefinition(cmakeDefinition); \
  77. if (value) { \
  78. var = value; \
  79. } \
  80. }
  81. __set_if_set(this->GraphType, "GRAPHVIZ_GRAPH_TYPE");
  82. __set_if_set(this->GraphName, "GRAPHVIZ_GRAPH_NAME");
  83. __set_if_set(this->GraphHeader, "GRAPHVIZ_GRAPH_HEADER");
  84. __set_if_set(this->GraphNodePrefix, "GRAPHVIZ_NODE_PREFIX");
  85. #define __set_bool_if_set(var, cmakeDefinition) \
  86. { \
  87. const char* value = mf->GetDefinition(cmakeDefinition); \
  88. if (value) { \
  89. var = mf->IsOn(cmakeDefinition); \
  90. } \
  91. }
  92. __set_bool_if_set(this->GenerateForExecutables, "GRAPHVIZ_EXECUTABLES");
  93. __set_bool_if_set(this->GenerateForStaticLibs, "GRAPHVIZ_STATIC_LIBS");
  94. __set_bool_if_set(this->GenerateForSharedLibs, "GRAPHVIZ_SHARED_LIBS");
  95. __set_bool_if_set(this->GenerateForModuleLibs, "GRAPHVIZ_MODULE_LIBS");
  96. __set_bool_if_set(this->GenerateForExternals, "GRAPHVIZ_EXTERNAL_LIBS");
  97. __set_bool_if_set(this->GeneratePerTarget, "GRAPHVIZ_GENERATE_PER_TARGET");
  98. __set_bool_if_set(this->GenerateDependers, "GRAPHVIZ_GENERATE_DEPENDERS");
  99. std::string ignoreTargetsRegexes;
  100. __set_if_set(ignoreTargetsRegexes, "GRAPHVIZ_IGNORE_TARGETS");
  101. this->TargetsToIgnoreRegex.clear();
  102. if (!ignoreTargetsRegexes.empty()) {
  103. std::vector<std::string> ignoreTargetsRegExVector;
  104. cmSystemTools::ExpandListArgument(ignoreTargetsRegexes,
  105. ignoreTargetsRegExVector);
  106. for (std::vector<std::string>::const_iterator itvIt =
  107. ignoreTargetsRegExVector.begin();
  108. itvIt != ignoreTargetsRegExVector.end(); ++itvIt) {
  109. std::string currentRegexString(*itvIt);
  110. cmsys::RegularExpression currentRegex;
  111. if (!currentRegex.compile(currentRegexString.c_str())) {
  112. std::cerr << "Could not compile bad regex \"" << currentRegexString
  113. << "\"" << std::endl;
  114. }
  115. this->TargetsToIgnoreRegex.push_back(currentRegex);
  116. }
  117. }
  118. }
  119. // Iterate over all targets and write for each one a graph which shows
  120. // which other targets depend on it.
  121. void cmGraphVizWriter::WriteTargetDependersFiles(const char* fileName)
  122. {
  123. if (!this->GenerateDependers) {
  124. return;
  125. }
  126. this->CollectTargetsAndLibs();
  127. for (std::map<std::string, const cmGeneratorTarget*>::const_iterator ptrIt =
  128. this->TargetPtrs.begin();
  129. ptrIt != this->TargetPtrs.end(); ++ptrIt) {
  130. if (ptrIt->second == CM_NULLPTR) {
  131. continue;
  132. }
  133. if (!this->GenerateForTargetType(ptrIt->second->GetType())) {
  134. continue;
  135. }
  136. std::string currentFilename = fileName;
  137. currentFilename += ".";
  138. currentFilename += ptrIt->first;
  139. currentFilename += ".dependers";
  140. cmGeneratedFileStream str(currentFilename.c_str());
  141. if (!str) {
  142. return;
  143. }
  144. std::set<std::string> insertedConnections;
  145. std::set<std::string> insertedNodes;
  146. std::cout << "Writing " << currentFilename << "..." << std::endl;
  147. this->WriteHeader(str);
  148. this->WriteDependerConnections(ptrIt->first, insertedNodes,
  149. insertedConnections, str);
  150. this->WriteFooter(str);
  151. }
  152. }
  153. // Iterate over all targets and write for each one a graph which shows
  154. // on which targets it depends.
  155. void cmGraphVizWriter::WritePerTargetFiles(const char* fileName)
  156. {
  157. if (!this->GeneratePerTarget) {
  158. return;
  159. }
  160. this->CollectTargetsAndLibs();
  161. for (std::map<std::string, const cmGeneratorTarget*>::const_iterator ptrIt =
  162. this->TargetPtrs.begin();
  163. ptrIt != this->TargetPtrs.end(); ++ptrIt) {
  164. if (ptrIt->second == CM_NULLPTR) {
  165. continue;
  166. }
  167. if (!this->GenerateForTargetType(ptrIt->second->GetType())) {
  168. continue;
  169. }
  170. std::set<std::string> insertedConnections;
  171. std::set<std::string> insertedNodes;
  172. std::string currentFilename = fileName;
  173. currentFilename += ".";
  174. currentFilename += ptrIt->first;
  175. cmGeneratedFileStream str(currentFilename.c_str());
  176. if (!str) {
  177. return;
  178. }
  179. std::cout << "Writing " << currentFilename << "..." << std::endl;
  180. this->WriteHeader(str);
  181. this->WriteConnections(ptrIt->first, insertedNodes, insertedConnections,
  182. str);
  183. this->WriteFooter(str);
  184. }
  185. }
  186. void cmGraphVizWriter::WriteGlobalFile(const char* fileName)
  187. {
  188. this->CollectTargetsAndLibs();
  189. cmGeneratedFileStream str(fileName);
  190. if (!str) {
  191. return;
  192. }
  193. this->WriteHeader(str);
  194. std::cout << "Writing " << fileName << "..." << std::endl;
  195. std::set<std::string> insertedConnections;
  196. std::set<std::string> insertedNodes;
  197. for (std::map<std::string, const cmGeneratorTarget*>::const_iterator ptrIt =
  198. this->TargetPtrs.begin();
  199. ptrIt != this->TargetPtrs.end(); ++ptrIt) {
  200. if (ptrIt->second == CM_NULLPTR) {
  201. continue;
  202. }
  203. if (!this->GenerateForTargetType(ptrIt->second->GetType())) {
  204. continue;
  205. }
  206. this->WriteConnections(ptrIt->first, insertedNodes, insertedConnections,
  207. str);
  208. }
  209. this->WriteFooter(str);
  210. }
  211. void cmGraphVizWriter::WriteHeader(cmGeneratedFileStream& str) const
  212. {
  213. str << this->GraphType << " \"" << this->GraphName << "\" {" << std::endl;
  214. str << this->GraphHeader << std::endl;
  215. }
  216. void cmGraphVizWriter::WriteFooter(cmGeneratedFileStream& str) const
  217. {
  218. str << "}" << std::endl;
  219. }
  220. void cmGraphVizWriter::WriteConnections(
  221. const std::string& targetName, std::set<std::string>& insertedNodes,
  222. std::set<std::string>& insertedConnections, cmGeneratedFileStream& str) const
  223. {
  224. std::map<std::string, const cmGeneratorTarget*>::const_iterator targetPtrIt =
  225. this->TargetPtrs.find(targetName);
  226. if (targetPtrIt == this->TargetPtrs.end()) // not found at all
  227. {
  228. return;
  229. }
  230. this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
  231. if (targetPtrIt->second == CM_NULLPTR) // it's an external library
  232. {
  233. return;
  234. }
  235. std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
  236. const cmTarget::LinkLibraryVectorType* ll =
  237. &(targetPtrIt->second->Target->GetOriginalLinkLibraries());
  238. for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
  239. llit != ll->end(); ++llit) {
  240. const char* libName = llit->first.c_str();
  241. std::map<std::string, std::string>::const_iterator libNameIt =
  242. this->TargetNamesNodes.find(libName);
  243. // can happen e.g. if GRAPHVIZ_TARGET_IGNORE_REGEX is used
  244. if (libNameIt == this->TargetNamesNodes.end()) {
  245. continue;
  246. }
  247. std::string connectionName = myNodeName;
  248. connectionName += "-";
  249. connectionName += libNameIt->second;
  250. if (insertedConnections.find(connectionName) ==
  251. insertedConnections.end()) {
  252. insertedConnections.insert(connectionName);
  253. this->WriteNode(libName, this->TargetPtrs.find(libName)->second,
  254. insertedNodes, str);
  255. str << " \"" << myNodeName << "\" -> \"" << libNameIt->second << "\"";
  256. str << " // " << targetName << " -> " << libName << std::endl;
  257. this->WriteConnections(libName, insertedNodes, insertedConnections, str);
  258. }
  259. }
  260. }
  261. void cmGraphVizWriter::WriteDependerConnections(
  262. const std::string& targetName, std::set<std::string>& insertedNodes,
  263. std::set<std::string>& insertedConnections, cmGeneratedFileStream& str) const
  264. {
  265. std::map<std::string, const cmGeneratorTarget*>::const_iterator targetPtrIt =
  266. this->TargetPtrs.find(targetName);
  267. if (targetPtrIt == this->TargetPtrs.end()) // not found at all
  268. {
  269. return;
  270. }
  271. this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
  272. if (targetPtrIt->second == CM_NULLPTR) // it's an external library
  273. {
  274. return;
  275. }
  276. std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
  277. // now search who links against me
  278. for (std::map<std::string, const cmGeneratorTarget*>::const_iterator
  279. dependerIt = this->TargetPtrs.begin();
  280. dependerIt != this->TargetPtrs.end(); ++dependerIt) {
  281. if (dependerIt->second == CM_NULLPTR) {
  282. continue;
  283. }
  284. if (!this->GenerateForTargetType(dependerIt->second->GetType())) {
  285. continue;
  286. }
  287. // Now we have a target, check whether it links against targetName.
  288. // If so, draw a connection, and then continue with dependers on that one.
  289. const cmTarget::LinkLibraryVectorType* ll =
  290. &(dependerIt->second->Target->GetOriginalLinkLibraries());
  291. for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
  292. llit != ll->end(); ++llit) {
  293. std::string libName = llit->first;
  294. if (libName == targetName) {
  295. // So this target links against targetName.
  296. std::map<std::string, std::string>::const_iterator dependerNodeNameIt =
  297. this->TargetNamesNodes.find(dependerIt->first);
  298. if (dependerNodeNameIt != this->TargetNamesNodes.end()) {
  299. std::string connectionName = dependerNodeNameIt->second;
  300. connectionName += "-";
  301. connectionName += myNodeName;
  302. if (insertedConnections.find(connectionName) ==
  303. insertedConnections.end()) {
  304. insertedConnections.insert(connectionName);
  305. this->WriteNode(dependerIt->first, dependerIt->second,
  306. insertedNodes, str);
  307. str << " \"" << dependerNodeNameIt->second << "\" -> \""
  308. << myNodeName << "\"";
  309. str << " // " << targetName << " -> " << dependerIt->first
  310. << std::endl;
  311. this->WriteDependerConnections(dependerIt->first, insertedNodes,
  312. insertedConnections, str);
  313. }
  314. }
  315. break;
  316. }
  317. }
  318. }
  319. }
  320. void cmGraphVizWriter::WriteNode(const std::string& targetName,
  321. const cmGeneratorTarget* target,
  322. std::set<std::string>& insertedNodes,
  323. cmGeneratedFileStream& str) const
  324. {
  325. if (insertedNodes.find(targetName) == insertedNodes.end()) {
  326. insertedNodes.insert(targetName);
  327. std::map<std::string, std::string>::const_iterator nameIt =
  328. this->TargetNamesNodes.find(targetName);
  329. str << " \"" << nameIt->second << "\" [ label=\"" << targetName
  330. << "\" shape=\"" << getShapeForTarget(target) << "\"];" << std::endl;
  331. }
  332. }
  333. void cmGraphVizWriter::CollectTargetsAndLibs()
  334. {
  335. if (!this->HaveTargetsAndLibs) {
  336. this->HaveTargetsAndLibs = true;
  337. int cnt = this->CollectAllTargets();
  338. if (this->GenerateForExternals) {
  339. this->CollectAllExternalLibs(cnt);
  340. }
  341. }
  342. }
  343. int cmGraphVizWriter::CollectAllTargets()
  344. {
  345. int cnt = 0;
  346. // First pass get the list of all cmake targets
  347. for (std::vector<cmLocalGenerator*>::const_iterator lit =
  348. this->LocalGenerators.begin();
  349. lit != this->LocalGenerators.end(); ++lit) {
  350. std::vector<cmGeneratorTarget*> targets = (*lit)->GetGeneratorTargets();
  351. for (std::vector<cmGeneratorTarget*>::const_iterator it = targets.begin();
  352. it != targets.end(); ++it) {
  353. const char* realTargetName = (*it)->GetName().c_str();
  354. if (this->IgnoreThisTarget(realTargetName)) {
  355. // Skip ignored targets
  356. continue;
  357. }
  358. // std::cout << "Found target: " << tit->first << std::endl;
  359. std::ostringstream ostr;
  360. ostr << this->GraphNodePrefix << cnt++;
  361. this->TargetNamesNodes[realTargetName] = ostr.str();
  362. this->TargetPtrs[realTargetName] = *it;
  363. }
  364. }
  365. return cnt;
  366. }
  367. int cmGraphVizWriter::CollectAllExternalLibs(int cnt)
  368. {
  369. // Ok, now find all the stuff we link to that is not in cmake
  370. for (std::vector<cmLocalGenerator*>::const_iterator lit =
  371. this->LocalGenerators.begin();
  372. lit != this->LocalGenerators.end(); ++lit) {
  373. std::vector<cmGeneratorTarget*> targets = (*lit)->GetGeneratorTargets();
  374. for (std::vector<cmGeneratorTarget*>::const_iterator it = targets.begin();
  375. it != targets.end(); ++it) {
  376. const char* realTargetName = (*it)->GetName().c_str();
  377. if (this->IgnoreThisTarget(realTargetName)) {
  378. // Skip ignored targets
  379. continue;
  380. }
  381. const cmTarget::LinkLibraryVectorType* ll =
  382. &((*it)->Target->GetOriginalLinkLibraries());
  383. for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
  384. llit != ll->end(); ++llit) {
  385. const char* libName = llit->first.c_str();
  386. if (this->IgnoreThisTarget(libName)) {
  387. // Skip ignored targets
  388. continue;
  389. }
  390. std::map<std::string, const cmGeneratorTarget*>::const_iterator tarIt =
  391. this->TargetPtrs.find(libName);
  392. if (tarIt == this->TargetPtrs.end()) {
  393. std::ostringstream ostr;
  394. ostr << this->GraphNodePrefix << cnt++;
  395. this->TargetNamesNodes[libName] = ostr.str();
  396. this->TargetPtrs[libName] = CM_NULLPTR;
  397. // str << " \"" << ostr << "\" [ label=\"" << libName
  398. // << "\" shape=\"ellipse\"];" << std::endl;
  399. }
  400. }
  401. }
  402. }
  403. return cnt;
  404. }
  405. bool cmGraphVizWriter::IgnoreThisTarget(const std::string& name)
  406. {
  407. for (std::vector<cmsys::RegularExpression>::iterator itvIt =
  408. this->TargetsToIgnoreRegex.begin();
  409. itvIt != this->TargetsToIgnoreRegex.end(); ++itvIt) {
  410. cmsys::RegularExpression& regEx = *itvIt;
  411. if (regEx.is_valid()) {
  412. if (regEx.find(name)) {
  413. return true;
  414. }
  415. }
  416. }
  417. return false;
  418. }
  419. bool cmGraphVizWriter::GenerateForTargetType(
  420. cmState::TargetType targetType) const
  421. {
  422. switch (targetType) {
  423. case cmState::EXECUTABLE:
  424. return this->GenerateForExecutables;
  425. case cmState::STATIC_LIBRARY:
  426. return this->GenerateForStaticLibs;
  427. case cmState::SHARED_LIBRARY:
  428. return this->GenerateForSharedLibs;
  429. case cmState::MODULE_LIBRARY:
  430. return this->GenerateForModuleLibs;
  431. default:
  432. break;
  433. }
  434. return false;
  435. }