main shinobi / backends / graphviz.c
  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}