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 }