commit 1dbb521

shrub  ·  2026-05-30 12:21:31 +0000 UTC
parent 32cdfd6
fix subgraph target with no recipe emission and fix pattern/suffix rule precendence, add new test
8 files changed,  +51, -21
+0, -14
 1@@ -196,18 +196,6 @@ rulecmd(const struct Target *t)
 2 	return escaped;
 3 }
 4 
 5-static int
 6-issubgraphparent(const struct Graph *graph, const char *name)
 7-{
 8-	size_t i;
 9-
10-	for (i = 0; i < graph->nsubs; i++) {
11-		if (hasword(&graph->subs[i].parents, name))
12-			return 1;
13-	}
14-	return 0;
15-}
16-
17 static int
18 findrule(struct Rule *rules, size_t n, const char *cmd)
19 {
20@@ -256,8 +244,6 @@ genninjafile(const struct Graph *graph, const char *path, const char *prefix, in
21 	for (i = 0; i < graph->n; i++) {
22 		if (!targetownedby(&graph->v[i], prefix))
23 			continue;
24-		if (issubgraphparent(graph, graph->v[i].name) && graph->v[i].recipes.n == 0)
25-			continue;
26 		if (graph->v[i].recipes.n > 0) {
27 			char *cmd;
28 			int id;
+1, -1
1@@ -167,7 +167,7 @@ loadimppatrules(struct PatRules *rules, const char *src)
2 
3 		if (node->kind != NODE_RULE)
4 			continue;
5-		collectpat(rules, &node->data.rule);
6+		collectpat(rules, &node->data.rule, 1);
7 	}
8 	freeast(&ast);
9 	return 0;
+5, -2
 1@@ -106,7 +106,7 @@ patmatches(const char *pat, const char *name)
 2 }
 3 
 4 void
 5-collectpat(struct PatRules *rules, const struct RuleNode *rule)
 6+collectpat(struct PatRules *rules, const struct RuleNode *rule, int builtin)
 7 {
 8 	size_t i;
 9 
10@@ -122,6 +122,7 @@ collectpat(struct PatRules *rules, const struct RuleNode *rule)
11 		addwords(&p->prereqs, &rule->prereqs);
12 		addwords(&p->order_only, &rule->order_only);
13 		addrecipes(&p->recipes, &rule->recipes);
14+		p->builtin = builtin;
15 	}
16 }
17 
18@@ -157,13 +158,15 @@ patruleviable(const struct PatRule *rule, const struct Graph *graph, const char
19 }
20 
21 int
22-instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct EvalCtx *ctx)
23+instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct EvalCtx *ctx, int allow_builtin)
24 {
25 	size_t i, j;
26 
27 	for (i = 0; i < rules->n; i++) {
28 		char *stem;
29 
30+		if (rules->v[i].builtin && !allow_builtin)
31+			continue;
32 		stem = patmatchstem(rules->v[i].target, t->name);
33 		if (!stem)
34 			continue;
+3, -2
 1@@ -8,6 +8,7 @@ struct PatRule {
 2 	struct StrList prereqs;
 3 	struct StrList order_only;
 4 	struct RecipeList recipes;
 5+	int builtin;
 6 };
 7 
 8 struct PatRules {
 9@@ -20,8 +21,8 @@ int patmatches(const char *pat, const char *name);
10 char *patmatchstem(const char *pat, const char *name);
11 char *patapplystem(const char *s, const char *stem);
12 char *patexpandstem(const char *s, const char *stem);
13-void collectpat(struct PatRules *rules, const struct RuleNode *rule);
14-int instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct EvalCtx *ctx);
15+void collectpat(struct PatRules *rules, const struct RuleNode *rule, int builtin);
16+int instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct EvalCtx *ctx, int allow_builtin);
17 void freepatrule(struct PatRules *rules);
18 
19 #endif
+11, -2
 1@@ -346,7 +346,7 @@ buildgraph(const struct RuleSet *ruleset, const struct StrList *goals, struct Gr
 2 			continue;
 3 		/* pattern rules (%.o: %.c) are a gnu extension */
 4 		if (gs.mode == MODE_GNU)
 5-			collectpat(&gs.patterns, &gs.rules[i]);
 6+			collectpat(&gs.patterns, &gs.rules[i], 0);
 7 		for (k = 0; k < gs.rules[i].targets.n; k++) {
 8 			if (!ispat(gs.rules[i].targets.v[k]))
 9 				addrule(&gs, gs.rules[i].targets.v[k], &gs.rules[i]);
10@@ -417,11 +417,20 @@ buildgraph(const struct RuleSet *ruleset, const struct StrList *goals, struct Gr
11 				targetctx.env = &env;
12 				targetctx.mode = gs.mode;
13 				targetenv(&gs, &targetctx, &t->env, t->name);
14+				/* rule precedence in GNU mode:
15+				 * 1) user defined pattern rules
16+				 * 2) suffix rules 
17+				 * 3) builtin pattern rules
18+				 *
19+				 * we make sure local suffix compile recipes dont get overshadowed
20+				 * by GNU builtins like %.o: %.c (dropbear fails without this). */
21 				if (gs.mode == MODE_GNU)
22-					instpatrule(&gs.patterns, graph, t, &targetctx);
23+					instpatrule(&gs.patterns, graph, t, &targetctx, 0);
24 				matched = t->recipes.n > 0;
25 				if (!matched) {
26 					instsufrule(&gs.sufs, graph, t, &targetctx);
27+					if (gs.mode == MODE_GNU && t->recipes.n == 0)
28+						instpatrule(&gs.patterns, graph, t, &targetctx, 1);
29 					if (t->recipes.n == 0)
30 						instdefaultrule(&gs, t, &targetctx);
31 				}
+6, -0
1@@ -0,0 +1,6 @@
2+.SUFFIXES: .o .c
3+
4+all: x.o
5+
6+.c.o:
7+	@echo suffix-rule $<
+1, -0
1@@ -0,0 +1 @@
2+suffix-rule x.c
+24, -0
 1@@ -0,0 +1,24 @@
 2+{
 3+  "case": "t001",
 4+  "category": "suffixrules",
 5+  "compare_output": true,
 6+  "description": "user .c.o suffix rule should beat builtin %.o: %.c rule",
 7+  "details": "",
 8+  "env": {},
 9+  "expected_exit": 0,
10+  "options": "",
11+  "options_mode": "argv",
12+  "output_mode": "exact",
13+  "setup": [
14+    {
15+      "content": "#include \"missing-header-for-regression-test.h\"\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+}