cmGraphVizWriter.cxx 17 KB

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