commit d283034
shrub
·
2026-04-22 09:34:23 +0000 UTC
parent f57d326
handle recipeprefix and posix targets
13 files changed,
+189,
-26
M
Makefile
+1,
-0
1@@ -10,6 +10,7 @@ SRCS = cli/main.c \
2 src/graph/graph.c \
3 src/graph/subgraph.c \
4 src/posix.c \
5+ src/targets.c \
6 src/gnu/pattern.c \
7 backends/ninja.c \
8 backends/graphviz.c \
+7,
-0
1@@ -215,6 +215,7 @@ main(int argc, char **argv)
2 char *src;
3 int dump_ast;
4 int dump_graph;
5+ int env_override;
6 int i;
7 char **assigns;
8 size_t nassigns;
9@@ -227,6 +228,7 @@ main(int argc, char **argv)
10 pathbuf = 0;
11 dump_ast = 0;
12 dump_graph = 0;
13+ env_override = 0;
14 gen = GEN_NINJA;
15 assigns = 0;
16 nassigns = 0;
17@@ -280,6 +282,8 @@ main(int argc, char **argv)
18 }
19 ++i;
20 path = argv[i];
21+ } else if (strcmp(argv[i], "-e") == 0) {
22+ env_override = 1;
23 } else if (isvarassign(argv[i])) {
24 assigns = realloc(assigns, (nassigns + 1) * sizeof(assigns[0]));
25 if (!assigns) {
26@@ -342,6 +346,7 @@ main(int argc, char **argv)
27 freesubgraph(&sg);
28 return 1;
29 }
30+ rs.envoverride = env_override;
31 dumpruleset(&rs);
32 freeruleset(&rs);
33 freeast(&ast);
34@@ -349,6 +354,8 @@ main(int argc, char **argv)
35 }
36 if (path)
37 sg.makefile = xstrdup(path);
38+ if (env_override)
39+ addstr(&sg.flags, "-e");
40 free(assigns);
41 assigns = 0;
42 if (buildsubgraph(&sg) < 0) {
+9,
-1
1@@ -212,6 +212,9 @@ static int
2 evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
3 {
4 size_t i;
5+ struct SpecialTargets targets;
6+
7+ initspecialtargets(&targets);
8
9 for (i = 0; i < in->n; i++) {
10 const struct Node *src;
11@@ -243,6 +246,7 @@ evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
12 }
13 continue;
14 case NODE_ASSIGN:
15+ updatespecialassign(&targets, src->data.assign.lhs, src->data.assign.rhs);
16 if (src->data.assign.tspec) {
17 addrulesetassign(&out->tvars, &out->ntvars, src, ctx);
18 } else {
19@@ -251,10 +255,14 @@ evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
20 }
21 break;
22 case NODE_RULE:
23+ if (handlespecialrule(&targets, &src->data.rule))
24+ break;
25 addrulesetrule(out, &src->data.rule, ctx);
26 break;
27 }
28 }
29+ if (out)
30+ out->posix = targets.posix;
31 return 0;
32 }
33
34@@ -281,7 +289,7 @@ eval(const struct Ast *ast, struct RuleSet *out)
35 memset(out, 0, sizeof(*out));
36 memset(&env, 0, sizeof(env));
37 memset(&ctx, 0, sizeof(ctx));
38- seedenv(&env);
39+ seedenv(&env, 0, out->envoverride);
40 ctx.env = &env;
41 ctx.out = out;
42 rc = evalnodes((const struct NodeList *)ast, out, &ctx);
+6,
-0
1@@ -539,9 +539,15 @@ fnorigin(struct EvalCtx *ctx, const char *args)
2 case ORIGIN_DEFAULT:
3 origin = "default";
4 break;
5+ case ORIGIN_ENV:
6+ origin = "environment";
7+ break;
8 case ORIGIN_FILE:
9 origin = "file";
10 break;
11+ case ORIGIN_ENV_OVERRIDE:
12+ origin = "environment override";
13+ break;
14 case ORIGIN_COMMAND:
15 origin = "command line";
16 break;
+2,
-2
1@@ -180,11 +180,11 @@ buildgraph(const struct RuleSet *ruleset, struct Graph *graph)
2 memset(&gs, 0, sizeof(gs));
3 memset(&ctx, 0, sizeof(ctx));
4 gs.graph = graph;
5- seedenv(&gs.env);
6+ seedenv(&gs.env, ruleset->posix, ruleset->envoverride);
7 gs.rules = ruleset->rules;
8 gs.nrules = ruleset->nrules;
9 ctx.env = &gs.env;
10- imprules(&gs.sufs);
11+ imprules(&gs.sufs, ruleset->posix);
12
13 for (i = 0; i < ruleset->nvars; i++)
14 evalassign(&ctx, &ruleset->vars[i]);
+10,
-0
1@@ -487,6 +487,8 @@ buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack)
2 int rc;
3 size_t marked;
4 size_t k;
5+ size_t i;
6+ int envoverride;
7
8 memset(&ast, 0, sizeof(ast));
9 memset(&rs, 0, sizeof(rs));
10@@ -519,6 +521,13 @@ buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack)
11 }
12 pushstack(stack, key);
13 free(key);
14+ envoverride = 0;
15+ for (i = 0; i < sg->flags.n; i++) {
16+ if (strcmp(sg->flags.v[i], "-e") == 0) {
17+ envoverride = 1;
18+ break;
19+ }
20+ }
21 src = appendassigns(src, &sg->assigns);
22 rc = parse(path, src, &ast);
23 if (rc < 0) {
24@@ -539,6 +548,7 @@ buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack)
25 fprintf(stderr, "eval error in %s/%s\n", sg->cwd, path);
26 goto out;
27 }
28+ rs.envoverride = envoverride;
29 rc = buildgraph(&rs, &sg->graph);
30 if (rc < 0) {
31 fprintf(stderr, "graph error in %s/%s\n", sg->cwd, path);
+9,
-1
1@@ -21,6 +21,11 @@ struct EvalCtx {
2 struct StrList *side_effects;
3 };
4
5+struct SpecialTargets {
6+ int posix;
7+ char recipeprefix;
8+};
9+
10 void *xmalloc(size_t n);
11 void *xrealloc(void *p, size_t n);
12 char *xstrndup(const char *s, size_t n);
13@@ -50,10 +55,13 @@ int parsesubmake(struct SubMake *dst, const char *cmd);
14 void envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin origin);
15 struct Target *findtarget(struct Graph *graph, const char *name);
16 const struct Target *findctarget(const struct Graph *graph, const char *name);
17+void initspecialtargets(struct SpecialTargets *targets);
18+void updatespecialassign(struct SpecialTargets *targets, const char *lhs, const char *rhs);
19+int handlespecialrule(struct SpecialTargets *targets, const struct RuleNode *rule);
20
21 struct Var *findvar(struct Env *env, const char *name);
22 char *expandstr(struct EvalCtx *ctx, const char *s);
23-void seedenv(struct Env *env);
24+void seedenv(struct Env *env, int posix, int envoverride);
25 void freeenv(struct Env *env);
26 void copyenv(struct Env *dst, const struct Env *src);
27 void evalassign(struct EvalCtx *ctx, const struct AssignNode *in);
+52,
-13
1@@ -32,6 +32,8 @@ struct Inc {
2 static int parseerrs;
3
4 static int preprocfile(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc);
5+static int preprocfile0(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc,
6+ struct SpecialTargets *targets);
7
8 static void
9 parseerr(const struct PreLine *line, const char *msg, const char *detail)
10@@ -253,7 +255,7 @@ splitwords(struct StrList *out, const char *s, size_t n)
11 }
12
13 static int
14-readline(const char **src, int *lineno, struct PreLine *line)
15+readline(const char **src, int *lineno, struct PreLine *line, char recipeprefix)
16 {
17 const char *p, *start;
18 size_t cap, len, chunk;
19@@ -274,6 +276,7 @@ readline(const char **src, int *lineno, struct PreLine *line)
20 comment_cont = 0;
21 first = 1;
22 line->isrecipe = 0;
23+ line->recipeprefix = 0;
24
25 for (;;) {
26 start = p;
27@@ -283,8 +286,10 @@ readline(const char **src, int *lineno, struct PreLine *line)
28 if (*p == '\n')
29 p++;
30
31- if (first && chunk > 0 && start[0] == '\t')
32+ if (first && chunk > 0 && start[0] == recipeprefix) {
33 line->isrecipe = 1;
34+ line->recipeprefix = recipeprefix;
35+ }
36 /* a line that starts as a comment stays a comment across
37 backslash line joins. this is a hack that bearssl uses
38 to hide microsoft nmake directives, so nmake sees them
39@@ -372,11 +377,12 @@ dirpart(const char *path)
40 }
41
42 static int
43-preprocinclude(const char *dir, const char *incarg, struct Pre *pre, struct Inc *inc)
44+preprocinclude(const char *dir, const char *incarg, struct Pre *pre, struct Inc *inc,
45+ struct SpecialTargets *targets)
46 {
47 int rc;
48
49- rc = preprocfile(incarg, 0, pre, inc);
50+ rc = preprocfile0(incarg, 0, pre, inc, targets);
51 if (rc == 0)
52 return 0;
53 if (strcmp(dir, ".") == 0)
54@@ -385,14 +391,15 @@ preprocinclude(const char *dir, const char *incarg, struct Pre *pre, struct Inc
55 char *full;
56
57 full = joinpath(dir, incarg);
58- rc = preprocfile(full, 0, pre, inc);
59+ rc = preprocfile0(full, 0, pre, inc, targets);
60 free(full);
61 return rc;
62 }
63 }
64
65 static int
66-preprocfile(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc)
67+preprocfile0(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc,
68+ struct SpecialTargets *targets)
69 {
70 char *src, *dir;
71 const char *p;
72@@ -415,7 +422,7 @@ preprocfile(const char *path, const char *src_override, struct Pre *pre, struct
73 dir = dirpart(path);
74 p = src;
75 lineno = 1;
76- while (readline(&p, &lineno, &line)) {
77+ while (readline(&p, &lineno, &line, targets->recipeprefix)) {
78 line.path = xstrdup(path);
79 if (!line.isrecipe) {
80 char *trim, *incarg;
81@@ -434,7 +441,7 @@ preprocfile(const char *path, const char *src_override, struct Pre *pre, struct
82 free(line.path);
83 free(line.text);
84 free(trim);
85- rc = preprocinclude(dir, incarg, pre, inc);
86+ rc = preprocinclude(dir, incarg, pre, inc, targets);
87 free(incarg);
88 if (rc < 0 && !opt) {
89 free(dir);
90@@ -446,6 +453,24 @@ preprocfile(const char *path, const char *src_override, struct Pre *pre, struct
91 }
92 free(incarg);
93 }
94+ {
95+ size_t n;
96+ ptrdiff_t colon;
97+ struct AssignScan as;
98+
99+ n = strlen(trim);
100+ colon = findtop(trim, n, ':');
101+ as = findassign(trim, n, 0);
102+ if (as.ok && (colon < 0 || as.pos <= (size_t)colon)) {
103+ char *lhs, *rhs;
104+
105+ lhs = trimdup(trim, as.pos);
106+ rhs = trimdup(trim + as.pos + as.len, n - as.pos - as.len);
107+ updatespecialassign(targets, lhs, rhs);
108+ free(lhs);
109+ free(rhs);
110+ }
111+ }
112 free(trim);
113 }
114 pre->v = xrealloc(pre->v, (pre->n + 1) * sizeof(pre->v[0]));
115@@ -457,6 +482,15 @@ preprocfile(const char *path, const char *src_override, struct Pre *pre, struct
116 return 0;
117 }
118
119+static int
120+preprocfile(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc)
121+{
122+ struct SpecialTargets targets;
123+
124+ initspecialtargets(&targets);
125+ return preprocfile0(path, src_override, pre, inc, &targets);
126+}
127+
128 int
129 preproc(const char *path, struct Pre *pre)
130 {
131@@ -887,7 +921,8 @@ parseline(const struct PreLine *line)
132 }
133
134 static int
135-parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node **last_rulep)
136+parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node **last_rulep,
137+ struct SpecialTargets *targets)
138 {
139 struct Node state;
140 struct Node *last_rule;
141@@ -941,7 +976,7 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
142 state = parsecond(line, trim);
143 free(trim);
144 (*i)++;
145- if (parseblock(pre, i, &state.data.cond.thenpart, &last_rule) < 0)
146+ if (parseblock(pre, i, &state.data.cond.thenpart, &last_rule, targets) < 0)
147 return -1;
148 if (*i < pre->n) {
149 struct PreLine *endline;
150@@ -961,7 +996,7 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
151 free(endline->text);
152 endline->text = rep;
153 free(endtrim);
154- if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule) < 0)
155+ if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule, targets) < 0)
156 return -1;
157 state.loc.line1 = endline->line1;
158 addnode(out, state);
159@@ -971,7 +1006,7 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
160 }
161 free(endtrim);
162 (*i)++;
163- if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule) < 0)
164+ if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule, targets) < 0)
165 return -1;
166 if (*i >= pre->n)
167 return -1;
168@@ -996,6 +1031,8 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
169 free(trim);
170
171 state = parseline(line);
172+ if (state.kind == NODE_ASSIGN)
173+ updatespecialassign(targets, state.data.assign.lhs, state.data.assign.rhs);
174 addnode(out, state);
175 if (state.kind == NODE_RULE)
176 last_rule = &out->v[out->n - 1];
177@@ -1012,12 +1049,14 @@ int
178 buildast(const char *path, const struct Pre *pre, struct Ast *ast)
179 {
180 size_t i;
181+ struct SpecialTargets targets;
182
183 (void)path;
184 memset(ast, 0, sizeof(*ast));
185 parseerrs = 0;
186+ initspecialtargets(&targets);
187 i = 0;
188- if (parseblock(pre, &i, (struct NodeList *)ast, 0) < 0)
189+ if (parseblock(pre, &i, (struct NodeList *)ast, 0, &targets) < 0)
190 return -1;
191 if (parseerrs)
192 return -1;
+36,
-6
1@@ -4,6 +4,8 @@
2 #include <stdlib.h>
3 #include <string.h>
4
5+extern char **environ;
6+
7 static int
8 sufeq(const char *a, const char *b)
9 {
10@@ -215,8 +217,10 @@ expandauto(const char *s, const struct Target *t, const char *stem)
11 }
12
13 void
14-seedenv(struct Env *env)
15+seedenv(struct Env *env, int posix, int envoverride)
16 {
17+ size_t i;
18+
19 envsetvar(env, "CC", xstrdup("cc"), 1, ORIGIN_DEFAULT);
20 envsetvar(env, "CFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);
21 envsetvar(env, "CXX", xstrdup("c++"), 1, ORIGIN_DEFAULT);
22@@ -238,10 +242,28 @@ seedenv(struct Env *env)
23 envsetvar(env, "YACC", xstrdup("yacc"), 1, ORIGIN_DEFAULT);
24 envsetvar(env, "YFLAGS", xstrdup(""), 1, ORIGIN_DEFAULT);
25 envsetvar(env, "SHELL", xstrdup("/bin/sh"), 1, ORIGIN_DEFAULT);
26+ envsetvar(env, "MAKE", xstrdup("make"), 1, ORIGIN_DEFAULT);
27+ if (posix) {
28+ envsetvar(env, "CC", xstrdup("c99"), 1, ORIGIN_DEFAULT);
29+ envsetvar(env, "CFLAGS", xstrdup("-O1"), 1, ORIGIN_DEFAULT);
30+ envsetvar(env, "FFLAGS", xstrdup("-O1"), 1, ORIGIN_DEFAULT);
31+ }
32+ for (i = 0; environ && environ[i]; i++) {
33+ const char *eq;
34+ char *name;
35+
36+ eq = strchr(environ[i], '=');
37+ if (!eq || eq == environ[i])
38+ continue;
39+ name = xstrndup(environ[i], (size_t)(eq - environ[i]));
40+ envsetvar(env, name, xstrdup(eq + 1), 1,
41+ envoverride ? ORIGIN_ENV_OVERRIDE : ORIGIN_ENV);
42+ free(name);
43+ }
44 }
45
46 void
47-imprules(struct SufRules *rules)
48+imprules(struct SufRules *rules, int posix)
49 {
50 static const char *const c_bin[] = {
51 "$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<",
52@@ -253,14 +275,18 @@ imprules(struct SufRules *rules)
53 "cp $< $@",
54 "chmod a+x $@",
55 };
56- /* TODO -o here is NOT POSIX. im keeping it here for now for gnu compatibility,
57- * because it's conveinient. when .POSIX is passed, we should drop the -o. */
58 static const char *const c_o[] = {
59 "$(CC) $(CFLAGS) -c -o $@ $<",
60 };
61+ static const char *const c_o_posix[] = {
62+ "$(CC) $(CFLAGS) -c $<",
63+ };
64 static const char *const f_o[] = {
65 "$(FC) $(FFLAGS) -c -o $@ $<",
66 };
67+ static const char *const f_o_posix[] = {
68+ "$(FC) $(FFLAGS) -c $<",
69+ };
70 static const char *const y_o[] = {
71 "$(YACC) $(YFLAGS) $<",
72 "$(CC) $(CFLAGS) -c y.tab.c",
73@@ -299,8 +325,12 @@ imprules(struct SufRules *rules)
74 addimpsingle(rules, ".c", c_bin, sizeof(c_bin) / sizeof(c_bin[0]));
75 addimpsingle(rules, ".f", f_bin, sizeof(f_bin) / sizeof(f_bin[0]));
76 addimpsingle(rules, ".sh", sh_bin, sizeof(sh_bin) / sizeof(sh_bin[0]));
77- addimpdouble(rules, ".c", ".o", c_o, sizeof(c_o) / sizeof(c_o[0]));
78- addimpdouble(rules, ".f", ".o", f_o, sizeof(f_o) / sizeof(f_o[0]));
79+ addimpdouble(rules, ".c", ".o",
80+ posix ? c_o_posix : c_o,
81+ posix ? sizeof(c_o_posix) / sizeof(c_o_posix[0]) : sizeof(c_o) / sizeof(c_o[0]));
82+ addimpdouble(rules, ".f", ".o",
83+ posix ? f_o_posix : f_o,
84+ posix ? sizeof(f_o_posix) / sizeof(f_o_posix[0]) : sizeof(f_o) / sizeof(f_o[0]));
85 addimpdouble(rules, ".y", ".o", y_o, sizeof(y_o) / sizeof(y_o[0]));
86 addimpdouble(rules, ".l", ".o", l_o, sizeof(l_o) / sizeof(l_o[0]));
87 addimpdouble(rules, ".y", ".c", y_c, sizeof(y_c) / sizeof(y_c[0]));
+2,
-2
1@@ -35,8 +35,8 @@ struct SufRules {
2
3 int issinglesuf(const char *s, char **from);
4 int issuf(const char *s, char **from, char **to);
5-void seedenv(struct Env *env);
6-void imprules(struct SufRules *rules);
7+void seedenv(struct Env *env, int posix, int envoverride);
8+void imprules(struct SufRules *rules, int posix);
9 int collectsufrule(struct SufRules *rules, const struct RuleNode *rule);
10 int instsufrule(const struct SufRules *rules,
11 const struct Graph *graph,
+5,
-0
1@@ -31,7 +31,9 @@ enum AssignOp {
2
3 enum Origin {
4 ORIGIN_DEFAULT,
5+ ORIGIN_ENV,
6 ORIGIN_FILE,
7+ ORIGIN_ENV_OVERRIDE,
8 ORIGIN_COMMAND,
9 ORIGIN_OVERRIDE,
10 };
11@@ -56,6 +58,7 @@ struct PreLine {
12 int line0;
13 int line1;
14 int isrecipe;
15+ char recipeprefix;
16 };
17
18 struct Pre {
19@@ -157,6 +160,8 @@ struct RuleSet {
20 size_t ntvars;
21 struct RuleNode *rules;
22 size_t nrules;
23+ int posix;
24+ int envoverride;
25 };
26
27 struct Var {
+34,
-0
1@@ -0,0 +1,34 @@
2+#include "internal.h"
3+
4+#include <ctype.h>
5+#include <string.h>
6+
7+void
8+initspecialtargets(struct SpecialTargets *targets)
9+{
10+ targets->posix = 0;
11+ targets->recipeprefix = '\t';
12+}
13+
14+void
15+updatespecialassign(struct SpecialTargets *targets, const char *lhs, const char *rhs)
16+{
17+ size_t i;
18+
19+ if (strcmp(lhs, ".RECIPEPREFIX") != 0)
20+ return;
21+ for (i = 0; rhs[i] && isspace((unsigned char)rhs[i]); i++)
22+ ;
23+ if (rhs[i])
24+ targets->recipeprefix = rhs[i];
25+}
26+
27+int
28+handlespecialrule(struct SpecialTargets *targets, const struct RuleNode *rule)
29+{
30+ if (rule->targets.n == 1 && strcmp(rule->targets.v[0], ".POSIX") == 0) {
31+ targets->posix = 1;
32+ return 1;
33+ }
34+ return 0;
35+}
+16,
-1
1@@ -14,6 +14,7 @@ dryrun=0
2 jobs=
3 targets=
4 vars=
5+pass_flags=
6
7 lie() {
8 printf '%s\n' 'GNU Make 4.4'
9@@ -127,7 +128,12 @@ while [ "$#" -gt 0 ]; do
10 dryrun=1
11 shift
12 ;;
13- --no-print-directory|--warn-undefined-variables|-r|-R|-k|-B|-e|-i|-q|-t)
14+ -e)
15+ pass_flags=${pass_flags}${pass_flags:+'
16+'}$1
17+ shift
18+ ;;
19+ --no-print-directory|--warn-undefined-variables|-r|-R|-k|-B|-i|-q|-t)
20 shift
21 ;;
22 --)
23@@ -183,6 +189,15 @@ if [ "$makefile_set" -eq 1 ]; then
24 else
25 set -- "$shin_bin"
26 fi
27+if [ -n "$pass_flags" ]; then
28+ oldifs=$IFS
29+ IFS='
30+'
31+ for flag in $pass_flags; do
32+ set -- "$@" "$flag"
33+ done
34+ IFS=$oldifs
35+fi
36 if [ -n "$vars" ]; then
37 oldifs=$IFS
38 IFS='