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);