commit 7ffaa2c
shrub
·
2026-08-28 10:38:00 +0000 UTC
parent 8c91ec0
be more accurate to GNU make in terms of implicit rules and default variable initialization, as well as how we handle .POSIX in gnu mode
14 files changed,
+244,
-58
+96,
-39
1@@ -19,11 +19,11 @@ seeddefaults(struct Env *env, const struct DefaultVar *vars)
2 size_t i;
3
4 for (i = 0; vars[i].name; i++)
5- envsetvar(env, vars[i].name, xstrdup(vars[i].val), 1, ORIGIN_DEFAULT, 0);
6+ envsetvar(env, vars[i].name, xstrdup(vars[i].val), 0, ORIGIN_DEFAULT, 0);
7 }
8
9-/* rules shared by all dialects except the .c.o rule, which differs */
10-#define IMPRULES_COMMON \
11+/* posix default inference rules in both 08 and 24 */
12+#define IMPRULES_POSIX_COMMON \
13 ".c:\n" \
14 "\t$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<\n" \
15 ".sh:\n" \
16@@ -49,7 +49,7 @@ seeddefaults(struct Env *env, const struct DefaultVar *vars)
17 /* posix 2008 includes fortran; posix 2024 dropped it */
18 #define IMPRULES_POSIX_2008 \
19 ".SUFFIXES: .o .c .f .y .l .sh\n" \
20- IMPRULES_COMMON \
21+ IMPRULES_POSIX_COMMON \
22 ".f:\n" \
23 "\t$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $<\n" \
24 ".c.o:\n" \
25@@ -59,73 +59,92 @@ seeddefaults(struct Env *env, const struct DefaultVar *vars)
26
27 #define IMPRULES_POSIX_2024 \
28 ".SUFFIXES: .o .c .y .l .sh\n" \
29- IMPRULES_COMMON \
30+ IMPRULES_POSIX_COMMON \
31 ".c.o:\n" \
32 "\t$(CC) $(CFLAGS) -c $<\n"
33
34 #define IMPRULES_GNU \
35 ".SUFFIXES: .o .c .f .y .l .sh\n" \
36- IMPRULES_COMMON \
37+ ".c:\n" \
38+ "\t$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
39+ ".sh:\n" \
40+ "\tcp $< $@\n" \
41+ "\tchmod a+x $@\n" \
42+ ".y.o:\n" \
43+ "\t$(YACC.y) $<\n" \
44+ "\t$(COMPILE.c) y.tab.c\n" \
45+ "\trm -f y.tab.c\n" \
46+ "\tmv y.tab.o $@\n" \
47+ ".l.o:\n" \
48+ "\t$(LEX.l) $<\n" \
49+ "\t$(COMPILE.c) lex.yy.c\n" \
50+ "\trm -f lex.yy.c\n" \
51+ "\tmv lex.yy.o $@\n" \
52+ ".y.c:\n" \
53+ "\t$(YACC.y) $<\n" \
54+ "\tmv y.tab.c $@\n" \
55+ ".l.c:\n" \
56+ "\t$(LEX.l) $< > $@\n" \
57 ".f:\n" \
58- "\t$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $<\n" \
59+ "\t$(LINK.f) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
60 ".c.o:\n" \
61- "\t$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<\n" \
62+ "\t$(COMPILE.c) $(OUTPUT_OPTION) $<\n" \
63 ".f.o:\n" \
64- "\t$(FC) $(FFLAGS) -c -o $@ $<\n"
65+ "\t$(COMPILE.f) $(OUTPUT_OPTION) $<\n"
66
67 #define IMPPATRULES_GNU \
68 "%: %.o\n" \
69- "\t$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
70+ "\t$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
71 "%: %.c\n" \
72- "\t$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
73+ "\t$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
74 "%.o: %.c\n" \
75- "\t$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<\n" \
76+ "\t$(COMPILE.c) $(OUTPUT_OPTION) $<\n" \
77 "%: %.cc\n" \
78- "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
79+ "\t$(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
80 "%.o: %.cc\n" \
81- "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<\n" \
82+ "\t$(COMPILE.cc) $(OUTPUT_OPTION) $<\n" \
83 "%: %.C\n" \
84- "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
85+ "\t$(LINK.C) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
86 "%.o: %.C\n" \
87- "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<\n" \
88+ "\t$(COMPILE.C) $(OUTPUT_OPTION) $<\n" \
89 "%: %.cpp\n" \
90- "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
91+ "\t$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
92 "%.o: %.cpp\n" \
93- "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<\n" \
94+ "\t$(COMPILE.cpp) $(OUTPUT_OPTION) $<\n" \
95 "%: %.p\n" \
96- "\t$(FC) $(FFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
97+ "\t$(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
98 "%.o: %.p\n" \
99- "\t$(FC) $(FFLAGS) -c -o $@ $<\n" \
100+ "\t$(COMPILE.p) $(OUTPUT_OPTION) $<\n" \
101 "%: %.f\n" \
102- "\t$(FC) $(FFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
103+ "\t$(LINK.f) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
104 "%.o: %.f\n" \
105- "\t$(FC) $(FFLAGS) -c -o $@ $<\n" \
106+ "\t$(COMPILE.f) $(OUTPUT_OPTION) $<\n" \
107 "%: %.F\n" \
108- "\t$(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
109+ "\t$(LINK.F) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
110 "%.o: %.F\n" \
111- "\t$(FC) $(CPPFLAGS) $(FFLAGS) -c -o $@ $<\n" \
112+ "\t$(COMPILE.F) $(OUTPUT_OPTION) $<\n" \
113 "%.f: %.F\n" \
114- "\t$(FC) $(CPPFLAGS) $(FFLAGS) -o $@ -c $<\n" \
115+ "\t$(PREPROCESS.F) $(OUTPUT_OPTION) $<\n" \
116 "%: %.m\n" \
117- "\t$(OBJC) $(OBJCFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
118+ "\t$(LINK.m) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
119 "%.o: %.m\n" \
120- "\t$(OBJC) $(CPPFLAGS) $(OBJCFLAGS) -c -o $@ $<\n" \
121+ "\t$(COMPILE.m) $(OUTPUT_OPTION) $<\n" \
122 "%: %.r\n" \
123- "\t$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
124+ "\t$(LINK.r) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
125 "%.o: %.r\n" \
126- "\t$(FC) $(FFLAGS) $(RFLAGS) -c -o $@ $<\n" \
127+ "\t$(COMPILE.r) $(OUTPUT_OPTION) $<\n" \
128 "%.f: %.r\n" \
129- "\t$(FC) $(FFLAGS) $(RFLAGS) -F $< > $@\n" \
130+ "\t$(PREPROCESS.r) $(OUTPUT_OPTION) $<\n" \
131 "%: %.s\n" \
132- "\t$(AS) $(ASFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
133+ "\t$(LINK.s) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
134 "%.o: %.s\n" \
135- "\t$(AS) $(ASFLAGS) -o $@ $<\n" \
136+ "\t$(COMPILE.s) -o $@ $<\n" \
137 "%: %.S\n" \
138- "\t$(CC) $(ASFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
139+ "\t$(LINK.S) $^ $(LOADLIBES) $(LDLIBS) -o $@\n" \
140 "%.o: %.S\n" \
141- "\t$(CC) $(ASFLAGS) $(CPPFLAGS) -c -o $@ $<\n" \
142+ "\t$(COMPILE.S) -o $@ $<\n" \
143 "%.s: %.S\n" \
144- "\t$(CC) $(ASFLAGS) $(CPPFLAGS) -E $< > $@\n"
145+ "\t$(PREPROCESS.S) $< > $@\n"
146
147 static int
148 loadimprules(struct SufRules *rules, const char *src)
149@@ -146,7 +165,7 @@ loadimprules(struct SufRules *rules, const char *src)
150 addsufs(&rules->active, &node->data.rule.prereqs);
151 continue;
152 }
153- (void)collectsufrule(rules, &node->data.rule);
154+ (void)collectsufrule(rules, &node->data.rule, 1);
155 }
156 freeast(&ast);
157 return 0;
158@@ -208,6 +227,8 @@ seedenv(struct Env *env, int isposix, int envoverride, enum ShinMode mode)
159 {"CPP", "$(CC) -E"},
160 {"FC", "fort77"},
161 {"FFLAGS", ""},
162+ {"PC", "pc"},
163+ {"OBJC", "cc"},
164 {"AR", "ar"},
165 {"ARFLAGS", "-rv"},
166 {"RANLIB", "ranlib"},
167@@ -216,6 +237,35 @@ seedenv(struct Env *env, int isposix, int envoverride, enum ShinMode mode)
168 {"GFLAGS", ""},
169 {"LD", "ld"},
170 {"LDFLAGS", ""},
171+ {"OUTPUT_OPTION", "-o $@"},
172+ {"COMPILE.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c"},
173+ {"LINK.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)"},
174+ {"COMPILE.cc", "$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c"},
175+ {"LINK.cc", "$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)"},
176+ {"COMPILE.C", "$(COMPILE.cc)"},
177+ {"LINK.C", "$(LINK.cc)"},
178+ {"COMPILE.cpp", "$(COMPILE.cc)"},
179+ {"LINK.cpp", "$(LINK.cc)"},
180+ {"COMPILE.p", "$(PC) $(PFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c"},
181+ {"LINK.p", "$(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)"},
182+ {"COMPILE.f", "$(FC) $(FFLAGS) $(TARGET_ARCH) -c"},
183+ {"LINK.f", "$(FC) $(FFLAGS) $(LDFLAGS) $(TARGET_ARCH)"},
184+ {"COMPILE.F", "$(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c"},
185+ {"LINK.F", "$(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)"},
186+ {"PREPROCESS.F", "$(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -F"},
187+ {"COMPILE.m", "$(OBJC) $(OBJCFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c"},
188+ {"LINK.m", "$(OBJC) $(OBJCFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)"},
189+ {"COMPILE.r", "$(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -c"},
190+ {"LINK.r", "$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) $(TARGET_ARCH)"},
191+ {"PREPROCESS.r", "$(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -F"},
192+ {"COMPILE.s", "$(AS) $(ASFLAGS) $(TARGET_MACH)"},
193+ {"LINK.s", "$(CC) $(ASFLAGS) $(LDFLAGS) $(TARGET_MACH)"},
194+ {"COMPILE.S", "$(CC) $(ASFLAGS) $(CPPFLAGS) $(TARGET_MACH) -c"},
195+ {"LINK.S", "$(CC) $(ASFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_MACH)"},
196+ {"PREPROCESS.S", "$(CPP) $(CPPFLAGS)"},
197+ {"LINK.o", "$(CC) $(LDFLAGS) $(TARGET_ARCH)"},
198+ {"LEX.l", "$(LEX) $(LFLAGS) -t"},
199+ {"YACC.y", "$(YACC) $(YFLAGS)"},
200 {"LEX", "lex"},
201 {"LFLAGS", ""},
202 {"SCCSFLAGS", ""},
203@@ -235,11 +285,18 @@ seedenv(struct Env *env, int isposix, int envoverride, enum ShinMode mode)
204 if (mode == MODE_POSIX_2024) {
205 seeddefaults(env, posix);
206 seeddefaults(env, posix2024);
207- } else if (mode == MODE_POSIX_2008 || isposix) {
208+ } else if (mode == MODE_POSIX_2008) {
209 seeddefaults(env, posix);
210 seeddefaults(env, posix2008);
211 } else {
212 seeddefaults(env, gnu);
213+ if (isposix) {
214+ /* GNU make keeps the whole gnu variable set under .POSIX
215+ * then replaces only defaults for variables specifically specified
216+ * by posix */
217+ seeddefaults(env, posix);
218+ seeddefaults(env, posix2008);
219+ }
220 cwd = getcwddup();
221 /*TODO: do this properly, this only expands at evaluation time,
222 * unsure about behaviour under directory change condition or
223@@ -265,13 +322,13 @@ seedenv(struct Env *env, int isposix, int envoverride, enum ShinMode mode)
224 }
225
226 void
227-imprules(struct SufRules *rules, int posix, enum ShinMode mode)
228+imprules(struct SufRules *rules, enum ShinMode mode)
229 {
230 const char *src;
231
232 if (mode == MODE_POSIX_2024)
233 src = IMPRULES_POSIX_2024;
234- else if (mode == MODE_POSIX_2008 || posix)
235+ else if (mode == MODE_POSIX_2008)
236 src = IMPRULES_POSIX_2008;
237 else
238 src = IMPRULES_GNU;
+5,
-3
1@@ -140,7 +140,8 @@ pattargetexists(const struct Graph *graph, const char *name)
2 }
3
4 static int
5-patruleviable(const struct PatRule *rule, const struct Graph *graph, const char *stem)
6+patruleviable(const struct PatRule *rule, const struct Graph *graph,
7+ const struct Target *target, const char *stem)
8 {
9 size_t i;
10
11@@ -149,7 +150,8 @@ patruleviable(const struct PatRule *rule, const struct Graph *graph, const char
12 int ok;
13
14 s = patapplystem(rule->prereqs.v[i], stem);
15- ok = pattargetexists(graph, s);
16+ ok = pattargetexists(graph, s) || hasword(&target->prereqs, s) ||
17+ hasword(&target->order_only, s);
18 free(s);
19 if (!ok)
20 return 0;
21@@ -170,7 +172,7 @@ instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Targ
22 stem = patmatchstem(rules->v[i].target, t->name);
23 if (!stem)
24 continue;
25- if (!patruleviable(&rules->v[i], graph, stem)) {
26+ if (!patruleviable(&rules->v[i], graph, t, stem)) {
27 free(stem);
28 continue;
29 }
+14,
-9
1@@ -304,7 +304,7 @@ buildgraph(const struct RuleSet *ruleset, const struct StrList *goals, struct Gr
2 ctx.env = &gs.env;
3 ctx.export_all = ruleset->export_all;
4 ctx.mode = mode;
5- imprules(&gs.sufs, ruleset->posix, mode);
6+ imprules(&gs.sufs, mode);
7
8 for (i = 0; i < ruleset->nvars; i++)
9 evalassign(&ctx, &ruleset->vars[i]);
10@@ -332,7 +332,7 @@ buildgraph(const struct RuleSet *ruleset, const struct StrList *goals, struct Gr
11
12 if (issufrule(&gs.rules[i]))
13 continue;
14- if (collectsufrule(&gs.sufs, &gs.rules[i]))
15+ if (collectsufrule(&gs.sufs, &gs.rules[i], 0))
16 continue;
17 /* pattern rules (%.o: %.c) are a gnu extension */
18 if (gs.mode == MODE_GNU)
19@@ -405,19 +405,24 @@ buildgraph(const struct RuleSet *ruleset, const struct StrList *goals, struct Gr
20 targetctx.mode = gs.mode;
21 targetenv(&gs, &targetctx, &t->env, t->name);
22 /* rule precedence in GNU mode:
23- * 1) user defined pattern rules
24- * 2) suffix rules
25- * 3) builtin pattern rules
26+ * 1) user-defined pattern rules
27+ * 2) user-defined suffix rules
28+ * 3) built-in pattern rules
29+ * 4) built-in suffix rules
30 *
31- * we make sure local suffix compile recipes dont get overshadowed
32- * by GNU builtins like %.o: %.c (dropbear fails without this). */
33+ * this makes sure we keep local suffix compile recipes ahead of GNU builtins,
34+ * while allowing builtins such as %: %.o to beat the built-in .c
35+ * link rule when we are interpreting a GNU makefile which matches what gmake does. */
36 if (gs.mode == MODE_GNU)
37 instpatrule(&gs.patterns, graph, t, &targetctx, 0);
38 matched = t->recipes.n > 0;
39 if (!matched) {
40- instsufrule(&gs.sufs, graph, t, &targetctx);
41- if (gs.mode == MODE_GNU && t->recipes.n == 0)
42+ instsufrule(&gs.sufs, graph, t, &targetctx, 0);
43+ if (gs.mode == MODE_GNU && t->recipes.n == 0) {
44 instpatrule(&gs.patterns, graph, t, &targetctx, 1);
45+ }
46+ if (t->recipes.n == 0)
47+ instsufrule(&gs.sufs, graph, t, &targetctx, 1);
48 if (t->recipes.n == 0)
49 instdefaultrule(&gs, t, &targetctx);
50 }
+6,
-3
1@@ -10,6 +10,7 @@
2 struct SingleSufRule {
3 char *from;
4 struct RecipeList recipes;
5+ int builtin;
6 };
7
8 struct SuffixList {
9@@ -22,6 +23,7 @@ struct SufRule {
10 char *from;
11 char *to;
12 struct RecipeList recipes;
13+ int builtin;
14 };
15
16 /*holds all stored suffix rules*/
17@@ -38,13 +40,14 @@ struct PatRules;
18 int issinglesuf(const char *s, char **from);
19 int issuf(const char *s, char **from, char **to);
20 void seedenv(struct Env *env, int posix, int envoverride, enum ShinMode mode);
21-void imprules(struct SufRules *rules, int posix, enum ShinMode mode);
22+void imprules(struct SufRules *rules, enum ShinMode mode);
23 int imppatrules(struct PatRules *rules, enum ShinMode mode);
24-int collectsufrule(struct SufRules *rules, const struct RuleNode *rule);
25+int collectsufrule(struct SufRules *rules, const struct RuleNode *rule, int builtin);
26 int instsufrule(const struct SufRules *rules,
27 const struct Graph *graph,
28 struct Target *t,
29- struct EvalCtx *ctx);
30+ struct EvalCtx *ctx,
31+ int allow_builtin);
32 int issufrule(const struct RuleNode *rule);
33 void addsufs(struct SuffixList *list, const struct StrList *sufs);
34 void clearsufs(struct SuffixList *list);
+13,
-4
1@@ -106,7 +106,7 @@ issuf(const char *s, char **from, char **to)
2 {
3 const char *mid;
4
5- if (!s || s[0] != '.')
6+ if (!s || s[0] != '.' || strchr(s, '/'))
7 return 0;
8 mid = strchr(s + 1, '.');
9 if (!mid || mid == s + 1 || !mid[1] || strchr(mid + 1, '.'))
10@@ -119,7 +119,7 @@ issuf(const char *s, char **from, char **to)
11 int
12 issinglesuf(const char *s, char **from)
13 {
14- if (!s || s[0] != '.')
15+ if (!s || s[0] != '.' || strchr(s, '/'))
16 return 0;
17 if (!s[1] || strchr(s + 1, '.'))
18 return 0;
19@@ -248,7 +248,7 @@ clearsufs(struct SuffixList *list)
20 }
21
22 int
23-collectsufrule(struct SufRules *rules, const struct RuleNode *rule)
24+collectsufrule(struct SufRules *rules, const struct RuleNode *rule, int builtin)
25 {
26 size_t i;
27
28@@ -281,6 +281,7 @@ collectsufrule(struct SufRules *rules, const struct RuleNode *rule)
29 freerecipes(&rules->singlesuf[k].recipes);
30 memset(&rules->singlesuf[k].recipes, 0, sizeof(rules->singlesuf[k].recipes));
31 addrecipes(&rules->singlesuf[k].recipes, &rule->recipes);
32+ rules->singlesuf[k].builtin = builtin;
33 goto next_target;
34 }
35 }
36@@ -290,6 +291,7 @@ collectsufrule(struct SufRules *rules, const struct RuleNode *rule)
37 memset(sr, 0, sizeof(*sr));
38 sr->from = from;
39 addrecipes(&sr->recipes, &rule->recipes);
40+ sr->builtin = builtin;
41 continue;
42 }
43 issuf(rule->targets.v[i], &from, &to);
44@@ -302,6 +304,7 @@ collectsufrule(struct SufRules *rules, const struct RuleNode *rule)
45 freerecipes(&rules->sufs[k].recipes);
46 memset(&rules->sufs[k].recipes, 0, sizeof(rules->sufs[k].recipes));
47 addrecipes(&rules->sufs[k].recipes, &rule->recipes);
48+ rules->sufs[k].builtin = builtin;
49 goto next_target;
50 }
51 }
52@@ -310,6 +313,7 @@ collectsufrule(struct SufRules *rules, const struct RuleNode *rule)
53 rules->sufs[rules->nsufs].from = from;
54 rules->sufs[rules->nsufs].to = to;
55 addrecipes(&rules->sufs[rules->nsufs].recipes, &rule->recipes);
56+ rules->sufs[rules->nsufs].builtin = builtin;
57 rules->nsufs++;
58 next_target:
59 ;
60@@ -321,7 +325,8 @@ int
61 instsufrule(const struct SufRules *rules,
62 const struct Graph *graph,
63 struct Target *t,
64- struct EvalCtx *ctx)
65+ struct EvalCtx *ctx,
66+ int allow_builtin)
67 {
68 size_t i, k;
69
70@@ -333,6 +338,8 @@ instsufrule(const struct SufRules *rules,
71 char *stem, *src;
72 int exists;
73
74+ if (rules->sufs[r].builtin && !allow_builtin)
75+ continue;
76 if (!sufeq(rules->sufs[r].to, rules->active.v[i]))
77 continue;
78 if (!sufeq(rules->sufs[r].from, rules->active.v[j]))
79@@ -389,6 +396,8 @@ instsufrule(const struct SufRules *rules,
80 char *src;
81 int exists;
82
83+ if (rules->singlesuf[j].builtin && !allow_builtin)
84+ continue;
85 if (!sufeq(rules->singlesuf[j].from, rules->active.v[i]))
86 continue;
87 src = cat3(t->name, "", rules->singlesuf[j].from);
+8,
-0
1@@ -0,0 +1,8 @@
2+.POSIX:
3+
4+CPPFLAGS =
5+LDFLAGS =
6+LOADLIBES =
7+LDLIBS =
8+
9+wawa: wawa.o protocol.o
+3,
-0
1@@ -0,0 +1,3 @@
2+c99 -O1 -c -o wawa.o wawa.c
3+c99 -O1 -c -o protocol.o protocol.c
4+c99 wawa.o protocol.o -o wawa
+31,
-0
1@@ -0,0 +1,31 @@
2+{
3+ "case": "t002",
4+ "category": "suffixrules",
5+ "compare_output": true,
6+ "description": ".POSIX changes some GNU defaults without disabling GNU implicit pattern rules",
7+ "details": "In GNU mode, .POSIX selects c99 and -O1 while an explicitly listed object prereq makes the built-in .o pattern link rule viable",
8+ "env": {},
9+ "expected_exit": 0,
10+ "options": "-n wawa",
11+ "options_mode": "shell",
12+ "output_mode": "exact",
13+ "setup": [
14+ {
15+ "content": "int main(void) { return 0; }\n",
16+ "kind": "file",
17+ "mode": "0644",
18+ "mtime": 1778707209,
19+ "path": "wawa.c"
20+ },
21+ {
22+ "content": "int protocol;\n",
23+ "kind": "file",
24+ "mode": "0644",
25+ "mtime": 1778707209,
26+ "path": "protocol.c"
27+ }
28+ ],
29+ "stdin": "",
30+ "suite": "shin",
31+ "timeout_seconds": 60
32+}
+7,
-0
1@@ -0,0 +1,7 @@
2+COMPILE.c = custom-compile
3+OUTPUT_OPTION = custom-output
4+LINK.o = custom-link
5+LOADLIBES = custom-loadlibs
6+LDLIBS = custom-ldlibs
7+
8+program: program.o
+2,
-0
1@@ -0,0 +1,2 @@
2+custom-compile custom-output program.c
3+custom-link program.o custom-loadlibs custom-ldlibs -o program
+24,
-0
1@@ -0,0 +1,24 @@
2+{
3+ "case": "t003",
4+ "category": "suffixrules",
5+ "compare_output": true,
6+ "description": "GNU built-in rules use their standard command variables",
7+ "details": "The C compile and object link pattern rules allow overrides of COMPILE.c, OUTPUT_OPTION, LINK.o, LOADLIBES, and LDLIBS.",
8+ "env": {},
9+ "expected_exit": 0,
10+ "options": "-n program",
11+ "options_mode": "shell",
12+ "output_mode": "exact",
13+ "setup": [
14+ {
15+ "content": "int main(void) { return 0; }\n",
16+ "kind": "file",
17+ "mode": "0644",
18+ "mtime": 1778707209,
19+ "path": "program.c"
20+ }
21+ ],
22+ "stdin": "",
23+ "suite": "shin",
24+ "timeout_seconds": 60
25+}
+9,
-0
1@@ -0,0 +1,9 @@
2+OBJ_DIR = ./obj
3+
4+all: $(OBJ_DIR)/x.o
5+
6+$(OBJ_DIR):
7+ mkdir -p $@
8+
9+$(OBJ_DIR)/%.o: %.c | $(OBJ_DIR)
10+ cp $< $@
+2,
-0
1@@ -0,0 +1,2 @@
2+mkdir -p ./obj
3+cp x.c ./obj/x.o
+24,
-0
1@@ -0,0 +1,24 @@
2+{
3+ "case": "t004",
4+ "category": "suffixrules",
5+ "compare_output": true,
6+ "description": "dot prefixed dir targets are not suffix rules",
7+ "details": "some target like ./obj contains a slash and must remain an explicit dir target when used as an order only prereq, although generic parsing may see it as a suffix rule (from dropbear)",
8+ "env": {},
9+ "expected_exit": 0,
10+ "options": "-n all",
11+ "options_mode": "shell",
12+ "output_mode": "exact",
13+ "setup": [
14+ {
15+ "content": "int x;\n",
16+ "kind": "file",
17+ "mode": "0644",
18+ "mtime": 1778707209,
19+ "path": "x.c"
20+ }
21+ ],
22+ "stdin": "",
23+ "suite": "shin",
24+ "timeout_seconds": 60
25+}