commit c353699
xplshn
·
2026-04-08 03:02:51 +0000 UTC
parent de8d436
reorganize, added dep graph & ninja backend Signed-off-by: xplshn <anto@xplshn.com.ar>
M
Makefile
+6,
-3
1@@ -1,10 +1,10 @@
2 BIN = shin
3-SRCS = main.c parse.c eval.c util.c
4-FMTSRCS = $(SRCS) shinobi.h
5+SRCS = cli/main.c src/parse.c src/eval.c src/graph.c backends/ninja.c src/util.c
6+FMTSRCS = $(SRCS) src/shinobi.h src/internal.h backends/ninja.h
7 OBJS = $(SRCS:.c=.o)
8
9 CC = clang
10-CFLAGS = -O2 -Wall -Wextra -pedantic
11+CFLAGS = -O2 -Wall -Wextra -pedantic -Isrc -Ibackends
12 CPPFLAGS =
13 LDFLAGS = -static
14
15@@ -13,6 +13,9 @@ all: $(BIN)
16 $(BIN): $(OBJS)
17 $(CC) $(LDFLAGS) -o $(BIN) $(OBJS) $(LDLIBS)
18
19+%.o: %.c
20+ $(CC) $(CFLAGS) -c -o $@ $<
21+
22 fmt:
23 clang-format -i $(FMTSRCS)
24
+52,
-0
1@@ -0,0 +1,52 @@
2+#include "ninja.h"
3+#include "internal.h"
4+
5+#include <stdio.h>
6+#include <stdlib.h>
7+#include <string.h>
8+
9+/* ninja backend */
10+
11+static void
12+emitrule(FILE *fp, const struct Target *t, int id)
13+{
14+ size_t i;
15+
16+ fprintf(fp, "rule r%d\n", id);
17+ fprintf(fp, " command =");
18+ for (i = 0; i < t->recipes.n; i++) {
19+ fprintf(fp, "%s %s", i == 0 ? "" : " &&", t->recipes.v[i]);
20+ }
21+ fprintf(fp, "\n\n");
22+}
23+
24+int
25+genninja(const struct Graph *graph, const char *path)
26+{
27+ FILE *fp;
28+ size_t i, j;
29+ int ruleid;
30+
31+ fp = fopen(path, "w");
32+ if (!fp)
33+ return -1;
34+
35+ ruleid = 0;
36+ for (i = 0; i < graph->n; i++) {
37+ if (graph->v[i].recipes.n > 0) {
38+ emitrule(fp, &graph->v[i], ++ruleid);
39+ fprintf(fp, "build %s: r%d", graph->v[i].name, ruleid);
40+ for (j = 0; j < graph->v[i].prereqs.n; j++)
41+ fprintf(fp, " %s", graph->v[i].prereqs.v[j]);
42+ if (graph->v[i].order_only.n) {
43+ fprintf(fp, " ||");
44+ for (j = 0; j < graph->v[i].order_only.n; j++)
45+ fprintf(fp, " %s", graph->v[i].order_only.v[j]);
46+ }
47+ fprintf(fp, "\n\n");
48+ }
49+ }
50+
51+ fclose(fp);
52+ return 0;
53+}
+8,
-0
1@@ -0,0 +1,8 @@
2+#ifndef NINJA_H
3+#define NINJA_H
4+
5+#include "shinobi.h"
6+
7+int genninja(const struct Graph *graph, const char *path);
8+
9+#endif
+88,
-0
1@@ -0,0 +1,88 @@
2+#include "shinobi.h"
3+#include "ninja.h"
4+
5+#include <stdio.h>
6+#include <stdlib.h>
7+#include <string.h>
8+
9+static char *
10+readfile(const char *path)
11+{
12+ FILE *fp;
13+ long n;
14+ char *buf;
15+
16+ fp = fopen(path, "rb");
17+ if (!fp)
18+ return 0;
19+ if (fseek(fp, 0, SEEK_END) < 0) {
20+ fclose(fp);
21+ return 0;
22+ }
23+ n = ftell(fp);
24+ if (n < 0) {
25+ fclose(fp);
26+ return 0;
27+ }
28+ if (fseek(fp, 0, SEEK_SET) < 0) {
29+ fclose(fp);
30+ return 0;
31+ }
32+ buf = malloc((size_t)n + 1);
33+ if (fread(buf, 1, (size_t)n, fp) != (size_t)n) {
34+ fclose(fp);
35+ free(buf);
36+ return 0;
37+ }
38+ buf[n] = 0;
39+ fclose(fp);
40+ return buf;
41+}
42+
43+int
44+main(int argc, char **argv)
45+{
46+ const char *path;
47+ char *src;
48+ struct Ast ast;
49+ struct Ast out;
50+ struct Graph graph;
51+
52+ path = argc > 1 ? argv[1] : "Makefile";
53+ src = readfile(path);
54+ if (!src) {
55+ fprintf(stderr, "could not read %s\n", path);
56+ return 1;
57+ }
58+ if (parse(path, src, &ast) < 0) {
59+ fprintf(stderr, "parse error in %s\n", path);
60+ free(src);
61+ return 1;
62+ }
63+ if (eval(&ast, &out) < 0) {
64+ fprintf(stderr, "eval error in %s\n", path);
65+ freeast(&ast);
66+ free(src);
67+ return 1;
68+ }
69+ if (buildgraph(&out, &graph) < 0) {
70+ fprintf(stderr, "graph error in %s\n", path);
71+ freeast(&out);
72+ freeast(&ast);
73+ free(src);
74+ return 1;
75+ }
76+ if (genninja(&graph, "build.ninja") < 0) {
77+ fprintf(stderr, "ninja generation error\n");
78+ freegraph(&graph);
79+ freeast(&out);
80+ freeast(&ast);
81+ free(src);
82+ return 1;
83+ }
84+ freegraph(&graph);
85+ freeast(&out);
86+ freeast(&ast);
87+ free(src);
88+ return 0;
89+}
D
main.c
+0,
-196
1@@ -1,196 +0,0 @@
2-#include "shinobi.h"
3-
4-#include <stdio.h>
5-#include <stdlib.h>
6-#include <string.h>
7-
8-static char *
9-readfile(const char *path)
10-{
11- FILE *fp;
12- long n;
13- char *buf;
14-
15- fp = fopen(path, "rb");
16- if (!fp)
17- return 0;
18- if (fseek(fp, 0, SEEK_END) < 0) {
19- fclose(fp);
20- return 0;
21- }
22- n = ftell(fp);
23- if (n < 0) {
24- fclose(fp);
25- return 0;
26- }
27- if (fseek(fp, 0, SEEK_SET) < 0) {
28- fclose(fp);
29- return 0;
30- }
31- buf = malloc((size_t)n + 1);
32- if (!buf) {
33- fclose(fp);
34- return 0;
35- }
36- if (fread(buf, 1, (size_t)n, fp) != (size_t)n) {
37- fclose(fp);
38- free(buf);
39- return 0;
40- }
41- buf[n] = 0;
42- fclose(fp);
43- return buf;
44-}
45-
46-static const char *
47-assignname(enum AssignOp op)
48-{
49- switch (op) {
50- case ASSIGN_EQ:
51- return "=";
52- case ASSIGN_PLUS_EQ:
53- return "+=";
54- case ASSIGN_COLON_EQ:
55- return ":=";
56- case ASSIGN_QMARK_EQ:
57- return "?=";
58- case ASSIGN_BANG_EQ:
59- return "!=";
60- }
61- return "?";
62-}
63-
64-static const char *
65-condname(enum CondKind kind)
66-{
67- switch (kind) {
68- case COND_IFEQ:
69- return "ifeq";
70- case COND_IFNEQ:
71- return "ifneq";
72- case COND_ELSE:
73- return "else";
74- case COND_ENDIF:
75- return "endif";
76- }
77- return "?";
78-}
79-
80-static void
81-printwords(const char *name, const struct StrList *list)
82-{
83- size_t i;
84-
85- printf(" %s:", name);
86- for (i = 0; i < list->n; i++)
87- printf(" [%s]", list->v[i]);
88- printf("\n");
89-}
90-
91-static void
92-indent(size_t n)
93-{
94- size_t i;
95-
96- for (i = 0; i < n; i++)
97- printf(" ");
98-}
99-
100-static void
101-dumpnodes(const struct Node *v, size_t n, size_t depth)
102-{
103- size_t i, j;
104-
105- for (i = 0; i < n; i++) {
106- const struct Node *s = &v[i];
107- indent(depth);
108- printf("%d-%d ", s->loc.line0, s->loc.line1);
109- switch (s->kind) {
110- case NODE_BLANK:
111- puts("blank");
112- break;
113- case NODE_COMMENT:
114- printf("comment %s\n", s->data.raw.text);
115- break;
116- case NODE_INCLUDE:
117- printf("include optional=%d path=%s\n", s->data.include.optional, s->data.include.path);
118- break;
119- case NODE_COND:
120- printf("cond %s", condname(s->data.cond.kind));
121- if (s->data.cond.arg1)
122- printf(" arg1=%s", s->data.cond.arg1);
123- if (s->data.cond.arg2)
124- printf(" arg2=%s", s->data.cond.arg2);
125- printf("\n");
126- if (s->data.cond.thenpart.n) {
127- indent(depth);
128- puts("then");
129- dumpnodes(s->data.cond.thenpart.v, s->data.cond.thenpart.n, depth + 1);
130- }
131- if (s->data.cond.elsepart.n) {
132- indent(depth);
133- puts("else");
134- dumpnodes(s->data.cond.elsepart.v, s->data.cond.elsepart.n, depth + 1);
135- }
136- break;
137- case NODE_ASSIGN:
138- printf("assign%s lhs=%s op=%s rhs=%s\n",
139- s->data.assign.tspec ? " target" : "",
140- s->data.assign.lhs,
141- assignname(s->data.assign.op),
142- s->data.assign.rhs);
143- if (s->data.assign.tspec)
144- printwords("targets", &s->data.assign.targets);
145- break;
146- case NODE_RULE:
147- puts("rule");
148- printwords("targets", &s->data.rule.targets);
149- printwords("prereqs", &s->data.rule.prereqs);
150- printwords("order_only", &s->data.rule.order_only);
151- for (j = 0; j < s->data.rule.recipes.n; j++)
152- printf(" recipe: %s\n", s->data.rule.recipes.v[j]);
153- break;
154- case NODE_RAW:
155- printf("raw %s\n", s->data.raw.text);
156- break;
157- }
158- }
159-}
160-
161-static void
162-dump(const struct Ast *ast)
163-{
164- dumpnodes(ast->v, ast->n, 0);
165-}
166-
167-int
168-main(int argc, char **argv)
169-{
170- const char *path;
171- char *src;
172- struct Ast ast;
173- struct Ast out;
174-
175- path = argc > 1 ? argv[1] : "Makefile";
176- src = readfile(path);
177- if (!src) {
178- fprintf(stderr, "could not read %s\n", path);
179- return 1;
180- }
181- if (parse(path, src, &ast) < 0) {
182- fprintf(stderr, "parse error in %s\n", path);
183- free(src);
184- return 1;
185- }
186- if (eval(&ast, &out) < 0) {
187- fprintf(stderr, "eval error in %s\n", path);
188- freeast(&ast);
189- free(src);
190- return 1;
191- }
192- dump(&out);
193- freeast(&out);
194- freeast(&ast);
195- free(src);
196- return 0;
197-}
R eval.c =>
src/eval.c
+17,
-40
1@@ -40,21 +40,6 @@ findvar(struct Env *env, const char *name)
2 return 0;
3 }
4
5-static char *
6-cat2(const char *a, const char *b)
7-{
8- size_t na, nb;
9- char *s;
10-
11- na = strlen(a);
12- nb = strlen(b);
13- s = xmalloc(na + nb + 1);
14- memcpy(s, a, na);
15- memcpy(s + na, b, nb);
16- s[na + nb] = 0;
17- return s;
18-}
19-
20 static int
21 isplainvar(const char *s, size_t n)
22 {
23@@ -351,7 +336,7 @@ evalassign(struct Env *env, const struct AssignNode *in)
24 break;
25 }
26 rhs = v->simple ? expandstr(env, in->rhs) : xstrdup(in->rhs);
27- joined = cat2(v->val, rhs);
28+ joined = cat3(v->val, " ", rhs);
29 free(rhs);
30 free(v->val);
31 v->val = joined;
32@@ -365,9 +350,20 @@ evalassign(struct Env *env, const struct AssignNode *in)
33 static int
34 testcond(struct Env *env, const struct CondNode *cond)
35 {
36- char *a, *b;
37+ char *a, *b, *name;
38+ struct Var *v;
39 int ok;
40
41+ if (cond->kind == COND_IFDEF || cond->kind == COND_IFNDEF) {
42+ name = expandstr(env, cond->arg1);
43+ v = findvar(env, name);
44+ ok = v != 0;
45+ free(name);
46+ if (cond->kind == COND_IFNDEF)
47+ ok = !ok;
48+ return ok;
49+ }
50+
51 a = cond->arg1 ? expandstr(env, cond->arg1) : xstrdup("");
52 b = cond->arg2 ? expandstr(env, cond->arg2) : xstrdup("");
53 ok = strcmp(a, b) == 0;
54@@ -385,8 +381,9 @@ copywords(struct StrList *out, const struct StrList *in, struct Env *env)
55
56 memset(out, 0, sizeof(*out));
57 for (i = 0; i < in->n; i++) {
58- out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
59- out->v[out->n++] = expandstr(env, in->v[i]);
60+ char *s = expandstr(env, in->v[i]);
61+ splitwords(out, s, strlen(s));
62+ free(s);
63 }
64 }
65
66@@ -472,26 +469,6 @@ eval(const struct Ast *ast, struct Ast *out)
67 return rc;
68 }
69
70-static void
71-freestrs(struct StrList *list)
72-{
73- size_t i;
74-
75- for (i = 0; i < list->n; i++)
76- free(list->v[i]);
77- free(list->v);
78-}
79-
80-static void
81-freerec(struct RecipeList *list)
82-{
83- size_t i;
84-
85- for (i = 0; i < list->n; i++)
86- free(list->v[i]);
87- free(list->v);
88-}
89-
90 static void
91 freenodes(struct NodeList *list)
92 {
93@@ -512,7 +489,7 @@ freenodes(struct NodeList *list)
94 freestrs(&list->v[i].data.rule.targets);
95 freestrs(&list->v[i].data.rule.prereqs);
96 freestrs(&list->v[i].data.rule.order_only);
97- freerec(&list->v[i].data.rule.recipes);
98+ freerecipes(&list->v[i].data.rule.recipes);
99 break;
100 case NODE_INCLUDE:
101 free(list->v[i].data.include.path);
+299,
-0
1@@ -0,0 +1,299 @@
2+#include "shinobi.h"
3+#include "internal.h"
4+
5+#include <stdlib.h>
6+#include <string.h>
7+
8+/* graph builder */
9+
10+static struct Target *
11+findtarget(struct Graph *graph, const char *name)
12+{
13+ size_t i;
14+
15+ for (i = 0; i < graph->n; i++) {
16+ if (strcmp(graph->v[i].name, name) == 0)
17+ return &graph->v[i];
18+ }
19+ return 0;
20+}
21+
22+static void
23+addwords(struct StrList *dest, const struct StrList *src)
24+{
25+ size_t i;
26+
27+ for (i = 0; i < src->n; i++) {
28+ dest->v = xrealloc(dest->v, (dest->n + 1) * sizeof(dest->v[0]));
29+ dest->v[dest->n++] = xstrdup(src->v[i]);
30+ }
31+}
32+
33+static void
34+addrecipes(struct RecipeList *dest, const struct RecipeList *src)
35+{
36+ size_t i;
37+
38+ for (i = 0; i < src->n; i++) {
39+ dest->v = xrealloc(dest->v, (dest->n + 1) * sizeof(dest->v[0]));
40+ dest->v[dest->n++] = xstrdup(src->v[i]);
41+ }
42+}
43+
44+static void
45+addrule(struct Graph *graph, const char *name, const struct RuleNode *rule)
46+{
47+ size_t i;
48+ struct Target *t;
49+
50+ t = findtarget(graph, name);
51+ if (!t) {
52+ graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
53+ t = &graph->v[graph->n++];
54+ memset(t, 0, sizeof(*t));
55+ t->name = xstrdup(name);
56+ }
57+ addwords(&t->prereqs, &rule->prereqs);
58+ addwords(&t->order_only, &rule->order_only);
59+ addrecipes(&t->recipes, &rule->recipes);
60+
61+ for (i = 0; i < rule->prereqs.n; i++) {
62+ if (strchr(rule->prereqs.v[i], '%'))
63+ continue;
64+ if (!findtarget(graph, rule->prereqs.v[i])) {
65+ graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
66+ t = &graph->v[graph->n++];
67+ memset(t, 0, sizeof(*t));
68+ t->name = xstrdup(rule->prereqs.v[i]);
69+ }
70+ }
71+}
72+
73+struct Pattern {
74+ char *target;
75+ struct StrList prereqs;
76+ struct StrList order_only;
77+ struct RecipeList recipes;
78+};
79+
80+struct GraphState {
81+ struct Graph *graph;
82+ struct Pattern *pats;
83+ size_t npats;
84+};
85+
86+static void
87+addpats(struct GraphState *gs, const struct RuleNode *rule)
88+{
89+ size_t i;
90+
91+ for (i = 0; i < rule->targets.n; i++) {
92+ if (strchr(rule->targets.v[i], '%')) {
93+ gs->pats = xrealloc(gs->pats, (gs->npats + 1) * sizeof(gs->pats[0]));
94+ struct Pattern *p = &gs->pats[gs->npats++];
95+ memset(p, 0, sizeof(*p));
96+ p->target = xstrdup(rule->targets.v[i]);
97+ addwords(&p->prereqs, &rule->prereqs);
98+ addwords(&p->order_only, &rule->order_only);
99+ addrecipes(&p->recipes, &rule->recipes);
100+ } else {
101+ addrule(gs->graph, rule->targets.v[i], rule);
102+ }
103+ }
104+}
105+
106+static char *
107+matchpat(const char *pat, const char *name)
108+{
109+ const char *p;
110+ size_t pre, suf, n, plen;
111+
112+ p = strchr(pat, '%');
113+ if (!p)
114+ return 0;
115+ pre = (size_t)(p - pat);
116+ suf = strlen(p + 1);
117+ n = strlen(name);
118+ plen = strlen(pat);
119+
120+ if (n < plen - 1)
121+ return 0;
122+ if (memcmp(pat, name, pre) != 0)
123+ return 0;
124+ if (memcmp(p + 1, name + n - suf, suf) != 0)
125+ return 0;
126+ return xstrndup(name + pre, n - pre - suf);
127+}
128+
129+static char *
130+applystem(const char *s, const char *stem)
131+{
132+ const char *p;
133+ size_t pre, suf, stemlen, slen;
134+ char *out;
135+
136+ p = strchr(s, '%');
137+ if (!p)
138+ return xstrdup(s);
139+ pre = (size_t)(p - s);
140+ slen = strlen(s);
141+ suf = slen - pre - 1;
142+ stemlen = strlen(stem);
143+ out = xmalloc(pre + stemlen + suf + 1);
144+ memcpy(out, s, pre);
145+ memcpy(out + pre, stem, stemlen);
146+ memcpy(out + pre + stemlen, p + 1, suf);
147+ out[pre + stemlen + suf] = 0;
148+ return out;
149+}
150+
151+static char *
152+expandauto(const char *s, const struct Target *t)
153+{
154+ size_t i, n, cap, len;
155+ char *out;
156+
157+ n = strlen(s);
158+ cap = n + 1;
159+ len = 0;
160+ out = xmalloc(cap);
161+ for (i = 0; i < n; i++) {
162+ if (s[i] == '$' && i + 1 < n) {
163+ char *val = 0;
164+ int mustfree = 0;
165+
166+ if (s[i + 1] == '@') {
167+ val = t->name;
168+ } else if (s[i + 1] == '<') {
169+ if (t->prereqs.n > 0)
170+ val = t->prereqs.v[0];
171+ } else if (s[i + 1] == '^') {
172+ size_t k;
173+ char *joined = xstrdup("");
174+ for (k = 0; k < t->prereqs.n; k++) {
175+ char *next = joined[0] ? cat3(joined, " ", t->prereqs.v[k]) : xstrdup(t->prereqs.v[k]);
176+ free(joined);
177+ joined = next;
178+ }
179+ val = joined;
180+ mustfree = 1;
181+ }
182+
183+ if (val) {
184+ size_t vlen = strlen(val);
185+ if (len + vlen + 1 > cap) {
186+ cap = len + vlen + n - i + 1;
187+ out = xrealloc(out, cap);
188+ }
189+ memcpy(out + len, val, vlen);
190+ len += vlen;
191+ if (mustfree)
192+ free(val);
193+ i++;
194+ continue;
195+ }
196+ }
197+ if (len + 2 > cap) {
198+ cap *= 2;
199+ out = xrealloc(out, cap);
200+ }
201+ out[len++] = s[i];
202+ }
203+ out[len] = 0;
204+ return out;
205+}
206+
207+static void
208+instantiate(struct Target *t, struct Pattern *p, const char *stem)
209+{
210+ size_t i;
211+
212+ for (i = 0; i < p->prereqs.n; i++) {
213+ char *s = applystem(p->prereqs.v[i], stem);
214+ t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
215+ t->prereqs.v[t->prereqs.n++] = s;
216+ }
217+ for (i = 0; i < p->order_only.n; i++) {
218+ char *s = applystem(p->order_only.v[i], stem);
219+ t->order_only.v = xrealloc(t->order_only.v, (t->order_only.n + 1) * sizeof(t->order_only.v[0]));
220+ t->order_only.v[t->order_only.n++] = s;
221+ }
222+ for (i = 0; i < p->recipes.n; i++) {
223+ char *s = applystem(p->recipes.v[i], stem);
224+ char *exp = expandauto(s, t);
225+ free(s);
226+ t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
227+ t->recipes.v[t->recipes.n++] = exp;
228+ }
229+}
230+
231+int
232+buildgraph(const struct Ast *ast, struct Graph *graph)
233+{
234+ size_t i, j, start;
235+ struct GraphState gs;
236+
237+ memset(graph, 0, sizeof(*graph));
238+ memset(&gs, 0, sizeof(gs));
239+ gs.graph = graph;
240+
241+ for (i = 0; i < ast->n; i++) {
242+ if (ast->v[i].kind == NODE_RULE)
243+ addpats(&gs, &ast->v[i].data.rule);
244+ }
245+
246+ start = 0;
247+ while (start < graph->n) {
248+ size_t current_n = graph->n;
249+ for (i = start; i < current_n; i++) {
250+ struct Target *t = &graph->v[i];
251+ if (t->recipes.n == 0) {
252+ for (j = 0; j < gs.npats; j++) {
253+ char *stem = matchpat(gs.pats[j].target, t->name);
254+ if (stem) {
255+ instantiate(t, &gs.pats[j], stem);
256+ free(stem);
257+ break;
258+ }
259+ }
260+ }
261+ for (j = 0; j < t->prereqs.n; j++) {
262+ if (!findtarget(graph, t->prereqs.v[j])) {
263+ graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
264+ struct Target *nt = &graph->v[graph->n++];
265+ memset(nt, 0, sizeof(*nt));
266+ nt->name = xstrdup(t->prereqs.v[j]);
267+ }
268+ }
269+ }
270+ start = current_n;
271+ }
272+
273+ for (i = 0; i < gs.npats; i++) {
274+ free(gs.pats[i].target);
275+ freestrs(&gs.pats[i].prereqs);
276+ freestrs(&gs.pats[i].order_only);
277+ freerecipes(&gs.pats[i].recipes);
278+ }
279+ free(gs.pats);
280+
281+ return 0;
282+}
283+
284+void
285+freegraph(struct Graph *graph)
286+{
287+ size_t i;
288+
289+ if (!graph)
290+ return;
291+ for (i = 0; i < graph->n; i++) {
292+ free(graph->v[i].name);
293+ freestrs(&graph->v[i].prereqs);
294+ freestrs(&graph->v[i].order_only);
295+ freerecipes(&graph->v[i].recipes);
296+ }
297+ free(graph->v);
298+ graph->v = 0;
299+ graph->n = 0;
300+}
R internal.h =>
src/internal.h
+4,
-0
1@@ -8,5 +8,9 @@ void *xrealloc(void *p, size_t n);
2 char *xstrndup(const char *s, size_t n);
3 char *xstrdup(const char *s);
4 void addnode(struct NodeList *list, struct Node node);
5+void splitwords(struct StrList *out, const char *s, size_t n);
6+char *cat3(const char *a, const char *b, const char *c);
7+void freestrs(struct StrList *list);
8+void freerecipes(struct RecipeList *list);
9
10 #endif
R parse.c =>
src/parse.c
+35,
-13
1@@ -197,7 +197,7 @@ findtop(const char *s, size_t n, char want)
2 return -1;
3 }
4
5-static void
6+void
7 splitwords(struct StrList *out, const char *s, size_t n)
8 {
9 size_t i, j, start;
10@@ -377,7 +377,7 @@ joinpath(const char *dir, const char *name)
11 }
12
13 static int
14-preprocfile(const char *path, struct Pre *pre, struct Inc *inc)
15+preprocfile(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc)
16 {
17 char *src, *dir;
18 const char *p;
19@@ -388,9 +388,13 @@ preprocfile(const char *path, struct Pre *pre, struct Inc *inc)
20 fprintf(stderr, "include cycle: %s\n", path);
21 return -1;
22 }
23- src = readfile(path);
24- if (!src)
25- return -1;
26+ if (src_override) {
27+ src = xstrdup(src_override);
28+ } else {
29+ src = readfile(path);
30+ if (!src)
31+ return -1;
32+ }
33 inc->v = xrealloc(inc->v, (inc->n + 1) * sizeof(inc->v[0]));
34 inc->v[inc->n++] = xstrdup(path);
35 dir = dirpart(path);
36@@ -416,7 +420,7 @@ preprocfile(const char *path, struct Pre *pre, struct Inc *inc)
37 free(line.text);
38 free(trim);
39 free(incarg);
40- rc = preprocfile(full, pre, inc);
41+ rc = preprocfile(full, 0, pre, inc);
42 free(full);
43 if (rc < 0 && !opt) {
44 free(dir);
45@@ -446,7 +450,7 @@ preproc(const char *path, struct Pre *pre)
46
47 memset(pre, 0, sizeof(*pre));
48 memset(&inc, 0, sizeof(inc));
49- if (preprocfile(path, pre, &inc) < 0) {
50+ if (preprocfile(path, 0, pre, &inc) < 0) {
51 free(inc.v);
52 freepre(pre);
53 return -1;
54@@ -509,6 +513,12 @@ parsecond(const struct PreLine *line, const char *s)
55 } else if (haskw(s, "ifneq")) {
56 state.data.cond.kind = COND_IFNEQ;
57 p = s + 5;
58+ } else if (haskw(s, "ifdef")) {
59+ state.data.cond.kind = COND_IFDEF;
60+ p = s + 5;
61+ } else if (haskw(s, "ifndef")) {
62+ state.data.cond.kind = COND_IFNDEF;
63+ p = s + 6;
64 } else {
65 state.data.cond.kind = COND_ENDIF;
66 return state;
67@@ -522,6 +532,11 @@ parsecond(const struct PreLine *line, const char *s)
68 state.data.cond.arg2 = xstrndup("", 0);
69 return state;
70 }
71+ if (state.data.cond.kind == COND_IFDEF || state.data.cond.kind == COND_IFNDEF) {
72+ state.data.cond.arg1 = xstrndup(p, n);
73+ state.data.cond.arg2 = xstrndup("", 0);
74+ return state;
75+ }
76 if ((p[0] == '(' && p[n - 1] == ')') || (p[0] == '"' && p[n - 1] == '"')) {
77 mid = n / 2;
78 state.data.cond.arg1 = xstrndup(p, n);
79@@ -583,7 +598,7 @@ parserule(const struct PreLine *line, const char *s, size_t n, size_t colon)
80 return state;
81 }
82
83-/*unclassified line*/
84+/* unclassified line */
85 static struct Node
86 parseraw(const struct PreLine *line, const char *s)
87 {
88@@ -626,6 +641,7 @@ parseline(const struct PreLine *line)
89 return state;
90 }
91 if (haskw(trim, "ifeq") || haskw(trim, "ifneq") ||
92+ haskw(trim, "ifdef") || haskw(trim, "ifndef") ||
93 haskw(trim, "else") || haskw(trim, "endif")) {
94 state = parsecond(line, trim);
95 free(trim);
96@@ -688,7 +704,8 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out)
97 free(trim);
98 break;
99 }
100- if (haskw(trim, "ifeq") || haskw(trim, "ifneq")) {
101+ if (haskw(trim, "ifeq") || haskw(trim, "ifneq") ||
102+ haskw(trim, "ifdef") || haskw(trim, "ifndef")) {
103 state = parsecond(line, trim);
104 free(trim);
105 (*i)++;
106@@ -751,17 +768,22 @@ buildast(const char *path, const struct Pre *pre, struct Ast *ast)
107 return 0;
108 }
109
110-/* pre procces build ast, */
111+/* preprocess and parse */
112 int
113 parse(const char *path, const char *src, struct Ast *ast)
114 {
115 struct Pre pre;
116+ struct Inc inc;
117 int rc;
118
119- (void)src;
120- rc = preproc(path, &pre);
121- if (rc < 0)
122+ memset(&pre, 0, sizeof(pre));
123+ memset(&inc, 0, sizeof(inc));
124+ rc = preprocfile(path, src, &pre, &inc);
125+ free(inc.v);
126+ if (rc < 0) {
127+ freepre(&pre);
128 return rc;
129+ }
130 rc = buildast(path, &pre, ast);
131 freepre(&pre);
132 return rc;
R shinobi.h =>
src/shinobi.h
+16,
-0
1@@ -32,6 +32,8 @@ enum AssignOp {
2 enum CondKind {
3 COND_IFEQ,
4 COND_IFNEQ,
5+ COND_IFDEF,
6+ COND_IFNDEF,
7 COND_ELSE,
8 COND_ENDIF,
9 };
10@@ -121,11 +123,25 @@ struct Ast {
11 size_t n;
12 };
13
14+struct Target {
15+ char *name;
16+ struct StrList prereqs;
17+ struct StrList order_only;
18+ struct RecipeList recipes;
19+};
20+
21+struct Graph {
22+ struct Target *v;
23+ size_t n;
24+};
25+
26 int preproc(const char *path, struct Pre *pre);
27 void freepre(struct Pre *pre);
28 int buildast(const char *path, const struct Pre *pre, struct Ast *ast);
29 int eval(const struct Ast *ast, struct Ast *out);
30 int parse(const char *path, const char *src, struct Ast *ast);
31 void freeast(struct Ast *ast);
32+int buildgraph(const struct Ast *ast, struct Graph *graph);
33+void freegraph(struct Graph *graph);
34
35 #endif
R util.c =>
src/util.c
+45,
-0
1@@ -49,9 +49,54 @@ xstrdup(const char *s)
2 return xstrndup(s, strlen(s));
3 }
4
5+char *
6+cat3(const char *a, const char *b, const char *c)
7+{
8+ size_t na, nb, nc;
9+ char *s;
10+
11+ na = strlen(a);
12+ nb = strlen(b);
13+ nc = strlen(c);
14+ s = xmalloc(na + nb + nc + 1);
15+ memcpy(s, a, na);
16+ memcpy(s + na, b, nb);
17+ memcpy(s + na + nb, c, nc);
18+ s[na + nb + nc] = 0;
19+ return s;
20+}
21+
22 void
23 addnode(struct NodeList *list, struct Node node)
24 {
25 list->v = xrealloc(list->v, (list->n + 1) * sizeof(list->v[0]));
26 list->v[list->n++] = node;
27 }
28+
29+void
30+freestrs(struct StrList *list)
31+{
32+ size_t i;
33+
34+ if (!list)
35+ return;
36+ for (i = 0; i < list->n; i++)
37+ free(list->v[i]);
38+ free(list->v);
39+ list->v = 0;
40+ list->n = 0;
41+}
42+
43+void
44+freerecipes(struct RecipeList *list)
45+{
46+ size_t i;
47+
48+ if (!list)
49+ return;
50+ for (i = 0; i < list->n; i++)
51+ free(list->v[i]);
52+ free(list->v);
53+ list->v = 0;
54+ list->n = 0;
55+}