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=$?