add simple graphing
authorEvan Martin <martine@danga.com>
Tue, 9 Nov 2010 09:15:36 +0000 (01:15 -0800)
committerEvan Martin <martine@danga.com>
Tue, 9 Nov 2010 09:15:36 +0000 (01:15 -0800)
build.ninja
graphviz.h [new file with mode: 0644]
ninja.cc

index 1499d21a7f12bd675b3bee08fb6c1ec7ab031e3e..c4d33f997cafb1cfaffe261efda87d65dca5a9f7 100644 (file)
@@ -39,3 +39,14 @@ build @parsers_test.o: cxx parsers_test.cc
 build ninja_test: link @ninja_test.o @parsers_test.o @ninja.a
 ldflags = -lgtest -lgtest_main -lpthread
 
+
+# Generate a graph of the dependency tree (including the
+# graph generation itself in the resulting tree).
+graph_targets = ninja ninja_test graph.png
+rule gendot
+  command = ./ninja -g $graph_targets > $out
+rule gengraph
+  command = dot -Tpng $in > $out
+
+build @graph.dot: gendot ninja build.ninja
+build graph.png: gengraph @graph.dot
diff --git a/graphviz.h b/graphviz.h
new file mode 100644 (file)
index 0000000..f1a48b9
--- /dev/null
@@ -0,0 +1,59 @@
+#include <set>
+
+struct Node;
+
+struct GraphViz {
+  void Start();
+  void AddTarget(Node* node);
+  void Finish();
+
+  set<Node*> visited_;
+};
+
+void GraphViz::AddTarget(Node* node) {
+  if (visited_.find(node) != visited_.end())
+    return;
+  printf("\"%p\" [label=\"%s\"]\n", node, node->file_->path_.c_str());
+  visited_.insert(node);
+
+  if (!node->in_edge_) {
+    // Leaf node.
+    // Draw as a rect?
+    return;
+  }
+
+  Edge* edge = node->in_edge_;
+
+  if (edge->inputs_.size() == 1 && edge->outputs_.size() == 1) {
+    // Can draw simply.
+    printf("\"%p\" -> \"%p\" [label=\"%s\"]\n",
+           edge->inputs_[0], edge->outputs_[0], edge->rule_->name_.c_str());
+  } else {
+    printf("\"%p\" [label=\"%s\", shape=plaintext]\n",
+           edge, edge->rule_->name_.c_str());
+    for (vector<Node*>::iterator out = edge->outputs_.begin();
+         out != edge->outputs_.end(); ++out) {
+      printf("\"%p\" -> \"%p\"\n", edge, *out);
+    }
+    for (vector<Node*>::iterator in = edge->inputs_.begin();
+         in != edge->inputs_.end(); ++in) {
+      printf("\"%p\" -> \"%p\"\n", (*in), edge);
+    }
+  }
+
+  for (vector<Node*>::iterator in = edge->inputs_.begin();
+       in != edge->inputs_.end(); ++in) {
+    AddTarget(*in);
+  }
+}
+
+void GraphViz::Start() {
+  printf("digraph ninja {\n");
+  printf("node [fontsize=10, shape=box, height=0.25]\n");
+  printf("edge [fontsize=10]\n");
+}
+
+void GraphViz::Finish() {
+  printf("}\n");
+}
+
index 0bcfb9577c7598ee3b5a9f345610221dc1d74ca2..e648f838089619452881c6be29a4623bc3c30019 100644 (file)
--- a/ninja.cc
+++ b/ninja.cc
@@ -3,6 +3,7 @@
 #include <getopt.h>
 #include <stdio.h>
 
+#include "graphviz.h"
 #include "parsers.h"
 
 option options[] = {
@@ -15,6 +16,7 @@ void usage() {
 "usage: ninja [options] target\n"
 "\n"
 "options:\n"
+"  -g       output graphviz dot file for targets and exit\n"
 "  -i FILE  specify input build file [default=build.ninja]\n"
           );
 }
@@ -29,8 +31,12 @@ int main(int argc, char** argv) {
   const char* input_file = "build.ninja";
 
   int opt;
-  while ((opt = getopt_long(argc, argv, "hi:", options, NULL)) != -1) {
+  bool graph = false;
+  while ((opt = getopt_long(argc, argv, "ghi:", options, NULL)) != -1) {
     switch (opt) {
+      case 'g':
+        graph = true;
+        break;
       case 'i':
         input_file = optarg;
         break;
@@ -45,6 +51,8 @@ int main(int argc, char** argv) {
     usage();
     return 1;
   }
+  argv += optind;
+  argc -= optind;
 
   State state;
   RealFileReader file_reader;
@@ -54,9 +62,19 @@ int main(int argc, char** argv) {
     fprintf(stderr, "error loading '%s': %s\n", input_file, err.c_str());
     return 1;
   }
+
+  if (graph) {
+    GraphViz graph;
+    graph.Start();
+    for (int i = 0; i < argc; ++i)
+      graph.AddTarget(state.GetNode(argv[i]));
+    graph.Finish();
+    return 0;
+  }
+
   Shell shell;
   Builder builder(&state);
-  for (int i = optind; i < argc; ++i) {
+  for (int i = 0; i < argc; ++i) {
     if (!builder.AddTarget(argv[i], &err)) {
       if (!err.empty()) {
         fprintf(stderr, "%s\n", err.c_str());