1#include <stdbool.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include "arg.h"
6#include "env.h"
7#include "graph.h"
8#include "os.h"
9#include "parse.h"
10#include "util.h"
11
12const char *argv0;
13
14static bool graphviz;
15static bool ascii;
16static bool firstroot = true;
17static struct node **seen;
18static size_t nseen, capseen;
19static struct node **dotnodes;
20static size_t ndotnodes, capdotnodes;
21static struct node **dotseen_nodes;
22static size_t ndotseen_nodes, capdotseen_nodes;
23
24static const char *
25progname(const char *arg, const char *def)
26{
27 const char *slash;
28
29 if (!arg)
30 return def;
31 slash = strrchr(arg, '/');
32 return slash ? slash + 1 : arg;
33}
34
35static void
36usage(void)
37{
38 fprintf(stderr, "usage: %s [-a] [-g] [-C dir] [-f buildfile] [target ...]\n", argv0);
39 exit(2);
40}
41
42static bool
43onstack(struct node *n, struct node **stack, size_t nstack)
44{
45 size_t i;
46
47 for (i = 0; i < nstack; ++i) {
48 if (stack[i] == n)
49 return true;
50 }
51 return false;
52}
53
54static bool
55seenbefore(struct node *n)
56{
57 size_t i;
58
59 for (i = 0; i < nseen; ++i) {
60 if (seen[i] == n)
61 return true;
62 }
63 return false;
64}
65
66static void
67markseen(struct node *n)
68{
69 if (seenbefore(n))
70 return;
71 if (nseen == capseen) {
72 capseen = capseen ? capseen * 2 : 128;
73 seen = xreallocarray(seen, capseen, sizeof(seen[0]));
74 }
75 seen[nseen++] = n;
76}
77
78static void
79emitstr(const char *s)
80{
81 for (; *s; ++s) {
82 if (*s == '"' || *s == '\\')
83 putchar('\\');
84 putchar(*s);
85 }
86}
87
88static bool
89isroot(struct node *n)
90{
91 return n->nuse == 0;
92}
93
94//dedup so we dont get two arrows
95static bool
96dotseen(struct node *n)
97{
98 size_t i;
99
100 for (i = 0; i < ndotnodes; ++i) {
101 if (dotnodes[i] == n)
102 return true;
103 }
104 return false;
105}
106
107static bool
108dotwalkseen(struct node *n)
109{
110 size_t i;
111
112 for (i = 0; i < ndotseen_nodes; ++i) {
113 if (dotseen_nodes[i] == n)
114 return true;
115 }
116 return false;
117}
118
119static void
120markdotwalk(struct node *n)
121{
122 if (dotwalkseen(n))
123 return;
124 if (ndotseen_nodes == capdotseen_nodes) {
125 capdotseen_nodes = capdotseen_nodes ? capdotseen_nodes * 2 : 128;
126 dotseen_nodes = xreallocarray(dotseen_nodes, capdotseen_nodes, sizeof(dotseen_nodes[0]));
127 }
128 dotseen_nodes[ndotseen_nodes++] = n;
129}
130
131static size_t
132dotid(struct node *n)
133{
134 size_t i;
135
136 for (i = 0; i < ndotnodes; ++i) {
137 if (dotnodes[i] == n)
138 return i;
139 }
140 if (ndotnodes == capdotnodes) {
141 capdotnodes = capdotnodes ? capdotnodes * 2 : 128;
142 dotnodes = xreallocarray(dotnodes, capdotnodes, sizeof(dotnodes[0]));
143 }
144 dotnodes[ndotnodes] = n;
145 return ndotnodes++;
146}
147
148static void
149emitdotnode(struct node *n)
150{
151 size_t id;
152
153 if (dotseen(n))
154 return;
155 id = dotid(n);
156 printf(" n%zu [label=\"", id);
157 emitstr(n->path->s);
158 printf("\"");
159 printf(", shape=box");
160 if (n->gen && n->gen->rule == &phonyrule) {
161 printf(", style=dashed");
162 } else if (isroot(n)) {
163 printf(", style=bold");
164 }
165 printf("];\n");
166}
167
168static void
169printindent(size_t depth, bool *more)
170{
171 size_t i;
172
173 for (i = 0; i < depth; ++i)
174 printf("%s", more[i] ? (ascii ? "| " : "│ ") : " ");
175}
176
177static void
178dumpdot(struct node *n, struct node **stack, size_t nstack)
179{
180 size_t i;
181 size_t nid, iid;
182
183 if (dotwalkseen(n))
184 return;
185 if (onstack(n, stack, nstack))
186 return;
187 markdotwalk(n);
188 emitdotnode(n);
189 stack[nstack++] = n;
190 if (!n->gen)
191 return;
192 nid = dotid(n);
193 for (i = 0; i < n->gen->nin; ++i) {
194 struct node *in = n->gen->in[i];
195
196 emitdotnode(in);
197 iid = dotid(in);
198 printf(" n%zu -> n%zu", iid, nid);
199 if (i >= n->gen->inorderidx)
200 printf(" [style=dashed]");
201 printf(";\n");
202 dumpdot(in, stack, nstack);
203 }
204}
205
206static void
207dumptree(struct node *n, size_t depth, bool last, bool *more, struct node **stack, size_t nstack)
208{
209 size_t i;
210
211 if (depth == 0) {
212 if (!firstroot)
213 putchar('\n');
214 firstroot = false;
215 printf("%s\n", n->path->s);
216 } else {
217 printindent(depth - 1, more);
218 printf("%s%s", last ? (ascii ? "`-- " : "└── ") : (ascii ? "|-- " : "├── "), n->path->s);
219 if (seenbefore(n)) {
220 printf(" [shared]\n");
221 return;
222 }
223 putchar('\n');
224 }
225 if (onstack(n, stack, nstack))
226 return;
227 markseen(n);
228 stack[nstack++] = n;
229 if (!n->gen)
230 return;
231 for (i = 0; i < n->gen->nin; ++i) {
232 more[depth] = i + 1 < n->gen->nin;
233 dumptree(n->gen->in[i], depth + 1, i + 1 == n->gen->nin, more, stack, nstack);
234 }
235}
236
237static void
238showroot(struct node *n)
239{
240 struct node *stack[512];
241 bool more[512];
242
243 memset(more, 0, sizeof(more));
244 if (graphviz)
245 dumpdot(n, stack, 0);
246 else
247 dumptree(n, 0, true, more, stack, 0);
248}
249
250int
251main(int argc, char *argv[])
252{
253 char *manifest;
254 struct node *n;
255 struct edge *e;
256 size_t i;
257
258 argv0 = progname(argv[0], "nviz");
259 manifest = "build.ninja";
260
261 ARGBEGIN {
262 case 'a':
263 ascii = true;
264 break;
265 case 'C':
266 oschdir(EARGF(usage()));
267 break;
268 case 'f':
269 manifest = EARGF(usage());
270 break;
271 case 'g':
272 graphviz = true;
273 break;
274 default:
275 usage();
276 } ARGEND
277
278 graphinit();
279 envinit();
280 parseinit();
281 parse(manifest, rootenv);
282
283 if (graphviz)
284 printf("digraph build {\n"
285 " rankdir=\"LR\";\n"
286 " graph [fontname=\"serif\"];\n"
287 " node [fontsize=10, fontname=\"serif\", shape=box, height=0.25];\n"
288 " edge [fontsize=10, fontname=\"serif\"];\n");
289
290 if (argc > 0) {
291 while (*argv) {
292 n = nodeget(*argv, 0);
293 if (!n)
294 fatal("unknown target '%s'", *argv);
295 showroot(n);
296 ++argv;
297 }
298 } else {
299 for (e = alledges; e; e = e->allnext) {
300 for (i = 0; i < e->nout; ++i) {
301 n = e->out[i];
302 if (n->nuse == 0)
303 showroot(n);
304 }
305 }
306 }
307
308 if (graphviz)
309 printf("}\n");
310 free(dotnodes);
311 free(dotseen_nodes);
312 free(seen);
313 return 0;
314}