commit e7c76d3
shrub
·
2026-04-12 08:32:33 +0000 UTC
parent 91483b0
organise a bit and add single-suffix rule handling
11 files changed,
+672,
-458
M
Makefile
+10,
-2
1@@ -1,6 +1,10 @@
2+PREFIX = /usr
3+DESTDIR =
4+BINDIR = $(DESTDIR)$(PREFIX)/bin
5+
6 BIN = shin
7-SRCS = cli/main.c src/parse.c src/eval.c src/graph.c backends/ninja.c src/util.c src/functions.c
8-FMTSRCS = $(SRCS) src/shinobi.h src/internal.h backends/ninja.h
9+SRCS = cli/main.c src/parse.c src/eval.c src/graph.c src/posix.c src/gnu/pattern.c backends/ninja.c src/util.c src/gnu/functions.c
10+FMTSRCS = $(SRCS) src/shinobi.h src/internal.h src/posix.h src/gnu/pattern.h backends/ninja.h
11 OBJS = $(SRCS:.c=.o)
12
13 CC = clang
14@@ -22,5 +26,9 @@ fmt:
15 test: $(BIN)
16 tests/shintest.sh
17
18+install: $(BIN)
19+ install -d $(BINDIR)
20+ install -m 755 $(BIN) $(BINDIR)/$(BIN)
21+
22 clean:
23 rm -f $(BIN) $(OBJS)
+2,
-13
1@@ -51,6 +51,7 @@ joinrecipes(const struct Target *t)
2 return s;
3 }
4
5+/*turn remaining automatic vars into ninja equivalents*/
6 static char *
7 translateauto(const char *s, const struct Target *t)
8 {
9@@ -130,24 +131,12 @@ findrule(struct Rule *rules, size_t n, const char *cmd)
10 return 0;
11 }
12
13-static const struct Target *
14-findtarget(const struct Graph *graph, const char *name)
15-{
16- size_t i;
17-
18- for (i = 0; i < graph->n; i++) {
19- if (strcmp(graph->v[i].name, name) == 0)
20- return &graph->v[i];
21- }
22- return 0;
23-}
24-
25 static const char *
26 defaulttarget(const struct Graph *graph)
27 {
28 size_t i;
29
30- if (findtarget(graph, "all"))
31+ if (findctarget(graph, "all"))
32 return "all";
33 for (i = 0; i < graph->n; i++) {
34 if (strcmp(graph->v[i].name, "clean") == 0 || strcmp(graph->v[i].name, "fmt") == 0)
+5,
-39
1@@ -469,40 +469,6 @@ expandstr(struct Env *env, const char *s)
2 return out;
3 }
4
5-static void
6-setvar(struct Env *env, const char *name, char *val, int simple)
7-{
8- struct Var *v;
9-
10- v = findvar(env, name);
11- if (v) {
12- free(v->val);
13- v->val = val;
14- v->simple = simple;
15- return;
16- }
17- env->v = xrealloc(env->v, (env->n + 1) * sizeof(env->v[0]));
18- env->v[env->n].name = xstrdup(name);
19- env->v[env->n].val = val;
20- env->v[env->n].simple = simple;
21- env->n++;
22-}
23-
24-void
25-seedenv(struct Env *env)
26-{
27- setvar(env, "CC", xstrdup("cc"), 1);
28- setvar(env, "CXX", xstrdup("c++"), 1);
29- setvar(env, "CPP", xstrdup("$(CC) -E"), 0);
30- setvar(env, "AR", xstrdup("ar"), 1);
31- setvar(env, "ARFLAGS", xstrdup("-rv"), 1);
32- setvar(env, "AS", xstrdup("as"), 1);
33- setvar(env, "LD", xstrdup("ld"), 1);
34- setvar(env, "LEX", xstrdup("lex"), 1);
35- setvar(env, "YACC", xstrdup("yacc"), 1);
36- setvar(env, "SHELL", xstrdup("/bin/sh"), 1);
37-}
38-
39 void
40 freeenv(struct Env *env)
41 {
42@@ -541,19 +507,19 @@ evalassign(struct Env *env, const struct AssignNode *in)
43
44 switch (in->op) {
45 case ASSIGN_EQ:
46- setvar(env, in->lhs, xstrdup(in->rhs), 0);
47+ envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
48 break;
49 case ASSIGN_COLON_EQ:
50- setvar(env, in->lhs, expandstr(env, in->rhs), 1);
51+ envsetvar(env, in->lhs, expandstr(env, in->rhs), 1);
52 break;
53 case ASSIGN_QMARK_EQ:
54 if (!findvar(env, in->lhs))
55- setvar(env, in->lhs, xstrdup(in->rhs), 0);
56+ envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
57 break;
58 case ASSIGN_PLUS_EQ:
59 v = findvar(env, in->lhs);
60 if (!v) {
61- setvar(env, in->lhs, xstrdup(in->rhs), 0);
62+ envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
63 break;
64 }
65 rhs = v->simple ? expandstr(env, in->rhs) : xstrdup(in->rhs);
66@@ -563,7 +529,7 @@ evalassign(struct Env *env, const struct AssignNode *in)
67 v->val = joined;
68 break;
69 case ASSIGN_BANG_EQ:
70- setvar(env, in->lhs, expandstr(env, in->rhs), 1);
71+ envsetvar(env, in->lhs, expandstr(env, in->rhs), 1);
72 break;
73 }
74 }
R src/functions.c =>
src/gnu/functions.c
+2,
-2
1@@ -285,7 +285,7 @@ fnaddprefix(const char *prefix, const char *names)
2 char *
3 fninfo(const char *text)
4 {
5- fputs(text, stdout);
6- fputc('\n', stdout);
7+ fputs(text, stderr);
8+ fputc('\n', stderr);
9 return xstrdup("");
10 }
+188,
-0
1@@ -0,0 +1,188 @@
2+#include "gnu/pattern.h"
3+
4+#include <stdlib.h>
5+#include <string.h>
6+
7+/*helpers for handling gnu % pattern rules*/
8+
9+static char *
10+matchpat(const char *pat, const char *name)
11+{
12+ const char *p;
13+ size_t pre, suf, n, plen;
14+
15+ p = strchr(pat, '%');
16+ if (!p)
17+ return 0;
18+ pre = (size_t)(p - pat);
19+ suf = strlen(p + 1);
20+ n = strlen(name);
21+ plen = strlen(pat);
22+
23+ if (n < plen - 1)
24+ return 0;
25+ if (memcmp(pat, name, pre) != 0)
26+ return 0;
27+ if (memcmp(p + 1, name + n - suf, suf) != 0)
28+ return 0;
29+ return xstrndup(name + pre, n - pre - suf);
30+}
31+
32+static char *
33+applystem(const char *s, const char *stem)
34+{
35+ const char *p;
36+ size_t pre, suf, stemlen, slen;
37+ char *out;
38+
39+ p = strchr(s, '%');
40+ if (!p)
41+ return xstrdup(s);
42+ pre = (size_t)(p - s);
43+ slen = strlen(s);
44+ suf = slen - pre - 1;
45+ stemlen = strlen(stem);
46+ out = xmalloc(pre + stemlen + suf + 1);
47+ memcpy(out, s, pre);
48+ memcpy(out + pre, stem, stemlen);
49+ memcpy(out + pre + stemlen, p + 1, suf);
50+ out[pre + stemlen + suf] = 0;
51+ return out;
52+}
53+
54+static char *
55+expandstem(const char *s, const char *stem)
56+{
57+ size_t i, n, cap, len;
58+ char *out;
59+
60+ n = strlen(s);
61+ cap = n + 1;
62+ len = 0;
63+ out = xmalloc(cap);
64+ for (i = 0; i < n; i++) {
65+ if (s[i] == '$' && i + 1 < n && s[i + 1] == '*') {
66+ size_t slen;
67+
68+ slen = stem ? strlen(stem) : 0;
69+ if (len + slen + 1 > cap) {
70+ cap = len + slen + (n - i) + 1;
71+ out = xrealloc(out, cap);
72+ }
73+ if (slen) {
74+ memcpy(out + len, stem, slen);
75+ len += slen;
76+ }
77+ i++;
78+ continue;
79+ }
80+ if (len + 2 > cap) {
81+ cap *= 2;
82+ out = xrealloc(out, cap);
83+ }
84+ out[len++] = s[i];
85+ }
86+ out[len] = 0;
87+ return out;
88+}
89+
90+int
91+ispat(const char *s)
92+{
93+ return strchr(s, '%') != 0;
94+}
95+
96+int
97+patmatches(const char *pat, const char *name)
98+{
99+ char *stem;
100+
101+ stem = matchpat(pat, name);
102+ if (!stem)
103+ return 0;
104+ free(stem);
105+ return 1;
106+}
107+
108+void
109+collectpat(struct PatRules *rules, const struct RuleNode *rule)
110+{
111+ size_t i;
112+
113+ for (i = 0; i < rule->targets.n; i++) {
114+ struct PatRule *p;
115+
116+ if (!ispat(rule->targets.v[i]))
117+ continue;
118+ rules->v = xrealloc(rules->v, (rules->n + 1) * sizeof(rules->v[0]));
119+ p = &rules->v[rules->n++];
120+ memset(p, 0, sizeof(*p));
121+ p->target = xstrdup(rule->targets.v[i]);
122+ addwords(&p->prereqs, &rule->prereqs);
123+ addwords(&p->order_only, &rule->order_only);
124+ addrecipes(&p->recipes, &rule->recipes);
125+ }
126+}
127+
128+int
129+instpatrule(const struct PatRules *rules, struct Target *t, struct Env *env)
130+{
131+ size_t i, j;
132+
133+ for (i = 0; i < rules->n; i++) {
134+ char *stem;
135+
136+ stem = matchpat(rules->v[i].target, t->name);
137+ if (!stem)
138+ continue;
139+ for (j = 0; j < rules->v[i].prereqs.n; j++) {
140+ char *s;
141+
142+ s = applystem(rules->v[i].prereqs.v[j], stem);
143+ t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
144+ t->prereqs.v[t->prereqs.n++] = s;
145+ }
146+ for (j = 0; j < rules->v[i].order_only.n; j++) {
147+ char *s;
148+
149+ s = applystem(rules->v[i].order_only.v[j], stem);
150+ t->order_only.v = xrealloc(t->order_only.v,
151+ (t->order_only.n + 1) * sizeof(t->order_only.v[0]));
152+ t->order_only.v[t->order_only.n++] = s;
153+ }
154+ for (j = 0; j < rules->v[i].recipes.n; j++) {
155+ char *s, *vars, *exp;
156+
157+ s = applystem(rules->v[i].recipes.v[j], stem);
158+ vars = expandstr(env, s);
159+ exp = expandstem(vars, stem);
160+ free(vars);
161+ free(s);
162+ if (!exp[0]) {
163+ free(exp);
164+ continue;
165+ }
166+ t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
167+ t->recipes.v[t->recipes.n++] = exp;
168+ }
169+ free(stem);
170+ return 1;
171+ }
172+ return 0;
173+}
174+
175+void
176+freepatrule(struct PatRules *rules)
177+{
178+ size_t i;
179+
180+ for (i = 0; i < rules->n; i++) {
181+ free(rules->v[i].target);
182+ freestrs(&rules->v[i].prereqs);
183+ freestrs(&rules->v[i].order_only);
184+ freerecipes(&rules->v[i].recipes);
185+ }
186+ free(rules->v);
187+ rules->v = 0;
188+ rules->n = 0;
189+}
+24,
-0
1@@ -0,0 +1,24 @@
2+#ifndef GNU_PATTERN_H
3+#define GNU_PATTERN_H
4+
5+#include "internal.h"
6+
7+struct PatRule {
8+ char *target;
9+ struct StrList prereqs;
10+ struct StrList order_only;
11+ struct RecipeList recipes;
12+};
13+
14+struct PatRules {
15+ struct PatRule *v;
16+ size_t n;
17+};
18+
19+int ispat(const char *s);
20+int patmatches(const char *pat, const char *name);
21+void collectpat(struct PatRules *rules, const struct RuleNode *rule);
22+int instpatrule(const struct PatRules *rules, struct Target *t, struct Env *env);
23+void freepatrule(struct PatRules *rules);
24+
25+#endif
+48,
-402
1@@ -1,58 +1,13 @@
2 #include "shinobi.h"
3 #include "internal.h"
4+#include "posix.h"
5+#include "gnu/pattern.h"
6
7 #include <stdlib.h>
8 #include <string.h>
9
10 /* graph builder */
11
12-static struct Target *
13-findtarget(struct Graph *graph, const char *name)
14-{
15- size_t i;
16-
17- for (i = 0; i < graph->n; i++) {
18- if (strcmp(graph->v[i].name, name) == 0)
19- return &graph->v[i];
20- }
21- return 0;
22-}
23-
24-static void
25-addwords(struct StrList *dest, const struct StrList *src)
26-{
27- size_t i;
28-
29- for (i = 0; i < src->n; i++) {
30- dest->v = xrealloc(dest->v, (dest->n + 1) * sizeof(dest->v[0]));
31- dest->v[dest->n++] = xstrdup(src->v[i]);
32- }
33-}
34-
35-static void
36-addrecipes(struct RecipeList *dest, const struct RecipeList *src)
37-{
38- size_t i;
39-
40- for (i = 0; i < src->n; i++) {
41- dest->v = xrealloc(dest->v, (dest->n + 1) * sizeof(dest->v[0]));
42- dest->v[dest->n++] = xstrdup(src->v[i]);
43- }
44-}
45-
46-struct Pattern {
47- char *target;
48- struct StrList prereqs;
49- struct StrList order_only;
50- struct RecipeList recipes;
51-};
52-
53-struct Suffix {
54- char *from;
55- char *to;
56- struct RecipeList recipes;
57-};
58-
59 struct TAssign {
60 struct StrList targets;
61 char *lhs;
62@@ -65,10 +20,8 @@ struct GraphState {
63 struct Env env;
64 const struct RuleNode **rules;
65 size_t nrules;
66- struct Pattern *pats;
67- size_t npats;
68- struct Suffix *sufs;
69- size_t nsufs;
70+ struct PatRules patterns;
71+ struct SufRules sufs;
72 struct TAssign *tas;
73 size_t ntas;
74 };
75@@ -80,142 +33,12 @@ collectrule(struct GraphState *gs, const struct RuleNode *rule)
76 gs->rules[gs->nrules++] = rule;
77 }
78
79-static char *
80-matchpat(const char *pat, const char *name)
81-{
82- const char *p;
83- size_t pre, suf, n, plen;
84-
85- p = strchr(pat, '%');
86- if (!p)
87- return 0;
88- pre = (size_t)(p - pat);
89- suf = strlen(p + 1);
90- n = strlen(name);
91- plen = strlen(pat);
92-
93- if (n < plen - 1)
94- return 0;
95- if (memcmp(pat, name, pre) != 0)
96- return 0;
97- if (memcmp(p + 1, name + n - suf, suf) != 0)
98- return 0;
99- return xstrndup(name + pre, n - pre - suf);
100-}
101-
102-static int
103-matchsuf(const char *suf, const char *name, char **stem)
104-{
105- size_t nsuf, nname;
106-
107- nsuf = strlen(suf);
108- nname = strlen(name);
109- if (nname < nsuf || strcmp(name + nname - nsuf, suf) != 0)
110- return 0;
111- *stem = xstrndup(name, nname - nsuf);
112- return 1;
113-}
114-
115 static int
116 targetmatches(const char *pat, const char *name)
117 {
118- char *stem;
119-
120- if (!strchr(pat, '%'))
121+ if (!ispat(pat))
122 return strcmp(pat, name) == 0;
123- stem = matchpat(pat, name);
124- if (!stem)
125- return 0;
126- free(stem);
127- return 1;
128-}
129-
130-static char *
131-applystem(const char *s, const char *stem)
132-{
133- const char *p;
134- size_t pre, suf, stemlen, slen;
135- char *out;
136-
137- p = strchr(s, '%');
138- if (!p)
139- return xstrdup(s);
140- pre = (size_t)(p - s);
141- slen = strlen(s);
142- suf = slen - pre - 1;
143- stemlen = strlen(stem);
144- out = xmalloc(pre + stemlen + suf + 1);
145- memcpy(out, s, pre);
146- memcpy(out + pre, stem, stemlen);
147- memcpy(out + pre + stemlen, p + 1, suf);
148- out[pre + stemlen + suf] = 0;
149- return out;
150-}
151-
152-static int
153-issuffix(const char *s, char **from, char **to)
154-{
155- const char *mid;
156-
157- if (!s || s[0] != '.')
158- return 0;
159- mid = strchr(s + 1, '.');
160- if (!mid || mid == s + 1 || !mid[1] || strchr(mid + 1, '.'))
161- return 0;
162- *from = xstrndup(s, (size_t)(mid - s));
163- *to = xstrdup(mid);
164- return 1;
165-}
166-
167-static char *
168-joinprereqs(const struct Target *t)
169-{
170- size_t k;
171- char *joined, *next;
172-
173- joined = xstrdup("");
174- for (k = 0; k < t->prereqs.n; k++) {
175- next = joined[0] ? cat3(joined, " ", t->prereqs.v[k]) : xstrdup(t->prereqs.v[k]);
176- free(joined);
177- joined = next;
178- }
179- return joined;
180-}
181-
182-static char *
183-expandstem(const char *s, const char *stem)
184-{
185- size_t i, n, cap, len;
186- char *out;
187-
188- n = strlen(s);
189- cap = n + 1;
190- len = 0;
191- out = xmalloc(cap);
192- for (i = 0; i < n; i++) {
193- if (s[i] == '$' && i + 1 < n && s[i + 1] == '*') {
194- size_t slen;
195-
196- slen = stem ? strlen(stem) : 0;
197- if (len + slen + 1 > cap) {
198- cap = len + slen + (n - i) + 1;
199- out = xrealloc(out, cap);
200- }
201- if (slen) {
202- memcpy(out + len, stem, slen);
203- len += slen;
204- }
205- i++;
206- continue;
207- }
208- if (len + 2 > cap) {
209- cap *= 2;
210- out = xrealloc(out, cap);
211- }
212- out[len++] = s[i];
213- }
214- out[len] = 0;
215- return out;
216+ return patmatches(pat, name);
217 }
218
219 static void
220@@ -236,81 +59,6 @@ addexprecipes(struct RecipeList *dest, const struct RecipeList *src, struct Env
221 }
222 }
223
224-/*
225- * expand gnu make automatic variables
226- * $@ target
227- * $< first prereq
228- * $^ all prereqs
229- * $+ all prereqs
230- * $? all prereqs, let ninja decide
231- * $* stem
232- * $$ literal $
233- */
234-static char *
235-expandauto(const char *s, const struct Target *t, const char *stem)
236-{
237- size_t i, n, cap, len;
238- char *out;
239-
240- n = strlen(s);
241- cap = n + 1;
242- len = 0;
243- out = xmalloc(cap);
244- for (i = 0; i < n; i++) {
245- if (s[i] == '$' && i + 1 < n) {
246- const char *val;
247- int mustfree;
248-
249- val = 0;
250- mustfree = 0;
251-
252- if (s[i + 1] == '$') {
253- if (len + 3 > cap) {
254- cap = len + 3 + (n - i);
255- out = xrealloc(out, cap);
256- }
257- out[len++] = '$';
258- out[len++] = '$';
259- i++;
260- continue;
261- }
262- if (s[i + 1] == '@') {
263- val = t->name;
264- } else if (s[i + 1] == '<') {
265- if (t->prereqs.n > 0)
266- val = t->prereqs.v[0];
267- else
268- val = "";
269- } else if (s[i + 1] == '^' || s[i + 1] == '+' || s[i + 1] == '?') {
270- val = joinprereqs(t);
271- mustfree = 1;
272- } else if (s[i + 1] == '*') {
273- val = stem ? stem : "";
274- }
275-
276- if (val) {
277- size_t vlen = strlen(val);
278- if (len + vlen + 1 > cap) {
279- cap = len + vlen + (n - i) + 1;
280- out = xrealloc(out, cap);
281- }
282- memcpy(out + len, val, vlen);
283- len += vlen;
284- if (mustfree)
285- free((char *)val);
286- i++;
287- continue;
288- }
289- }
290- if (len + 2 > cap) {
291- cap *= 2;
292- out = xrealloc(out, cap);
293- }
294- out[len++] = s[i];
295- }
296- out[len] = 0;
297- return out;
298-}
299 /* we apply target assignments from the graphstate (gs.tas) to those specific
300 * targets, for some target like
301 *
302@@ -343,6 +91,10 @@ targetenv(struct GraphState *gs, struct Env *env, const char *name)
303 applytassigns(gs, env, name);
304 }
305
306+
307+/* add an explicit target rule to the graph, and add all
308+ * non-pattern prereqs also as placeholder nodes. later
309+ * we discorver how to build them if they need to be built */
310 static void
311 addrule(struct GraphState *gs, const char *name, const struct RuleNode *rule)
312 {
313@@ -374,52 +126,6 @@ addrule(struct GraphState *gs, const char *name, const struct RuleNode *rule)
314 }
315 }
316 }
317-/* if the target is a pattern rule, store it in the graphstate
318- * instead of making a target immediately */
319-static void
320-addpattern(struct GraphState *gs, const struct RuleNode *rule)
321-{
322- size_t i;
323-
324- for (i = 0; i < rule->targets.n; i++) {
325- if (strchr(rule->targets.v[i], '%')) {
326- struct Pattern *p;
327-
328- gs->pats = xrealloc(gs->pats, (gs->npats + 1) * sizeof(gs->pats[0]));
329- p = &gs->pats[gs->npats++];
330- memset(p, 0, sizeof(*p));
331- p->target = xstrdup(rule->targets.v[i]);
332- addwords(&p->prereqs, &rule->prereqs);
333- addwords(&p->order_only, &rule->order_only);
334- addrecipes(&p->recipes, &rule->recipes);
335- } else {
336- addrule(gs, rule->targets.v[i], rule);
337- }
338- }
339-}
340-
341-static void
342-addsuffix(struct GraphState *gs, const struct RuleNode *rule)
343-{
344- char *from, *to;
345- struct Suffix *sr;
346-
347- if (rule->targets.n != 1 || rule->prereqs.n != 0 || rule->order_only.n != 0) {
348- addpattern(gs, rule);
349- return;
350- }
351- if (!issuffix(rule->targets.v[0], &from, &to)) {
352- addpattern(gs, rule);
353- return;
354- }
355- gs->sufs = xrealloc(gs->sufs, (gs->nsufs + 1) * sizeof(gs->sufs[0]));
356- sr = &gs->sufs[gs->nsufs++];
357- memset(sr, 0, sizeof(*sr));
358- sr->from = from;
359- sr->to = to;
360- addrecipes(&sr->recipes, &rule->recipes);
361-}
362-
363 static void
364 addtassign(struct GraphState *gs, const struct AssignNode *assign)
365 {
366@@ -434,76 +140,10 @@ addtassign(struct GraphState *gs, const struct AssignNode *assign)
367 ta->op = assign->op;
368 }
369
370-static void
371-instantiate(struct GraphState *gs, struct Target *t, struct Pattern *p, const char *stem)
372-{
373- size_t i;
374- struct Env env;
375-
376- for (i = 0; i < p->prereqs.n; i++) {
377- char *s = applystem(p->prereqs.v[i], stem);
378- t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
379- t->prereqs.v[t->prereqs.n++] = s;
380- }
381- for (i = 0; i < p->order_only.n; i++) {
382- char *s = applystem(p->order_only.v[i], stem);
383- t->order_only.v = xrealloc(t->order_only.v, (t->order_only.n + 1) * sizeof(t->order_only.v[0]));
384- t->order_only.v[t->order_only.n++] = s;
385- }
386- targetenv(gs, &env, t->name);
387- for (i = 0; i < p->recipes.n; i++) {
388- char *s, *vars, *exp;
389-
390- s = applystem(p->recipes.v[i], stem);
391- vars = expandstr(&env, s);
392- exp = expandstem(vars, stem);
393- free(vars);
394- free(s);
395- if (!exp[0]) {
396- free(exp);
397- continue;
398- }
399- t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
400- t->recipes.v[t->recipes.n++] = exp;
401- }
402- freeenv(&env);
403-}
404-
405-static int
406-instantiatesuffix(struct GraphState *gs, struct Target *t)
407-{
408- size_t i, k;
409-
410- for (i = 0; i < gs->nsufs; i++) {
411- char *stem, *src;
412- struct Env env;
413-
414- if (!matchsuf(gs->sufs[i].to, t->name, &stem))
415- continue;
416- src = cat3(stem, "", gs->sufs[i].from);
417- t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
418- t->prereqs.v[t->prereqs.n++] = src;
419- targetenv(gs, &env, t->name);
420- for (k = 0; k < gs->sufs[i].recipes.n; k++) {
421- char *vars, *exp;
422-
423- vars = expandstr(&env, gs->sufs[i].recipes.v[k]);
424- exp = expandauto(vars, t, stem);
425- free(vars);
426- if (!exp[0]) {
427- free(exp);
428- continue;
429- }
430- t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
431- t->recipes.v[t->recipes.n++] = exp;
432- }
433- freeenv(&env);
434- free(stem);
435- return 1;
436- }
437- return 0;
438-}
439-
440+/*generate the graph from the evaluated ast
441+ * get the env and target specific assignments,
442+ * then expand placeholder targets until all prereqs
443+ * we can find are in the graph. */
444 int
445 buildgraph(const struct Ast *ast, struct Graph *graph)
446 {
447@@ -515,6 +155,8 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
448 gs.graph = graph;
449 seedenv(&gs.env);
450
451+ /* get the state, env vars in gs.env, target-specific var in gs.tas,
452+ * and rules in gs.rules */
453 for (i = 0; i < ast->n; i++) {
454 if (ast->v[i].kind == NODE_ASSIGN) {
455 if (ast->v[i].data.assign.tspec)
456@@ -526,59 +168,63 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
457 }
458 }
459
460- for (i = 0; i < gs.nrules; i++)
461- addsuffix(&gs, gs.rules[i]);
462+ for (i = 0; i < gs.nrules; i++) {
463+ size_t k;
464+
465+ if (collectsufrule(&gs.sufs, gs.rules[i]))
466+ continue;
467+ collectpat(&gs.patterns, gs.rules[i]);
468+ for (k = 0; k < gs.rules[i]->targets.n; k++) {
469+ if (!ispat(gs.rules[i]->targets.v[k]))
470+ addrule(&gs, gs.rules[i]->targets.v[k], gs.rules[i]);
471+ }
472+ }
473
474 start = 0;
475 while (start < graph->n) {
476 size_t current_n = graph->n;
477 for (i = start; i < current_n; i++) {
478 struct Target *t = &graph->v[i];
479- if (t->recipes.n == 0) {
480- for (j = 0; j < gs.npats; j++) {
481- char *stem = matchpat(gs.pats[j].target, t->name);
482- if (stem) {
483- instantiate(&gs, t, &gs.pats[j], stem);
484- free(stem);
485- break;
486- }
487+ size_t nprereqs;
488+ /* for placeholder targets with no recipe or prereqs,
489+ * try a pattern rule or suffix rule */
490+ if (t->recipes.n == 0 && t->prereqs.n == 0 && t->order_only.n == 0) {
491+ int matched;
492+ struct Env env;
493+
494+ targetenv(&gs, &env, t->name);
495+ instpatrule(&gs.patterns, t, &env);
496+ matched = t->recipes.n > 0;
497+ if (!matched) {
498+ instsufrule(&gs.sufs, graph, t, &env);
499 }
500- if (t->recipes.n == 0)
501- instantiatesuffix(&gs, t);
502+ freeenv(&env);
503 }
504- for (j = 0; j < t->prereqs.n; j++) {
505- if (!findtarget(graph, t->prereqs.v[j])) {
506+ nprereqs = t->prereqs.n;
507+ for (j = 0; j < nprereqs; j++) {
508+ const char *prereq = t->prereqs.v[j];
509+
510+ if (!findtarget(graph, prereq)) {
511 struct Target *nt;
512
513 graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
514 nt = &graph->v[graph->n++];
515 memset(nt, 0, sizeof(*nt));
516- nt->name = xstrdup(t->prereqs.v[j]);
517+ nt->name = xstrdup(prereq);
518 }
519 }
520 }
521 start = current_n;
522 }
523
524- for (i = 0; i < gs.npats; i++) {
525- free(gs.pats[i].target);
526- freestrs(&gs.pats[i].prereqs);
527- freestrs(&gs.pats[i].order_only);
528- freerecipes(&gs.pats[i].recipes);
529- }
530- for (i = 0; i < gs.nsufs; i++) {
531- free(gs.sufs[i].from);
532- free(gs.sufs[i].to);
533- freerecipes(&gs.sufs[i].recipes);
534- }
535 for (i = 0; i < gs.ntas; i++) {
536 freestrs(&gs.tas[i].targets);
537 free(gs.tas[i].lhs);
538 free(gs.tas[i].rhs);
539 }
540 free(gs.tas);
541- free(gs.pats);
542- free(gs.sufs);
543+ freepatrule(&gs.patterns);
544+ freesufrules(&gs.sufs);
545 free(gs.rules);
546 freeenv(&gs.env);
547
+5,
-0
1@@ -21,8 +21,13 @@ char *xstrdup(const char *s);
2 void addnode(struct NodeList *list, struct Node node);
3 void splitwords(struct StrList *out, const char *s, size_t n);
4 char *cat3(const char *a, const char *b, const char *c);
5+void addwords(struct StrList *dest, const struct StrList *src);
6+void addrecipes(struct RecipeList *dest, const struct RecipeList *src);
7 void freestrs(struct StrList *list);
8 void freerecipes(struct RecipeList *list);
9+void envsetvar(struct Env *env, const char *name, char *val, int simple);
10+struct Target *findtarget(struct Graph *graph, const char *name);
11+const struct Target *findctarget(const struct Graph *graph, const char *name);
12
13 struct Var *findvar(struct Env *env, const char *name);
14 char *expandstr(struct Env *env, const char *s);
+285,
-0
1@@ -0,0 +1,285 @@
2+#include "posix.h"
3+
4+#include <unistd.h>
5+#include <stdlib.h>
6+#include <string.h>
7+
8+static int
9+pathorargetexists(const struct Graph *graph, const char *name)
10+{
11+ size_t i;
12+
13+ for (i = 0; i < graph->n; i++) {
14+ if (strcmp(graph->v[i].name, name) == 0)
15+ return 1;
16+ }
17+ return access(name, F_OK) == 0;
18+}
19+
20+static int
21+issuf(const char *s, char **from, char **to)
22+{
23+ const char *mid;
24+
25+ if (!s || s[0] != '.')
26+ return 0;
27+ mid = strchr(s + 1, '.');
28+ if (!mid || mid == s + 1 || !mid[1] || strchr(mid + 1, '.'))
29+ return 0;
30+ *from = xstrndup(s, (size_t)(mid - s));
31+ *to = xstrdup(mid);
32+ return 1;
33+}
34+
35+static int
36+issinglesuf(const char *s, char **from)
37+{
38+ if (!s || s[0] != '.')
39+ return 0;
40+ if (!s[1] || strchr(s + 1, '.'))
41+ return 0;
42+ *from = xstrdup(s);
43+ return 1;
44+}
45+
46+static int
47+matchsuf(const char *suf, const char *name, char **stem)
48+{
49+ size_t nsuf, nname;
50+
51+ nsuf = strlen(suf);
52+ nname = strlen(name);
53+ if (nname < nsuf || strcmp(name + nname - nsuf, suf) != 0)
54+ return 0;
55+ *stem = xstrndup(name, nname - nsuf);
56+ return 1;
57+}
58+
59+static char *
60+joinprereqs(const struct Target *t)
61+{
62+ size_t i;
63+ char *joined, *next;
64+
65+ joined = xstrdup("");
66+ for (i = 0; i < t->prereqs.n; i++) {
67+ next = joined[0] ? cat3(joined, " ", t->prereqs.v[i]) : xstrdup(t->prereqs.v[i]);
68+ free(joined);
69+ joined = next;
70+ }
71+ return joined;
72+}
73+
74+
75+/*
76+ * expand automatic variables like so:
77+ * $@ target
78+ * $< first prereq
79+ * $^ all prereqs
80+ * $+ all prereqs
81+ * $? all prereqs, let ninja decide
82+ * $* stem
83+ * $$ literal $
84+ *
85+ * this turns them into concrete strings,
86+ * later during ninja generation
87+ * translateauto() turns any remaining ones
88+ * into ninja variables if possible
89+ */
90+static char *
91+expandauto(const char *s, const struct Target *t, const char *stem)
92+{
93+ size_t i, n, cap, len;
94+ char *out;
95+
96+ n = strlen(s);
97+ cap = n + 1;
98+ len = 0;
99+ out = xmalloc(cap);
100+ for (i = 0; i < n; i++) {
101+ if (s[i] == '$' && i + 1 < n) {
102+ const char *val;
103+ int mustfree;
104+
105+ val = 0;
106+ mustfree = 0;
107+ if (s[i + 1] == '$') {
108+ if (len + 3 > cap) {
109+ cap = len + 3 + (n - i);
110+ out = xrealloc(out, cap);
111+ }
112+ out[len++] = '$';
113+ out[len++] = '$';
114+ i++;
115+ continue;
116+ }
117+ if (s[i + 1] == '@') {
118+ val = t->name;
119+ } else if (s[i + 1] == '<') {
120+ val = t->prereqs.n > 0 ? t->prereqs.v[0] : "";
121+ } else if (s[i + 1] == '^' || s[i + 1] == '+' || s[i + 1] == '?') {
122+ val = joinprereqs(t);
123+ mustfree = 1;
124+ } else if (s[i + 1] == '*') {
125+ val = stem ? stem : "";
126+ }
127+ if (val) {
128+ size_t vlen = strlen(val);
129+ if (len + vlen + 1 > cap) {
130+ cap = len + vlen + (n - i) + 1;
131+ out = xrealloc(out, cap);
132+ }
133+ memcpy(out + len, val, vlen);
134+ len += vlen;
135+ if (mustfree)
136+ free((char *)val);
137+ i++;
138+ continue;
139+ }
140+ }
141+ if (len + 2 > cap) {
142+ cap *= 2;
143+ out = xrealloc(out, cap);
144+ }
145+ out[len++] = s[i];
146+ }
147+ out[len] = 0;
148+ return out;
149+}
150+
151+void
152+seedenv(struct Env *env)
153+{
154+ envsetvar(env, "CC", xstrdup("cc"), 1);
155+ envsetvar(env, "CFLAGS", xstrdup(""), 1);
156+ envsetvar(env, "CXX", xstrdup("c++"), 1);
157+ envsetvar(env, "CPP", xstrdup("$(CC) -E"), 0);
158+ envsetvar(env, "AR", xstrdup("ar"), 1);
159+ envsetvar(env, "ARFLAGS", xstrdup("-rv"), 1);
160+ envsetvar(env, "AS", xstrdup("as"), 1);
161+ envsetvar(env, "GET", xstrdup("get"), 1);
162+ envsetvar(env, "GFLAGS", xstrdup(""), 1);
163+ envsetvar(env, "LD", xstrdup("ld"), 1);
164+ envsetvar(env, "LDFLAGS", xstrdup(""), 1);
165+ envsetvar(env, "LEX", xstrdup("lex"), 1);
166+ envsetvar(env, "LFLAGS", xstrdup(""), 1);
167+ envsetvar(env, "SCCSFLAGS", xstrdup(""), 1);
168+ envsetvar(env, "SCCSGETFLAGS", xstrdup("-s"), 1);
169+ envsetvar(env, "YACC", xstrdup("yacc"), 1);
170+ envsetvar(env, "YFLAGS", xstrdup(""), 1);
171+ envsetvar(env, "SHELL", xstrdup("/bin/sh"), 1);
172+}
173+
174+int
175+collectsufrule(struct SufRules *rules, const struct RuleNode *rule)
176+{
177+ char *from, *to;
178+
179+ if (rule->targets.n != 1 || rule->prereqs.n != 0 || rule->order_only.n != 0)
180+ return 0;
181+ if (issinglesuf(rule->targets.v[0], &from)) {
182+ struct SingleSufRule *sr;
183+
184+ rules->singlesuf = xrealloc(rules->singlesuf,
185+ (rules->nsinglesuf + 1) * sizeof(rules->singlesuf[0]));
186+ sr = &rules->singlesuf[rules->nsinglesuf++];
187+ memset(sr, 0, sizeof(*sr));
188+ sr->from = from;
189+ addrecipes(&sr->recipes, &rule->recipes);
190+ return 1;
191+ }
192+ if (!issuf(rule->targets.v[0], &from, &to))
193+ return 0;
194+ rules->sufs = xrealloc(rules->sufs, (rules->nsufs + 1) * sizeof(rules->sufs[0]));
195+ memset(&rules->sufs[rules->nsufs], 0, sizeof(rules->sufs[rules->nsufs]));
196+ rules->sufs[rules->nsufs].from = from;
197+ rules->sufs[rules->nsufs].to = to;
198+ addrecipes(&rules->sufs[rules->nsufs].recipes, &rule->recipes);
199+ rules->nsufs++;
200+ return 1;
201+}
202+
203+int
204+instsufrule(const struct SufRules *rules,
205+ const struct Graph *graph,
206+ struct Target *t,
207+ struct Env *env)
208+{
209+ size_t i, k;
210+
211+ for (i = 0; i < rules->nsufs; i++) {
212+ char *stem, *src;
213+
214+ if (!matchsuf(rules->sufs[i].to, t->name, &stem))
215+ continue;
216+ src = cat3(stem, "", rules->sufs[i].from);
217+ t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
218+ t->prereqs.v[t->prereqs.n++] = src;
219+ for (k = 0; k < rules->sufs[i].recipes.n; k++) {
220+ char *vars, *exp;
221+
222+ vars = expandstr(env, rules->sufs[i].recipes.v[k]);
223+ exp = expandauto(vars, t, stem);
224+ free(vars);
225+ if (!exp[0]) {
226+ free(exp);
227+ continue;
228+ }
229+ t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
230+ t->recipes.v[t->recipes.n++] = exp;
231+ }
232+ free(stem);
233+ return 1;
234+ }
235+
236+ if (strchr(t->name, '.'))
237+ return 0;
238+ for (i = 0; i < rules->nsinglesuf; i++) {
239+ char *src;
240+
241+ src = cat3(t->name, "", rules->singlesuf[i].from);
242+ if (!pathorargetexists(graph, src)) {
243+ free(src);
244+ continue;
245+ }
246+ t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
247+ t->prereqs.v[t->prereqs.n++] = src;
248+ for (k = 0; k < rules->singlesuf[i].recipes.n; k++) {
249+ char *vars, *exp;
250+
251+ vars = expandstr(env, rules->singlesuf[i].recipes.v[k]);
252+ exp = expandauto(vars, t, t->name);
253+ free(vars);
254+ if (!exp[0]) {
255+ free(exp);
256+ continue;
257+ }
258+ t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
259+ t->recipes.v[t->recipes.n++] = exp;
260+ }
261+ return 1;
262+ }
263+ return 0;
264+}
265+
266+void
267+freesufrules(struct SufRules *rules)
268+{
269+ size_t i;
270+
271+ for (i = 0; i < rules->nsinglesuf; i++) {
272+ free(rules->singlesuf[i].from);
273+ freerecipes(&rules->singlesuf[i].recipes);
274+ }
275+ for (i = 0; i < rules->nsufs; i++) {
276+ free(rules->sufs[i].from);
277+ free(rules->sufs[i].to);
278+ freerecipes(&rules->sufs[i].recipes);
279+ }
280+ free(rules->singlesuf);
281+ free(rules->sufs);
282+ rules->singlesuf = 0;
283+ rules->nsinglesuf = 0;
284+ rules->sufs = 0;
285+ rules->nsufs = 0;
286+}
+38,
-0
1@@ -0,0 +1,38 @@
2+#ifndef POSIX_H
3+#define POSIX_H
4+
5+#include "shinobi.h"
6+#include "internal.h"
7+
8+/* https://pubs.opengroup.org/onlinepubs/9799919799/ */
9+
10+/* single suffix rule like .sh */
11+struct SingleSufRule {
12+ char *from;
13+ struct RecipeList recipes;
14+};
15+
16+/* normal suffix like .c.o */
17+struct SufRule {
18+ char *from;
19+ char *to;
20+ struct RecipeList recipes;
21+};
22+
23+/*holds all stored suffix rules*/
24+struct SufRules {
25+ struct SingleSufRule *singlesuf;
26+ size_t nsinglesuf;
27+ struct SufRule *sufs;
28+ size_t nsufs;
29+};
30+
31+void seedenv(struct Env *env);
32+int collectsufrule(struct SufRules *rules, const struct RuleNode *rule);
33+int instsufrule(const struct SufRules *rules,
34+ const struct Graph *graph,
35+ struct Target *t,
36+ struct Env *env);
37+void freesufrules(struct SufRules *rules);
38+
39+#endif
+65,
-0
1@@ -73,6 +73,52 @@ addnode(struct NodeList *list, struct Node node)
2 list->v[list->n++] = node;
3 }
4
5+void
6+addwords(struct StrList *dest, const struct StrList *src)
7+{
8+ size_t i;
9+
10+ for (i = 0; i < src->n; i++) {
11+ dest->v = xrealloc(dest->v, (dest->n + 1) * sizeof(dest->v[0]));
12+ dest->v[dest->n++] = xstrdup(src->v[i]);
13+ }
14+}
15+
16+void
17+addrecipes(struct RecipeList *dest, const struct RecipeList *src)
18+{
19+ size_t i;
20+
21+ for (i = 0; i < src->n; i++) {
22+ dest->v = xrealloc(dest->v, (dest->n + 1) * sizeof(dest->v[0]));
23+ dest->v[dest->n++] = xstrdup(src->v[i]);
24+ }
25+}
26+
27+static const struct Target *
28+findtarget0(const struct Graph *graph, const char *name)
29+{
30+ size_t i;
31+
32+ for (i = 0; i < graph->n; i++) {
33+ if (strcmp(graph->v[i].name, name) == 0)
34+ return &graph->v[i];
35+ }
36+ return 0;
37+}
38+
39+struct Target *
40+findtarget(struct Graph *graph, const char *name)
41+{
42+ return (struct Target *)findtarget0(graph, name);
43+}
44+
45+const struct Target *
46+findctarget(const struct Graph *graph, const char *name)
47+{
48+ return findtarget0(graph, name);
49+}
50+
51 void
52 freestrs(struct StrList *list)
53 {
54@@ -100,3 +146,22 @@ freerecipes(struct RecipeList *list)
55 list->v = 0;
56 list->n = 0;
57 }
58+
59+void
60+envsetvar(struct Env *env, const char *name, char *val, int simple)
61+{
62+ struct Var *v;
63+
64+ v = findvar(env, name);
65+ if (v) {
66+ free(v->val);
67+ v->val = val;
68+ v->simple = simple;
69+ return;
70+ }
71+ env->v = xrealloc(env->v, (env->n + 1) * sizeof(env->v[0]));
72+ env->v[env->n].name = xstrdup(name);
73+ env->v[env->n].val = val;
74+ env->v[env->n].simple = simple;
75+ env->n++;
76+}