commit ba037f2

shrub  ·  2026-04-22 14:41:14 +0000 UTC
parent 0cd474b
handle join, value, export
10 files changed,  +222, -42
+73, -5
  1@@ -19,6 +19,69 @@ emitrule(FILE *fp, const char *cmd, int id)
  2 	fprintf(fp, "  command = %s\n\n", cmd);
  3 }
  4 
  5+static char *
  6+shellquote(const char *s)
  7+{
  8+	size_t i, len, cap;
  9+	char *out;
 10+
 11+	cap = strlen(s) * 4 + 3;
 12+	out = xmalloc(cap);
 13+	len = 0;
 14+	out[len++] = '\'';
 15+	for (i = 0; s[i]; i++) {
 16+		if (s[i] == '\'') {
 17+			memcpy(out + len, "'\\''", 4);
 18+			len += 4;
 19+		} else {
 20+			out[len++] = s[i];
 21+		}
 22+	}
 23+	out[len++] = '\'';
 24+	out[len] = 0;
 25+	return out;
 26+}
 27+
 28+static char *
 29+exportenv(const struct Target *t)
 30+{
 31+	size_t i, len, cap;
 32+	char *out;
 33+
 34+	cap = 16;
 35+	len = 0;
 36+	out = xmalloc(cap);
 37+	out[0] = 0;
 38+	for (i = 0; i < t->env.n; i++) {
 39+		char *quoted;
 40+		size_t need, nname, nquoted;
 41+
 42+		if (!t->env.v[i].exported)
 43+			continue;
 44+		quoted = shellquote(t->env.v[i].val);
 45+		nname = strlen(t->env.v[i].name);
 46+		nquoted = strlen(quoted);
 47+		need = len + nname + nquoted + 10;
 48+		if (need > cap) {
 49+			while (cap < need)
 50+				cap *= 2;
 51+			out = xrealloc(out, cap);
 52+		}
 53+		memcpy(out + len, "export ", 7);
 54+		len += 7;
 55+		memcpy(out + len, t->env.v[i].name, nname);
 56+		len += nname;
 57+		out[len++] = '=';
 58+		memcpy(out + len, quoted, nquoted);
 59+		len += nquoted;
 60+		out[len++] = ';';
 61+		out[len++] = ' ';
 62+		out[len] = 0;
 63+		free(quoted);
 64+	}
 65+	return out;
 66+}
 67+
 68 static char *
 69 joinrecipes(const struct Target *t)
 70 {
 71@@ -140,11 +203,12 @@ translateauto(const char *s, const struct Target *t)
 72 		}
 73 
 74 		if (!val) {
 75-			if (len + 2 > cap) {
 76-				cap *= 2;
 77+			if (len + 3 > cap) {
 78+				cap = len + (n - i) + 3;
 79 				out = xrealloc(out, cap);
 80 			}
 81-			out[len++] = s[i];
 82+			out[len++] = '$';
 83+			out[len++] = '$';
 84 			continue;
 85 		}
 86 
 87@@ -166,10 +230,14 @@ translateauto(const char *s, const struct Target *t)
 88 static char *
 89 rulecmd(const struct Target *t)
 90 {
 91-	char *cmd, *xlat;
 92+	char *cmd, *env, *full, *xlat;
 93 
 94 	cmd = joinrecipes(t);
 95-	xlat = translateauto(cmd, t);
 96+	env = exportenv(t);
 97+	full = cat3(env, cmd, "");
 98+	xlat = translateauto(full, t);
 99+	free(full);
100+	free(env);
101 	free(cmd);
102 	return xlat;
103 }
+2, -0
 1@@ -408,6 +408,7 @@ static const struct func funcs[] = {
 2     {"findstring", FNEXP2, {.f2 = fnfindstring}},
 3     {"addprefix", FNEXP2, {.f2 = fnaddprefix}},
 4     {"addsuffix", FNEXP2, {.f2 = fnaddsuffix}},
 5+    {"join", FNEXP2, {.f2 = fnjoin}},
 6     {"strip", FNEXP1, {.f1 = fnstrip}},
 7     {"subst", FNEXP3, {.f3 = fnsubst}},
 8     {"patsubst", FNEXP3, {.f3 = fnpatsubst}},
 9@@ -415,6 +416,7 @@ static const struct func funcs[] = {
10     {"call", FNCTX, {.ctx = fncall}},
11     {"foreach", FNCTX, {.ctx = fnforeach}},
12     {"eval", FNCTX, {.ctx = fneval}},
13+    {"value", FNCTX, {.ctx = fnvalue}},
14     {"words", FNEXP1, {.f1 = fnwords}},
15     {"word", FNEXP2, {.f2 = fnword}},
16     {"wordlist", FNEXP3, {.f3 = fnwordlist}},
+9, -5
 1@@ -51,6 +51,7 @@ copyenv(struct Env *dst, const struct Env *src)
 2 		dst->v[i].val = xstrdup(src->v[i].val);
 3 		dst->v[i].simple = src->v[i].simple;
 4 		dst->v[i].origin = src->v[i].origin;
 5+		dst->v[i].exported = src->v[i].exported;
 6 	}
 7 	dst->n = src->n;
 8 }
 9@@ -67,19 +68,19 @@ evalassign(struct EvalCtx *ctx, const struct AssignNode *in)
10 	o = in->origin ? in->origin : ORIGIN_FILE;
11 	switch (in->op) {
12 	case ASSIGN_EQ:
13-		envsetvar(env, in->lhs, xstrdup(in->rhs), 0, o);
14+		envsetvar(env, in->lhs, xstrdup(in->rhs), 0, o, in->exported);
15 		break;
16 	case ASSIGN_COLON_EQ:
17-		envsetvar(env, in->lhs, expandstr(ctx, in->rhs), 1, o);
18+		envsetvar(env, in->lhs, expandstr(ctx, in->rhs), 1, o, in->exported);
19 		break;
20 	case ASSIGN_QMARK_EQ:
21 		if (!findvar(env, in->lhs))
22-			envsetvar(env, in->lhs, xstrdup(in->rhs), 0, o);
23+			envsetvar(env, in->lhs, xstrdup(in->rhs), 0, o, in->exported);
24 		break;
25 	case ASSIGN_PLUS_EQ:
26 		v = findvar(env, in->lhs);
27 		if (!v) {
28-			envsetvar(env, in->lhs, xstrdup(in->rhs), 0, o);
29+			envsetvar(env, in->lhs, xstrdup(in->rhs), 0, o, in->exported);
30 			break;
31 		}
32 		if ((int)o < (int)v->origin)
33@@ -90,9 +91,11 @@ evalassign(struct EvalCtx *ctx, const struct AssignNode *in)
34 		free(v->val);
35 		v->val = joined;
36 		v->origin = o;
37+		if (in->exported)
38+			v->exported = 1;
39 		break;
40 	case ASSIGN_BANG_EQ:
41-		envsetvar(env, in->lhs, expandstr(ctx, in->rhs), 1, o);
42+		envsetvar(env, in->lhs, expandstr(ctx, in->rhs), 1, o, in->exported);
43 		break;
44 	}
45 }
46@@ -150,6 +153,7 @@ addrulesetassign(struct AssignNode **vec, size_t *n, const struct Node *src, str
47 	dst->lhs = xstrdup(src->data.assign.lhs);
48 	dst->op = src->data.assign.op;
49 	dst->origin = src->data.assign.origin;
50+	dst->exported = src->data.assign.exported;
51 	dst->tspec = src->data.assign.tspec;
52 	if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
53 		dst->rhs = expandstr(ctx, src->data.assign.rhs);
+80, -2
  1@@ -358,6 +358,65 @@ fnaddsuffix(const char *suffix, const char *names)
  2 	return out;
  3 }
  4 
  5+char *
  6+fnjoin(const char *list1, const char *list2)
  7+{
  8+	char *out;
  9+	size_t i1, j1, i2, j2, len, cap;
 10+
 11+	cap = strlen(list1) + strlen(list2) + 1;
 12+	if (cap < 64)
 13+		cap = 64;
 14+	len = 0;
 15+	out = xmalloc(cap);
 16+	out[0] = 0;
 17+
 18+	i1 = 0;
 19+	i2 = 0;
 20+	for (;;) {
 21+		size_t w1, w2, need;
 22+
 23+		while (list1[i1] && isspace((unsigned char)list1[i1]))
 24+			i1++;
 25+		while (list2[i2] && isspace((unsigned char)list2[i2]))
 26+			i2++;
 27+		if (!list1[i1] && !list2[i2])
 28+			break;
 29+
 30+		j1 = i1;
 31+		while (list1[j1] && !isspace((unsigned char)list1[j1]))
 32+			j1++;
 33+		j2 = i2;
 34+		while (list2[j2] && !isspace((unsigned char)list2[j2]))
 35+			j2++;
 36+		w1 = j1 - i1;
 37+		w2 = j2 - i2;
 38+
 39+		need = len + w1 + w2 + 2;
 40+		if (need > cap) {
 41+			while (cap < need)
 42+				cap *= 2;
 43+			out = xrealloc(out, cap);
 44+		}
 45+		if (len)
 46+			out[len++] = ' ';
 47+		if (w1) {
 48+			memcpy(out + len, list1 + i1, w1);
 49+			len += w1;
 50+		}
 51+		if (w2) {
 52+			memcpy(out + len, list2 + i2, w2);
 53+			len += w2;
 54+		}
 55+		out[len] = 0;
 56+
 57+		i1 = j1;
 58+		i2 = j2;
 59+	}
 60+
 61+	return out;
 62+}
 63+
 64 char *
 65 fnstrip(const char *text)
 66 {
 67@@ -562,6 +621,22 @@ fnorigin(struct EvalCtx *ctx, const char *args)
 68 	return xstrdup(origin);
 69 }
 70 
 71+char *
 72+fnvalue(struct EvalCtx *ctx, const char *args)
 73+{
 74+	char *name, *trimmed;
 75+	struct Var *v;
 76+
 77+	name = expandstr(ctx, args);
 78+	trimmed = trimspacesdup(name);
 79+	free(name);
 80+	v = findvar(ctx->env, trimmed);
 81+	free(trimmed);
 82+	if (!v)
 83+		return xstrdup("");
 84+	return xstrdup(v->val);
 85+}
 86+
 87 char *
 88 fnnotdir(const char *names)
 89 {
 90@@ -1104,6 +1179,7 @@ fnforeach(struct EvalCtx *ctx, const char *args)
 91 	struct Var *saved;
 92 	size_t argc, i, j, len, cap;
 93 	int saved_simple;
 94+	int saved_exported;
 95 	enum Origin saved_origin;
 96 	int had_saved;
 97 
 98@@ -1125,11 +1201,13 @@ fnforeach(struct EvalCtx *ctx, const char *args)
 99 		saved_val = xstrdup(saved->val);
100 		saved_simple = saved->simple;
101 		saved_origin = saved->origin;
102+		saved_exported = saved->exported;
103 	} else {
104 		saved_name = 0;
105 		saved_val = 0;
106 		saved_simple = 0;
107 		saved_origin = ORIGIN_FILE;
108+		saved_exported = 0;
109 	}
110 
111 	cap = strlen(list) + 1;
112@@ -1151,7 +1229,7 @@ fnforeach(struct EvalCtx *ctx, const char *args)
113 		while (list[j] && !isspace((unsigned char)list[j]))
114 			j++;
115 		word = xstrndup(list + i, j - i);
116-		envsetvar(ctx->env, name, word, 1, ORIGIN_OVERRIDE);
117+		envsetvar(ctx->env, name, word, 1, ORIGIN_OVERRIDE, 0);
118 		exp = expandstr(ctx, raw[2]);
119 		wn = strlen(exp);
120 		need = len + wn + 2;
121@@ -1170,7 +1248,7 @@ fnforeach(struct EvalCtx *ctx, const char *args)
122 	}
123 
124 	if (had_saved) {
125-		envsetvar(ctx->env, saved_name, saved_val, saved_simple, saved_origin);
126+		envsetvar(ctx->env, saved_name, saved_val, saved_simple, saved_origin, saved_exported);
127 		free(saved_name);
128 	} else {
129 		size_t k;
+0, -1
1@@ -336,7 +336,6 @@ expandgraph(struct Graph *graph)
2 		freestrs(&side_effects);
3 		freerecipes(&t->recipes);
4 		t->recipes = new_recipes;
5-		freeenv(&t->env);
6 	}
7 
8 	for (i = 0; i < graph->nsubs; i++) {
+4, -1
 1@@ -52,7 +52,8 @@ void freerecipes(struct RecipeList *list);
 2 void freesubmake(struct SubMake *sm);
 3 void copysubmake(struct SubMake *dst, const struct SubMake *src);
 4 int parsesubmake(struct SubMake *dst, const char *cmd);
 5-void envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin origin);
 6+void envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin origin,
 7+               int exported);
 8 struct Target *findtarget(struct Graph *graph, const char *name);
 9 const struct Target *findctarget(const struct Graph *graph, const char *name);
10 void initspecialtargets(struct SpecialTargets *targets);
11@@ -73,10 +74,12 @@ char *fnfilterout(const char *patterns, const char *text);
12 char *fnfindstring(const char *find, const char *in);
13 char *fnaddprefix(const char *prefix, const char *names);
14 char *fnaddsuffix(const char *suffix, const char *names);
15+char *fnjoin(const char *list1, const char *list2);
16 char *fnstrip(const char *text);
17 char *fnsort(const char *text);
18 char *fninfo(struct EvalCtx *ctx, const char *args);
19 char *fnorigin(struct EvalCtx *ctx, const char *args);
20+char *fnvalue(struct EvalCtx *ctx, const char *args);
21 char *fnnotdir(const char *names);
22 char *fndir(const char *names);
23 char *fnbasename(const char *names);
+22, -1
 1@@ -48,7 +48,7 @@ parseerr(const struct PreLine *line, const char *msg, const char *detail)
 2 }
 3 
 4 static const char *const unsupported_kws[] = {
 5-    "export", "unexport",
 6+    "unexport",
 7     "undefine", "vpath", "private", "load", 0};
 8 
 9 static char *
10@@ -651,6 +651,7 @@ parseassign(const struct PreLine *line, const char *s, size_t n, size_t base, st
11 	state.loc.line0 = line->line0;
12 	state.loc.line1 = line->line1;
13 	state.data.assign.op = as.op;
14+	state.data.assign.exported = 0;
15 	state.data.assign.tspec = tspec;
16 	state.data.assign.lhs = trimdup(s + base, as.pos - base);
17 	state.data.assign.rhs = trimdup(s + as.pos + as.len, n - as.pos - as.len);
18@@ -816,11 +817,13 @@ parseline(const struct PreLine *line)
19 	char *trim;
20 	int dcolon;
21 	int is_override;
22+	int is_export;
23 	size_t n;
24 	ptrdiff_t colon;
25 
26 	trim = trimdup(line->text, strlen(line->text));
27 	is_override = 0;
28+	is_export = 0;
29 	if (haskw(trim, "override")) {
30 		char *rest;
31 
32@@ -829,6 +832,14 @@ parseline(const struct PreLine *line)
33 		trim = rest;
34 		is_override = 1;
35 	}
36+	if (haskw(trim, "export")) {
37+		char *rest;
38+
39+		rest = trimdup(trim + 6, strlen(trim + 6));
40+		free(trim);
41+		trim = rest;
42+		is_export = 1;
43+	}
44 	n = strlen(trim);
45 
46 	if (!n) {
47@@ -894,6 +905,8 @@ parseline(const struct PreLine *line)
48 			state = parseassign(line, trim, n, base, as, 1, (size_t)colon);
49 			if (assign_override)
50 				state.data.assign.origin = ORIGIN_OVERRIDE;
51+			if (is_export)
52+				state.data.assign.exported = 1;
53 			free(trim);
54 			return state;
55 		}
56@@ -902,6 +915,14 @@ parseline(const struct PreLine *line)
57 		state = parseassign(line, trim, n, 0, as, 0, 0);
58 		if (is_override)
59 			state.data.assign.origin = ORIGIN_OVERRIDE;
60+		if (is_export)
61+			state.data.assign.exported = 1;
62+		free(trim);
63+		return state;
64+	}
65+	if (is_export) {
66+		parseerr(line, "directive", "export");
67+		state = blanknode(line);
68 		free(trim);
69 		return state;
70 	}
+26, -26
 1@@ -221,32 +221,32 @@ seedenv(struct Env *env, int posix, int envoverride)
 2 {
 3 	size_t i;
 4 
 5-	envsetvar(env, "CC", xstrdup("cc"), 1, ORIGIN_DEFAULT);
 6-	envsetvar(env, "CFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);
 7-	envsetvar(env, "CXX", xstrdup("c++"), 1, ORIGIN_DEFAULT);
 8-	envsetvar(env, "CPP", xstrdup("$(CC) -E"), 0, ORIGIN_DEFAULT);
 9-	envsetvar(env, "FC", xstrdup("fort77"), 1, ORIGIN_DEFAULT);
10-	envsetvar(env, "FFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);
11-	envsetvar(env, "AR", xstrdup("ar"), 1, ORIGIN_DEFAULT);
12-	envsetvar(env, "ARFLAGS", xstrdup("-rv"), 1, ORIGIN_DEFAULT);
13-	envsetvar(env, "RANLIB", xstrdup("ranlib"), 1, ORIGIN_DEFAULT);
14-	envsetvar(env, "AS", xstrdup("as"), 1, ORIGIN_DEFAULT);
15-	envsetvar(env, "GET", xstrdup("get"), 1, ORIGIN_DEFAULT);
16-	envsetvar(env, "GFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);
17-	envsetvar(env, "LD", xstrdup("ld"), 1, ORIGIN_DEFAULT);
18-	envsetvar(env, "LDFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);
19-	envsetvar(env, "LEX", xstrdup("lex"), 1, ORIGIN_DEFAULT);
20-	envsetvar(env, "LFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);
21-	envsetvar(env, "SCCSFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);
22-	envsetvar(env, "SCCSGETFLAGS", xstrdup("-s"), 1, ORIGIN_DEFAULT);
23-	envsetvar(env, "YACC", xstrdup("yacc"), 1, ORIGIN_DEFAULT);
24-	envsetvar(env, "YFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);
25-	envsetvar(env, "SHELL", xstrdup("/bin/sh"), 1, ORIGIN_DEFAULT);
26-	envsetvar(env, "MAKE", xstrdup("make"), 1, ORIGIN_DEFAULT);
27+	envsetvar(env, "CC", xstrdup("cc"), 1, ORIGIN_DEFAULT, 0);
28+	envsetvar(env, "CFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT, 0);
29+	envsetvar(env, "CXX", xstrdup("c++"), 1, ORIGIN_DEFAULT, 0);
30+	envsetvar(env, "CPP", xstrdup("$(CC) -E"), 0, ORIGIN_DEFAULT, 0);
31+	envsetvar(env, "FC", xstrdup("fort77"), 1, ORIGIN_DEFAULT, 0);
32+	envsetvar(env, "FFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT, 0);
33+	envsetvar(env, "AR", xstrdup("ar"), 1, ORIGIN_DEFAULT, 0);
34+	envsetvar(env, "ARFLAGS", xstrdup("-rv"), 1, ORIGIN_DEFAULT, 0);
35+	envsetvar(env, "RANLIB", xstrdup("ranlib"), 1, ORIGIN_DEFAULT, 0);
36+	envsetvar(env, "AS", xstrdup("as"), 1, ORIGIN_DEFAULT, 0);
37+	envsetvar(env, "GET", xstrdup("get"), 1, ORIGIN_DEFAULT, 0);
38+	envsetvar(env, "GFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT, 0);
39+	envsetvar(env, "LD", xstrdup("ld"), 1, ORIGIN_DEFAULT, 0);
40+	envsetvar(env, "LDFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT, 0);
41+	envsetvar(env, "LEX", xstrdup("lex"), 1, ORIGIN_DEFAULT, 0);
42+	envsetvar(env, "LFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT, 0);
43+	envsetvar(env, "SCCSFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT, 0);
44+	envsetvar(env, "SCCSGETFLAGS", xstrdup("-s"), 1, ORIGIN_DEFAULT, 0);
45+	envsetvar(env, "YACC", xstrdup("yacc"), 1, ORIGIN_DEFAULT, 0);
46+	envsetvar(env, "YFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT, 0);
47+	envsetvar(env, "SHELL", xstrdup("/bin/sh"), 1, ORIGIN_DEFAULT, 0);
48+	envsetvar(env, "MAKE", xstrdup("make"), 1, ORIGIN_DEFAULT, 0);
49 	if (posix) {
50-		envsetvar(env, "CC", xstrdup("c99"), 1, ORIGIN_DEFAULT);
51-		envsetvar(env, "CFLAGS", xstrdup("-O1"), 1, ORIGIN_DEFAULT);
52-		envsetvar(env, "FFLAGS", xstrdup("-O1"), 1, ORIGIN_DEFAULT);
53+		envsetvar(env, "CC", xstrdup("c99"), 1, ORIGIN_DEFAULT, 0);
54+		envsetvar(env, "CFLAGS", xstrdup("-O1"), 1, ORIGIN_DEFAULT, 0);
55+		envsetvar(env, "FFLAGS", xstrdup("-O1"), 1, ORIGIN_DEFAULT, 0);
56 	}
57 	for (i = 0; environ && environ[i]; i++) {
58 		const char *eq;
59@@ -257,7 +257,7 @@ seedenv(struct Env *env, int posix, int envoverride)
60 			continue;
61 		name = xstrndup(environ[i], (size_t)(eq - environ[i]));
62 		envsetvar(env, name, xstrdup(eq + 1), 1,
63-		          envoverride ? ORIGIN_ENV_OVERRIDE : ORIGIN_ENV);
64+		          envoverride ? ORIGIN_ENV_OVERRIDE : ORIGIN_ENV, 0);
65 		free(name);
66 	}
67 }
+2, -0
 1@@ -99,6 +99,7 @@ struct AssignNode {
 2 	char *rhs;
 3 	enum AssignOp op;
 4 	enum Origin origin;
 5+	int exported;
 6 	int tspec;
 7 	struct StrList targets;
 8 };
 9@@ -169,6 +170,7 @@ struct Var {
10 	char *val;
11 	int simple;
12 	enum Origin origin;
13+	int exported;
14 };
15 
16 struct Env {
+4, -1
 1@@ -453,7 +453,7 @@ freerecipes(struct RecipeList *list)
 2 }
 3 
 4 void
 5-envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin origin)
 6+envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin origin, int exported)
 7 {
 8 	struct Var *v;
 9 
10@@ -467,6 +467,8 @@ envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin
11 		v->val = val;
12 		v->simple = simple;
13 		v->origin = origin;
14+		if (exported)
15+			v->exported = 1;
16 		return;
17 	}
18 	env->v = xrealloc(env->v, (env->n + 1) * sizeof(env->v[0]));
19@@ -474,5 +476,6 @@ envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin
20 	env->v[env->n].val = val;
21 	env->v[env->n].simple = simple;
22 	env->v[env->n].origin = origin;
23+	env->v[env->n].exported = exported;
24 	env->n++;
25 }