commit b1a8a94
shrub
·
2026-04-08 19:43:09 +0000 UTC
parent 59a6db8
add targ-specific assingments and cli ops to dump ast/graph
5 files changed,
+434,
-82
M
Makefile
+1,
-1
1@@ -13,7 +13,7 @@ all: $(BIN)
2 $(BIN): $(OBJS)
3 $(CC) $(LDFLAGS) -o $(BIN) $(OBJS) $(LDLIBS)
4
5-%.o: %.c
6+.c.o:
7 $(CC) $(CFLAGS) -c -o $@ $<
8
9 fmt:
+198,
-1
1@@ -5,6 +5,12 @@
2 #include <stdlib.h>
3 #include <string.h>
4
5+static void
6+usage(FILE *fp, const char *argv0)
7+{
8+ fprintf(fp, "usage: %s [-ag] [makefile]\n", argv0);
9+}
10+
11 static char *
12 readfile(const char *path)
13 {
14@@ -39,16 +45,203 @@ readfile(const char *path)
15 return buf;
16 }
17
18+static const char *
19+assignopname(enum AssignOp op)
20+{
21+ switch (op) {
22+ case ASSIGN_EQ:
23+ return "=";
24+ case ASSIGN_PLUS_EQ:
25+ return "+=";
26+ case ASSIGN_COLON_EQ:
27+ return ":=";
28+ case ASSIGN_QMARK_EQ:
29+ return "?=";
30+ case ASSIGN_BANG_EQ:
31+ return "!=";
32+ }
33+ return "?";
34+}
35+
36+static const char *
37+condkindname(enum CondKind kind)
38+{
39+ switch (kind) {
40+ case COND_IFEQ:
41+ return "ifeq";
42+ case COND_IFNEQ:
43+ return "ifneq";
44+ case COND_IFDEF:
45+ return "ifdef";
46+ case COND_IFNDEF:
47+ return "ifndef";
48+ case COND_ELSE:
49+ return "else";
50+ case COND_ENDIF:
51+ return "endif";
52+ }
53+ return "?";
54+}
55+
56+static void
57+printindent(int depth)
58+{
59+ int i;
60+
61+ for (i = 0; i < depth; i++)
62+ fputs(" ", stdout);
63+}
64+
65+static void
66+dumpstrlist(const char *label, const struct StrList *list, int depth)
67+{
68+ size_t i;
69+
70+ printindent(depth);
71+ printf("%s (%zu):", label, list->n);
72+ for (i = 0; i < list->n; i++)
73+ printf(" %s", list->v[i]);
74+ putchar('\n');
75+}
76+
77+static void
78+dumprecipes(const struct RecipeList *list, int depth)
79+{
80+ size_t i;
81+
82+ printindent(depth);
83+ printf("recipes (%zu):\n", list->n);
84+ for (i = 0; i < list->n; i++) {
85+ printindent(depth + 1);
86+ printf("%s\n", list->v[i]);
87+ }
88+}
89+
90+static void dumpnodes(const struct Node *nodes, size_t n, int depth);
91+
92+static void
93+dumpnode(const struct Node *node, int depth)
94+{
95+ printindent(depth);
96+ printf("%d-%d ", node->loc.line0, node->loc.line1);
97+ switch (node->kind) {
98+ case NODE_BLANK:
99+ printf("blank\n");
100+ break;
101+ case NODE_COMMENT:
102+ printf("comment %s\n", node->data.raw.text);
103+ break;
104+ case NODE_RAW:
105+ printf("raw %s\n", node->data.raw.text);
106+ break;
107+ case NODE_INCLUDE:
108+ printf("include%s %s\n",
109+ node->data.include.optional ? " optional" : "",
110+ node->data.include.path);
111+ break;
112+ case NODE_ASSIGN:
113+ printf("assign %s %s %s\n",
114+ node->data.assign.lhs,
115+ assignopname(node->data.assign.op),
116+ node->data.assign.rhs);
117+ if (node->data.assign.tspec)
118+ dumpstrlist("targets", &node->data.assign.targets, depth + 1);
119+ break;
120+ case NODE_RULE:
121+ printf("rule\n");
122+ dumpstrlist("targets", &node->data.rule.targets, depth + 1);
123+ dumpstrlist("prereqs", &node->data.rule.prereqs, depth + 1);
124+ dumpstrlist("order-only", &node->data.rule.order_only, depth + 1);
125+ dumprecipes(&node->data.rule.recipes, depth + 1);
126+ break;
127+ case NODE_COND:
128+ printf("cond %s", condkindname(node->data.cond.kind));
129+ if (node->data.cond.arg1)
130+ printf(" arg1=%s", node->data.cond.arg1);
131+ if (node->data.cond.arg2)
132+ printf(" arg2=%s", node->data.cond.arg2);
133+ putchar('\n');
134+ printindent(depth + 1);
135+ printf("then:\n");
136+ dumpnodes(node->data.cond.thenpart.v, node->data.cond.thenpart.n, depth + 2);
137+ if (node->data.cond.elsepart.n) {
138+ printindent(depth + 1);
139+ printf("else:\n");
140+ dumpnodes(node->data.cond.elsepart.v, node->data.cond.elsepart.n, depth + 2);
141+ }
142+ break;
143+ }
144+}
145+
146+static void
147+dumpnodes(const struct Node *nodes, size_t n, int depth)
148+{
149+ size_t i;
150+
151+ for (i = 0; i < n; i++)
152+ dumpnode(&nodes[i], depth);
153+}
154+
155+static void
156+dumpast(const struct Ast *ast)
157+{
158+ printf("ast (%zu nodes)\n", ast->n);
159+ dumpnodes(ast->v, ast->n, 1);
160+}
161+
162+static void
163+dumpgraph(const struct Graph *graph)
164+{
165+ size_t i;
166+
167+ printf("graph (%zu targets)\n", graph->n);
168+ for (i = 0; i < graph->n; i++) {
169+ const struct Target *t = &graph->v[i];
170+
171+ printindent(1);
172+ printf("target %s\n", t->name);
173+ dumpstrlist("prereqs", &t->prereqs, 2);
174+ dumpstrlist("order-only", &t->order_only, 2);
175+ dumprecipes(&t->recipes, 2);
176+ }
177+}
178+
179 int
180 main(int argc, char **argv)
181 {
182 const char *path;
183 char *src;
184+ int dump_ast;
185+ int dump_graph;
186+ int have_path;
187+ int i;
188 struct Ast ast;
189 struct Ast out;
190 struct Graph graph;
191
192- path = argc > 1 ? argv[1] : "Makefile";
193+ path = "Makefile";
194+ dump_ast = 0;
195+ dump_graph = 0;
196+ have_path = 0;
197+ for (i = 1; i < argc; i++) {
198+ if (strcmp(argv[i], "-a") == 0) {
199+ dump_ast = 1;
200+ } else if (strcmp(argv[i], "-g") == 0) {
201+ dump_graph = 1;
202+ } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
203+ usage(stdout, argv[0]);
204+ return 0;
205+ } else if (argv[i][0] == '-') {
206+ usage(stderr, argv[0]);
207+ return 1;
208+ } else if (have_path) {
209+ usage(stderr, argv[0]);
210+ return 1;
211+ } else {
212+ path = argv[i];
213+ have_path = 1;
214+ }
215+ }
216 src = readfile(path);
217 if (!src) {
218 fprintf(stderr, "could not read %s\n", path);
219@@ -65,6 +258,8 @@ main(int argc, char **argv)
220 free(src);
221 return 1;
222 }
223+ if (dump_ast)
224+ dumpast(&out);
225 if (buildgraph(&out, &graph) < 0) {
226 fprintf(stderr, "graph error in %s\n", path);
227 freeast(&out);
228@@ -72,6 +267,8 @@ main(int argc, char **argv)
229 free(src);
230 return 1;
231 }
232+ if (dump_graph)
233+ dumpgraph(&graph);
234 if (genninja(&graph, "build.ninja") < 0) {
235 fprintf(stderr, "ninja generation error\n");
236 freegraph(&graph);
+36,
-24
1@@ -17,17 +17,6 @@
2 * right now only a small subset of gmake syntax is handled.
3 */
4
5-struct Var {
6- char *name;
7- char *val;
8- int simple;
9-};
10-
11-struct Env {
12- struct Var *v;
13- size_t n;
14-};
15-
16 static int evalerrs;
17
18 static void
19@@ -40,7 +29,7 @@ evalerr(const char *msg, const char *detail)
20 evalerrs++;
21 }
22
23-static struct Var *
24+struct Var *
25 findvar(struct Env *env, const char *name)
26 {
27 size_t i;
28@@ -177,7 +166,7 @@ issubstref(const char *s, size_t n, size_t *colon, size_t *eq)
29 return 0;
30 }
31
32-static char *
33+char *
34 expandstr(struct Env *env, const char *s)
35 {
36 size_t i, j, k, n, cap, len, inner, start, colon, eq;
37@@ -190,6 +179,16 @@ expandstr(struct Env *env, const char *s)
38 len = 0;
39 out = xmalloc(cap);
40 for (i = 0; i < n; i++) {
41+ if (s[i] == '$' && i + 1 < n && s[i + 1] == '$') {
42+ if (len + 3 > cap) {
43+ cap *= 2;
44+ out = xrealloc(out, cap);
45+ }
46+ out[len++] = '$';
47+ out[len++] = '$';
48+ i++;
49+ continue;
50+ }
51 if (s[i] != '$' || i + 1 >= n || (s[i + 1] != '(' && s[i + 1] != '{')) {
52 if (len + 2 > cap) {
53 cap *= 2;
54@@ -318,7 +317,7 @@ setvar(struct Env *env, const char *name, char *val, int simple)
55 env->n++;
56 }
57
58-static void
59+void
60 seedenv(struct Env *env)
61 {
62 setvar(env, "CC", xstrdup("cc"), 1);
63@@ -333,7 +332,7 @@ seedenv(struct Env *env)
64 setvar(env, "SHELL", xstrdup("/bin/sh"), 1);
65 }
66
67-static void
68+void
69 freeenv(struct Env *env)
70 {
71 size_t i;
72@@ -347,16 +346,28 @@ freeenv(struct Env *env)
73 env->n = 0;
74 }
75
76-static void
77+void
78+copyenv(struct Env *dst, const struct Env *src)
79+{
80+ size_t i;
81+
82+ memset(dst, 0, sizeof(*dst));
83+ if (src->n)
84+ dst->v = xrealloc(0, src->n * sizeof(dst->v[0]));
85+ for (i = 0; i < src->n; i++) {
86+ dst->v[i].name = xstrdup(src->v[i].name);
87+ dst->v[i].val = xstrdup(src->v[i].val);
88+ dst->v[i].simple = src->v[i].simple;
89+ }
90+ dst->n = src->n;
91+}
92+
93+void
94 evalassign(struct Env *env, const struct AssignNode *in)
95 {
96 struct Var *v;
97 char *rhs, *joined;
98
99- if (in->tspec) {
100- evalerr("target-specific assignment", in->lhs);
101- return;
102- }
103 switch (in->op) {
104 case ASSIGN_EQ:
105 setvar(env, in->lhs, xstrdup(in->rhs), 0);
106@@ -427,14 +438,14 @@ copywords(struct StrList *out, const struct StrList *in, struct Env *env)
107 }
108
109 static void
110-copyrecipes(struct RecipeList *out, const struct RecipeList *in, struct Env *env)
111+copyrecipes(struct RecipeList *out, const struct RecipeList *in)
112 {
113 size_t i;
114
115 memset(out, 0, sizeof(*out));
116 for (i = 0; i < in->n; i++) {
117 out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
118- out->v[out->n++] = expandstr(env, in->v[i]);
119+ out->v[out->n++] = xstrdup(in->v[i]);
120 }
121 }
122
123@@ -481,13 +492,14 @@ evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
124 else
125 node.data.assign.rhs = xstrdup(src->data.assign.rhs);
126 copywords(&node.data.assign.targets, &src->data.assign.targets, env);
127- evalassign(env, &src->data.assign);
128+ if (!src->data.assign.tspec)
129+ evalassign(env, &src->data.assign);
130 break;
131 case NODE_RULE:
132 copywords(&node.data.rule.targets, &src->data.rule.targets, env);
133 copywords(&node.data.rule.prereqs, &src->data.rule.prereqs, env);
134 copywords(&node.data.rule.order_only, &src->data.rule.order_only, env);
135- copyrecipes(&node.data.rule.recipes, &src->data.rule.recipes, env);
136+ copyrecipes(&node.data.rule.recipes, &src->data.rule.recipes);
137 break;
138 }
139 addnode(out, node);
+181,
-56
1@@ -40,35 +40,6 @@ addrecipes(struct RecipeList *dest, const struct RecipeList *src)
2 }
3 }
4
5-static void
6-addrule(struct Graph *graph, const char *name, const struct RuleNode *rule)
7-{
8- size_t i;
9- struct Target *t;
10-
11- t = findtarget(graph, name);
12- if (!t) {
13- graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
14- t = &graph->v[graph->n++];
15- memset(t, 0, sizeof(*t));
16- t->name = xstrdup(name);
17- }
18- addwords(&t->prereqs, &rule->prereqs);
19- addwords(&t->order_only, &rule->order_only);
20- addrecipes(&t->recipes, &rule->recipes);
21-
22- for (i = 0; i < rule->prereqs.n; i++) {
23- if (strchr(rule->prereqs.v[i], '%'))
24- continue;
25- if (!findtarget(graph, rule->prereqs.v[i])) {
26- graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
27- t = &graph->v[graph->n++];
28- memset(t, 0, sizeof(*t));
29- t->name = xstrdup(rule->prereqs.v[i]);
30- }
31- }
32-}
33-
34 struct Pattern {
35 char *target;
36 struct StrList prereqs;
37@@ -76,30 +47,29 @@ struct Pattern {
38 struct RecipeList recipes;
39 };
40
41+struct TAssign {
42+ struct StrList targets;
43+ char *lhs;
44+ char *rhs;
45+ enum AssignOp op;
46+};
47+
48 struct GraphState {
49 struct Graph *graph;
50+ struct Env env;
51+ const struct RuleNode **rules;
52+ size_t nrules;
53 struct Pattern *pats;
54 size_t npats;
55+ struct TAssign *tas;
56+ size_t ntas;
57 };
58
59 static void
60-addpats(struct GraphState *gs, const struct RuleNode *rule)
61+collectrule(struct GraphState *gs, const struct RuleNode *rule)
62 {
63- size_t i;
64-
65- for (i = 0; i < rule->targets.n; i++) {
66- if (strchr(rule->targets.v[i], '%')) {
67- gs->pats = xrealloc(gs->pats, (gs->npats + 1) * sizeof(gs->pats[0]));
68- struct Pattern *p = &gs->pats[gs->npats++];
69- memset(p, 0, sizeof(*p));
70- p->target = xstrdup(rule->targets.v[i]);
71- addwords(&p->prereqs, &rule->prereqs);
72- addwords(&p->order_only, &rule->order_only);
73- addrecipes(&p->recipes, &rule->recipes);
74- } else {
75- addrule(gs->graph, rule->targets.v[i], rule);
76- }
77- }
78+ gs->rules = xrealloc(gs->rules, (gs->nrules + 1) * sizeof(gs->rules[0]));
79+ gs->rules[gs->nrules++] = rule;
80 }
81
82 static char *
83@@ -125,6 +95,20 @@ matchpat(const char *pat, const char *name)
84 return xstrndup(name + pre, n - pre - suf);
85 }
86
87+static int
88+targetmatches(const char *pat, const char *name)
89+{
90+ char *stem;
91+
92+ if (!strchr(pat, '%'))
93+ return strcmp(pat, name) == 0;
94+ stem = matchpat(pat, name);
95+ if (!stem)
96+ return 0;
97+ free(stem);
98+ return 1;
99+}
100+
101 static char *
102 applystem(const char *s, const char *stem)
103 {
104@@ -162,14 +146,28 @@ joinprereqs(const struct Target *t)
105 return joined;
106 }
107
108+static void
109+addexprecipes(struct RecipeList *dest, const struct RecipeList *src, struct Env *env)
110+{
111+ size_t i;
112+
113+ for (i = 0; i < src->n; i++) {
114+ char *exp;
115+
116+ exp = expandstr(env, src->v[i]);
117+ dest->v = xrealloc(dest->v, (dest->n + 1) * sizeof(dest->v[0]));
118+ dest->v[dest->n++] = exp;
119+ }
120+}
121+
122 /*
123- * expand gnu make automatic variables
124+ * expand gnu make automatic variables
125 * $@ target
126 * $< first prereq
127- * $^ all prereqs
128+ * $^ all prereqs
129 * $+ all prereqs
130- * $? all prereqs, let ninja decide
131- * $* stem
132+ * $? all prereqs, let ninja decide
133+ * $* stem
134 * $$ literal $
135 */
136 static char *
137@@ -237,12 +235,113 @@ expandauto(const char *s, const struct Target *t, const char *stem)
138 out[len] = 0;
139 return out;
140 }
141+/* we apply target assignments from the graphstate (gs.tas) to those specific
142+ * targets, for some target like
143+ *
144+ * binary : LDFLAGS += -static
145+ */
146+static void
147+applytassigns(struct GraphState *gs, struct Env *env, const char *name)
148+{
149+ size_t i, j;
150+
151+ for (i = 0; i < gs->ntas; i++) {
152+ struct AssignNode in;
153
154+ for (j = 0; j < gs->tas[i].targets.n; j++) {
155+ if (!targetmatches(gs->tas[i].targets.v[j], name))
156+ continue;
157+ memset(&in, 0, sizeof(in));
158+ in.lhs = gs->tas[i].lhs;
159+ in.rhs = gs->tas[i].rhs;
160+ in.op = gs->tas[i].op;
161+ evalassign(env, &in);
162+ }
163+ }
164+}
165+
166+static void
167+targetenv(struct GraphState *gs, struct Env *env, const char *name)
168+{
169+ copyenv(env, &gs->env);
170+ applytassigns(gs, env, name);
171+}
172+
173+static void
174+addrule(struct GraphState *gs, const char *name, const struct RuleNode *rule)
175+{
176+ size_t i;
177+ struct Target *t;
178+ struct Env env;
179+
180+ t = findtarget(gs->graph, name);
181+ if (!t) {
182+ gs->graph->v = xrealloc(gs->graph->v, (gs->graph->n + 1) * sizeof(gs->graph->v[0]));
183+ t = &gs->graph->v[gs->graph->n++];
184+ memset(t, 0, sizeof(*t));
185+ t->name = xstrdup(name);
186+ }
187+ addwords(&t->prereqs, &rule->prereqs);
188+ addwords(&t->order_only, &rule->order_only);
189+ targetenv(gs, &env, name);
190+ addexprecipes(&t->recipes, &rule->recipes, &env);
191+ freeenv(&env);
192+
193+ for (i = 0; i < rule->prereqs.n; i++) {
194+ if (strchr(rule->prereqs.v[i], '%'))
195+ continue;
196+ if (!findtarget(gs->graph, rule->prereqs.v[i])) {
197+ gs->graph->v = xrealloc(gs->graph->v, (gs->graph->n + 1) * sizeof(gs->graph->v[0]));
198+ t = &gs->graph->v[gs->graph->n++];
199+ memset(t, 0, sizeof(*t));
200+ t->name = xstrdup(rule->prereqs.v[i]);
201+ }
202+ }
203+}
204+/* if the target is a pattern rule, store it in the graphstate
205+ * instead of making a target immediately */
206 static void
207-instantiate(struct Target *t, struct Pattern *p, const char *stem)
208+addpattern(struct GraphState *gs, const struct RuleNode *rule)
209 {
210 size_t i;
211
212+ for (i = 0; i < rule->targets.n; i++) {
213+ if (strchr(rule->targets.v[i], '%')) {
214+ struct Pattern *p;
215+
216+ gs->pats = xrealloc(gs->pats, (gs->npats + 1) * sizeof(gs->pats[0]));
217+ p = &gs->pats[gs->npats++];
218+ memset(p, 0, sizeof(*p));
219+ p->target = xstrdup(rule->targets.v[i]);
220+ addwords(&p->prereqs, &rule->prereqs);
221+ addwords(&p->order_only, &rule->order_only);
222+ addrecipes(&p->recipes, &rule->recipes);
223+ } else {
224+ addrule(gs, rule->targets.v[i], rule);
225+ }
226+ }
227+}
228+
229+static void
230+addtassign(struct GraphState *gs, const struct AssignNode *assign)
231+{
232+ struct TAssign *ta;
233+
234+ gs->tas = xrealloc(gs->tas, (gs->ntas + 1) * sizeof(gs->tas[0]));
235+ ta = &gs->tas[gs->ntas++];
236+ memset(ta, 0, sizeof(*ta));
237+ addwords(&ta->targets, &assign->targets);
238+ ta->lhs = xstrdup(assign->lhs);
239+ ta->rhs = xstrdup(assign->rhs);
240+ ta->op = assign->op;
241+}
242+
243+static void
244+instantiate(struct GraphState *gs, struct Target *t, struct Pattern *p, const char *stem)
245+{
246+ size_t i;
247+ struct Env env;
248+
249 for (i = 0; i < p->prereqs.n; i++) {
250 char *s = applystem(p->prereqs.v[i], stem);
251 t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
252@@ -253,13 +352,19 @@ instantiate(struct Target *t, struct Pattern *p, const char *stem)
253 t->order_only.v = xrealloc(t->order_only.v, (t->order_only.n + 1) * sizeof(t->order_only.v[0]));
254 t->order_only.v[t->order_only.n++] = s;
255 }
256+ targetenv(gs, &env, t->name);
257 for (i = 0; i < p->recipes.n; i++) {
258- char *s = applystem(p->recipes.v[i], stem);
259- char *exp = expandauto(s, t, stem);
260+ char *s, *vars, *exp;
261+
262+ s = applystem(p->recipes.v[i], stem);
263+ vars = expandstr(&env, s);
264+ exp = expandauto(vars, t, stem);
265+ free(vars);
266 free(s);
267 t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
268 t->recipes.v[t->recipes.n++] = exp;
269 }
270+ freeenv(&env);
271 }
272
273 int
274@@ -271,12 +376,22 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
275 memset(graph, 0, sizeof(*graph));
276 memset(&gs, 0, sizeof(gs));
277 gs.graph = graph;
278+ seedenv(&gs.env);
279
280 for (i = 0; i < ast->n; i++) {
281- if (ast->v[i].kind == NODE_RULE)
282- addpats(&gs, &ast->v[i].data.rule);
283+ if (ast->v[i].kind == NODE_ASSIGN) {
284+ if (ast->v[i].data.assign.tspec)
285+ addtassign(&gs, &ast->v[i].data.assign);
286+ else
287+ evalassign(&gs.env, &ast->v[i].data.assign);
288+ } else if (ast->v[i].kind == NODE_RULE) {
289+ collectrule(&gs, &ast->v[i].data.rule);
290+ }
291 }
292
293+ for (i = 0; i < gs.nrules; i++)
294+ addpattern(&gs, gs.rules[i]);
295+
296 start = 0;
297 while (start < graph->n) {
298 size_t current_n = graph->n;
299@@ -286,7 +401,7 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
300 for (j = 0; j < gs.npats; j++) {
301 char *stem = matchpat(gs.pats[j].target, t->name);
302 if (stem) {
303- instantiate(t, &gs.pats[j], stem);
304+ instantiate(&gs, t, &gs.pats[j], stem);
305 free(stem);
306 break;
307 }
308@@ -294,8 +409,10 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
309 }
310 for (j = 0; j < t->prereqs.n; j++) {
311 if (!findtarget(graph, t->prereqs.v[j])) {
312+ struct Target *nt;
313+
314 graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
315- struct Target *nt = &graph->v[graph->n++];
316+ nt = &graph->v[graph->n++];
317 memset(nt, 0, sizeof(*nt));
318 nt->name = xstrdup(t->prereqs.v[j]);
319 }
320@@ -321,7 +438,15 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
321 freestrs(&gs.pats[i].order_only);
322 freerecipes(&gs.pats[i].recipes);
323 }
324+ for (i = 0; i < gs.ntas; i++) {
325+ freestrs(&gs.tas[i].targets);
326+ free(gs.tas[i].lhs);
327+ free(gs.tas[i].rhs);
328+ }
329+ free(gs.tas);
330 free(gs.pats);
331+ free(gs.rules);
332+ freeenv(&gs.env);
333
334 return 0;
335 }
+18,
-0
1@@ -3,6 +3,17 @@
2
3 #include "shinobi.h"
4
5+struct Var {
6+ char *name;
7+ char *val;
8+ int simple;
9+};
10+
11+struct Env {
12+ struct Var *v;
13+ size_t n;
14+};
15+
16 void *xmalloc(size_t n);
17 void *xrealloc(void *p, size_t n);
18 char *xstrndup(const char *s, size_t n);
19@@ -13,6 +24,13 @@ char *cat3(const char *a, const char *b, const char *c);
20 void freestrs(struct StrList *list);
21 void freerecipes(struct RecipeList *list);
22
23+struct Var *findvar(struct Env *env, const char *name);
24+char *expandstr(struct Env *env, const char *s);
25+void seedenv(struct Env *env);
26+void freeenv(struct Env *env);
27+void copyenv(struct Env *dst, const struct Env *src);
28+void evalassign(struct Env *env, const struct AssignNode *in);
29+
30 char *fnwildcard(const char *patterns);
31
32 #endif