commit 1e4639b

shrub  ·  2026-04-21 19:27:16 +0000 UTC
parent ec04fe8
add define and endef stipport, make variable name expansion more dynamic.  make subgraph expansion more robust, and subninja emission more robust, seed default ranlib variable
7 files changed,  +291, -38
+74, -4
  1@@ -25,18 +25,81 @@ 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+
 27+		body = t->recipes.v[i].body;
 28+		if (strip_prefix)
 29+			body += nprefix;
 30 		bodies.v = xrealloc(bodies.v, (bodies.n + 1) * sizeof(bodies.v[0]));
 31-		bodies.v[bodies.n++] = t->recipes.v[i].body;
 32+		bodies.v[bodies.n++] = (char *)body;
 33 	}
 34 	s = joinstrs(&bodies, " && ");
 35+	if (strip_prefix) {
 36+		char *tmp;
 37+
 38+		tmp = cat3(prefix, s, "");
 39+		free(s);
 40+		s = tmp;
 41+	}
 42+	free(prefix);
 43 	free(bodies.v);
 44 	return s;
 45 }
 46 
 47 /*turn remaining automatic vars into ninja equivalents*/
 48+static char *
 49+localpath(const struct Target *t, const char *path)
 50+{
 51+	size_t n;
 52+
 53+	if (!t->owner || !t->owner[0])
 54+		return xstrdup(path);
 55+	n = strlen(t->owner);
 56+	if (strncmp(path, t->owner, n) != 0 || path[n] != '/')
 57+		return xstrdup(path);
 58+	return xstrdup(path + n + 1);
 59+}
 60+
 61+static char *
 62+joinlocalprereqs(const struct Target *t)
 63+{
 64+	size_t i;
 65+	struct StrList list;
 66+	char *s;
 67+
 68+	memset(&list, 0, sizeof(list));
 69+	for (i = 0; i < t->prereqs.n; i++) {
 70+		char *name;
 71+
 72+		name = localpath(t, t->prereqs.v[i]);
 73+		addstr(&list, name);
 74+		free(name);
 75+	}
 76+	s = joinstrs(&list, " ");
 77+	freestrs(&list);
 78+	return s;
 79+}
 80+
 81 static char *
 82 translateauto(const char *s, const struct Target *t)
 83 {
 84@@ -49,6 +112,7 @@ translateauto(const char *s, const struct Target *t)
 85 	out = xmalloc(cap);
 86 	for (i = 0; i < n; i++) {
 87 		const char *val;
 88+		int mustfree;
 89 		size_t vlen;
 90 
 91 		if (s[i] != '$' || i + 1 >= n) {
 92@@ -61,14 +125,18 @@ translateauto(const char *s, const struct Target *t)
 93 		}
 94 
 95 		val = 0;
 96+		mustfree = 0;
 97 		if (s[i + 1] == '$') {
 98 			val = "$$";
 99 		} else if (s[i + 1] == '@') {
100-			val = "$out";
101+			val = localpath(t, t->name);
102+			mustfree = 1;
103 		} else if (s[i + 1] == '<') {
104-			val = t->prereqs.n == 1 ? "$in" : (t->prereqs.n ? t->prereqs.v[0] : "");
105+			val = t->prereqs.n ? localpath(t, t->prereqs.v[0]) : xstrdup("");
106+			mustfree = 1;
107 		} else if (s[i + 1] == '^' || s[i + 1] == '+' || s[i + 1] == '?') {
108-			val = "$in";
109+			val = joinlocalprereqs(t);
110+			mustfree = 1;
111 		}
112 
113 		if (!val) {
114@@ -87,6 +155,8 @@ translateauto(const char *s, const struct Target *t)
115 		}
116 		memcpy(out + len, val, vlen);
117 		len += vlen;
118+		if (mustfree)
119+			free((char *)val);
120 		i++;
121 	}
122 	out[len] = 0;
+86, -16
  1@@ -226,39 +226,110 @@ findclose(const char *s, size_t i, size_t n, char close)
  2 	return inner ? 0 : j;
  3 }
  4 
  5+static char *expandvarref(struct EvalCtx *ctx, const char *s, size_t n);
  6+static char *expandref(struct EvalCtx *ctx, const char *s, size_t n);
  7+
  8+static char *
  9+expandname(struct EvalCtx *ctx, const char *s)
 10+{
 11+	size_t i, j, n;
 12+	char close;
 13+	char *val;
 14+	struct Buf out;
 15+
 16+	n = strlen(s);
 17+	bufinit(&out, n + 1);
 18+	for (i = 0; i < n; i++) {
 19+		if (s[i] != '$' || i + 1 >= n) {
 20+			bufappendc(&out, s[i]);
 21+			continue;
 22+		}
 23+		if (s[i + 1] == '$') {
 24+			bufappendc(&out, '$');
 25+			i++;
 26+			continue;
 27+		}
 28+		if (s[i + 1] == '@' && ctx->auto_target) {
 29+			bufappend(&out, ctx->auto_target);
 30+			i++;
 31+			continue;
 32+		}
 33+		if ((s[i + 1] == '<') && ctx->auto_prereqs) {
 34+			if (ctx->auto_prereqs->n > 0)
 35+				bufappend(&out, ctx->auto_prereqs->v[0]);
 36+			i++;
 37+			continue;
 38+		}
 39+		if ((s[i + 1] == '^' || s[i + 1] == '+' || s[i + 1] == '?') &&
 40+		    ctx->auto_prereqs) {
 41+			char *joined;
 42+
 43+			joined = joinstrs(ctx->auto_prereqs, " ");
 44+			bufappend(&out, joined);
 45+			free(joined);
 46+			i++;
 47+			continue;
 48+		}
 49+		if (s[i + 1] == '*' && ctx->auto_stem) {
 50+			bufappend(&out, ctx->auto_stem);
 51+			i++;
 52+			continue;
 53+		}
 54+		if (s[i + 1] != '(' && s[i + 1] != '{') {
 55+			val = expandvarref(ctx, s + i + 1, 1);
 56+			bufappend(&out, val);
 57+			free(val);
 58+			i++;
 59+			continue;
 60+		}
 61+		close = s[i + 1] == '(' ? ')' : '}';
 62+		j = findclose(s, i, n, close);
 63+		if (!j) {
 64+			bufappendn(&out, s + i, n - i);
 65+			break;
 66+		}
 67+		val = expandref(ctx, s + i + 2, j - i - 3);
 68+		bufappend(&out, val);
 69+		free(val);
 70+		i = j - 1;
 71+	}
 72+	return bufdone(&out);
 73+}
 74+
 75 static char *
 76 expandvarref(struct EvalCtx *ctx, const char *s, size_t n)
 77 {
 78-	char *name, *val;
 79+	char *name, *raw, *val;
 80 	struct Var *v;
 81 
 82+	raw = xstrndup(s, n);
 83+	name = expandname(ctx, raw);
 84+	free(raw);
 85+
 86 	if (n > 0 && ctx->call) {
 87 		size_t i;
 88 		int numeric;
 89 
 90 		numeric = 1;
 91-		for (i = 0; i < n; i++) {
 92-			if (!isdigit((unsigned char)s[i])) {
 93+		for (i = 0; name[i]; i++) {
 94+			if (!isdigit((unsigned char)name[i])) {
 95 				numeric = 0;
 96 				break;
 97 			}
 98 		}
 99 		if (numeric) {
100 			unsigned long idx;
101-			char *end, *tmp;
102+			char *end;
103 
104-			tmp = xstrndup(s, n);
105-			idx = strtoul(tmp, &end, 10);
106+			idx = strtoul(name, &end, 10);
107 			if (*end == 0 && idx < ctx->call->nargs) {
108 				val = expandstr(ctx, ctx->call->args[idx]);
109-				free(tmp);
110+				free(name);
111 				return val;
112 			}
113-			free(tmp);
114 		}
115 	}
116 
117-	name = xstrndup(s, n);
118 	v = findvar(ctx->env, name);
119 	free(name);
120 	val = v ? expandstr(ctx, v->val) : xstrdup("");
121@@ -272,9 +343,11 @@ expandsubstref(struct EvalCtx *ctx, const char *s, size_t colon, size_t eq, size
122 	struct Var *v;
123 
124 	{
125-		char *toraw, *fromraw;
126+		char *nameraw, *toraw, *fromraw;
127 
128-		name = xstrndup(s, colon);
129+		nameraw = xstrndup(s, colon);
130+		name = expandname(ctx, nameraw);
131+		free(nameraw);
132 		fromraw = xstrndup(s + colon + 1, eq - colon - 1);
133 		toraw = xstrndup(s + eq + 1, n - eq - 1);
134 		from = expandstr(ctx, fromraw);
135@@ -446,7 +519,7 @@ static char *
136 expandref(struct EvalCtx *ctx, const char *s, size_t n)
137 {
138 	size_t colon, eq;
139-	char *val, *unsup;
140+	char *val;
141 
142 	if (isplainvar(s, n))
143 		return expandvarref(ctx, s, n);
144@@ -455,10 +528,7 @@ expandref(struct EvalCtx *ctx, const char *s, size_t n)
145 	val = funcref(ctx, s, n);
146 	if (val)
147 		return val;
148-	unsup = xstrndup(s - 2, n + 3);
149-	evalerr(ctx, "variable reference or function", unsup);
150-	free(unsup);
151-	return xstrndup(s - 2, n + 3);
152+	return expandvarref(ctx, s, n);
153 }
154 
155 char *
+2, -0
1@@ -295,6 +295,8 @@ expandgraph(struct Graph *graph)
2 		memset(&side_effects, 0, sizeof(side_effects));
3 		memset(&new_recipes, 0, sizeof(new_recipes));
4 		ctx.env = &t->env;
5+		ctx.auto_target = t->name;
6+		ctx.auto_prereqs = &t->prereqs;
7 		ctx.avoid_io = 1;
8 		ctx.side_effects = &side_effects;
9 
+19, -9
 1@@ -413,6 +413,7 @@ expandsubgraphs(struct SubGraph *sg, struct SubGraphStack *stack)
 2 			struct Recipe *r;
 3 			struct SubGraph child;
 4 			struct StrList goals;
 5+			int rc;
 6 
 7 			r = &sg->graph.v[i].recipes.v[k];
 8 			if (!r->submake) {
 9@@ -428,14 +429,26 @@ expandsubgraphs(struct SubGraph *sg, struct SubGraphStack *stack)
10 			addwords(&child.assigns, &r->sm.assigns);
11 			addwords(&child.flags, &r->sm.flags);
12 			addwords(&child.goals, &r->sm.goals);
13-			/* remove the make invocation, we replace it with a graph*/
14-			removerecipe(&sg->graph.v[i].recipes, k);
15-			if (buildsubgraph0(&child, stack) < 0) {
16+			if (strcmp(child.cwd, sg->cwd) == 0 &&
17+			    (!child.makefile || strcmp(child.makefile, sg->makefile) == 0)) {
18+				freesubgraph(&child);
19+				k++;
20+				continue;
21+			}
22+			rc = buildsubgraph0(&child, stack);
23+			if (rc > 0) {
24+				freesubgraph(&child);
25+				k++;
26+				continue;
27+			}
28+			if (rc < 0) {
29 				freesubgraph(&child);
30 				freestrs(&prevgoals);
31 				free(tname);
32 				return -1;
33 			}
34+			/* remove the make invocation, we replace it with a graph*/
35+			removerecipe(&sg->graph.v[i].recipes, k);
36 			if (resolvegoals(&child, &goals) < 0) {
37 				freesubgraph(&child);
38 				freestrs(&prevgoals);
39@@ -483,29 +496,26 @@ buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack)
40 	path = 0;
41 	oldcwd = getcwddup();
42 	if (chdir(sg->cwd) != 0) {
43-		fprintf(stderr, "failed to chdir to %s\n", sg->cwd);
44 		free(oldcwd);
45-		return -1;
46+		return 1;
47 	}
48 	free(sg->cwd);
49 	sg->cwd = getcwddup();
50 	if (loadmakefile(sg->makefile, &path, &src) < 0) {
51-		fprintf(stderr, "could not read submakefile in %s\n", sg->cwd);
52 		chdir(oldcwd);
53 		free(oldcwd);
54-		return -1;
55+		return 1;
56 	}
57 	free(sg->makefile);
58 	sg->makefile = xstrdup(path);
59 	key = subgraphkey(sg);
60 	if (instack(stack, key)) {
61-		fprintf(stderr, "recursive subgraph cycle detected: %s\n", key);
62 		free(key);
63 		free(path);
64 		free(src);
65 		chdir(oldcwd);
66 		free(oldcwd);
67-		return -1;
68+		return 1;
69 	}
70 	pushstack(stack, key);
71 	free(key);
+3, -0
 1@@ -13,6 +13,9 @@ struct EvalCtx {
 2 	struct Env *env;
 3 	struct RuleSet *out;
 4 	struct CallFrame *call;
 5+	const char *auto_target;
 6+	const struct StrList *auto_prereqs;
 7+	const char *auto_stem;
 8 	int errors;
 9 	int avoid_io;
10 	struct StrList *side_effects;
+106, -9
  1@@ -46,7 +46,7 @@ parseerr(const struct PreLine *line, const char *msg, const char *detail)
  2 }
  3 
  4 static const char *const unsupported_kws[] = {
  5-    "define", "endef", "export", "unexport",
  6+    "export", "unexport",
  7     "undefine", "vpath", "private", "load", 0};
  8 
  9 static char *
 10@@ -705,6 +705,75 @@ blanknode(const struct PreLine *line)
 11 	return state;
 12 }
 13 
 14+static int
 15+parsedefine(const struct Pre *pre, size_t *i, struct Node *out)
 16+{
 17+	struct PreLine *line;
 18+	char *trim, *name;
 19+	size_t bodycap, bodylen;
 20+	int depth;
 21+	char *body;
 22+
 23+	line = &pre->v[*i];
 24+	trim = trimdup(line->text, strlen(line->text));
 25+	name = trimdup(trim + strlen("define"), strlen(trim + strlen("define")));
 26+	free(trim);
 27+
 28+	memset(out, 0, sizeof(*out));
 29+	out->kind = NODE_ASSIGN;
 30+	out->loc.line0 = line->line0;
 31+	out->loc.line1 = line->line1;
 32+	out->data.assign.lhs = name;
 33+	out->data.assign.op = ASSIGN_EQ;
 34+	out->data.assign.origin = ORIGIN_FILE;
 35+
 36+	bodycap = 64;
 37+	bodylen = 0;
 38+	body = xmalloc(bodycap);
 39+	body[0] = 0;
 40+	depth = 1;
 41+	(*i)++;
 42+	while (*i < pre->n) {
 43+		struct PreLine *cur;
 44+		char *curtrim;
 45+		size_t n;
 46+
 47+		cur = &pre->v[*i];
 48+		curtrim = trimdup(cur->text, strlen(cur->text));
 49+		if (haskw(curtrim, "define")) {
 50+			depth++;
 51+			free(curtrim);
 52+		} else if (haskw(curtrim, "endef")) {
 53+			depth--;
 54+			free(curtrim);
 55+			if (depth == 0) {
 56+				out->loc.line1 = cur->line1;
 57+				(*i)++;
 58+				out->data.assign.rhs = body;
 59+				return 0;
 60+			}
 61+		} else {
 62+			free(curtrim);
 63+		}
 64+
 65+		n = strlen(cur->text);
 66+		if (bodylen + n + 2 > bodycap) {
 67+			while (bodycap < bodylen + n + 2)
 68+				bodycap *= 2;
 69+			body = xrealloc(body, bodycap);
 70+		}
 71+		memcpy(body + bodylen, cur->text, n);
 72+		bodylen += n;
 73+		body[bodylen++] = '\n';
 74+		body[bodylen] = 0;
 75+		out->loc.line1 = cur->line1;
 76+		(*i)++;
 77+	}
 78+
 79+	free(body);
 80+	return -1;
 81+}
 82+
 83 static struct Node
 84 parseline(const struct PreLine *line)
 85 {
 86@@ -818,12 +887,12 @@ parseline(const struct PreLine *line)
 87 }
 88 
 89 static int
 90-parseblock(const struct Pre *pre, size_t *i, struct NodeList *out)
 91+parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node **last_rulep)
 92 {
 93 	struct Node state;
 94 	struct Node *last_rule;
 95 
 96-	last_rule = 0;
 97+	last_rule = last_rulep ? *last_rulep : 0;
 98 	while (*i < pre->n) {
 99 		struct PreLine *line;
100 		char *trim;
101@@ -843,12 +912,36 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out)
102 			free(trim);
103 			break;
104 		}
105+		if (haskw(trim, "define")) {
106+			free(trim);
107+			if (parsedefine(pre, i, &state) < 0)
108+				return -1;
109+			addnode(out, state);
110+			last_rule = 0;
111+			continue;
112+		}
113 		if (haskw(trim, "ifeq") || haskw(trim, "ifneq") ||
114 		    haskw(trim, "ifdef") || haskw(trim, "ifndef")) {
115+			size_t last_rule_idx;
116+			int last_rule_in_out;
117+
118+			last_rule_idx = 0;
119+			last_rule_in_out = 0;
120+			if (last_rule) {
121+				size_t lrk;
122+
123+				for (lrk = 0; lrk < out->n; lrk++) {
124+					if (&out->v[lrk] == last_rule) {
125+						last_rule_idx = lrk;
126+						last_rule_in_out = 1;
127+						break;
128+					}
129+				}
130+			}
131 			state = parsecond(line, trim);
132 			free(trim);
133 			(*i)++;
134-			if (parseblock(pre, i, &state.data.cond.thenpart) < 0)
135+			if (parseblock(pre, i, &state.data.cond.thenpart, &last_rule) < 0)
136 				return -1;
137 			if (*i < pre->n) {
138 				struct PreLine *endline;
139@@ -868,16 +961,17 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out)
140 						free(endline->text);
141 						endline->text = rep;
142 						free(endtrim);
143-						if (parseblock(pre, i, &state.data.cond.elsepart) < 0)
144+						if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule) < 0)
145 							return -1;
146 						state.loc.line1 = endline->line1;
147 						addnode(out, state);
148-						last_rule = 0;
149+						if (last_rule_in_out)
150+							last_rule = &out->v[last_rule_idx];
151 						continue;
152 					}
153 					free(endtrim);
154 					(*i)++;
155-					if (parseblock(pre, i, &state.data.cond.elsepart) < 0)
156+					if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule) < 0)
157 						return -1;
158 					if (*i >= pre->n)
159 						return -1;
160@@ -895,7 +989,8 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out)
161 				return -1;
162 			}
163 			addnode(out, state);
164-			last_rule = 0;
165+			if (last_rule_in_out)
166+				last_rule = &out->v[last_rule_idx];
167 			continue;
168 		}
169 		free(trim);
170@@ -908,6 +1003,8 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out)
171 			last_rule = 0;
172 		(*i)++;
173 	}
174+	if (last_rulep)
175+		*last_rulep = last_rule;
176 	return 0;
177 }
178 
179@@ -920,7 +1017,7 @@ buildast(const char *path, const struct Pre *pre, struct Ast *ast)
180 	memset(ast, 0, sizeof(*ast));
181 	parseerrs = 0;
182 	i = 0;
183-	if (parseblock(pre, &i, (struct NodeList *)ast) < 0)
184+	if (parseblock(pre, &i, (struct NodeList *)ast, 0) < 0)
185 		return -1;
186 	if (parseerrs)
187 		return -1;
+1, -0
1@@ -225,6 +225,7 @@ seedenv(struct Env *env)
2 	envsetvar(env, "FFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);
3 	envsetvar(env, "AR", xstrdup("ar"), 1, ORIGIN_DEFAULT);
4 	envsetvar(env, "ARFLAGS", xstrdup("-rv"), 1, ORIGIN_DEFAULT);
5+	envsetvar(env, "RANLIB", xstrdup("ranlib"), 1, ORIGIN_DEFAULT);
6 	envsetvar(env, "AS", xstrdup("as"), 1, ORIGIN_DEFAULT);
7 	envsetvar(env, "GET", xstrdup("get"), 1, ORIGIN_DEFAULT);
8 	envsetvar(env, "GFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);