commit 66046eb

shrub  ·  2026-04-12 16:17:59 +0000 UTC
parent f12acb7
graphviz backend
5 files changed,  +101, -2
+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