commit 2536dc6

shrub  ·  2026-05-15 17:04:20 +0000 UTC
parent 3ce4bce
propogate stderr/stout properly in runner, wrapper fixes, and misc error plumbing and fixes
8 files changed,  +113, -44
+5, -0
 1@@ -214,6 +214,7 @@ main(int argc, char **argv)
 2 		GEN_COMPDB,
 3 	};
 4 	const char *path;
 5+	const char *pname;
 6 	char *pathbuf;
 7 	char *src;
 8 	int dump_ast;
 9@@ -229,6 +230,10 @@ main(int argc, char **argv)
10 	struct RuleSet rs;
11 	struct SubGraph sg;
12 
13+	/* needed for test runner outputs to have accurate 'prog:' prefix */
14+	pname = getenv("SHIN_PROGNAME");
15+	if (pname)
16+		set_progname(pname);
17 	path = 0;
18 	pathbuf = 0;
19 	dump_ast = 0;
+56, -12
  1@@ -1,5 +1,6 @@
  2 #include "shinobi.h"
  3 #include "internal.h"
  4+#include "posix.h"
  5 
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8@@ -342,6 +343,29 @@ addrulesetassign(struct AssignNode **vec, size_t *n, const struct Node *src, str
  9 	copywords(&dst->targets, &src->data.assign.targets, ctx);
 10 }
 11 
 12+static int
 13+issufruletargets(const struct RuleNode *rule)
 14+{
 15+	size_t i;
 16+	char *a, *b;
 17+
 18+	if (rule->targets.n == 0)
 19+		return 0;
 20+	for (i = 0; i < rule->targets.n; i++) {
 21+		if (issinglesuf(rule->targets.v[i], &a)) {
 22+			free(a);
 23+			continue;
 24+		}
 25+		if (issuf(rule->targets.v[i], &a, &b)) {
 26+			free(a);
 27+			free(b);
 28+			continue;
 29+		}
 30+		return 0;
 31+	}
 32+	return 1;
 33+}
 34+
 35 static void
 36 addrulesetrule(struct RuleSet *out, const struct RuleNode *src, struct EvalCtx *ctx)
 37 {
 38@@ -373,12 +397,9 @@ evalinclude(struct EvalCtx *ctx, const struct IncludeNode *inc)
 39 
 40 		src = readfile(paths.v[i]);
 41 		if (!src) {
 42-			if (!inc->optional) {
 43-				dielikemake(ctx->cur_path, ctx->cur_line, "include not found", paths.v[i]);
 44-				ctx->errors++;
 45-				freestrs(&paths);
 46-				return -1;
 47-			}
 48+			if (!inc->optional)
 49+				fprintf(stderr, "%s:%d: %s: No such file or directory\n",
 50+				        ctx->cur_path, ctx->cur_line, paths.v[i]);
 51 			continue;
 52 		}
 53 		if (evalsnippet(ctx, paths.v[i], src) < 0) {
 54@@ -442,23 +463,46 @@ evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
 55 		case NODE_EXPORT:
 56 			evalexport(ctx, &src->data.export);
 57 			break;
 58-		case NODE_RULE:
 59-			if (src->data.rule.targets.n == 1 &&
 60-			    strcmp(src->data.rule.targets.v[0], ".EXPORT_ALL_VARIABLES") == 0) {
 61+		case NODE_RULE: {
 62+			struct StrList exptargets;
 63+			struct RuleNode tmprule;
 64+
 65+			/* expand targets before special target checks so that some pattern like
 66+			 * "$X.POSIX:" with X undefined correctly resolves to just ".POSIX:" and
 67+			 * set targets.posix before suffix rule prereq warnings fire
 68+			 * (this is for gnu/features/suffixrules/t009). */
 69+			memset(&exptargets, 0, sizeof(exptargets));
 70+			copywords(&exptargets, &src->data.rule.targets, ctx);
 71+			tmprule = src->data.rule;
 72+			tmprule.targets = exptargets;
 73+			if (tmprule.targets.n == 1 &&
 74+			    strcmp(tmprule.targets.v[0], ".EXPORT_ALL_VARIABLES") == 0) {
 75 				ctx->export_all = 1;
 76+				freestrs(&exptargets);
 77 				break;
 78 			}
 79-			if (src->data.rule.targets.n == 1 &&
 80-			    strcmp(src->data.rule.targets.v[0], ".DEFAULT") == 0) {
 81+			if (tmprule.targets.n == 1 &&
 82+			    strcmp(tmprule.targets.v[0], ".DEFAULT") == 0) {
 83 				freerecipes(&out->defaultrule);
 84 				addrecipes(&out->defaultrule, &src->data.rule.recipes);
 85+				freestrs(&exptargets);
 86 				break;
 87 			}
 88-			if (handlespecialrule(&targets, &src->data.rule))
 89+			if (handlespecialrule(&targets, &tmprule)) {
 90+				freestrs(&exptargets);
 91 				break;
 92+			}
 93+			if (!targets.posix &&
 94+			    src->data.rule.prereqs.n > 0 &&
 95+			    !issufrule(&tmprule) &&
 96+			    issufruletargets(&tmprule))
 97+				warnlikemake(ctx->cur_path, ctx->cur_line,
 98+				             "ignoring prerequisites on suffix rule definition");
 99+			freestrs(&exptargets);
100 			addrulesetrule(out, &src->data.rule, ctx);
101 			break;
102 		}
103+		}
104 	}
105 	if (out)
106 		addwords(&out->phony, &targets.phony);
+2, -0
1@@ -34,6 +34,8 @@ struct SpecialTargets {
2 };
3 
4 void *xmalloc(size_t n);
5+void set_progname(const char *name);
6+void warnlikemake(const char *path, int line, const char *msg);
7 void dielikemake(const char *path, int line, const char *msg, const char *detail);
8 void *xrealloc(void *p, size_t n);
9 char *xstrndup(const char *s, size_t n);
+23, -18
 1@@ -82,12 +82,6 @@ parseerr(const struct PreLine *line, const char *msg, const char *detail)
 2 	parseerrs++;
 3 }
 4 
 5-static void
 6-parsewarn(const struct PreLine *line, const char *msg)
 7-{
 8-	fprintf(stderr, "%s:%d: warning: %s\n",
 9-	        line->path, line->line0, msg);
10-}
11 
12 static const char *const unsupported_kws[] = {
13     "undefine", "vpath", "private", "load", 0};
14@@ -580,17 +574,13 @@ preprocfile0(const char *path, const char *src_override, struct Pre *pre, struct
15 				if (isplainpath(incarg)) {
16 					free(trim);
17 					rc = preprocinclude(dir, incarg, pre, inc, targets);
18-					if (rc < 0 && !opt)
19-						parseerr(&line, "include not found", 0);
20+					if (rc < 0 && !opt) {
21+						fprintf(stderr, "%s:%d: %s: No such file or directory\n",
22+						        line.path, line.line0, incarg);
23+					}
24 					free(incarg);
25 					free(line.path);
26 					free(line.text);
27-					if (rc < 0 && !opt) {
28-						free(dir);
29-						free(src);
30-						popinc(inc);
31-						return -1;
32-					}
33 					continue;
34 				}
35 				free(incarg);
36@@ -1174,13 +1164,28 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
37 		line = &pre->v[*i];
38 		if (line->isrecipe) {
39 			char *rt;
40-			int condtab;
41+			const char *tabwarn;
42 
43 			rt = trimdup(line->text + 1, strlen(line->text + 1));
44-			condtab = iscondkw(rt);
45+			tabwarn = 0;
46+			if (iscondkw(rt)) {
47+				tabwarn = "conditional directive lines cannot start with TAB";
48+			} else if (haskw(rt, "-include")) {
49+				tabwarn = "-include lines cannot start with TAB";
50+			} else if (haskw(rt, "sinclude")) {
51+				tabwarn = "sinclude lines cannot start with TAB";
52+			} else if (haskw(rt, "include")) {
53+				tabwarn = "include lines cannot start with TAB";
54+			} else if (haskw(rt, "override") || haskw(rt, "export") ||
55+			           haskw(rt, "unexport") || haskw(rt, "define") ||
56+			           haskw(rt, "endef") || haskw(rt, "undefine") ||
57+			           haskw(rt, "vpath") || haskw(rt, "private") ||
58+			           haskw(rt, "load")) {
59+				tabwarn = "directive lines cannot start with TAB";
60+			}
61 			free(rt);
62-			if (condtab) {
63-				parsewarn(line, "conditional directive lines cannot start with TAB");
64+			if (tabwarn) {
65+				warnlikemake(line->path, line->line0, tabwarn);
66 				memmove(line->text, line->text + 1, strlen(line->text + 1) + 1);
67 				line->isrecipe = 0;
68 			}
+5, -5
 1@@ -70,17 +70,17 @@ struct PreLine {
 2 	char recipeprefix;
 3 };
 4 
 5-struct Pre {
 6-	struct PreLine *v;
 7-	size_t n;
 8-};
 9-
10 struct StrList {
11 	char **v;
12 	size_t n;
13 	size_t cap;
14 };
15 
16+struct Pre {
17+	struct PreLine *v;
18+	size_t n;
19+};
20+
21 struct SubMake {
22 	char *makeprog;
23 	char *dir;
+17, -2
 1@@ -8,6 +8,14 @@
 2 
 3 /* shared utility functions */
 4 
 5+static const char *progname = "shin";
 6+
 7+void
 8+set_progname(const char *name)
 9+{
10+	progname = name;
11+}
12+
13 void *
14 xmalloc(size_t n)
15 {
16@@ -719,6 +727,12 @@ envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin
17 	env->n++;
18 }
19 
20+void
21+warnlikemake(const char *path, int line, const char *msg)
22+{
23+	fprintf(stderr, "%s:%d: warning: %s\n", path, line, msg);
24+}
25+
26 void
27 dielikemake(const char *path, int line, const char *msg, const char *detail)
28 {
29@@ -729,8 +743,9 @@ dielikemake(const char *path, int line, const char *msg, const char *detail)
30 		        detail ? ": " : "",
31 		        detail ? detail : "");
32 	else
33-		fprintf(stderr, "*** %s%s%s.  Stop.\n",
34+		fprintf(stderr, "%s: *** %s%s%s.  Stop.\n",
35+		        progname,
36 		        msg,
37-		        detail ? ": " : "",
38+		        detail ? " " : "",
39 		        detail ? detail : "");
40 }
+4, -6
 1@@ -358,17 +358,15 @@ func executecase(cmd *exec.Cmd, workdir string, env []string, stdin string, time
 2 	wrapped := exec.CommandContext(ctx, cmd.Path, cmd.Args[1:]...)
 3 	wrapped.Dir = workdir
 4 	wrapped.Env = env
 5-	wrapped.Stderr = &bytes.Buffer{}
 6-	wrapped.Stdout = &bytes.Buffer{}
 7+	var combined bytes.Buffer
 8+	wrapped.Stderr = &combined
 9+	wrapped.Stdout = &combined
10 	if stdin != "" {
11 		wrapped.Stdin = strings.NewReader(stdin)
12 	}
13 
14-	stdoutbuf := wrapped.Stdout.(*bytes.Buffer)
15-	stderrbuf := wrapped.Stderr.(*bytes.Buffer)
16 	err := wrapped.Run()
17-	output := append([]byte{}, stdoutbuf.Bytes()...)
18-	output = append(output, stderrbuf.Bytes()...)
19+	output := combined.Bytes()
20 
21 	timedout := errors.Is(ctx.Err(), context.DeadlineExceeded)
22 	if timedout {
+1, -1
1@@ -249,7 +249,7 @@ if [ -n "$targets" ]; then
2 	IFS=$oldifs
3 fi
4 
5-if "$@"; then
6+if SHIN_PROGNAME="${0##*/}" "$@"; then
7 	:
8 else
9 	rc=$?