1#include "graphviz.h"
2#include "internal.h"
3
4#include <stdio.h>
5#include <string.h>
6
7/* graphviz backend */
8
9static void
10emitstr(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
21static void
22emitownerlabel(FILE *fp, const char *owner)
23{
24 const char *label;
25
26 label = owner && owner[0] ? owner : "(root)";
27 emitstr(fp, label);
28}
29
30static int
31hasdependents(const struct Graph *graph, size_t idx)
32{
33 size_t i, j;
34 const char *name;
35
36 name = graph->v[idx].name;
37 for (i = 0; i < graph->n; i++) {
38 if (i == idx)
39 continue;
40 for (j = 0; j < graph->v[i].prereqs.n; j++) {
41 if (strcmp(graph->v[i].prereqs.v[j], name) == 0)
42 return 1;
43 }
44 for (j = 0; j < graph->v[i].impprereqs.n; j++) {
45 if (strcmp(graph->v[i].impprereqs.v[j], name) == 0)
46 return 1;
47 }
48 for (j = 0; j < graph->v[i].order_only.n; j++) {
49 if (strcmp(graph->v[i].order_only.v[j], name) == 0)
50 return 1;
51 }
52 }
53 return 0;
54}
55
56static void
57emitnode(FILE *fp, const struct Graph *graph, size_t i, const struct Target *t, int is_default)
58{
59 int dashed;
60
61 dashed = t->phony || !hasdependents(graph, i);
62 fprintf(fp, " n%zu [label=\"", i);
63 emitstr(fp, t->name);
64 fprintf(fp, "\"");
65 fprintf(fp, ", shape=box");
66 if (dashed || is_default) {
67 fprintf(fp, ", style=");
68 if (dashed && is_default)
69 fprintf(fp, "\"dashed,bold\"");
70 else if (dashed)
71 fprintf(fp, "dashed");
72 else
73 fprintf(fp, "bold");
74 }
75 fprintf(fp, "];\n");
76}
77
78static void
79emitowners(FILE *fp, const struct Graph *graph, const char *owner)
80{
81 size_t i;
82 const struct Target *def;
83
84 def = defaulttarget(graph, owner);
85 for (i = 0; i < graph->n; i++) {
86 if (!targetownedby(&graph->v[i], owner))
87 continue;
88 emitnode(fp, graph, i, &graph->v[i], def == &graph->v[i]);
89 }
90}
91
92static void
93emitcluster(FILE *fp, const struct Graph *root, const struct SubGraph *sg, size_t *nextid)
94{
95 size_t i;
96 size_t id;
97
98 id = (*nextid)++;
99 fprintf(fp, " subgraph cluster_%zu {\n", id);
100 fprintf(fp, " label=\"");
101 emitownerlabel(fp, sg->prefix);
102 fprintf(fp, "\";\n");
103 fprintf(fp, " labelloc=t;\n");
104 fprintf(fp, " margin=12;\n");
105 fprintf(fp, " style=dashed;\n");
106 fprintf(fp, " color=\"#999999\";\n");
107 emitowners(fp, root, sg->prefix);
108 for (i = 0; i < sg->graph.nsubs; i++)
109 emitcluster(fp, root, &sg->graph.subs[i], nextid);
110 fprintf(fp, " }\n");
111}
112
113static void
114emitedges(FILE *fp, const struct Graph *graph, size_t i, const struct Target *t)
115{
116 size_t j;
117 const struct Target *dep;
118
119 for (j = 0; j < t->prereqs.n; j++) {
120 dep = findctarget(graph, t->prereqs.v[j]);
121 if (!dep)
122 continue;
123 fprintf(fp, " n%zu -> n%zu;\n", (size_t)(dep - graph->v), i);
124 }
125 for (j = 0; j < t->impprereqs.n; j++) {
126 dep = findctarget(graph, t->impprereqs.v[j]);
127 if (!dep)
128 continue;
129 fprintf(fp, " n%zu -> n%zu;\n", (size_t)(dep - graph->v), i);
130 }
131 for (j = 0; j < t->order_only.n; j++) {
132 dep = findctarget(graph, t->order_only.v[j]);
133 if (!dep)
134 continue;
135 fprintf(fp, " n%zu -> n%zu [style=dashed];\n", (size_t)(dep - graph->v), i);
136 }
137}
138
139int
140gengraphviz(const struct Graph *graph, const char *path)
141{
142 FILE *fp;
143 size_t i, clusterid;
144
145 fp = fopen(path, "w");
146 if (!fp)
147 return -1;
148
149 fprintf(fp, "digraph shin {\n");
150 fprintf(fp, " rankdir=\"LR\";\n");
151 fprintf(fp, " graph [fontname=\"serif\"];\n");
152 fprintf(fp, " node [fontsize=10, fontname=\"serif\", shape=box, height=0.25];\n");
153 fprintf(fp, " edge [fontsize=10, fontname=\"serif\"];\n");
154 emitowners(fp, graph, 0);
155 clusterid = 0;
156 for (i = 0; i < graph->nsubs; i++)
157 emitcluster(fp, graph, &graph->subs[i], &clusterid);
158 fprintf(fp, "\n");
159 for (i = 0; i < graph->n; i++)
160 emitedges(fp, graph, i, &graph->v[i]);
161 fprintf(fp, "}\n");
162
163 fclose(fp);
164 return 0;
165}