commit d2e2d0c
shrub
·
2026-05-05 20:12:37 +0000 UTC
parent 5519266
.PHONY fix for graphs with multiple subgraphs + die faster and consistently
9 files changed,
+72,
-71
+2,
-1
1@@ -278,7 +278,8 @@ genninjafile(const struct Graph *graph, const char *path, const char *prefix, in
2 if (!fp)
3 return -1;
4 fprintf(fp, "# this file was generated from a makefile by shinobi\n");
5- fprintf(fp, "build __shin_always_build__: phony\n\n");
6+ if (root)
7+ fprintf(fp, "build __shin_always_build__: phony\n\n");
8 ruleid = 0;
9 rules = 0;
10 nrules = 0;
+1,
-6
1@@ -334,8 +334,6 @@ main(int argc, char **argv)
2
3 rc = parse(path, src, &ast);
4 if (rc < 0) {
5- if (rc != -2)
6- fprintf(stderr, "parse error in %s\n", path);
7 free(assigns);
8 free(pathbuf);
9 free(src);
10@@ -355,8 +353,7 @@ main(int argc, char **argv)
11 }
12 }
13 }
14- if (eval(&ast, 0, env_override, &rs) < 0) {
15- fprintf(stderr, "eval error in %s\n", path);
16+ if (eval(path, &ast, 0, env_override, &rs) < 0) {
17 free(assigns);
18 freeast(&ast);
19 free(pathbuf);
20@@ -380,8 +377,6 @@ main(int argc, char **argv)
21
22 rc = buildsubgraph(&sg);
23 if (rc < 0) {
24- if (rc != -2)
25- fprintf(stderr, "graph error in %s\n", path ? path : "(default)");
26 free(pathbuf);
27 freesubgraph(&sg);
28 return rc == -2 ? 2 : 1;
+7,
-5
1@@ -17,10 +17,7 @@ struct Buf {
2 static void
3 evalerr(struct EvalCtx *ctx, const char *msg, const char *detail)
4 {
5- if (detail)
6- fprintf(stderr, "eval: unsupported: %s: %s\n", msg, detail);
7- else
8- fprintf(stderr, "eval: unsupported: %s\n", msg);
9+ dielikemake(ctx->cur_path, ctx->cur_line, msg, detail);
10 ctx->errors++;
11 }
12
13@@ -553,7 +550,12 @@ expandref(struct EvalCtx *ctx, const char *s, size_t n)
14 val = funcref(ctx, s, n);
15 if (val)
16 return val;
17- return expandvarref(ctx, s, n);
18+ {
19+ char *detail = xstrndup(s, n);
20+ evalerr(ctx, "i don't know how to handle that yet", detail);
21+ free(detail);
22+ }
23+ return xstrdup("");
24 }
25
26 char *
+8,
-2
1@@ -244,7 +244,7 @@ evalinclude(struct EvalCtx *ctx, const struct IncludeNode *inc)
2 src = readfile(paths.v[i]);
3 if (!src) {
4 if (!inc->optional) {
5- fprintf(stderr, "eval: include not found: %s\n", paths.v[i]);
6+ dielikemake(ctx->cur_path, ctx->cur_line, "include not found", paths.v[i]);
7 ctx->errors++;
8 freestrs(&paths);
9 return -1;
10@@ -274,6 +274,7 @@ evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
11 const struct Node *src;
12
13 src = &in->v[i];
14+ ctx->cur_line = src->loc.line0;
15 switch (src->kind) {
16 case NODE_BLANK:
17 break;
18@@ -327,17 +328,21 @@ int
19 evalsnippet(struct EvalCtx *ctx, const char *path, const char *src)
20 {
21 struct Ast ast;
22+ const char *saved_path;
23 int rc;
24
25 if (parse(path, src, &ast) < 0)
26 return -1;
27+ saved_path = ctx->cur_path;
28+ ctx->cur_path = path;
29 rc = evalnodes((const struct NodeList *)&ast, ctx->out, ctx);
30+ ctx->cur_path = saved_path;
31 freeast(&ast);
32 return rc;
33 }
34
35 int
36-eval(const struct Ast *ast, const struct Ast *pre, int envoverride, struct RuleSet *out)
37+eval(const char *path, const struct Ast *ast, const struct Ast *pre, int envoverride, struct RuleSet *out)
38 {
39 struct Env env;
40 struct EvalCtx ctx;
41@@ -350,6 +355,7 @@ eval(const struct Ast *ast, const struct Ast *pre, int envoverride, struct RuleS
42 seedenv(&env, 0, out->envoverride);
43 ctx.env = &env;
44 ctx.out = out;
45+ ctx.cur_path = path;
46 if (pre) {
47 rc = evalnodes((const struct NodeList *)pre, out, &ctx);
48 if (rc < 0) {
+6,
-18
1@@ -541,37 +541,25 @@ buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack)
2 assignsrc = appendassigns(xstrdup(""), &sg->assigns);
3 rc = parse("<command line>", assignsrc, &pre);
4 free(assignsrc);
5- if (rc < 0) {
6- if (rc != -2)
7- fprintf(stderr, "parse error in command line assignments\n");
8+ if (rc < 0)
9 goto out;
10- }
11 for (i = 0; i < pre.n; i++) {
12 if (pre.v[i].kind == NODE_ASSIGN)
13 pre.v[i].data.assign.origin = ORIGIN_COMMAND;
14 }
15 }
16 rc = parse(path, src, &ast);
17- if (rc < 0) {
18- if (rc != -2)
19- fprintf(stderr, "parse error in %s/%s\n", sg->cwd, path);
20+ if (rc < 0)
21 goto out;
22- }
23- rc = eval(&ast, sg->assigns.n ? &pre : 0, envoverride, &rs);
24- if (rc < 0) {
25- fprintf(stderr, "eval error in %s/%s\n", sg->cwd, path);
26+ rc = eval(path, &ast, sg->assigns.n ? &pre : 0, envoverride, &rs);
27+ if (rc < 0)
28 goto out;
29- }
30 rc = buildgraph(&rs, &sg->graph);
31- if (rc < 0) {
32- fprintf(stderr, "graph error in %s/%s\n", sg->cwd, path);
33+ if (rc < 0)
34 goto out;
35- }
36 rc = expandgraph(&sg->graph);
37- if (rc < 0) {
38- fprintf(stderr, "expand error in %s/%s\n", sg->cwd, path);
39+ if (rc < 0)
40 goto out;
41- }
42 scopegraph(&sg->graph, sg->prefix);
43 rc = expandsubgraphs(sg, stack);
44 out:
+3,
-0
1@@ -19,6 +19,8 @@ struct EvalCtx {
2 int errors;
3 int avoid_io;
4 struct StrList *side_effects;
5+ const char *cur_path;
6+ int cur_line;
7 };
8
9 struct SpecialTargets {
10@@ -28,6 +30,7 @@ struct SpecialTargets {
11 };
12
13 void *xmalloc(size_t n);
14+void dielikemake(const char *path, int line, const char *msg, const char *detail);
15 void *xrealloc(void *p, size_t n);
16 char *xstrndup(const char *s, size_t n);
17 char *xstrdup(const char *s);
+28,
-38
1@@ -77,12 +77,8 @@ addwords_ast(struct StrList *dest, const struct StrList *src)
2 static void
3 parseerr(const struct PreLine *line, const char *msg, const char *detail)
4 {
5- if (detail)
6- fprintf(stderr, "%s:%d: unsupported: %s: %s\n",
7- line->path, line->line0, msg, detail);
8- else
9- fprintf(stderr, "%s:%d: unsupported: %s\n",
10- line->path, line->line0, msg);
11+ dielikemake(line->path, line->line0, msg, detail);
12+ deadlikemake = 1;
13 parseerrs++;
14 }
15
16@@ -93,15 +89,6 @@ parsewarn(const struct PreLine *line, const char *msg)
17 line->path, line->line0, msg);
18 }
19
20-static void
21-dielikemake(const struct PreLine *line, const char *msg)
22-{
23- fprintf(stderr, "%s:%d: *** %s. Stop.\n",
24- line->path, line->line0, msg);
25- deadlikemake = 1;
26- parseerrs++;
27-}
28-
29 static const char *const unsupported_kws[] = {
30 "unexport",
31 "undefine", "vpath", "private", "load", 0};
32@@ -561,7 +548,7 @@ preprocfile0(const char *path, const char *src_override, struct Pre *pre, struct
33 struct PreLine line;
34
35 if (hasinc(inc, path)) {
36- fprintf(stderr, "include cycle: %s\n", path);
37+ dielikemake(path, 0, "include cycle", path);
38 return -1;
39 }
40 if (src_override) {
41@@ -591,18 +578,20 @@ preprocfile0(const char *path, const char *src_override, struct Pre *pre, struct
42 opt = haskw(trim, "-include") || haskw(trim, "sinclude");
43 kwlen = haskw(trim, "-include") || haskw(trim, "sinclude") ? 8 : 7;
44 incarg = trimdup(trim + kwlen, strlen(trim + kwlen));
45- if (isplainpath(incarg)) {
46- free(line.path);
47- free(line.text);
48- free(trim);
49- rc = preprocinclude(dir, incarg, pre, inc, targets);
50- free(incarg);
51- if (rc < 0 && !opt) {
52- free(dir);
53- free(src);
54- popinc(inc);
55- return -1;
56- }
57+ if (isplainpath(incarg)) {
58+ free(trim);
59+ rc = preprocinclude(dir, incarg, pre, inc, targets);
60+ if (rc < 0 && !opt)
61+ parseerr(&line, "include not found", 0);
62+ free(incarg);
63+ free(line.path);
64+ free(line.text);
65+ if (rc < 0 && !opt) {
66+ free(dir);
67+ free(src);
68+ popinc(inc);
69+ return -1;
70+ }
71 continue;
72 }
73 free(incarg);
74@@ -874,9 +863,9 @@ parseraw(const struct PreLine *line, const char *s, char recipeprefix)
75
76 (void)s;
77 if (recipeprefix == '\t' && strncmp(line->text, " ", 8) == 0)
78- dielikemake(line, "missing separator (did you mean TAB instead of 8 spaces?)");
79+ parseerr(line, "missing separator (did you mean TAB instead of 8 spaces?)", 0);
80 else
81- dielikemake(line, "missing separator");
82+ parseerr(line, "missing separator", 0);
83 memset(&state, 0, sizeof(state));
84 state.kind = NODE_BLANK;
85 state.loc.line0 = line->line0;
86@@ -989,6 +978,7 @@ parsedefine(const struct Pre *pre, size_t *i, struct Node *out)
87 (*i)++;
88 }
89
90+ parseerr(&pre->v[*i > 0 ? *i - 1 : 0], "unterminated 'define'", 0);
91 free(body);
92 return -1;
93 }
94@@ -1044,7 +1034,7 @@ parseline(const struct PreLine *line, const struct SpecialTargets *targets)
95 return state;
96 }
97 if (strncmp(trim, "ifeq(", 5) == 0 || strncmp(trim, "ifneq(", 6) == 0) {
98- dielikemake(line, "missing separator (ifeq/ifneq must be followed by whitespace)");
99+ parseerr(line, "missing separator (ifeq/ifneq must be followed by whitespace)", 0);
100 free(trim);
101 return blanknode(line);
102 }
103@@ -1107,7 +1097,7 @@ parseline(const struct PreLine *line, const struct SpecialTargets *targets)
104 lhs = trimdup(trim, as.pos);
105 if (hasws(lhs)) {
106 free(lhs);
107- dielikemake(line, "missing separator");
108+ parseerr(line, "missing separator", 0);
109 free(trim);
110 return blanknode(line);
111 }
112@@ -1236,27 +1226,27 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
113 if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule, targets) < 0)
114 return -1;
115 if (*i >= pre->n) {
116- dielikemake(&pre->v[pre->n - 1], "missing 'endif'");
117+ parseerr(&pre->v[pre->n - 1], "missing 'endif'", 0);
118 return -1;
119 }
120 endline = &pre->v[*i];
121 endtrim = trimdup(endline->text, strlen(endline->text));
122 if (haskw(endtrim, "else")) {
123 free(endtrim);
124- dielikemake(endline, "only one 'else' per conditional");
125+ parseerr(endline, "only one 'else' per conditional", 0);
126 return -1;
127 }
128 }
129 if (!haskw(endtrim, "endif")) {
130 free(endtrim);
131- dielikemake(endline, "missing 'endif'");
132+ parseerr(endline, "missing 'endif'", 0);
133 return -1;
134 }
135 state.loc.line1 = endline->line1;
136 free(endtrim);
137 (*i)++;
138 } else {
139- dielikemake(&pre->v[pre->n - 1], "missing 'endif'");
140+ parseerr(&pre->v[pre->n - 1], "missing 'endif'", 0);
141 return -1;
142 }
143 addnode(out, state);
144@@ -1307,9 +1297,9 @@ buildast(const char *path, const struct Pre *pre, struct Ast *ast)
145
146 trim = trimdup(pre->v[i].text, strlen(pre->v[i].text));
147 if (haskw(trim, "endif"))
148- dielikemake(&pre->v[i], "extraneous 'endif'");
149+ parseerr(&pre->v[i], "extraneous 'endif'", 0);
150 else if (haskw(trim, "else"))
151- dielikemake(&pre->v[i], "extraneous 'else'");
152+ parseerr(&pre->v[i], "extraneous 'else'", 0);
153 free(trim);
154 }
155 if (parseerrs)
+1,
-1
1@@ -227,7 +227,7 @@ struct SubGraph {
2 int preproc(const char *path, struct Pre *pre);
3 void freepre(struct Pre *pre);
4 int buildast(const char *path, const struct Pre *pre, struct Ast *ast);
5-int eval(const struct Ast *ast, const struct Ast *pre, int envoverride, struct RuleSet *out);
6+int eval(const char *path, const struct Ast *ast, const struct Ast *pre, int envoverride, struct RuleSet *out);
7 int parse(const char *path, const char *src, struct Ast *ast);
8 void freeast(struct Ast *ast);
9 void freeruleset(struct RuleSet *ruleset);
+16,
-0
1@@ -660,3 +660,19 @@ envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin
2 env->v[env->n].exported = exported;
3 env->n++;
4 }
5+
6+void
7+dielikemake(const char *path, int line, const char *msg, const char *detail)
8+{
9+ if (path)
10+ fprintf(stderr, "%s:%d: *** %s%s%s. Stop.\n",
11+ path, line,
12+ msg,
13+ detail ? ": " : "",
14+ detail ? detail : "");
15+ else
16+ fprintf(stderr, "*** %s%s%s. Stop.\n",
17+ msg,
18+ detail ? ": " : "",
19+ detail ? detail : "");
20+}