commit 427bbfc
shrub
·
2026-04-16 16:40:49 +0000 UTC
parent d34ce1a
make eval produce a ruleset instead of another ast to simplify graph construction
6 files changed,
+150,
-212
+2,
-1
1@@ -1,3 +1,4 @@
2+build.dot
3 config.mak
4 test-musl.mk
5 *.o
6@@ -5,4 +6,4 @@ shin
7 tests/work
8 build.ninja
9 .ninja_log
10-.ninja_deps
11+.ninja_deps
+0,
-58
1@@ -1,58 +0,0 @@
2-digraph shin {
3- rankdir=LR;
4- node [fontname="monospace"];
5- n0 [label="all", style=dashed];
6- n1 [label="shin", shape=box];
7- n2 [label="cli/main.o", shape=box];
8- n3 [label="src/parse.o", shape=box];
9- n4 [label="src/eval/eval.o", shape=box];
10- n5 [label="src/eval/dollar.o", shape=box];
11- n6 [label="src/graph.o", shape=box];
12- n7 [label="src/posix.o", shape=box];
13- n8 [label="src/gnu/pattern.o", shape=box];
14- n9 [label="backends/ninja.o", shape=box];
15- n10 [label="backends/graphviz.o", shape=box];
16- n11 [label="src/util.o", shape=box];
17- n12 [label="src/gnu/functions.o", shape=box];
18- n13 [label="fmt", shape=box];
19- n14 [label="test", shape=box];
20- n15 [label="install", shape=box];
21- n16 [label="clean", shape=box];
22- n17 [label="cli/main.c"];
23- n18 [label="src/parse.c"];
24- n19 [label="src/eval/eval.c"];
25- n20 [label="src/eval/dollar.c"];
26- n21 [label="src/graph.c"];
27- n22 [label="src/posix.c"];
28- n23 [label="src/gnu/pattern.c"];
29- n24 [label="backends/ninja.c"];
30- n25 [label="backends/graphviz.c"];
31- n26 [label="src/util.c"];
32- n27 [label="src/gnu/functions.c"];
33-
34- n1 -> n0;
35- n2 -> n1;
36- n3 -> n1;
37- n4 -> n1;
38- n5 -> n1;
39- n6 -> n1;
40- n7 -> n1;
41- n8 -> n1;
42- n9 -> n1;
43- n10 -> n1;
44- n11 -> n1;
45- n12 -> n1;
46- n17 -> n2;
47- n18 -> n3;
48- n19 -> n4;
49- n20 -> n5;
50- n21 -> n6;
51- n22 -> n7;
52- n23 -> n8;
53- n24 -> n9;
54- n25 -> n10;
55- n26 -> n11;
56- n27 -> n12;
57- n1 -> n14;
58- n1 -> n15;
59-}
+30,
-87
1@@ -111,26 +111,6 @@ assignopname(enum AssignOp op)
2 return "?";
3 }
4
5-static const char *
6-condkindname(enum CondKind kind)
7-{
8- switch (kind) {
9- case COND_IFEQ:
10- return "ifeq";
11- case COND_IFNEQ:
12- return "ifneq";
13- case COND_IFDEF:
14- return "ifdef";
15- case COND_IFNDEF:
16- return "ifndef";
17- case COND_ELSE:
18- return "else";
19- case COND_ENDIF:
20- return "endif";
21- }
22- return "?";
23-}
24-
25 static void
26 printindent(int depth)
27 {
28@@ -165,76 +145,39 @@ dumprecipes(const struct RecipeList *list, int depth)
29 }
30 }
31
32-static void dumpnodes(const struct Node *nodes, size_t n, int depth);
33-
34 static void
35-dumpnode(const struct Node *node, int depth)
36+dumpassign(const struct AssignNode *assign, int depth)
37 {
38 printindent(depth);
39- printf("%d-%d ", node->loc.line0, node->loc.line1);
40- switch (node->kind) {
41- case NODE_BLANK:
42- printf("blank\n");
43- break;
44- case NODE_COMMENT:
45- printf("comment %s\n", node->data.raw.text);
46- break;
47- case NODE_RAW:
48- printf("raw %s\n", node->data.raw.text);
49- break;
50- case NODE_INCLUDE:
51- printf("include%s %s\n",
52- node->data.include.optional ? " optional" : "",
53- node->data.include.path);
54- break;
55- case NODE_ASSIGN:
56- printf("assign %s %s %s\n",
57- node->data.assign.lhs,
58- assignopname(node->data.assign.op),
59- node->data.assign.rhs);
60- if (node->data.assign.tspec)
61- dumpstrlist("targets", &node->data.assign.targets, depth + 1);
62- break;
63- case NODE_RULE:
64- printf("rule\n");
65- dumpstrlist("targets", &node->data.rule.targets, depth + 1);
66- dumpstrlist("prereqs", &node->data.rule.prereqs, depth + 1);
67- dumpstrlist("order-only", &node->data.rule.order_only, depth + 1);
68- dumprecipes(&node->data.rule.recipes, depth + 1);
69- break;
70- case NODE_COND:
71- printf("cond %s", condkindname(node->data.cond.kind));
72- if (node->data.cond.arg1)
73- printf(" arg1=%s", node->data.cond.arg1);
74- if (node->data.cond.arg2)
75- printf(" arg2=%s", node->data.cond.arg2);
76- putchar('\n');
77- printindent(depth + 1);
78- printf("then:\n");
79- dumpnodes(node->data.cond.thenpart.v, node->data.cond.thenpart.n, depth + 2);
80- if (node->data.cond.elsepart.n) {
81- printindent(depth + 1);
82- printf("else:\n");
83- dumpnodes(node->data.cond.elsepart.v, node->data.cond.elsepart.n, depth + 2);
84- }
85- break;
86- }
87+ printf("assign %s %s %s\n", assign->lhs, assignopname(assign->op), assign->rhs);
88+ if (assign->tspec)
89+ dumpstrlist("targets", &assign->targets, depth + 1);
90 }
91
92 static void
93-dumpnodes(const struct Node *nodes, size_t n, int depth)
94+dumprule(const struct RuleNode *rule, int depth)
95 {
96- size_t i;
97-
98- for (i = 0; i < n; i++)
99- dumpnode(&nodes[i], depth);
100+ printindent(depth);
101+ printf("rule\n");
102+ dumpstrlist("targets", &rule->targets, depth + 1);
103+ dumpstrlist("prereqs", &rule->prereqs, depth + 1);
104+ dumpstrlist("order-only", &rule->order_only, depth + 1);
105+ dumprecipes(&rule->recipes, depth + 1);
106 }
107
108 static void
109-dumpast(const struct Ast *ast)
110+dumpruleset(const struct RuleSet *ruleset)
111 {
112- printf("ast (%zu nodes)\n", ast->n);
113- dumpnodes(ast->v, ast->n, 1);
114+ size_t i;
115+
116+ printf("ruleset (%zu vars, %zu target vars, %zu rules)\n",
117+ ruleset->nvars, ruleset->ntvars, ruleset->nrules);
118+ for (i = 0; i < ruleset->nvars; i++)
119+ dumpassign(&ruleset->vars[i], 1);
120+ for (i = 0; i < ruleset->ntvars; i++)
121+ dumpassign(&ruleset->tvars[i], 1);
122+ for (i = 0; i < ruleset->nrules; i++)
123+ dumprule(&ruleset->rules[i], 1);
124 }
125
126 static void
127@@ -266,7 +209,7 @@ main(int argc, char **argv)
128 char **assigns;
129 size_t nassigns;
130 struct Ast ast;
131- struct Ast out;
132+ struct RuleSet rs;
133 struct Graph graph;
134
135 path = "Makefile";
136@@ -356,17 +299,17 @@ main(int argc, char **argv)
137 return 1;
138
139 }
140- if (eval(&ast, &out) < 0) {
141+ if (eval(&ast, &rs) < 0) {
142 fprintf(stderr, "eval error in %s\n", path);
143 freeast(&ast);
144 free(src);
145 return 1;
146 }
147 if (dump_ast)
148- dumpast(&out);
149- if (buildgraph(&out, &graph) < 0) {
150+ dumpruleset(&rs);
151+ if (buildgraph(&rs, &graph) < 0) {
152 fprintf(stderr, "graph error in %s\n", path);
153- freeast(&out);
154+ freeruleset(&rs);
155 freeast(&ast);
156 free(src);
157 return 1;
158@@ -377,7 +320,7 @@ main(int argc, char **argv)
159 if (gengraphviz(&graph, "build.dot") < 0) {
160 fprintf(stderr, "graphviz generation error\n");
161 freegraph(&graph);
162- freeast(&out);
163+ freeruleset(&rs);
164 freeast(&ast);
165 free(src);
166 return 1;
167@@ -386,13 +329,13 @@ main(int argc, char **argv)
168 if (genninja(&graph, "build.ninja") < 0) {
169 fprintf(stderr, "ninja generation error\n");
170 freegraph(&graph);
171- freeast(&out);
172+ freeruleset(&rs);
173 freeast(&ast);
174 free(src);
175 return 1;
176 }
177 freegraph(&graph);
178- freeast(&out);
179+ freeruleset(&rs);
180 freeast(&ast);
181 free(src);
182 return 0;
+81,
-28
1@@ -144,36 +144,60 @@ copyrecipes(struct RecipeList *out, const struct RecipeList *in)
2 }
3 }
4
5+static void
6+addrulesetassign(struct AssignNode **vec, size_t *n, const struct Node *src, struct EvalCtx *ctx)
7+{
8+ struct AssignNode *dst;
9+
10+ *vec = xrealloc(*vec, (*n + 1) * sizeof((*vec)[0]));
11+ dst = &(*vec)[(*n)++];
12+ memset(dst, 0, sizeof(*dst));
13+ dst->lhs = xstrdup(src->data.assign.lhs);
14+ dst->op = src->data.assign.op;
15+ dst->tspec = src->data.assign.tspec;
16+ if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
17+ dst->rhs = expandstr(ctx, src->data.assign.rhs);
18+ else
19+ dst->rhs = xstrdup(src->data.assign.rhs);
20+ copywords(&dst->targets, &src->data.assign.targets, ctx);
21+}
22+
23+static void
24+addrulesetrule(struct RuleSet *out, const struct RuleNode *src, struct EvalCtx *ctx)
25+{
26+ struct RuleNode *dst;
27+
28+ out->rules = xrealloc(out->rules, (out->nrules + 1) * sizeof(out->rules[0]));
29+ dst = &out->rules[out->nrules++];
30+ memset(dst, 0, sizeof(*dst));
31+ dst->dcolon = src->dcolon;
32+ copywords(&dst->targets, &src->targets, ctx);
33+ copywords(&dst->prereqs, &src->prereqs, ctx);
34+ copywords(&dst->order_only, &src->order_only, ctx);
35+ copyrecipes(&dst->recipes, &src->recipes);
36+}
37+
38 static int
39-evalnodes(const struct NodeList *in, struct NodeList *out, struct EvalCtx *ctx)
40+evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
41 {
42 size_t i;
43
44 for (i = 0; i < in->n; i++) {
45 const struct Node *src;
46- struct Node node;
47
48 src = &in->v[i];
49- memset(&node, 0, sizeof(node));
50- node.kind = src->kind;
51- node.loc = src->loc;
52 switch (src->kind) {
53 case NODE_BLANK:
54 break;
55- case NODE_COMMENT:
56- node.data.raw.text = xstrdup(src->data.raw.text);
57- break;
58 case NODE_RAW: {
59 char *exp;
60
61 exp = expandstr(ctx, src->data.raw.text);
62 free(exp);
63- node.kind = NODE_BLANK;
64 break;
65 }
66+ case NODE_COMMENT:
67 case NODE_INCLUDE:
68- node.data.include.optional = src->data.include.optional;
69- node.data.include.path = expandstr(ctx, src->data.include.path);
70 break;
71 case NODE_COND:
72 if (testcond(ctx, &src->data.cond)) {
73@@ -185,32 +209,23 @@ evalnodes(const struct NodeList *in, struct NodeList *out, struct EvalCtx *ctx)
74 }
75 continue;
76 case NODE_ASSIGN:
77- node.data.assign.lhs = xstrdup(src->data.assign.lhs);
78- node.data.assign.op = src->data.assign.op;
79- node.data.assign.tspec = src->data.assign.tspec;
80- if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
81- node.data.assign.rhs = expandstr(ctx, src->data.assign.rhs);
82- else
83- node.data.assign.rhs = xstrdup(src->data.assign.rhs);
84- copywords(&node.data.assign.targets, &src->data.assign.targets, ctx);
85- if (!src->data.assign.tspec)
86+ if (src->data.assign.tspec) {
87+ addrulesetassign(&out->tvars, &out->ntvars, src, ctx);
88+ } else {
89+ addrulesetassign(&out->vars, &out->nvars, src, ctx);
90 evalassign(ctx, &src->data.assign);
91+ }
92 break;
93 case NODE_RULE:
94- node.data.rule.dcolon = src->data.rule.dcolon;
95- copywords(&node.data.rule.targets, &src->data.rule.targets, ctx);
96- copywords(&node.data.rule.prereqs, &src->data.rule.prereqs, ctx);
97- copywords(&node.data.rule.order_only, &src->data.rule.order_only, ctx);
98- copyrecipes(&node.data.rule.recipes, &src->data.rule.recipes);
99+ addrulesetrule(out, &src->data.rule, ctx);
100 break;
101 }
102- addnode(out, node);
103 }
104 return 0;
105 }
106
107 int
108-eval(const struct Ast *ast, struct Ast *out)
109+eval(const struct Ast *ast, struct RuleSet *out)
110 {
111 struct Env env;
112 struct EvalCtx ctx;
113@@ -221,13 +236,51 @@ eval(const struct Ast *ast, struct Ast *out)
114 memset(&ctx, 0, sizeof(ctx));
115 seedenv(&env);
116 ctx.env = &env;
117- rc = evalnodes((const struct NodeList *)ast, (struct NodeList *)out, &ctx);
118+ rc = evalnodes((const struct NodeList *)ast, out, &ctx);
119 freeenv(&env);
120 if (rc == 0 && ctx.errors)
121 return -1;
122 return rc;
123 }
124
125+static void
126+freeassigns(struct AssignNode *v, size_t n)
127+{
128+ size_t i;
129+
130+ for (i = 0; i < n; i++) {
131+ free(v[i].lhs);
132+ free(v[i].rhs);
133+ freestrs(&v[i].targets);
134+ }
135+ free(v);
136+}
137+
138+static void
139+freerules(struct RuleNode *v, size_t n)
140+{
141+ size_t i;
142+
143+ for (i = 0; i < n; i++) {
144+ freestrs(&v[i].targets);
145+ freestrs(&v[i].prereqs);
146+ freestrs(&v[i].order_only);
147+ freerecipes(&v[i].recipes);
148+ }
149+ free(v);
150+}
151+
152+void
153+freeruleset(struct RuleSet *ruleset)
154+{
155+ if (!ruleset)
156+ return;
157+ freeassigns(ruleset->vars, ruleset->nvars);
158+ freeassigns(ruleset->tvars, ruleset->ntvars);
159+ freerules(ruleset->rules, ruleset->nrules);
160+ memset(ruleset, 0, sizeof(*ruleset));
161+}
162+
163 static void
164 freenodes(struct NodeList *list)
165 {
+25,
-36
1@@ -19,7 +19,7 @@ struct TAssign {
2 struct GraphState {
3 struct Graph *graph;
4 struct Env env;
5- const struct RuleNode **rules;
6+ const struct RuleNode *rules;
7 size_t nrules;
8 struct PatRules patterns;
9 struct SufRules sufs;
10@@ -88,13 +88,6 @@ seedsufs(struct SufRules *rules, const struct RuleNode *rule)
11 free(to);
12 }
13
14-static void
15-collectrule(struct GraphState *gs, const struct RuleNode *rule)
16-{
17- gs->rules = xrealloc(gs->rules, (gs->nrules + 1) * sizeof(gs->rules[0]));
18- gs->rules[gs->nrules++] = rule;
19-}
20-
21 static int
22 targetmatches(const char *pat, const char *name)
23 {
24@@ -211,12 +204,12 @@ addtassign(struct GraphState *gs, const struct AssignNode *assign)
25 ta->op = assign->op;
26 }
27
28-/*generate the graph from the evaluated ast
29+/*generate the graph from the evaluated rule set
30 * get the env and target specific assignments,
31 * then expand placeholder targets until all prereqs
32 * we can find are in the graph. */
33 int
34-buildgraph(const struct Ast *ast, struct Graph *graph)
35+buildgraph(const struct RuleSet *ruleset, struct Graph *graph)
36 {
37 size_t i, j, start;
38 struct GraphState gs;
39@@ -227,44 +220,41 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
40 memset(&ctx, 0, sizeof(ctx));
41 gs.graph = graph;
42 seedenv(&gs.env);
43+ gs.rules = ruleset->rules;
44+ gs.nrules = ruleset->nrules;
45 ctx.env = &gs.env;
46 imprules(&gs.sufs);
47
48- /* get the state, env vars in gs.env, target-specific var in gs.tas,
49- * and rules in gs.rules */
50- for (i = 0; i < ast->n; i++) {
51- if (ast->v[i].kind == NODE_ASSIGN) {
52- if (ast->v[i].data.assign.tspec)
53- addtassign(&gs, &ast->v[i].data.assign);
54- else
55- evalassign(&ctx, &ast->v[i].data.assign);
56- } else if (ast->v[i].kind == NODE_RULE) {
57- if (issufrule(&ast->v[i].data.rule)) {
58- gs.saw_suffixes = 1;
59- if (ast->v[i].data.rule.prereqs.n == 0)
60- clearsufs(&gs.sufs.active);
61- else
62- addsufs(&gs.sufs.active, &ast->v[i].data.rule.prereqs);
63- continue;
64- }
65- collectrule(&gs, &ast->v[i].data.rule);
66- }
67+ for (i = 0; i < ruleset->nvars; i++)
68+ evalassign(&ctx, &ruleset->vars[i]);
69+
70+ for (i = 0; i < ruleset->ntvars; i++)
71+ addtassign(&gs, &ruleset->tvars[i]);
72+
73+ for (i = 0; i < gs.nrules; i++) {
74+ if (!issufrule(&gs.rules[i]))
75+ continue;
76+ gs.saw_suffixes = 1;
77+ if (gs.rules[i].prereqs.n == 0)
78+ clearsufs(&gs.sufs.active);
79+ else
80+ addsufs(&gs.sufs.active, &gs.rules[i].prereqs);
81 }
82
83 if (!gs.saw_suffixes) {
84 for (i = 0; i < gs.nrules; i++)
85- seedsufs(&gs.sufs, gs.rules[i]);
86+ seedsufs(&gs.sufs, &gs.rules[i]);
87 }
88
89 for (i = 0; i < gs.nrules; i++) {
90 size_t k;
91
92- if (collectsufrule(&gs.sufs, gs.rules[i]))
93+ if (collectsufrule(&gs.sufs, &gs.rules[i]))
94 continue;
95- collectpat(&gs.patterns, gs.rules[i]);
96- for (k = 0; k < gs.rules[i]->targets.n; k++) {
97- if (!ispat(gs.rules[i]->targets.v[k]))
98- addrule(&gs, gs.rules[i]->targets.v[k], gs.rules[i]);
99+ collectpat(&gs.patterns, &gs.rules[i]);
100+ for (k = 0; k < gs.rules[i].targets.n; k++) {
101+ if (!ispat(gs.rules[i].targets.v[k]))
102+ addrule(&gs, gs.rules[i].targets.v[k], &gs.rules[i]);
103 }
104 }
105
106@@ -320,7 +310,6 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
107 free(gs.tas);
108 freepatrule(&gs.patterns);
109 freesufrules(&gs.sufs);
110- free(gs.rules);
111 freeenv(&gs.env);
112
113 return ctx.errors ? -1 : 0;
+12,
-2
1@@ -124,6 +124,15 @@ struct Ast {
2 size_t n;
3 };
4
5+struct RuleSet {
6+ struct AssignNode *vars;
7+ size_t nvars;
8+ struct AssignNode *tvars;
9+ size_t ntvars;
10+ struct RuleNode *rules;
11+ size_t nrules;
12+};
13+
14 struct Target {
15 char *name;
16 struct StrList prereqs;
17@@ -141,10 +150,11 @@ struct Graph {
18 int preproc(const char *path, struct Pre *pre);
19 void freepre(struct Pre *pre);
20 int buildast(const char *path, const struct Pre *pre, struct Ast *ast);
21-int eval(const struct Ast *ast, struct Ast *out);
22+int eval(const struct Ast *ast, struct RuleSet *out);
23 int parse(const char *path, const char *src, struct Ast *ast);
24 void freeast(struct Ast *ast);
25-int buildgraph(const struct Ast *ast, struct Graph *graph);
26+void freeruleset(struct RuleSet *ruleset);
27+int buildgraph(const struct RuleSet *ruleset, struct Graph *graph);
28 void freegraph(struct Graph *graph);
29
30 #endif