commit 75ca578
shrub
·
2026-05-05 22:08:36 +0000 UTC
parent d2e2d0c
tighten recursive make parsing and emission, make ninja backend a little prettier
4 files changed,
+229,
-72
+10,
-31
1@@ -88,43 +88,18 @@ joinrecipes(const struct Target *t)
2 size_t i;
3 struct StrList bodies;
4 char *s;
5- char *prefix;
6- size_t nprefix;
7- int strip_prefix;
8
9 memset(&bodies, 0, sizeof(bodies));
10- prefix = 0;
11- nprefix = 0;
12- strip_prefix = 0;
13- if (t->owner && t->owner[0]) {
14- prefix = cat3("cd ", t->owner, " && ");
15- nprefix = strlen(prefix);
16- strip_prefix = 1;
17- for (i = 0; i < t->recipes.n; i++) {
18- if (strncmp(t->recipes.v[i].body, prefix, nprefix) != 0) {
19- strip_prefix = 0;
20- break;
21- }
22- }
23- }
24 for (i = 0; i < t->recipes.n; i++) {
25- const char *body;
26+ char *body;
27
28- body = t->recipes.v[i].body;
29- if (strip_prefix)
30- body += nprefix;
31+ body = cat3("( ", t->recipes.v[i].body, " )");
32 bodies.v = xrealloc(bodies.v, (bodies.n + 1) * sizeof(bodies.v[0]));
33- bodies.v[bodies.n++] = (char *)body;
34+ bodies.v[bodies.n++] = body;
35 }
36 s = joinstrs(&bodies, " && ");
37- if (strip_prefix) {
38- char *tmp;
39-
40- tmp = cat3(prefix, s, "");
41- free(s);
42- s = tmp;
43- }
44- free(prefix);
45+ for (i = 0; i < bodies.n; i++)
46+ free(bodies.v[i]);
47 free(bodies.v);
48 return s;
49 }
50@@ -316,7 +291,11 @@ genninjafile(const struct Graph *graph, const char *path, const char *prefix, in
51 for (j = 0; j < graph->v[i].order_only.n; j++)
52 fprintf(fp, " %s", graph->v[i].order_only.v[j]);
53 }
54- fprintf(fp, "\n\n");
55+ fprintf(fp, "\n");
56+ if (prefix && prefix[0])
57+ fprintf(fp, " description = [%s] build %s\n\n", prefix, graph->v[i].name);
58+ else
59+ fprintf(fp, " description = build %s\n\n", graph->v[i].name);
60 } else if (graph->v[i].defined || graph->v[i].prereqs.n > 0 || graph->v[i].order_only.n > 0) {
61 fprintf(fp, "build %s: phony", graph->v[i].name);
62 if (graph->v[i].phony)
+108,
-7
1@@ -27,6 +27,19 @@ sameprefix(const char *a, const char *b)
2 return strcmp(a, b) == 0;
3 }
4
5+static int
6+isunderprefix(const char *prefix, const char *name)
7+{
8+ size_t n;
9+
10+ if (isempty(prefix))
11+ return 1;
12+ n = strlen(prefix);
13+ if (strncmp(name, prefix, n) != 0)
14+ return 0;
15+ return name[n] == 0 || name[n] == '/';
16+}
17+
18 static int
19 samelist(const struct StrList *a, const struct StrList *b)
20 {
21@@ -119,6 +132,39 @@ resolvegoals(const struct SubGraph *sg, struct StrList *out)
22 return -1;
23 }
24
25+static void
26+markreachable(const struct Graph *graph, const char *name, unsigned char *seen)
27+{
28+ const struct Target *t;
29+ size_t i, idx;
30+
31+ t = findctarget(graph, name);
32+ if (!t)
33+ return;
34+ idx = (size_t)(t - graph->v);
35+ if (seen[idx])
36+ return;
37+ seen[idx] = 1;
38+ for (i = 0; i < t->prereqs.n; i++)
39+ markreachable(graph, t->prereqs.v[i], seen);
40+ for (i = 0; i < t->order_only.n; i++)
41+ markreachable(graph, t->order_only.v[i], seen);
42+}
43+
44+static int
45+allsubmake(const struct Target *t)
46+{
47+ size_t i;
48+
49+ if (!t || t->recipes.n == 0)
50+ return 0;
51+ for (i = 0; i < t->recipes.n; i++) {
52+ if (!t->recipes.v[i].submake)
53+ return 0;
54+ }
55+ return 1;
56+}
57+
58 static void
59 pushstack(struct SubGraphStack *stack, const char *key)
60 {
61@@ -236,7 +282,8 @@ scopegraph(struct Graph *graph, const char *prefix)
62 graph->v[i].name = intern(name);
63 free(name);
64 free(graph->v[i].owner);
65- if (graph->v[i].defined || graph->v[i].recipes.n > 0)
66+ if ((graph->v[i].defined || graph->v[i].recipes.n > 0) &&
67+ isunderprefix(prefix, graph->v[i].name))
68 graph->v[i].owner = xstrdup(prefix);
69 else
70 graph->v[i].owner = 0;
71@@ -370,6 +417,7 @@ mergechildgraph(struct SubGraph *parent,
72 const struct StrList *goals)
73 {
74 struct SubGraph *known;
75+ unsigned char *reachable;
76 struct Target *t;
77 size_t i;
78
79@@ -380,10 +428,19 @@ mergechildgraph(struct SubGraph *parent,
80 return -1;
81 }
82 if (!known) {
83+ reachable = xmalloc(child->graph.n ? child->graph.n : 1);
84+ memset(reachable, 0, child->graph.n ? child->graph.n : 1);
85+ for (i = 0; i < goals->n; i++)
86+ markreachable(&child->graph, goals->v[i], reachable);
87 for (i = 0; i < child->graph.n; i++) {
88- if (mergetarget(&parent->graph, &child->graph.v[i]) < 0)
89+ if (!reachable[i])
90+ continue;
91+ if (mergetarget(&parent->graph, &child->graph.v[i]) < 0) {
92+ free(reachable);
93 return -1;
94+ }
95 }
96+ free(reachable);
97 }
98 t = findtarget(&parent->graph, tname);
99 if (!t) {
100@@ -409,13 +466,35 @@ static int buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack);
101 static int
102 expandsubgraphs(struct SubGraph *sg, struct SubGraphStack *stack)
103 {
104- size_t i;
105+ struct StrList roots;
106+ unsigned char *reachable;
107+ size_t i, n;
108
109+ if (resolvegoals(sg, &roots) < 0)
110+ return -1;
111 for (i = 0; i < sg->graph.n; i++) {
112+ if (!targetownedby(&sg->graph.v[i], sg->prefix))
113+ continue;
114+ if (!sg->graph.v[i].phony && !allsubmake(&sg->graph.v[i]))
115+ continue;
116+ if (!hasword(&roots, sg->graph.v[i].name))
117+ addstr(&roots, sg->graph.v[i].name);
118+ }
119+ n = sg->graph.n;
120+ reachable = xmalloc(n ? n : 1);
121+ memset(reachable, 0, n ? n : 1);
122+ for (i = 0; i < roots.n; i++)
123+ markreachable(&sg->graph, roots.v[i], reachable);
124+ freestrs(&roots);
125+
126+ for (i = 0; i < n; i++) {
127 size_t k;
128 const char *tname;
129 struct StrList prevgoals;
130
131+ if (!reachable[i])
132+ continue;
133+
134 memset(&prevgoals, 0, sizeof(prevgoals));
135 tname = sg->graph.v[i].name;
136 for (k = 0; k < sg->graph.v[i].recipes.n;) {
137@@ -440,14 +519,32 @@ expandsubgraphs(struct SubGraph *sg, struct SubGraphStack *stack)
138 addwords(&child.goals, &r->sm.goals);
139 if (strcmp(child.cwd, sg->cwd) == 0 &&
140 (!child.makefile || strcmp(child.makefile, sg->makefile) == 0)) {
141- fprintf(stderr, "cannot expand recursive submake graph: %s\n",
142- r->body);
143 freesubgraph(&child);
144- freestrs(&prevgoals);
145- return -1;
146+ k++;
147+ continue;
148 }
149 rc = buildsubgraph0(&child, stack);
150 if (rc > 0) {
151+ struct SubGraph *known;
152+
153+ known = sameprefix(sg->prefix, child.prefix) ? 0 : findgraphsub(&sg->graph, child.prefix);
154+ if (known && sameinvocation(known, &child) && child.goals.n > 0) {
155+ size_t gi;
156+ struct Target *pt;
157+
158+ removerecipe(&sg->graph.v[i].recipes, k);
159+ pt = findtarget(&sg->graph, tname);
160+ for (gi = 0; pt && gi < child.goals.n; gi++) {
161+ char *gname;
162+
163+ gname = rebaseword(child.prefix, child.goals.v[gi]);
164+ if (!hasword(&pt->prereqs, gname))
165+ addstr(&pt->prereqs, gname);
166+ free(gname);
167+ }
168+ freesubgraph(&child);
169+ continue;
170+ }
171 freesubgraph(&child);
172 k++;
173 continue;
174@@ -455,6 +552,7 @@ expandsubgraphs(struct SubGraph *sg, struct SubGraphStack *stack)
175 if (rc < 0) {
176 freesubgraph(&child);
177 freestrs(&prevgoals);
178+ free(reachable);
179 return -1;
180 }
181 /* remove the make invocation, we replace it with a graph*/
182@@ -462,6 +560,7 @@ expandsubgraphs(struct SubGraph *sg, struct SubGraphStack *stack)
183 if (resolvegoals(&child, &goals) < 0) {
184 freesubgraph(&child);
185 freestrs(&prevgoals);
186+ free(reachable);
187 return -1;
188 }
189 subgraphorder(&child.graph, child.prefix, &prevgoals);
190@@ -469,6 +568,7 @@ expandsubgraphs(struct SubGraph *sg, struct SubGraphStack *stack)
191 freesubgraph(&child);
192 freestrs(&goals);
193 freestrs(&prevgoals);
194+ free(reachable);
195 return -1;
196 }
197 freestrs(&prevgoals);
198@@ -479,6 +579,7 @@ expandsubgraphs(struct SubGraph *sg, struct SubGraphStack *stack)
199 }
200 freestrs(&prevgoals);
201 }
202+ free(reachable);
203 return 0;
204 }
205
+95,
-33
1@@ -6,33 +6,81 @@
2
3 /* recursive make handling */
4
5+static void
6+tokview(const char *s, const char **out, size_t *n)
7+{
8+ const char *p;
9+ size_t len;
10+
11+ p = s;
12+ while (*p == '(')
13+ p++;
14+ len = strlen(p);
15+ while (len > 0 && (p[len - 1] == ')' || p[len - 1] == ';'))
16+ len--;
17+ *out = p;
18+ *n = len;
19+}
20+
21+static int
22+tokeq(const char *s, const char *lit)
23+{
24+ const char *p;
25+ size_t n, ln;
26+
27+ tokview(s, &p, &n);
28+ ln = strlen(lit);
29+ return n == ln && strncmp(p, lit, n) == 0;
30+}
31+
32+static char *
33+tokdup(const char *s)
34+{
35+ const char *p;
36+ size_t n;
37+
38+ tokview(s, &p, &n);
39+ return xstrndup(p, n);
40+}
41+
42 static int
43 ismakevarref(const char *s)
44 {
45+ const char *p;
46 char close;
47 size_t n;
48
49- if (!s || s[0] != '$')
50+ if (!s)
51 return 0;
52- if (s[1] != '(' && s[1] != '{')
53+ tokview(s, &p, &n);
54+ if (n == 0 || p[0] != '$')
55 return 0;
56- n = strlen(s);
57 if (n != 7)
58 return 0;
59- close = s[1] == '(' ? ')' : '}';
60- return strncmp(s + 2, "MAKE", 4) == 0 && s[6] == close && s[7] == 0;
61+ if (p[1] != '(' && p[1] != '{')
62+ return 0;
63+ close = p[1] == '(' ? ')' : '}';
64+ return strncmp(p + 2, "MAKE", 4) == 0 && p[6] == close;
65 }
66
67 static int
68 ismaketoken(const char *s)
69 {
70+ const char *p;
71 const char *base;
72+ size_t n;
73+ int ok;
74+ char *tmp;
75
76 if (ismakevarref(s))
77 return 1;
78- base = strrchr(s, '/');
79- base = base ? base + 1 : s;
80- return strcmp(base, "make") == 0 || strcmp(base, "gmake") == 0;
81+ tokview(s, &p, &n);
82+ tmp = xstrndup(p, n);
83+ base = strrchr(tmp, '/');
84+ base = base ? base + 1 : tmp;
85+ ok = strcmp(base, "make") == 0 || strcmp(base, "gmake") == 0;
86+ free(tmp);
87+ return ok;
88 }
89
90 static int
91@@ -170,7 +218,7 @@ parsesubmake(struct SubMake *dst, const char *cmd)
92 makeprog = 0;
93 tokenizecmd(&toks, cmd);
94 start = 0;
95- if (toks.n >= 3 && strcmp(toks.v[0], "cd") == 0 && strcmp(toks.v[2], "&&") == 0) {
96+ if (toks.n >= 3 && tokeq(toks.v[0], "cd") && tokeq(toks.v[2], "&&")) {
97 cddir = toks.v[1];
98 start = 3;
99 }
100@@ -179,65 +227,79 @@ parsesubmake(struct SubMake *dst, const char *cmd)
101 return 0;
102 }
103 makeprog = toks.v[start];
104- dst->makeprog = xstrdup(makeprog);
105+ dst->makeprog = tokdup(makeprog);
106 if (cddir)
107- dst->dir = xstrdup(cddir);
108+ dst->dir = tokdup(cddir);
109 for (i = start + 1; i < toks.n; i++) {
110 const char *tok;
111+ char *ntok;
112
113 tok = toks.v[i];
114- if (strcmp(tok, "&&") == 0)
115+ ntok = tokdup(tok);
116+ if (strcmp(ntok, "&&") == 0) {
117+ free(ntok);
118 break;
119- if ((strcmp(tok, "-C") == 0 || strcmp(tok, "--directory") == 0) && i + 1 < toks.n) {
120- addstr(&dst->flags, tok);
121+ }
122+ if ((strcmp(ntok, "-C") == 0 || strcmp(ntok, "--directory") == 0) && i + 1 < toks.n) {
123+ addstr(&dst->flags, ntok);
124 free(dst->dir);
125- dst->dir = xstrdup(toks.v[++i]);
126+ dst->dir = tokdup(toks.v[++i]);
127+ free(ntok);
128 continue;
129 }
130- if (strncmp(tok, "-C", 2) == 0 && tok[2]) {
131+ if (strncmp(ntok, "-C", 2) == 0 && ntok[2]) {
132 addstr(&dst->flags, "-C");
133 free(dst->dir);
134- dst->dir = xstrdup(tok + 2);
135+ dst->dir = xstrdup(ntok + 2);
136+ free(ntok);
137 continue;
138 }
139- if (strncmp(tok, "--directory=", 12) == 0) {
140+ if (strncmp(ntok, "--directory=", 12) == 0) {
141 addstr(&dst->flags, "--directory");
142 free(dst->dir);
143- dst->dir = xstrdup(tok + 12);
144+ dst->dir = xstrdup(ntok + 12);
145+ free(ntok);
146 continue;
147 }
148- if ((strcmp(tok, "-f") == 0 || strcmp(tok, "--file") == 0) && i + 1 < toks.n) {
149- addstr(&dst->flags, tok);
150+ if ((strcmp(ntok, "-f") == 0 || strcmp(ntok, "--file") == 0) && i + 1 < toks.n) {
151+ addstr(&dst->flags, ntok);
152 free(dst->makefile);
153- dst->makefile = xstrdup(toks.v[++i]);
154+ dst->makefile = tokdup(toks.v[++i]);
155+ free(ntok);
156 continue;
157 }
158- if (strncmp(tok, "-f", 2) == 0 && tok[2]) {
159+ if (strncmp(ntok, "-f", 2) == 0 && ntok[2]) {
160 addstr(&dst->flags, "-f");
161 free(dst->makefile);
162- dst->makefile = xstrdup(tok + 2);
163+ dst->makefile = xstrdup(ntok + 2);
164+ free(ntok);
165 continue;
166 }
167- if (strncmp(tok, "--file=", 7) == 0) {
168+ if (strncmp(ntok, "--file=", 7) == 0) {
169 addstr(&dst->flags, "--file");
170 free(dst->makefile);
171- dst->makefile = xstrdup(tok + 7);
172+ dst->makefile = xstrdup(ntok + 7);
173+ free(ntok);
174 continue;
175 }
176- if (flagsarg(tok) && i + 1 < toks.n) {
177- addstr(&dst->flags, tok);
178+ if (flagsarg(ntok) && i + 1 < toks.n) {
179+ addstr(&dst->flags, ntok);
180 i++;
181+ free(ntok);
182 continue;
183 }
184- if (tok[0] == '-') {
185- addstr(&dst->flags, tok);
186+ if (ntok[0] == '-') {
187+ addstr(&dst->flags, ntok);
188+ free(ntok);
189 continue;
190 }
191- if (isassignment(tok)) {
192- addstr(&dst->assigns, tok);
193+ if (isassignment(ntok)) {
194+ addstr(&dst->assigns, ntok);
195+ free(ntok);
196 continue;
197 }
198- addstr(&dst->goals, tok);
199+ addstr(&dst->goals, ntok);
200+ free(ntok);
201 }
202 freetoks(&toks);
203 return 1;
+16,
-1
1@@ -462,14 +462,29 @@ const struct Target *
2 defaulttarget(const struct Graph *graph, const char *owner)
3 {
4 const struct Target *all;
5+ const char *base;
6+ char *ownedall;
7 size_t i;
8
9- all = findctarget(graph, "all");
10+ ownedall = 0;
11+ if (owner && owner[0]) {
12+ ownedall = cat3(owner, "/", "all");
13+ all = findctarget(graph, ownedall);
14+ free(ownedall);
15+ } else {
16+ all = findctarget(graph, "all");
17+ }
18 if (targetownedby(all, owner))
19 return all;
20 for (i = 0; i < graph->n; i++) {
21 if (!targetownedby(&graph->v[i], owner))
22 continue;
23+ base = strrchr(graph->v[i].name, '/');
24+ base = base ? base + 1 : graph->v[i].name;
25+ if (base[0] == '.')
26+ continue;
27+ if (strcmp(base, "_PHONY") == 0)
28+ continue;
29 if (graph->v[i].name == intern("clean") || graph->v[i].name == intern("fmt"))
30 continue;
31 if (graph->v[i].recipes.n > 0 || graph->v[i].prereqs.n > 0 || graph->v[i].order_only.n > 0)