commit 90228d3

shrub  ·  2026-04-08 10:37:48 +0000 UTC
parent c353699
dont emit a rule for every target and emit default and phony rules
1 files changed,  +168, -7
+168, -7
  1@@ -7,17 +7,145 @@
  2 
  3 /* ninja backend */
  4 
  5+struct Rule {
  6+	char *cmd;
  7+	int id;
  8+};
  9+
 10 static void
 11-emitrule(FILE *fp, const struct Target *t, int id)
 12+emitrule(FILE *fp, const char *cmd, int id)
 13+{
 14+	fprintf(fp, "rule r%d\n", id);
 15+	fprintf(fp, "  command = %s\n\n", cmd);
 16+}
 17+
 18+static char *
 19+joinprereqs(const struct Target *t)
 20 {
 21 	size_t i;
 22+	char *s;
 23 
 24-	fprintf(fp, "rule r%d\n", id);
 25-	fprintf(fp, "  command =");
 26+	s = xstrdup("");
 27+	for (i = 0; i < t->prereqs.n; i++) {
 28+		char *next;
 29+
 30+		next = s[0] ? cat3(s, " ", t->prereqs.v[i]) : xstrdup(t->prereqs.v[i]);
 31+		free(s);
 32+		s = next;
 33+	}
 34+	return s;
 35+}
 36+
 37+static char *
 38+joinrecipes(const struct Target *t)
 39+{
 40+	size_t i;
 41+	char *s;
 42+
 43+	s = xstrdup("");
 44 	for (i = 0; i < t->recipes.n; i++) {
 45-		fprintf(fp, "%s %s", i == 0 ? "" : " &&", t->recipes.v[i]);
 46+		char *next;
 47+
 48+		next = s[0] ? cat3(s, " && ", t->recipes.v[i]) : xstrdup(t->recipes.v[i]);
 49+		free(s);
 50+		s = next;
 51+	}
 52+	return s;
 53+}
 54+
 55+static char *
 56+replaceall(const char *s, const char *from, const char *to)
 57+{
 58+	size_t nfrom, nto, ns, count, i, j;
 59+	char *out;
 60+	const char *p;
 61+
 62+	if (!from || !from[0])
 63+		return xstrdup(s);
 64+	nfrom = strlen(from);
 65+	nto = strlen(to);
 66+	ns = strlen(s);
 67+	count = 0;
 68+	for (p = s; (p = strstr(p, from)) != 0; p += nfrom)
 69+		count++;
 70+	out = xmalloc(ns + count * (nto - nfrom) + 1);
 71+	for (i = 0, j = 0; i < ns;) {
 72+		if (i + nfrom <= ns && memcmp(s + i, from, nfrom) == 0) {
 73+			memcpy(out + j, to, nto);
 74+			j += nto;
 75+			i += nfrom;
 76+			continue;
 77+		}
 78+		out[j++] = s[i++];
 79 	}
 80-	fprintf(fp, "\n\n");
 81+	out[j] = 0;
 82+	return out;
 83+}
 84+
 85+static char *
 86+rulecmd(const struct Target *t)
 87+{
 88+	char *cmd, *deps, *tmp;
 89+
 90+	cmd = joinrecipes(t);
 91+	if (!cmd[0])
 92+		return cmd;
 93+	tmp = replaceall(cmd, t->name, "$out");
 94+	free(cmd);
 95+	cmd = tmp;
 96+	deps = joinprereqs(t);
 97+	if (deps[0]) {
 98+		tmp = replaceall(cmd, deps, "$in");
 99+		free(cmd);
100+		cmd = tmp;
101+		if (t->prereqs.n == 1) {
102+			tmp = replaceall(cmd, t->prereqs.v[0], "$in");
103+			free(cmd);
104+			cmd = tmp;
105+		}
106+	}
107+	free(deps);
108+	return cmd;
109+}
110+
111+static int
112+findrule(struct Rule *rules, size_t n, const char *cmd)
113+{
114+	size_t i;
115+
116+	for (i = 0; i < n; i++) {
117+		if (strcmp(rules[i].cmd, cmd) == 0)
118+			return rules[i].id;
119+	}
120+	return 0;
121+}
122+
123+static const struct Target *
124+findtarget(const struct Graph *graph, const char *name)
125+{
126+	size_t i;
127+
128+	for (i = 0; i < graph->n; i++) {
129+		if (strcmp(graph->v[i].name, name) == 0)
130+			return &graph->v[i];
131+	}
132+	return 0;
133+}
134+
135+static const char *
136+defaulttarget(const struct Graph *graph)
137+{
138+	size_t i;
139+
140+	if (findtarget(graph, "all"))
141+		return "all";
142+	for (i = 0; i < graph->n; i++) {
143+		if (strcmp(graph->v[i].name, "clean") == 0 || strcmp(graph->v[i].name, "fmt") == 0)
144+			continue;
145+		if (graph->v[i].recipes.n > 0 || graph->v[i].prereqs.n > 0 || graph->v[i].order_only.n > 0)
146+			return graph->v[i].name;
147+	}
148+	return 0;
149 }
150 
151 int
152@@ -26,16 +154,44 @@ genninja(const struct Graph *graph, const char *path)
153 	FILE *fp;
154 	size_t i, j;
155 	int ruleid;
156+	struct Rule *rules;
157+	size_t nrules;
158 
159 	fp = fopen(path, "w");
160 	if (!fp)
161 		return -1;
162 
163 	ruleid = 0;
164+	rules = 0;
165+	nrules = 0;
166 	for (i = 0; i < graph->n; i++) {
167 		if (graph->v[i].recipes.n > 0) {
168-			emitrule(fp, &graph->v[i], ++ruleid);
169-			fprintf(fp, "build %s: r%d", graph->v[i].name, ruleid);
170+			char *cmd;
171+			int id;
172+
173+			cmd = rulecmd(&graph->v[i]);
174+			id = findrule(rules, nrules, cmd);
175+			if (!id) {
176+				id = ++ruleid;
177+				emitrule(fp, cmd, id);
178+				rules = xrealloc(rules, (nrules + 1) * sizeof(rules[0]));
179+				rules[nrules].cmd = cmd;
180+				rules[nrules].id = id;
181+				nrules++;
182+			} else {
183+				free(cmd);
184+			}
185+			fprintf(fp, "build %s: r%d", graph->v[i].name, id);
186+			for (j = 0; j < graph->v[i].prereqs.n; j++)
187+				fprintf(fp, " %s", graph->v[i].prereqs.v[j]);
188+			if (graph->v[i].order_only.n) {
189+				fprintf(fp, " ||");
190+				for (j = 0; j < graph->v[i].order_only.n; j++)
191+					fprintf(fp, " %s", graph->v[i].order_only.v[j]);
192+			}
193+			fprintf(fp, "\n\n");
194+		} else if (graph->v[i].prereqs.n > 0 || graph->v[i].order_only.n > 0) {
195+			fprintf(fp, "build %s: phony", graph->v[i].name);
196 			for (j = 0; j < graph->v[i].prereqs.n; j++)
197 				fprintf(fp, " %s", graph->v[i].prereqs.v[j]);
198 			if (graph->v[i].order_only.n) {
199@@ -46,7 +202,12 @@ genninja(const struct Graph *graph, const char *path)
200 			fprintf(fp, "\n\n");
201 		}
202 	}
203+	if (defaulttarget(graph))
204+		fprintf(fp, "default %s\n", defaulttarget(graph));
205 
206 	fclose(fp);
207+	for (i = 0; i < nrules; i++)
208+		free(rules[i].cmd);
209+	free(rules);
210 	return 0;
211 }