commit b483835
shrub
·
2026-05-14 12:37:08 +0000 UTC
parent 9782164
handle .DEFAULT, add 1 new test
11 files changed,
+126,
-51
+0,
-41
1@@ -148,47 +148,6 @@ joinrecipes(const struct Target *t)
2 return s;
3 }
4
5-/*turn remaining automatic vars into ninja equivalents*/
6-static char *
7-localpath(const struct Target *t, const char *path)
8-{
9- size_t n;
10-
11- if (!t->owner || !t->owner[0])
12- return xstrdup(path);
13- n = strlen(t->owner);
14- if (strncmp(path, t->owner, n) != 0 || path[n] != '/')
15- return xstrdup(path);
16- return xstrdup(path + n + 1);
17-}
18-
19-static char *
20-joinlocalprereqs(const struct Target *t)
21-{
22- size_t i;
23- struct StrList list;
24- char *s;
25-
26- memset(&list, 0, sizeof(list));
27- for (i = 0; i < t->impprereqs.n; i++) {
28- char *name;
29-
30- name = localpath(t, t->impprereqs.v[i]);
31- addstr(&list, name);
32- free(name);
33- }
34- for (i = 0; i < t->prereqs.n; i++) {
35- char *name;
36-
37- name = localpath(t, t->prereqs.v[i]);
38- addstr(&list, name);
39- free(name);
40- }
41- s = joinstrs(&list, " ");
42- freestrs(&list);
43- return s;
44-}
45-
46 static char *
47 rulecmd(const struct Target *t)
48 {
+29,
-3
1@@ -219,9 +219,11 @@ main(int argc, char **argv)
2 int dump_ast;
3 int dump_graph;
4 int env_override;
5+ char **goals;
6 int i;
7 char **assigns;
8 size_t nassigns;
9+ size_t ngoals;
10 enum Generator gen;
11 struct Ast ast;
12 struct RuleSet rs;
13@@ -235,6 +237,8 @@ main(int argc, char **argv)
14 gen = GEN_NINJA;
15 assigns = 0;
16 nassigns = 0;
17+ goals = 0;
18+ ngoals = 0;
19 memset(&sg, 0, sizeof(sg));
20 memset(&ast, 0, sizeof(ast));
21 memset(&rs, 0, sizeof(rs));
22@@ -262,17 +266,20 @@ main(int argc, char **argv)
23 fprintf(stderr, "unknown generator: %s\n\n", argv[i]);
24 usage(stderr, argv[0]);
25 free(assigns);
26+ free(goals);
27 return 2;
28 }
29 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
30 usage(stdout, argv[0]);
31 free(assigns);
32+ free(goals);
33 return 0;
34 } else if (strcmp(argv[i], "-C") == 0) {
35 if (i + 1 >= argc) {
36 fprintf(stderr, "specify a directory\n\n");
37 usage(stderr, argv[0]);
38 free(assigns);
39+ free(goals);
40 return 2;
41 }
42 ++i;
43@@ -280,6 +287,7 @@ main(int argc, char **argv)
44 if (chdir(argv[i]) != 0) {
45 fprintf(stderr, "failed to chdir to %s", argv[i]);
46 free(assigns);
47+ free(goals);
48 return 2;
49 }
50 } else if (strcmp(argv[i], "-f") == 0) {
51@@ -287,6 +295,7 @@ main(int argc, char **argv)
52 fprintf(stderr, "specify a file\n\n");
53 usage(stderr, argv[0]);
54 free(assigns);
55+ free(goals);
56 return 2;
57 }
58 ++i;
59@@ -300,6 +309,7 @@ main(int argc, char **argv)
60 if (!tmp) {
61 fprintf(stderr, "out of memory\n");
62 free(assigns);
63+ free(goals);
64 return 2;
65 }
66 assigns = tmp;
67@@ -307,15 +317,26 @@ main(int argc, char **argv)
68 } else if (argv[i][0] == '-') {
69 usage(stderr, argv[0]);
70 free(assigns);
71+ free(goals);
72 return 2;
73 } else {
74- usage(stderr, argv[0]);
75- free(assigns);
76- return 2;
77+ char **tmp;
78+
79+ tmp = realloc(goals, (ngoals + 1) * sizeof(goals[0]));
80+ if (!tmp) {
81+ fprintf(stderr, "out of memory\n");
82+ free(assigns);
83+ free(goals);
84+ return 2;
85+ }
86+ goals = tmp;
87+ goals[ngoals++] = argv[i];
88 }
89 }
90 for (i = 0; i < (int)nassigns; i++)
91 addstr(&sg.assigns, assigns[i]);
92+ for (i = 0; i < (int)ngoals; i++)
93+ addstr(&sg.goals, goals[i]);
94 if (dump_ast) {
95 char *mkpath;
96
97@@ -326,6 +347,7 @@ main(int argc, char **argv)
98 else
99 fprintf(stderr, "could not find a makefile\n");
100 free(assigns);
101+ free(goals);
102 freesubgraph(&sg);
103 return 2;
104 }
105@@ -338,6 +360,7 @@ main(int argc, char **argv)
106 rc = parse(path, src, &ast);
107 if (rc < 0) {
108 free(assigns);
109+ free(goals);
110 free(pathbuf);
111 free(src);
112 freesubgraph(&sg);
113@@ -358,6 +381,7 @@ main(int argc, char **argv)
114 }
115 if (eval(path, &ast, 0, env_override, &rs) < 0) {
116 free(assigns);
117+ free(goals);
118 freeast(&ast);
119 free(pathbuf);
120 free(src);
121@@ -374,7 +398,9 @@ main(int argc, char **argv)
122 if (env_override)
123 addstr(&sg.flags, "-e");
124 free(assigns);
125+ free(goals);
126 assigns = 0;
127+ goals = 0;
128 {
129 int rc;
130
+7,
-0
1@@ -448,6 +448,12 @@ evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
2 ctx->export_all = 1;
3 break;
4 }
5+ if (src->data.rule.targets.n == 1 &&
6+ strcmp(src->data.rule.targets.v[0], ".DEFAULT") == 0) {
7+ freerecipes(&out->defaultrule);
8+ addrecipes(&out->defaultrule, &src->data.rule.recipes);
9+ break;
10+ }
11 if (handlespecialrule(&targets, &src->data.rule))
12 break;
13 addrulesetrule(out, &src->data.rule, ctx);
14@@ -547,6 +553,7 @@ freeruleset(struct RuleSet *ruleset)
15 freeassigns(ruleset->vars, ruleset->nvars);
16 freeassigns(ruleset->tvars, ruleset->ntvars);
17 freerules(ruleset->rules, ruleset->nrules);
18+ freerecipes(&ruleset->defaultrule);
19 freestrs(&ruleset->phony);
20 memset(ruleset, 0, sizeof(*ruleset));
21 }
+48,
-1
1@@ -22,6 +22,7 @@ struct GraphState {
2 struct Graph *graph;
3 struct Env env;
4 const struct StrList *phony;
5+ const struct RecipeList *defaultrule;
6 const struct RuleNode *rules;
7 size_t nrules;
8 struct PatRules patterns;
9@@ -234,12 +235,22 @@ addtassign(struct GraphState *gs, const struct AssignNode *assign)
10 ta->origin = assign->origin;
11 }
12
13+static void
14+instdefaultrule(const struct GraphState *gs, struct Target *t, struct EvalCtx *ctx)
15+{
16+ if (!gs->defaultrule || gs->defaultrule->n == 0 || t->defined || t->phony || t->recipes.n > 0)
17+ return;
18+ addrecipes(&t->recipes, gs->defaultrule);
19+ freeenv(&t->env);
20+ copyenv(&t->env, ctx->env);
21+}
22+
23 /*generate the graph from the evaluated rule set
24 * get the env and target specific assignments,
25 * then expand placeholder targets until all prereqs
26 * we can find are in the graph. */
27 int
28-buildgraph(const struct RuleSet *ruleset, struct Graph *graph)
29+buildgraph(const struct RuleSet *ruleset, const struct StrList *goals, struct Graph *graph)
30 {
31 size_t i, j;
32 struct GraphState gs;
33@@ -250,6 +261,7 @@ buildgraph(const struct RuleSet *ruleset, struct Graph *graph)
34 memset(&ctx, 0, sizeof(ctx));
35 gs.graph = graph;
36 gs.phony = &ruleset->phony;
37+ gs.defaultrule = &ruleset->defaultrule;
38 seedenv(&gs.env, ruleset->posix, ruleset->envoverride);
39 gs.rules = ruleset->rules;
40 gs.nrules = ruleset->nrules;
41@@ -291,6 +303,39 @@ buildgraph(const struct RuleSet *ruleset, struct Graph *graph)
42 }
43 }
44
45+ if (goals) {
46+ /* explicitly requested goals need to exist in the generated graph before ninja runs.
47+ * if a requested goal is missing but a .DEFAULT rule exists, create a
48+ * placeholder target here so the default recipe will be attached later. */
49+ for (i = 0; i < goals->n; i++) {
50+ struct Target *t;
51+
52+ t = findtarget(graph, goals->v[i]);
53+ if (t)
54+ continue;
55+ if (ruleset->defaultrule.n == 0) {
56+ /* if there is no default rule but a target that doesn't exist
57+ * is requested, die */
58+ char *detail;
59+
60+ detail = cat3("'", goals->v[i], "'");
61+ dielikemake(0, 0, "No rule to make target", detail);
62+ free(detail);
63+ freepatrule(&gs.patterns);
64+ freesufrules(&gs.sufs);
65+ freeenv(&gs.env);
66+ free(gs.tas);
67+ return -1;
68+ }
69+ graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
70+ t = &graph->v[graph->n++];
71+ memset(t, 0, sizeof(*t));
72+ t->name = intern(goals->v[i]);
73+ if (hasword(gs.phony, t->name))
74+ t->phony = 1;
75+ }
76+ }
77+
78 for (;;) {
79 int changed;
80
81@@ -318,6 +363,8 @@ buildgraph(const struct RuleSet *ruleset, struct Graph *graph)
82 matched = t->recipes.n > 0;
83 if (!matched) {
84 instsufrule(&gs.sufs, graph, t, &targetctx);
85+ if (t->recipes.n == 0)
86+ instdefaultrule(&gs, t, &targetctx);
87 }
88 freeenv(&env);
89 if (targetctx.errors)
+1,
-1
1@@ -663,7 +663,7 @@ buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack)
2 rc = eval(path, &ast, sg->assigns.n ? &pre : 0, envoverride, &rs);
3 if (rc < 0)
4 goto out;
5- rc = buildgraph(&rs, &sg->graph);
6+ rc = buildgraph(&rs, &sg->goals, &sg->graph);
7 if (rc < 0)
8 goto out;
9 rc = expandgraph(&sg->graph);
+1,
-4
1@@ -150,10 +150,7 @@ matchsuf(const char *suf, const char *name, char **stem)
2 * $* stem
3 * $$ literal $
4 *
5- * this turns them into concrete strings,
6- * later during ninja generation
7- * translateauto() turns any remaining ones
8- * into ninja variables if possible
9+ * this turns them into concrete strings.
10 */
11 static char *
12 expandauto(const char *s, const struct Target *t, const char *stem)
+2,
-1
1@@ -182,6 +182,7 @@ struct RuleSet {
2 size_t ntvars;
3 struct RuleNode *rules;
4 size_t nrules;
5+ struct RecipeList defaultrule;
6 struct StrList phony;
7 int export_all;
8 int posix;
9@@ -242,7 +243,7 @@ int eval(const char *path, const struct Ast *ast, const struct Ast *pre, int env
10 int parse(const char *path, const char *src, struct Ast *ast);
11 void freeast(struct Ast *ast);
12 void freeruleset(struct RuleSet *ruleset);
13-int buildgraph(const struct RuleSet *ruleset, struct Graph *graph);
14+int buildgraph(const struct RuleSet *ruleset, const struct StrList *goals, struct Graph *graph);
15 int expandgraph(struct Graph *graph);
16 void freegraph(struct Graph *graph);
17 int buildsubgraph(struct SubGraph *sg);
+3,
-0
1@@ -0,0 +1,3 @@
2+all:
3+ @echo top=$(CURDIR)
4+ @cd sub && $(MAKE) -f alt.mk show
+2,
-0
1@@ -0,0 +1,2 @@
2+top=#PWD#
3+sub=#PWD#/sub
+24,
-0
1@@ -0,0 +1,24 @@
2+{
3+ "case": "t002",
4+ "category": "recursion",
5+ "compare_output": true,
6+ "description": "recursive make preserves CURDIR across different directories",
7+ "details": "",
8+ "env": {},
9+ "expected_exit": 0,
10+ "options": "",
11+ "options_mode": "argv",
12+ "output_mode": "exact",
13+ "setup": [
14+ {
15+ "kind": "file",
16+ "mode": "0644",
17+ "mtime": 0,
18+ "path": "sub/alt.mk",
19+ "content": "show:\n\t@echo sub=$(CURDIR)\n"
20+ }
21+ ],
22+ "stdin": "",
23+ "suite": "shin",
24+ "timeout_seconds": 60
25+}
+9,
-0
1@@ -239,6 +239,15 @@ if [ -n "$vars" ]; then
2 done
3 IFS=$oldifs
4 fi
5+if [ -n "$targets" ]; then
6+ oldifs=$IFS
7+ IFS='
8+'
9+ for target in $targets; do
10+ set -- "$@" "$target"
11+ done
12+ IFS=$oldifs
13+fi
14
15 if "$@"; then
16 :