commit b508dcb
shrub
·
2026-05-18 17:42:51 +0000 UTC
parent 805b3a3
make posix modes more compliant
4 files changed,
+122,
-8
+108,
-2
1@@ -176,19 +176,34 @@ evalassign(struct EvalCtx *ctx, const struct AssignNode *in)
2 envsetvar(env, lhs, xstrdup(in->rhs), 0, o, exported);
3 break;
4 case ASSIGN_DCOLON_EQ:
5+ if (ctx->mode == MODE_POSIX_2008) {
6+ dielikemake(ctx->cur_path, ctx->cur_line, "'::=' is not valid in POSIX 2008", 0);
7+ ctx->errors++;
8+ break;
9+ }
10 envsetvar(env, lhs, expandstr(ctx, in->rhs), 1, o, exported);
11 break;
12 case ASSIGN_COLON_EQ:
13 /* := is a gnu extension. posix uses ::= for simple expansion.
14 * bsd := is different again, equivalent to posix :::= */
15- if (ctx->mode != MODE_GNU) {
16- dielikemake(ctx->cur_path, ctx->cur_line, "':=' is not valid in posix mode, use '::='", 0);
17+ if (ctx->mode == MODE_POSIX_2024) {
18+ dielikemake(ctx->cur_path, ctx->cur_line, "':=' is not valid in POSIX 2024, use '::='", 0);
19+ ctx->errors++;
20+ break;
21+ }
22+ if (ctx->mode == MODE_POSIX_2008) {
23+ dielikemake(ctx->cur_path, ctx->cur_line, "':=' is not valid in POSIX 2008", 0);
24 ctx->errors++;
25 break;
26 }
27 envsetvar(env, lhs, expandstr(ctx, in->rhs), 1, o, exported);
28 break;
29 case ASSIGN_COLON3_EQ:
30+ if (ctx->mode == MODE_POSIX_2008) {
31+ dielikemake(ctx->cur_path, ctx->cur_line, "':::=' is not valid in POSIX 2008", 0);
32+ ctx->errors++;
33+ break;
34+ }
35 /* expand now, escape the result so re-expansion on use gives back the same value */
36 rhs = expandstr(ctx, in->rhs);
37 joined = escapedollars(rhs);
38@@ -196,10 +211,20 @@ evalassign(struct EvalCtx *ctx, const struct AssignNode *in)
39 envsetvar(env, lhs, joined, 0, o, exported);
40 break;
41 case ASSIGN_QMARK_EQ:
42+ if (ctx->mode == MODE_POSIX_2008) {
43+ dielikemake(ctx->cur_path, ctx->cur_line, "'?=' is not valid in POSIX 2008", 0);
44+ ctx->errors++;
45+ break;
46+ }
47 if (!findvar(env, lhs))
48 envsetvar(env, lhs, xstrdup(in->rhs), 0, o, exported);
49 break;
50 case ASSIGN_PLUS_EQ:
51+ if (ctx->mode == MODE_POSIX_2008) {
52+ dielikemake(ctx->cur_path, ctx->cur_line, "'+=' is not valid in POSIX 2008", 0);
53+ ctx->errors++;
54+ break;
55+ }
56 v = findvar(env, lhs);
57 if (!v) {
58 envsetvar(env, lhs, xstrdup(in->rhs), 0, o, in->exported);
59@@ -217,6 +242,11 @@ evalassign(struct EvalCtx *ctx, const struct AssignNode *in)
60 v->exported = 1;
61 break;
62 case ASSIGN_BANG_EQ:
63+ if (ctx->mode == MODE_POSIX_2008) {
64+ dielikemake(ctx->cur_path, ctx->cur_line, "'!=' is not valid in POSIX 2008", 0);
65+ ctx->errors++;
66+ break;
67+ }
68 rhs = expandstr(ctx, in->rhs);
69 joined = runshellassign(rhs);
70 free(rhs);
71@@ -410,6 +440,31 @@ evalinclude(struct EvalCtx *ctx, const struct IncludeNode *inc)
72 memset(&paths, 0, sizeof(paths));
73 splitwords(&paths, exp, strlen(exp));
74 free(exp);
75+ /* posix 2008 only allows plain includes with one file, posix 2024 allows both
76+ * include and -include with multiple files, but no sinclude. */
77+ if (ctx->mode == MODE_POSIX_2008) {
78+ if (inc->optional) {
79+ dielikemake(ctx->cur_path, ctx->cur_line,
80+ "optional includes are not valid in POSIX 2008", 0);
81+ ctx->errors++;
82+ freestrs(&paths);
83+ return 0;
84+ }
85+ if (paths.n != 1) {
86+ dielikemake(ctx->cur_path, ctx->cur_line,
87+ "include in POSIX 2008 must specify exactly one file", 0);
88+ ctx->errors++;
89+ freestrs(&paths);
90+ return 0;
91+ }
92+ }
93+ if (ctx->mode == MODE_POSIX_2024 && inc->sinclude) {
94+ dielikemake(ctx->cur_path, ctx->cur_line,
95+ "'sinclude' is not valid in POSIX 2024; use '-include'", 0);
96+ ctx->errors++;
97+ freestrs(&paths);
98+ return 0;
99+ }
100 for (i = 0; i < paths.n; i++) {
101 size_t j, nmatch;
102 char *single;
103@@ -497,6 +552,23 @@ evalinclude(struct EvalCtx *ctx, const struct IncludeNode *inc)
104 return 0;
105 }
106
107+static int
108+isgnutarget(const char *s)
109+{
110+ /*gnu only special targets that we actually handle*/
111+ static const char *const gnutargets[] = {
112+ ".EXPORT_ALL_VARIABLES",
113+ 0
114+ };
115+ size_t i;
116+
117+ for (i = 0; gnutargets[i]; i++) {
118+ if (strcmp(s, gnutargets[i]) == 0)
119+ return 1;
120+ }
121+ return 0;
122+}
123+
124 static int
125 evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
126 {
127@@ -527,6 +599,11 @@ evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
128 return -1;
129 break;
130 case NODE_COND:
131+ if (ctx->mode != MODE_GNU) {
132+ dielikemake(ctx->cur_path, ctx->cur_line, "conditionals are only valid in GNU", 0);
133+ ctx->errors++;
134+ break;
135+ }
136 if (testcond(ctx, &src->data.cond)) {
137 if (evalnodes(&src->data.cond.thenpart, out, ctx) < 0)
138 return -1;
139@@ -536,6 +613,16 @@ evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
140 }
141 continue;
142 case NODE_ASSIGN:
143+ if (ctx->mode != MODE_GNU && src->data.assign.define_block) {
144+ dielikemake(ctx->cur_path, ctx->cur_line, "'define'/'endef' are only valid in GNU", 0);
145+ ctx->errors++;
146+ break;
147+ }
148+ if (ctx->mode != MODE_GNU && src->data.assign.exported != 0) {
149+ dielikemake(ctx->cur_path, ctx->cur_line, "'export'/'unexport' are only valid in GNU", 0);
150+ ctx->errors++;
151+ break;
152+ }
153 updatespecialassign(&targets, src->data.assign.lhs, src->data.assign.rhs);
154 if (src->data.assign.tspec) {
155 addrulesetassign(&out->tvars, &out->ntvars, src, ctx);
156@@ -545,6 +632,11 @@ evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
157 }
158 break;
159 case NODE_EXPORT:
160+ if (ctx->mode != MODE_GNU) {
161+ dielikemake(ctx->cur_path, ctx->cur_line, "'export'/'unexport' are only valid in GNU", 0);
162+ ctx->errors++;
163+ break;
164+ }
165 evalexport(ctx, &src->data.export);
166 break;
167 case NODE_RULE: {
168@@ -559,6 +651,20 @@ evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
169 copywords(&exptargets, &src->data.rule.targets, ctx);
170 tmprule = src->data.rule;
171 tmprule.targets = exptargets;
172+ if (ctx->mode != MODE_GNU && tmprule.dcolon) {
173+ dielikemake(ctx->cur_path, ctx->cur_line,
174+ "double-colon rules are only valid in GNU", 0);
175+ ctx->errors++;
176+ freestrs(&exptargets);
177+ break;
178+ }
179+ /* ignore gnu only targets if not in gnu mode */
180+ if (ctx->mode != MODE_GNU &&
181+ tmprule.targets.n == 1 &&
182+ isgnutarget(tmprule.targets.v[0])) {
183+ freestrs(&exptargets);
184+ break;
185+ }
186 if (tmprule.targets.n == 1 &&
187 strcmp(tmprule.targets.v[0], ".EXPORT_ALL_VARIABLES") == 0) {
188 ctx->export_all = 1;
+7,
-5
1@@ -564,12 +564,12 @@ preprocfile0(const char *path, const char *src_override, struct Pre *pre, struct
2
3 stripcomment(line.text);
4 trim = trimdup(line.text, strlen(line.text));
5- if (haskw(trim, "include") || haskw(trim, "-include") || haskw(trim, "sinclude")) {
6- int rc;
7- size_t kwlen;
8+ if (haskw(trim, "include") || haskw(trim, "-include")) {
9+ int rc;
10+ size_t kwlen;
11
12- opt = haskw(trim, "-include") || haskw(trim, "sinclude");
13- kwlen = haskw(trim, "-include") || haskw(trim, "sinclude") ? 8 : 7;
14+ opt = haskw(trim, "-include");
15+ kwlen = haskw(trim, "-include") ? 8 : 7;
16 incarg = trimdup(trim + kwlen, strlen(trim + kwlen));
17 if (isplainpath(incarg)) {
18 rc = preprocinclude(dir, incarg, pre, inc, targets);
19@@ -674,6 +674,7 @@ parseinclude(const struct PreLine *line, const char *s)
20 off = strlen("-include");
21 } else if (haskw(s, "sinclude")) {
22 state.data.include.optional = 1;
23+ state.data.include.sinclude = 1;
24 off = strlen("sinclude");
25 } else {
26 off = strlen("include");
27@@ -943,6 +944,7 @@ parsedefine(const struct Pre *pre, size_t *i, struct Node *out)
28 out->data.assign.lhs = name;
29 out->data.assign.op = ASSIGN_EQ;
30 out->data.assign.origin = ORIGIN_FILE;
31+ out->data.assign.define_block = 1;
32
33 bodycap = 64;
34 bodylen = 0;
+5,
-1
1@@ -326,7 +326,11 @@ seedenv(struct Env *env, int posix, int envoverride, enum ShinMode mode)
2 * in submakes. should work for most simple cases. */
3 envsetvar(env, "CURDIR", cwd, 1, ORIGIN_FILE, 0);
4 if (posix || mode != MODE_GNU) {
5- envsetvar(env, "CC", xstrdup("c99"), 1, ORIGIN_DEFAULT, 0);
6+ const char *cc = "c99";
7+
8+ if (mode == MODE_POSIX_2024)
9+ cc = "c17";
10+ envsetvar(env, "CC", xstrdup(cc), 1, ORIGIN_DEFAULT, 0);
11 envsetvar(env, "CFLAGS", xstrdup("-O1"), 1, ORIGIN_DEFAULT, 0);
12 envsetvar(env, "FFLAGS", xstrdup("-O1"), 1, ORIGIN_DEFAULT, 0);
13 }
+2,
-0
1@@ -119,6 +119,7 @@ struct AssignNode {
2 enum AssignOp op;
3 enum Origin origin;
4 int exported;
5+ int define_block;
6 int tspec;
7 struct StrList targets;
8 };
9@@ -133,6 +134,7 @@ struct RuleNode {
10
11 struct IncludeNode {
12 int optional;
13+ int sinclude;
14 char *path;
15 };
16