commit 2fadeb0

shrub  ·  2026-04-20 13:11:46 +0000 UTC
parent 67e2bc0
parse (but dont handle) recursive make
10 files changed,  +389, -3
+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;