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+}