commit 2fadeb0
shrub
·
2026-04-20 13:11:46 +0000 UTC
parent 67e2bc0
parse (but dont handle) recursive make
10 files changed,
+389,
-3
M
Makefile
+23,
-2
1@@ -3,8 +3,29 @@ DESTDIR =
2 BINDIR = $(DESTDIR)$(PREFIX)/bin
3
4 BIN = shin
5-SRCS = cli/main.c src/parse.c src/eval/eval.c src/eval/dollar.c src/graph.c src/posix.c src/gnu/pattern.c backends/ninja.c backends/graphviz.c backends/compcmd.c src/util.c src/gnu/functions.c
6-FMTSRCS = $(SRCS) src/shinobi.h src/internal.h src/posix.h src/gnu/pattern.h backends/ninja.h backends/graphviz.h backends/compcmd.h
7+SRCS = cli/main.c \
8+ src/parse.c \
9+ src/eval/eval.c \
10+ src/eval/dollar.c \
11+ src/graph.c \
12+ src/posix.c \
13+ src/gnu/pattern.c \
14+ backends/ninja.c \
15+ backends/graphviz.c \
16+ backends/compcmd.c \
17+ src/util.c \
18+ src/submake.c \
19+ src/gnu/functions.c
20+
21+FMTSRCS = $(SRCS) \
22+ src/shinobi.h \
23+ src/internal.h \
24+ src/posix.h \
25+ src/gnu/pattern.h \
26+ backends/ninja.h \
27+ backends/graphviz.h \
28+ backends/compcmd.h
29+
30 OBJS = $(SRCS:.c=.o)
31
32 CC = clang
+36,
-1
1@@ -133,6 +133,39 @@ dumpstrlist(const char *label, const struct StrList *list, int depth)
2 putchar('\n');
3 }
4
5+static void
6+dumpinlinewords(const char *label, const struct StrList *list)
7+{
8+ size_t i;
9+
10+ if (!list->n)
11+ return;
12+ printf(" %s=", label);
13+ for (i = 0; i < list->n; i++) {
14+ if (i)
15+ putchar(',');
16+ fputs(list->v[i], stdout);
17+ }
18+}
19+
20+static void
21+dumprecipesubmake(const struct Recipe *r)
22+{
23+ if (!r->submake)
24+ return;
25+ printf(" [submake");
26+ if (r->sm.makeprog)
27+ printf(" make=%s", r->sm.makeprog);
28+ if (r->sm.dir)
29+ printf(" dir=%s", r->sm.dir);
30+ if (r->sm.makefile)
31+ printf(" makefile=%s", r->sm.makefile);
32+ dumpinlinewords("assigns", &r->sm.assigns);
33+ dumpinlinewords("flags", &r->sm.flags);
34+ dumpinlinewords("goals", &r->sm.goals);
35+ putchar(']');
36+}
37+
38 static void
39 dumprecipes(const struct RecipeList *list, int depth)
40 {
41@@ -148,7 +181,9 @@ dumprecipes(const struct RecipeList *list, int depth)
42 putchar('-');
43 if (list->v[i].recursive)
44 putchar('+');
45- printf("%s\n", list->v[i].body);
46+ printf("%s", list->v[i].body);
47+ dumprecipesubmake(&list->v[i]);
48+ putchar('\n');
49 }
50 }
51
+2,
-0
1@@ -150,6 +150,8 @@ copyrecipes(struct RecipeList *out, const struct RecipeList *in)
2 out->v[out->n].silent = in->v[i].silent;
3 out->v[out->n].ignore = in->v[i].ignore;
4 out->v[out->n].recursive = in->v[i].recursive;
5+ out->v[out->n].submake = in->v[i].submake;
6+ copysubmake(&out->v[out->n].sm, &in->v[i].sm);
7 out->n++;
8 }
9 }
+2,
-0
1@@ -208,6 +208,8 @@ instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Targ
2 t->recipes.v[t->recipes.n].silent = rules->v[i].recipes.v[j].silent;
3 t->recipes.v[t->recipes.n].ignore = rules->v[i].recipes.v[j].ignore;
4 t->recipes.v[t->recipes.n].recursive = rules->v[i].recipes.v[j].recursive;
5+ t->recipes.v[t->recipes.n].submake = rules->v[i].recipes.v[j].submake;
6+ copysubmake(&t->recipes.v[t->recipes.n].sm, &rules->v[i].recipes.v[j].sm);
7 t->recipes.n++;
8 }
9 free(stem);
+52,
-0
1@@ -114,10 +114,43 @@ addexprecipes(struct RecipeList *dest, const struct RecipeList *src, struct Eval
2 dest->v[dest->n].silent = src->v[i].silent;
3 dest->v[dest->n].ignore = src->v[i].ignore;
4 dest->v[dest->n].recursive = src->v[i].recursive;
5+ dest->v[dest->n].submake = src->v[i].submake;
6+ copysubmake(&dest->v[dest->n].sm, &src->v[i].sm);
7 dest->n++;
8 }
9 }
10
11+static int
12+hasword(const struct StrList *list, const char *word)
13+{
14+ size_t i;
15+
16+ for (i = 0; i < list->n; i++) {
17+ if (strcmp(list->v[i], word) == 0)
18+ return 1;
19+ }
20+ return 0;
21+}
22+
23+static void
24+marksubmake(struct Graph *graph, const char *tname)
25+{
26+ const char *name = "SubMake";
27+ struct Target *t;
28+
29+ if (!findtarget(graph, name)) {
30+ graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
31+ memset(&graph->v[graph->n], 0, sizeof(graph->v[graph->n]));
32+ graph->v[graph->n].name = xstrdup(name);
33+ graph->n++;
34+ }
35+ t = tname ? findtarget(graph, tname) : 0;
36+ if (t && !hasword(&t->order_only, name)) {
37+ t->order_only.v = xrealloc(t->order_only.v, (t->order_only.n + 1) * sizeof(t->order_only.v[0]));
38+ t->order_only.v[t->order_only.n++] = xstrdup(name);
39+ }
40+}
41+
42 /* we apply target assignments from the graphstate (gs.tas) to those specific
43 * targets, for some target like
44 *
45@@ -306,6 +339,25 @@ buildgraph(const struct RuleSet *ruleset, struct Graph *graph)
46 start = current_n;
47 }
48
49+ for (i = 0; i < graph->n; i++) {
50+ struct Target *t = &graph->v[i];
51+ size_t k;
52+
53+ for (k = 0; k < t->recipes.n; k++) {
54+ char *tname;
55+
56+ if (!t->recipes.v[k].submake)
57+ continue;
58+ tname = xstrdup(t->name);
59+ marksubmake(graph, tname);
60+ free(tname);
61+ fprintf(stderr, "recursive make detected! i dont know how to handle that yet!\n");
62+ ctx.errors++;
63+ i = graph->n;
64+ break;
65+ }
66+ }
67+
68 for (i = 0; i < gs.ntas; i++) {
69 freestrs(&gs.tas[i].targets);
70 free(gs.tas[i].lhs);
+3,
-0
1@@ -40,6 +40,9 @@ void addrecipe(struct RecipeList *dest, const char *raw);
2 void addrecipes(struct RecipeList *dest, const struct RecipeList *src);
3 void freestrs(struct StrList *list);
4 void freerecipes(struct RecipeList *list);
5+void freesubmake(struct SubMake *sm);
6+void copysubmake(struct SubMake *dst, const struct SubMake *src);
7+int parsesubmake(struct SubMake *dst, const char *cmd);
8 void envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin origin);
9 struct Target *findtarget(struct Graph *graph, const char *name);
10 const struct Target *findctarget(const struct Graph *graph, const char *name);
+4,
-0
1@@ -452,6 +452,8 @@ instsufrule(const struct SufRules *rules,
2 t->recipes.v[t->recipes.n].silent = rules->sufs[r].recipes.v[k].silent;
3 t->recipes.v[t->recipes.n].ignore = rules->sufs[r].recipes.v[k].ignore;
4 t->recipes.v[t->recipes.n].recursive = rules->sufs[r].recipes.v[k].recursive;
5+ t->recipes.v[t->recipes.n].submake = rules->sufs[r].recipes.v[k].submake;
6+ copysubmake(&t->recipes.v[t->recipes.n].sm, &rules->sufs[r].recipes.v[k].sm);
7 t->recipes.n++;
8 }
9 free(stem);
10@@ -494,6 +496,8 @@ instsufrule(const struct SufRules *rules,
11 t->recipes.v[t->recipes.n].silent = rules->singlesuf[j].recipes.v[k].silent;
12 t->recipes.v[t->recipes.n].ignore = rules->singlesuf[j].recipes.v[k].ignore;
13 t->recipes.v[t->recipes.n].recursive = rules->singlesuf[j].recipes.v[k].recursive;
14+ t->recipes.v[t->recipes.n].submake = rules->singlesuf[j].recipes.v[k].submake;
15+ copysubmake(&t->recipes.v[t->recipes.n].sm, &rules->singlesuf[j].recipes.v[k].sm);
16 t->recipes.n++;
17 }
18 return 1;
+11,
-0
1@@ -68,11 +68,22 @@ struct StrList {
2 size_t n;
3 };
4
5+struct SubMake {
6+ char *makeprog;
7+ char *dir;
8+ char *makefile;
9+ struct StrList assigns;
10+ struct StrList flags;
11+ struct StrList goals;
12+};
13+
14 struct Recipe {
15 char *body;
16 int silent;
17 int ignore;
18 int recursive;
19+ int submake;
20+ struct SubMake sm;
21 };
22
23 struct RecipeList {
+251,
-0
1@@ -0,0 +1,251 @@
2+#include "internal.h"
3+
4+#include <ctype.h>
5+#include <stdlib.h>
6+#include <string.h>
7+
8+/* recursive make handling */
9+
10+static int
11+ismakevarref(const char *s)
12+{
13+ char close;
14+ size_t n;
15+
16+ if (!s || s[0] != '$')
17+ return 0;
18+ if (s[1] != '(' && s[1] != '{')
19+ return 0;
20+ n = strlen(s);
21+ if (n != 7)
22+ return 0;
23+ close = s[1] == '(' ? ')' : '}';
24+ return strncmp(s + 2, "MAKE", 4) == 0 && s[6] == close && s[7] == 0;
25+}
26+
27+static int
28+ismaketoken(const char *s)
29+{
30+ const char *base;
31+
32+ if (ismakevarref(s))
33+ return 1;
34+ base = strrchr(s, '/');
35+ base = base ? base + 1 : s;
36+ return strcmp(base, "make") == 0 || strcmp(base, "gmake") == 0;
37+}
38+
39+static int
40+isassignment(const char *s)
41+{
42+ const char *eq;
43+ size_t i;
44+
45+ eq = strchr(s, '=');
46+ if (!eq || eq == s)
47+ return 0;
48+ for (i = 0; s + i < eq; i++) {
49+ if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
50+ return 0;
51+ }
52+ return 1;
53+}
54+
55+static int
56+flagsarg(const char *s)
57+{
58+ static const char *const flags[] = {
59+ "-C", "--directory", "-f", "--file", "-I", "-o", "-W", "-j", "-l", "--eval", 0};
60+ size_t i;
61+
62+ for (i = 0; flags[i]; i++) {
63+ if (strcmp(s, flags[i]) == 0)
64+ return 1;
65+ }
66+ return 0;
67+}
68+
69+static void
70+addstr(struct StrList *list, const char *s)
71+{
72+ list->v = xrealloc(list->v, (list->n + 1) * sizeof(list->v[0]));
73+ list->v[list->n++] = xstrdup(s);
74+}
75+
76+static void
77+addtok(struct StrList *out, const char *s, size_t n)
78+{
79+ out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
80+ out->v[out->n++] = xstrndup(s, n);
81+}
82+
83+static void
84+freetoks(struct StrList *toks)
85+{
86+ freestrs(toks);
87+}
88+
89+static int
90+tokenizecmd(struct StrList *out, const char *s)
91+{
92+ size_t i;
93+
94+ memset(out, 0, sizeof(*out));
95+ for (i = 0; s[i];) {
96+ size_t start;
97+ char quote;
98+
99+ while (isspace((unsigned char)s[i]))
100+ i++;
101+ if (!s[i])
102+ break;
103+ if (s[i] == '&' && s[i + 1] == '&') {
104+ addtok(out, "&&", 2);
105+ i += 2;
106+ continue;
107+ }
108+ start = i;
109+ quote = 0;
110+ while (s[i]) {
111+ if (!quote && isspace((unsigned char)s[i]))
112+ break;
113+ if (!quote && s[i] == '&' && s[i + 1] == '&')
114+ break;
115+ if (s[i] == '\\' && s[i + 1]) {
116+ i += 2;
117+ continue;
118+ }
119+ if (!quote && (s[i] == '\'' || s[i] == '"')) {
120+ quote = s[i++];
121+ continue;
122+ }
123+ if (quote && s[i] == quote) {
124+ quote = 0;
125+ i++;
126+ continue;
127+ }
128+ i++;
129+ }
130+ addtok(out, s + start, i - start);
131+ }
132+ return 0;
133+}
134+
135+void
136+freesubmake(struct SubMake *sm)
137+{
138+ if (!sm)
139+ return;
140+ free(sm->makeprog);
141+ sm->makeprog = 0;
142+ free(sm->dir);
143+ sm->dir = 0;
144+ free(sm->makefile);
145+ sm->makefile = 0;
146+ freestrs(&sm->assigns);
147+ freestrs(&sm->flags);
148+ freestrs(&sm->goals);
149+}
150+
151+void
152+copysubmake(struct SubMake *dst, const struct SubMake *src)
153+{
154+ memset(dst, 0, sizeof(*dst));
155+ if (!src)
156+ return;
157+ if (src->makeprog)
158+ dst->makeprog = xstrdup(src->makeprog);
159+ if (src->dir)
160+ dst->dir = xstrdup(src->dir);
161+ if (src->makefile)
162+ dst->makefile = xstrdup(src->makefile);
163+ addwords(&dst->assigns, &src->assigns);
164+ addwords(&dst->flags, &src->flags);
165+ addwords(&dst->goals, &src->goals);
166+}
167+
168+int
169+parsesubmake(struct SubMake *dst, const char *cmd)
170+{
171+ struct StrList toks;
172+ size_t i, start;
173+ const char *cddir;
174+ const char *makeprog;
175+
176+ memset(dst, 0, sizeof(*dst));
177+ cddir = 0;
178+ makeprog = 0;
179+ tokenizecmd(&toks, cmd);
180+ start = 0;
181+ if (toks.n >= 3 && strcmp(toks.v[0], "cd") == 0 && strcmp(toks.v[2], "&&") == 0) {
182+ cddir = toks.v[1];
183+ start = 3;
184+ }
185+ if (start >= toks.n || !ismaketoken(toks.v[start])) {
186+ freetoks(&toks);
187+ return 0;
188+ }
189+ makeprog = toks.v[start];
190+ dst->makeprog = xstrdup(makeprog);
191+ if (cddir)
192+ dst->dir = xstrdup(cddir);
193+ for (i = start + 1; i < toks.n; i++) {
194+ const char *tok;
195+
196+ tok = toks.v[i];
197+ if (strcmp(tok, "&&") == 0)
198+ break;
199+ if ((strcmp(tok, "-C") == 0 || strcmp(tok, "--directory") == 0) && i + 1 < toks.n) {
200+ addstr(&dst->flags, tok);
201+ free(dst->dir);
202+ dst->dir = xstrdup(toks.v[++i]);
203+ continue;
204+ }
205+ if (strncmp(tok, "-C", 2) == 0 && tok[2]) {
206+ addstr(&dst->flags, "-C");
207+ free(dst->dir);
208+ dst->dir = xstrdup(tok + 2);
209+ continue;
210+ }
211+ if (strncmp(tok, "--directory=", 12) == 0) {
212+ addstr(&dst->flags, "--directory");
213+ free(dst->dir);
214+ dst->dir = xstrdup(tok + 12);
215+ continue;
216+ }
217+ if ((strcmp(tok, "-f") == 0 || strcmp(tok, "--file") == 0) && i + 1 < toks.n) {
218+ addstr(&dst->flags, tok);
219+ free(dst->makefile);
220+ dst->makefile = xstrdup(toks.v[++i]);
221+ continue;
222+ }
223+ if (strncmp(tok, "-f", 2) == 0 && tok[2]) {
224+ addstr(&dst->flags, "-f");
225+ free(dst->makefile);
226+ dst->makefile = xstrdup(tok + 2);
227+ continue;
228+ }
229+ if (strncmp(tok, "--file=", 7) == 0) {
230+ addstr(&dst->flags, "--file");
231+ free(dst->makefile);
232+ dst->makefile = xstrdup(tok + 7);
233+ continue;
234+ }
235+ if (flagsarg(tok) && i + 1 < toks.n) {
236+ addstr(&dst->flags, tok);
237+ i++;
238+ continue;
239+ }
240+ if (tok[0] == '-') {
241+ addstr(&dst->flags, tok);
242+ continue;
243+ }
244+ if (isassignment(tok)) {
245+ addstr(&dst->assigns, tok);
246+ continue;
247+ }
248+ addstr(&dst->goals, tok);
249+ }
250+ freetoks(&toks);
251+ return 1;
252+}
+5,
-0
1@@ -113,6 +113,7 @@ addrecipe(struct RecipeList *dest, const char *raw)
2 while (n > 0 && isspace((unsigned char)s[n - 1]))
3 n--;
4 r->body = xstrndup(s, n);
5+ r->submake = parsesubmake(&r->sm, r->body);
6 }
7
8 void
9@@ -126,6 +127,8 @@ addrecipes(struct RecipeList *dest, const struct RecipeList *src)
10 dest->v[dest->n].silent = src->v[i].silent;
11 dest->v[dest->n].ignore = src->v[i].ignore;
12 dest->v[dest->n].recursive = src->v[i].recursive;
13+ dest->v[dest->n].submake = src->v[i].submake;
14+ copysubmake(&dest->v[dest->n].sm, &src->v[i].sm);
15 dest->n++;
16 }
17 }
18@@ -177,6 +180,8 @@ freerecipes(struct RecipeList *list)
19 return;
20 for (i = 0; i < list->n; i++)
21 free(list->v[i].body);
22+ for (i = 0; i < list->n; i++)
23+ freesubmake(&list->v[i].sm);
24 free(list->v);
25 list->v = 0;
26 list->n = 0;