commit 66046eb
shrub
·
2026-04-12 16:17:59 +0000 UTC
parent f12acb7
graphviz backend
5 files changed,
+101,
-2
M
Makefile
+2,
-2
1@@ -3,8 +3,8 @@ DESTDIR =
2 BINDIR = $(DESTDIR)$(PREFIX)/bin
3
4 BIN = shin
5-SRCS = cli/main.c src/parse.c src/eval.c src/graph.c src/posix.c src/gnu/pattern.c backends/ninja.c src/util.c src/gnu/functions.c
6-FMTSRCS = $(SRCS) src/shinobi.h src/internal.h src/posix.h src/gnu/pattern.h backends/ninja.h
7+SRCS = cli/main.c src/parse.c src/eval/eval.c src/eval/dollar.c src/graph.c src/posix.c src/gnu/pattern.c backends/ninja.c backends/graphviz.c src/util.c src/gnu/functions.c
8+FMTSRCS = $(SRCS) src/shinobi.h src/internal.h src/posix.h src/gnu/pattern.h backends/ninja.h backends/graphviz.h
9 OBJS = $(SRCS:.c=.o)
10
11 CC = clang
+76,
-0
1@@ -0,0 +1,76 @@
2+#include "graphviz.h"
3+#include "internal.h"
4+
5+#include <stdio.h>
6+
7+/* graphviz backend */
8+
9+static void
10+emitstr(FILE *fp, const char *s)
11+{
12+ size_t i;
13+
14+ for (i = 0; s[i]; i++) {
15+ if (s[i] == '"' || s[i] == '\\')
16+ fputc('\\', fp);
17+ fputc(s[i], fp);
18+ }
19+}
20+
21+static void
22+emitnode(FILE *fp, size_t i, const struct Target *t)
23+{
24+ fprintf(fp, " n%zu [label=\"", i);
25+ emitstr(fp, t->name);
26+ fprintf(fp, "\"");
27+ if (t->recipes.n > 0) {
28+ fprintf(fp, ", shape=box");
29+ } else if (t->prereqs.n > 0 || t->order_only.n > 0) {
30+ fprintf(fp, ", style=dashed");
31+ }
32+ fprintf(fp, "];\n");
33+}
34+
35+static void
36+emitedges(FILE *fp, const struct Graph *graph, size_t i, const struct Target *t)
37+{
38+ size_t j;
39+ const struct Target *dep;
40+
41+ for (j = 0; j < t->prereqs.n; j++) {
42+ dep = findctarget(graph, t->prereqs.v[j]);
43+ if (!dep)
44+ continue;
45+ fprintf(fp, " n%zu -> n%zu;\n", (size_t)(dep - graph->v), i);
46+ }
47+ for (j = 0; j < t->order_only.n; j++) {
48+ dep = findctarget(graph, t->order_only.v[j]);
49+ if (!dep)
50+ continue;
51+ fprintf(fp, " n%zu -> n%zu [style=dashed];\n", (size_t)(dep - graph->v), i);
52+ }
53+}
54+
55+int
56+gengraphviz(const struct Graph *graph, const char *path)
57+{
58+ FILE *fp;
59+ size_t i;
60+
61+ fp = fopen(path, "w");
62+ if (!fp)
63+ return -1;
64+
65+ fprintf(fp, "digraph shin {\n");
66+ fprintf(fp, " rankdir=LR;\n");
67+ fprintf(fp, " node [fontname=\"monospace\"];\n");
68+ for (i = 0; i < graph->n; i++)
69+ emitnode(fp, i, &graph->v[i]);
70+ fprintf(fp, "\n");
71+ for (i = 0; i < graph->n; i++)
72+ emitedges(fp, graph, i, &graph->v[i]);
73+ fprintf(fp, "}\n");
74+
75+ fclose(fp);
76+ return 0;
77+}
+8,
-0
1@@ -0,0 +1,8 @@
2+#ifndef GRAPHVIZ_H
3+#define GRAPHVIZ_H
4+
5+#include "shinobi.h"
6+
7+int gengraphviz(const struct Graph *graph, const char *path);
8+
9+#endif
+15,
-0
1@@ -1,4 +1,5 @@
2 #include "shinobi.h"
3+#include "graphviz.h"
4 #include "ninja.h"
5
6 #include <stdio.h>
7@@ -212,6 +213,7 @@ main(int argc, char **argv)
8 const char *path;
9 char *src;
10 int dump_ast;
11+ int dump_dot;
12 int dump_graph;
13 int have_path;
14 int i;
15@@ -221,11 +223,14 @@ main(int argc, char **argv)
16
17 path = "Makefile";
18 dump_ast = 0;
19+ dump_dot = 0;
20 dump_graph = 0;
21 have_path = 0;
22 for (i = 1; i < argc; i++) {
23 if (strcmp(argv[i], "-a") == 0) {
24 dump_ast = 1;
25+ } else if (strcmp(argv[i], "-d") == 0) {
26+ dump_dot = 1;
27 } else if (strcmp(argv[i], "-g") == 0) {
28 dump_graph = 1;
29 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
30@@ -269,6 +274,16 @@ main(int argc, char **argv)
31 }
32 if (dump_graph)
33 dumpgraph(&graph);
34+ if (dump_dot) {
35+ if (gengraphviz(&graph, "build.dot") < 0) {
36+ fprintf(stderr, "graphviz generation error\n");
37+ freegraph(&graph);
38+ freeast(&out);
39+ freeast(&ast);
40+ free(src);
41+ return 1;
42+ }
43+ }
44 if (genninja(&graph, "build.ninja") < 0) {
45 fprintf(stderr, "ninja generation error\n");
46 freegraph(&graph);
+0,
-0