commit 088da53
shrub
·
2026-04-20 16:41:51 +0000 UTC
parent 2fadeb0
handle recursive make invocations
14 files changed,
+1148,
-359
M
Makefile
+2,
-1
1@@ -7,7 +7,8 @@ SRCS = cli/main.c \
2 src/parse.c \
3 src/eval/eval.c \
4 src/eval/dollar.c \
5- src/graph.c \
6+ src/graph/graph.c \
7+ src/graph/subgraph.c \
8 src/posix.c \
9 src/gnu/pattern.c \
10 backends/ninja.c \
+112,
-9
1@@ -2,9 +2,58 @@
2 #include "internal.h"
3
4 #include <stdio.h>
5+#include <stdlib.h>
6+#include <string.h>
7+#include <time.h>
8
9 /* graphviz backend */
10
11+struct Style {
12+ const char *stroke;
13+ const char *fill;
14+ const char *cluster;
15+};
16+
17+static const struct Style styles[] = {
18+ { "#0F766E", "#CCFBF1", "#F0FDFA" },
19+ { "#2563EB", "#DBEAFE", "#EFF6FF" },
20+ { "#CA8A04", "#FEF3C7", "#FFFBEB" },
21+ { "#C2410C", "#FED7AA", "#FFF7ED" },
22+ { "#9333EA", "#E9D5FF", "#FAF5FF" },
23+ { "#BE185D", "#FBCFE8", "#FDF2F8" },
24+ { "#4F46E5", "#C7D2FE", "#EEF2FF" },
25+ { "#059669", "#D1FAE5", "#ECFDF5" },
26+};
27+
28+static size_t styleorder[sizeof(styles) / sizeof(styles[0])];
29+static size_t nextstyle;
30+
31+static void
32+shufflestyles(void)
33+{
34+ size_t i;
35+
36+ for (i = 0; i < sizeof(styles) / sizeof(styles[0]); i++)
37+ styleorder[i] = i;
38+ for (i = sizeof(styles) / sizeof(styles[0]); i > 1; i--) {
39+ size_t j, t;
40+
41+ j = (size_t)(rand() % (int)i);
42+ t = styleorder[i - 1];
43+ styleorder[i - 1] = styleorder[j];
44+ styleorder[j] = t;
45+ }
46+ nextstyle = 0;
47+}
48+
49+static const struct Style *
50+pickstyle(void)
51+{
52+ if (nextstyle >= sizeof(styles) / sizeof(styles[0]))
53+ shufflestyles();
54+ return &styles[styleorder[nextstyle++]];
55+}
56+
57 static void
58 emitstr(FILE *fp, const char *s)
59 {
60@@ -17,20 +66,66 @@ emitstr(FILE *fp, const char *s)
61 }
62 }
63
64+static void
65+emitownerlabel(FILE *fp, const char *owner)
66+{
67+ const char *label;
68+
69+ label = owner && owner[0] ? owner : "(root)";
70+ emitstr(fp, label);
71+}
72+
73 static void
74 emitnode(FILE *fp, size_t i, const struct Target *t)
75 {
76 fprintf(fp, " n%zu [label=\"", i);
77 emitstr(fp, t->name);
78- fprintf(fp, "\"");
79+ fprintf(fp, "\", penwidth=1.2, color=\"#000000\", fillcolor=\"#FFFFFF\"");
80 if (t->recipes.n > 0) {
81- fprintf(fp, ", shape=box");
82+ fprintf(fp, ", shape=box, style=\"filled,rounded\"");
83 } else if (t->prereqs.n > 0 || t->order_only.n > 0) {
84- fprintf(fp, ", style=dashed");
85+ fprintf(fp, ", shape=box, style=\"filled,dashed,rounded\"");
86+ } else {
87+ fprintf(fp, ", style=filled");
88 }
89 fprintf(fp, "];\n");
90 }
91
92+static void
93+emitowners(FILE *fp, const struct Graph *graph, const char *owner)
94+{
95+ size_t i;
96+
97+ for (i = 0; i < graph->n; i++) {
98+ if (!targetownedby(&graph->v[i], owner))
99+ continue;
100+ emitnode(fp, i, &graph->v[i]);
101+ }
102+}
103+
104+static void
105+emitcluster(FILE *fp, const struct Graph *root, const struct SubGraph *sg, size_t *nextid)
106+{
107+ size_t i;
108+ const struct Style *st;
109+ size_t id;
110+
111+ id = (*nextid)++;
112+ st = pickstyle();
113+ fprintf(fp, " subgraph cluster_%zu {\n", id);
114+ fprintf(fp, " label=\"");
115+ emitownerlabel(fp, sg->prefix);
116+ fprintf(fp, "\";\n");
117+ fprintf(fp, " labelloc=t;\n");
118+ fprintf(fp, " margin=18;\n");
119+ fprintf(fp, " style=\"rounded,filled\";\n");
120+ fprintf(fp, " color=\"%s\";\n fillcolor=\"%s\";\n", st->stroke, st->cluster);
121+ emitowners(fp, root, sg->prefix);
122+ for (i = 0; i < sg->graph.nsubs; i++)
123+ emitcluster(fp, root, &sg->graph.subs[i], nextid);
124+ fprintf(fp, " }\n");
125+}
126+
127 static void
128 emitedges(FILE *fp, const struct Graph *graph, size_t i, const struct Target *t)
129 {
130@@ -41,13 +136,15 @@ emitedges(FILE *fp, const struct Graph *graph, size_t i, const struct Target *t)
131 dep = findctarget(graph, t->prereqs.v[j]);
132 if (!dep)
133 continue;
134- fprintf(fp, " n%zu -> n%zu;\n", (size_t)(dep - graph->v), i);
135+ fprintf(fp, " n%zu -> n%zu [color=\"#475569\", penwidth=1.2];\n",
136+ (size_t)(dep - graph->v), i);
137 }
138 for (j = 0; j < t->order_only.n; j++) {
139 dep = findctarget(graph, t->order_only.v[j]);
140 if (!dep)
141 continue;
142- fprintf(fp, " n%zu -> n%zu [style=dashed];\n", (size_t)(dep - graph->v), i);
143+ fprintf(fp, " n%zu -> n%zu [style=dashed, color=\"#F59E0B\", penwidth=1.2];\n",
144+ (size_t)(dep - graph->v), i);
145 }
146 }
147
148@@ -55,17 +152,23 @@ int
149 gengraphviz(const struct Graph *graph, const char *path)
150 {
151 FILE *fp;
152- size_t i;
153+ size_t i, clusterid;
154
155 fp = fopen(path, "w");
156 if (!fp)
157 return -1;
158+ srand((unsigned)time(0));
159+ shufflestyles();
160
161 fprintf(fp, "digraph shin {\n");
162 fprintf(fp, " rankdir=LR;\n");
163- fprintf(fp, " node [fontname=\"monospace\"];\n");
164- for (i = 0; i < graph->n; i++)
165- emitnode(fp, i, &graph->v[i]);
166+ fprintf(fp, " graph [bgcolor=\"#FFFFFF\", pad=0.3, nodesep=0.4, ranksep=0.7, splines=true];\n");
167+ fprintf(fp, " node [fontname=\"monospace\", fontsize=10];\n");
168+ fprintf(fp, " edge [arrowsize=0.7];\n");
169+ emitowners(fp, graph, 0);
170+ clusterid = 0;
171+ for (i = 0; i < graph->nsubs; i++)
172+ emitcluster(fp, graph, &graph->subs[i], &clusterid);
173 fprintf(fp, "\n");
174 for (i = 0; i < graph->n; i++)
175 emitedges(fp, graph, i, &graph->v[i]);
+33,
-26
1@@ -23,16 +23,16 @@ static char *
2 joinrecipes(const struct Target *t)
3 {
4 size_t i;
5+ struct StrList bodies;
6 char *s;
7
8- s = xstrdup("");
9+ memset(&bodies, 0, sizeof(bodies));
10 for (i = 0; i < t->recipes.n; i++) {
11- char *next;
12-
13- next = s[0] ? cat3(s, " && ", t->recipes.v[i].body) : xstrdup(t->recipes.v[i].body);
14- free(s);
15- s = next;
16+ bodies.v = xrealloc(bodies.v, (bodies.n + 1) * sizeof(bodies.v[0]));
17+ bodies.v[bodies.n++] = t->recipes.v[i].body;
18 }
19+ s = joinstrs(&bodies, " && ");
20+ free(bodies.v);
21 return s;
22 }
23
24@@ -116,24 +116,8 @@ findrule(struct Rule *rules, size_t n, const char *cmd)
25 return 0;
26 }
27
28-static const char *
29-defaulttarget(const struct Graph *graph)
30-{
31- size_t i;
32-
33- if (findctarget(graph, "all"))
34- return "all";
35- for (i = 0; i < graph->n; i++) {
36- if (strcmp(graph->v[i].name, "clean") == 0 || strcmp(graph->v[i].name, "fmt") == 0)
37- continue;
38- if (graph->v[i].recipes.n > 0 || graph->v[i].prereqs.n > 0 || graph->v[i].order_only.n > 0)
39- return graph->v[i].name;
40- }
41- return 0;
42-}
43-
44-int
45-genninja(const struct Graph *graph, const char *path)
46+static int
47+genninjafile(const struct Graph *graph, const char *path, const char *prefix, int root)
48 {
49 FILE *fp;
50 size_t i, j;
51@@ -141,6 +125,17 @@ genninja(const struct Graph *graph, const char *path)
52 struct Rule *rules;
53 size_t nrules;
54
55+ for (i = 0; i < graph->nsubs; i++) {
56+ char *childpath;
57+
58+ childpath = joinpath(graph->subs[i].cwd, "build.ninja");
59+ if (genninjafile(&graph->subs[i].graph, childpath, graph->subs[i].prefix, 0) < 0) {
60+ free(childpath);
61+ return -1;
62+ }
63+ free(childpath);
64+ }
65+
66 fp = fopen(path, "w");
67 if (!fp)
68 return -1;
69@@ -148,7 +143,13 @@ genninja(const struct Graph *graph, const char *path)
70 ruleid = 0;
71 rules = 0;
72 nrules = 0;
73+ for (i = 0; i < graph->nsubs; i++)
74+ fprintf(fp, "subninja %s/build.ninja\n", graph->subs[i].prefix);
75+ if (graph->nsubs)
76+ fputc('\n', fp);
77 for (i = 0; i < graph->n; i++) {
78+ if (!targetownedby(&graph->v[i], prefix))
79+ continue;
80 if (graph->v[i].recipes.n > 0) {
81 char *cmd;
82 int id;
83@@ -186,8 +187,8 @@ genninja(const struct Graph *graph, const char *path)
84 fprintf(fp, "\n\n");
85 }
86 }
87- if (defaulttarget(graph))
88- fprintf(fp, "default %s\n", defaulttarget(graph));
89+ if (root && defaulttarget(graph, prefix))
90+ fprintf(fp, "default %s\n", defaulttarget(graph, prefix)->name);
91
92 fclose(fp);
93 for (i = 0; i < nrules; i++)
94@@ -195,3 +196,9 @@ genninja(const struct Graph *graph, const char *path)
95 free(rules);
96 return 0;
97 }
98+
99+int
100+genninja(const struct Graph *graph, const char *path)
101+{
102+ return genninjafile(graph, path, 0, 1);
103+}
+101,
-144
1@@ -1,4 +1,5 @@
2 #include "shinobi.h"
3+#include "internal.h"
4 #include "compcmd.h"
5 #include "graphviz.h"
6 #include "ninja.h"
7@@ -15,40 +16,6 @@ usage(FILE *fp, const char *argv0)
8 fprintf(fp, "usage: %s [-ag] [-G ninja|dot|compcmd] [-C dir] [-f file]\n", argv0);
9 }
10
11-static char *
12-readfile(const char *path)
13-{
14- FILE *fp;
15- long n;
16- char *buf;
17-
18- fp = fopen(path, "rb");
19- if (!fp)
20- return 0;
21- if (fseek(fp, 0, SEEK_END) < 0) {
22- fclose(fp);
23- return 0;
24- }
25- n = ftell(fp);
26- if (n < 0) {
27- fclose(fp);
28- return 0;
29- }
30- if (fseek(fp, 0, SEEK_SET) < 0) {
31- fclose(fp);
32- return 0;
33- }
34- buf = malloc((size_t)n + 1);
35- if (fread(buf, 1, (size_t)n, fp) != (size_t)n) {
36- fclose(fp);
37- free(buf);
38- return 0;
39- }
40- buf[n] = 0;
41- fclose(fp);
42- return buf;
43-}
44-
45 static int
46 isvarassign(const char *s)
47 {
48@@ -60,40 +27,6 @@ isvarassign(const char *s)
49 return 1;
50 }
51
52-static char *
53-appendassigns(char *src, char **assigns, size_t nassigns)
54-{
55- size_t i, len, extra;
56- char *out;
57-
58- if (nassigns == 0)
59- return src;
60- len = strlen(src);
61- extra = len > 0 && src[len - 1] != '\n' ? 1 : 0;
62- for (i = 0; i < nassigns; i++)
63- extra += strlen(assigns[i]) + 1;
64- out = malloc(len + extra + 1);
65- if (!out) {
66- free(src);
67- return 0;
68- }
69- memcpy(out, src, len);
70- extra = len;
71- if (extra > 0 && out[extra - 1] != '\n')
72- out[extra++] = '\n';
73- for (i = 0; i < nassigns; i++) {
74- size_t n;
75-
76- n = strlen(assigns[i]);
77- memcpy(out + extra, assigns[i], n);
78- extra += n;
79- out[extra++] = '\n';
80- }
81- out[extra] = 0;
82- free(src);
83- return out;
84-}
85-
86 static const char *
87 assignopname(enum AssignOp op)
88 {
89@@ -232,11 +165,41 @@ dumpgraph(const struct Graph *graph)
90 const struct Target *t = &graph->v[i];
91
92 printindent(1);
93- printf("target %s\n", t->name);
94+ printf("target %s", t->name);
95+ if (t->owner)
96+ printf(" [owner=%s]", t->owner);
97+ putchar('\n');
98 dumpstrlist("prereqs", &t->prereqs, 2);
99 dumpstrlist("order-only", &t->order_only, 2);
100 dumprecipes(&t->recipes, 2);
101 }
102+ if (graph->nsubs) {
103+ printindent(1);
104+ printf("subgraphs (%zu):\n", graph->nsubs);
105+ for (i = 0; i < graph->nsubs; i++) {
106+ size_t j;
107+
108+ printindent(2);
109+ printf("%s", graph->subs[i].prefix ? graph->subs[i].prefix : ".");
110+ if (graph->subs[i].parents.n) {
111+ printf(" parents=");
112+ for (j = 0; j < graph->subs[i].parents.n; j++) {
113+ if (j)
114+ putchar(',');
115+ fputs(graph->subs[i].parents.v[j], stdout);
116+ }
117+ }
118+ if (graph->subs[i].goals.n) {
119+ printf(" goals=");
120+ for (j = 0; j < graph->subs[i].goals.n; j++) {
121+ if (j)
122+ putchar(',');
123+ fputs(graph->subs[i].goals.v[j], stdout);
124+ }
125+ }
126+ putchar('\n');
127+ }
128+ }
129 }
130
131 int
132@@ -248,6 +211,7 @@ main(int argc, char **argv)
133 GEN_COMPCMD,
134 };
135 const char *path;
136+ char *pathbuf;
137 char *src;
138 int dump_ast;
139 int dump_graph;
140@@ -257,14 +221,19 @@ main(int argc, char **argv)
141 enum Generator gen;
142 struct Ast ast;
143 struct RuleSet rs;
144- struct Graph graph;
145+ struct SubGraph sg;
146
147 path = 0;
148+ pathbuf = 0;
149 dump_ast = 0;
150 dump_graph = 0;
151 gen = GEN_NINJA;
152 assigns = 0;
153 nassigns = 0;
154+ memset(&sg, 0, sizeof(sg));
155+ memset(&ast, 0, sizeof(ast));
156+ memset(&rs, 0, sizeof(rs));
157+ src = 0;
158 for (i = 1; i < argc; i++) {
159 if (strcmp(argv[i], "-a") == 0) {
160 dump_ast = 1;
161@@ -326,105 +295,93 @@ main(int argc, char **argv)
162 return 1;
163 }
164 }
165- if (path) {
166- src = readfile(path);
167- if (!src) {
168- fprintf(stderr, "could not read %s\n", path);
169- return 1;
170- }
171- } else {
172- static const char *const defaults[] = {
173- "GNUmakefile",
174- "makefile",
175- "Makefile",
176- 0,
177- };
178- size_t mk;
179+ for (i = 0; i < (int)nassigns; i++)
180+ addstr(&sg.assigns, assigns[i]);
181+ if (dump_ast) {
182+ char *mkpath;
183
184- src = 0;
185- for (mk = 0; defaults[mk]; mk++) {
186- path = defaults[mk];
187- src = readfile(path);
188- if (src)
189- break;
190+ mkpath = 0;
191+ if (loadmakefile(path, &mkpath, &src) < 0) {
192+ if (path)
193+ fprintf(stderr, "could not read %s\n", path);
194+ else
195+ fprintf(stderr, "could not find a makefile\n");
196+ free(assigns);
197+ freesubgraph(&sg);
198+ return 1;
199 }
200- if (!src) {
201- fprintf(stderr, "could not find a makefile\n");
202+ path = mkpath;
203+ pathbuf = mkpath;
204+ src = appendassigns(src, &sg.assigns);
205+ if (parse(path, src, &ast) < 0) {
206+ fprintf(stderr, "parse error in %s\n", path);
207+ free(assigns);
208+ free(pathbuf);
209+ free(src);
210+ freesubgraph(&sg);
211 return 1;
212 }
213- }
214- src = appendassigns(src, assigns, nassigns);
215- if (!src) {
216- free(assigns);
217- fprintf(stderr, "out of memory\n");
218- return 1;
219- }
220- if (parse(path, src, &ast) < 0) {
221- fprintf(stderr, "parse error in %s\n", path);
222- free(assigns);
223- free(src);
224- return 1;
225- }
226- {
227- size_t marked = 0;
228- size_t k = ast.n;
229- while (k > 0 && marked < nassigns) {
230- k--;
231- if (ast.v[k].kind == NODE_ASSIGN) {
232- ast.v[k].data.assign.origin = ORIGIN_COMMAND;
233- marked++;
234+ {
235+ size_t marked = 0;
236+ size_t k = ast.n;
237+
238+ while (k > 0 && marked < nassigns) {
239+ k--;
240+ if (ast.v[k].kind == NODE_ASSIGN) {
241+ ast.v[k].data.assign.origin = ORIGIN_COMMAND;
242+ marked++;
243+ }
244 }
245 }
246- }
247- free(assigns);
248- if (eval(&ast, &rs) < 0) {
249- fprintf(stderr, "eval error in %s\n", path);
250- freeast(&ast);
251- free(src);
252- return 1;
253- }
254- if (dump_ast)
255+ if (eval(&ast, &rs) < 0) {
256+ fprintf(stderr, "eval error in %s\n", path);
257+ free(assigns);
258+ freeast(&ast);
259+ free(pathbuf);
260+ free(src);
261+ freesubgraph(&sg);
262+ return 1;
263+ }
264 dumpruleset(&rs);
265- if (buildgraph(&rs, &graph) < 0) {
266- fprintf(stderr, "graph error in %s\n", path);
267 freeruleset(&rs);
268 freeast(&ast);
269 free(src);
270+ }
271+ if (path)
272+ sg.makefile = xstrdup(path);
273+ free(assigns);
274+ assigns = 0;
275+ if (buildsubgraph(&sg) < 0) {
276+ fprintf(stderr, "graph error in %s\n", path ? path : "(default)");
277+ free(pathbuf);
278+ freesubgraph(&sg);
279 return 1;
280 }
281 if (dump_graph)
282- dumpgraph(&graph);
283+ dumpgraph(&sg.graph);
284 if (gen == GEN_DOT) {
285- if (gengraphviz(&graph, "build.dot") < 0) {
286+ if (gengraphviz(&sg.graph, "build.dot") < 0) {
287 fprintf(stderr, "graphviz generation error\n");
288- freegraph(&graph);
289- freeruleset(&rs);
290- freeast(&ast);
291- free(src);
292+ free(pathbuf);
293+ freesubgraph(&sg);
294 return 1;
295 }
296 }
297 if (gen == GEN_COMPCMD) {
298- if (gencompcmd(&graph, "compile_commands.json") < 0) {
299+ if (gencompcmd(&sg.graph, "compile_commands.json") < 0) {
300 fprintf(stderr, "compile_commands generation error\n");
301- freegraph(&graph);
302- freeruleset(&rs);
303- freeast(&ast);
304- free(src);
305+ free(pathbuf);
306+ freesubgraph(&sg);
307 return 1;
308 }
309 }
310- if (gen == GEN_NINJA && genninja(&graph, "build.ninja") < 0) {
311+ if (gen == GEN_NINJA && genninja(&sg.graph, "build.ninja") < 0) {
312 fprintf(stderr, "ninja generation error\n");
313- freegraph(&graph);
314- freeruleset(&rs);
315- freeast(&ast);
316- free(src);
317+ free(pathbuf);
318+ freesubgraph(&sg);
319 return 1;
320 }
321- freegraph(&graph);
322- freeruleset(&rs);
323- freeast(&ast);
324- free(src);
325+ free(pathbuf);
326+ freesubgraph(&sg);
327 return 0;
328 }
+1,
-19
1@@ -138,24 +138,6 @@ copywords(struct StrList *out, const struct StrList *in, struct EvalCtx *ctx)
2 }
3 }
4
5-static void
6-copyrecipes(struct RecipeList *out, const struct RecipeList *in)
7-{
8- size_t i;
9-
10- memset(out, 0, sizeof(*out));
11- for (i = 0; i < in->n; i++) {
12- out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
13- out->v[out->n].body = xstrdup(in->v[i].body);
14- out->v[out->n].silent = in->v[i].silent;
15- out->v[out->n].ignore = in->v[i].ignore;
16- out->v[out->n].recursive = in->v[i].recursive;
17- out->v[out->n].submake = in->v[i].submake;
18- copysubmake(&out->v[out->n].sm, &in->v[i].sm);
19- out->n++;
20- }
21-}
22-
23 static void
24 addrulesetassign(struct AssignNode **vec, size_t *n, const struct Node *src, struct EvalCtx *ctx)
25 {
26@@ -187,7 +169,7 @@ addrulesetrule(struct RuleSet *out, const struct RuleNode *src, struct EvalCtx *
27 copywords(&dst->targets, &src->targets, ctx);
28 copywords(&dst->prereqs, &src->prereqs, ctx);
29 copywords(&dst->order_only, &src->order_only, ctx);
30- copyrecipes(&dst->recipes, &src->recipes);
31+ addrecipes(&dst->recipes, &src->recipes);
32 }
33
34 static int
R src/graph.c =>
src/graph/graph.c
+8,
-78
1@@ -28,32 +28,6 @@ struct GraphState {
2 int saw_suffixes;
3 };
4
5-static int
6-isonesuffix(const char *s, char **from)
7-{
8- if (!s || s[0] != '.')
9- return 0;
10- if (!s[1] || strchr(s + 1, '.'))
11- return 0;
12- *from = xstrdup(s);
13- return 1;
14-}
15-
16-static int
17-istwosuffix(const char *s, char **from, char **to)
18-{
19- const char *mid;
20-
21- if (!s || s[0] != '.')
22- return 0;
23- mid = strchr(s + 1, '.');
24- if (!mid || mid == s + 1 || !mid[1] || strchr(mid + 1, '.'))
25- return 0;
26- *from = xstrndup(s, (size_t)(mid - s));
27- *to = xstrdup(mid);
28- return 1;
29-}
30-
31 static void
32 seedsufs(struct SufRules *rules, const struct RuleNode *rule)
33 {
34@@ -61,7 +35,7 @@ seedsufs(struct SufRules *rules, const struct RuleNode *rule)
35
36 if (rule->targets.n != 1 || rule->prereqs.n != 0 || rule->order_only.n != 0)
37 return;
38- if (isonesuffix(rule->targets.v[0], &from)) {
39+ if (issinglesuf(rule->targets.v[0], &from)) {
40 struct StrList list;
41
42 memset(&list, 0, sizeof(list));
43@@ -71,7 +45,7 @@ seedsufs(struct SufRules *rules, const struct RuleNode *rule)
44 free(from);
45 return;
46 }
47- if (!istwosuffix(rule->targets.v[0], &from, &to))
48+ if (!issuf(rule->targets.v[0], &from, &to))
49 return;
50 {
51 char *tmp[2];
52@@ -120,37 +94,6 @@ addexprecipes(struct RecipeList *dest, const struct RecipeList *src, struct Eval
53 }
54 }
55
56-static int
57-hasword(const struct StrList *list, const char *word)
58-{
59- size_t i;
60-
61- for (i = 0; i < list->n; i++) {
62- if (strcmp(list->v[i], word) == 0)
63- return 1;
64- }
65- return 0;
66-}
67-
68-static void
69-marksubmake(struct Graph *graph, const char *tname)
70-{
71- const char *name = "SubMake";
72- struct Target *t;
73-
74- if (!findtarget(graph, name)) {
75- graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
76- memset(&graph->v[graph->n], 0, sizeof(graph->v[graph->n]));
77- graph->v[graph->n].name = xstrdup(name);
78- graph->n++;
79- }
80- t = tname ? findtarget(graph, tname) : 0;
81- if (t && !hasword(&t->order_only, name)) {
82- t->order_only.v = xrealloc(t->order_only.v, (t->order_only.n + 1) * sizeof(t->order_only.v[0]));
83- t->order_only.v[t->order_only.n++] = xstrdup(name);
84- }
85-}
86-
87 /* we apply target assignments from the graphstate (gs.tas) to those specific
88 * targets, for some target like
89 *
90@@ -339,25 +282,6 @@ buildgraph(const struct RuleSet *ruleset, struct Graph *graph)
91 start = current_n;
92 }
93
94- for (i = 0; i < graph->n; i++) {
95- struct Target *t = &graph->v[i];
96- size_t k;
97-
98- for (k = 0; k < t->recipes.n; k++) {
99- char *tname;
100-
101- if (!t->recipes.v[k].submake)
102- continue;
103- tname = xstrdup(t->name);
104- marksubmake(graph, tname);
105- free(tname);
106- fprintf(stderr, "recursive make detected! i dont know how to handle that yet!\n");
107- ctx.errors++;
108- i = graph->n;
109- break;
110- }
111- }
112-
113 for (i = 0; i < gs.ntas; i++) {
114 freestrs(&gs.tas[i].targets);
115 free(gs.tas[i].lhs);
116@@ -380,11 +304,17 @@ freegraph(struct Graph *graph)
117 return;
118 for (i = 0; i < graph->n; i++) {
119 free(graph->v[i].name);
120+ free(graph->v[i].owner);
121 freestrs(&graph->v[i].prereqs);
122 freestrs(&graph->v[i].order_only);
123 freerecipes(&graph->v[i].recipes);
124 }
125+ for (i = 0; i < graph->nsubs; i++)
126+ freesubgraph(&graph->subs[i]);
127 free(graph->v);
128+ free(graph->subs);
129 graph->v = 0;
130 graph->n = 0;
131+ graph->subs = 0;
132+ graph->nsubs = 0;
133 }
+584,
-0
1@@ -0,0 +1,584 @@
2+#include "internal.h"
3+
4+#include <stdio.h>
5+#include <stdlib.h>
6+#include <string.h>
7+#include <unistd.h>
8+
9+struct SubGraphStack {
10+ char **v;
11+ size_t n;
12+};
13+
14+static int
15+isempty(const char *s)
16+{
17+ return !s || !s[0];
18+}
19+
20+static int
21+sameprefix(const char *a, const char *b)
22+{
23+ if (isempty(a))
24+ return isempty(b);
25+ if (isempty(b))
26+ return 0;
27+ return strcmp(a, b) == 0;
28+}
29+
30+static int
31+samelist(const struct StrList *a, const struct StrList *b)
32+{
33+ size_t i;
34+
35+ if (a->n != b->n)
36+ return 0;
37+ for (i = 0; i < a->n; i++) {
38+ if (strcmp(a->v[i], b->v[i]) != 0)
39+ return 0;
40+ }
41+ return 1;
42+}
43+
44+static char *
45+joinprefix(const char *parent, const char *child)
46+{
47+ char *raw, *norm;
48+
49+ if (isempty(child))
50+ return parent ? xstrdup(parent) : 0;
51+ if (child[0] == '/')
52+ return normpath(child);
53+ if (isempty(parent))
54+ return normpath(child);
55+ raw = cat3(parent, "/", child);
56+ norm = normpath(raw);
57+ free(raw);
58+ return norm;
59+}
60+
61+static char *
62+rebaseword(const char *prefix, const char *name)
63+{
64+ char *raw, *norm;
65+
66+ if (!name[0] || name[0] == '/')
67+ return normpath(name);
68+ if (isempty(prefix))
69+ return normpath(name);
70+ raw = cat3(prefix, "/", name);
71+ norm = normpath(raw);
72+ free(raw);
73+ return norm;
74+}
75+
76+static char *
77+prefixrecipe(const char *prefix, const char *body)
78+{
79+ size_t nprefix, nbody;
80+ char *out;
81+
82+ if (isempty(prefix) || strcmp(prefix, ".") == 0)
83+ return xstrdup(body);
84+ nprefix = strlen(prefix);
85+ nbody = strlen(body);
86+ out = xmalloc(5 + nprefix + 4 + nbody + 1);
87+ memcpy(out, "cd ", 3);
88+ memcpy(out + 3, prefix, nprefix);
89+ memcpy(out + 3 + nprefix, " && ", 4);
90+ memcpy(out + 7 + nprefix, body, nbody);
91+ out[7 + nprefix + nbody] = 0;
92+ return out;
93+}
94+
95+static int
96+resolvegoals(const struct SubGraph *sg, struct StrList *out)
97+{
98+ const struct Target *t;
99+
100+ memset(out, 0, sizeof(*out));
101+ if (sg->goals.n) {
102+ size_t i;
103+
104+ for (i = 0; i < sg->goals.n; i++) {
105+ char *name;
106+
107+ name = rebaseword(sg->prefix, sg->goals.v[i]);
108+ addstr(out, name);
109+ free(name);
110+ }
111+ return 0;
112+ }
113+ t = defaulttarget(&sg->graph, sg->prefix);
114+ if (t) {
115+ addstr(out, t->name);
116+ return 0;
117+ }
118+ fprintf(stderr, "submake has no default target\n");
119+ return -1;
120+}
121+
122+static void
123+pushstack(struct SubGraphStack *stack, const char *key)
124+{
125+ stack->v = xrealloc(stack->v, (stack->n + 1) * sizeof(stack->v[0]));
126+ stack->v[stack->n++] = xstrdup(key);
127+}
128+
129+static void
130+popstack(struct SubGraphStack *stack)
131+{
132+ if (!stack->n)
133+ return;
134+ free(stack->v[stack->n - 1]);
135+ stack->n--;
136+}
137+
138+static int
139+instack(const struct SubGraphStack *stack, const char *key)
140+{
141+ size_t i;
142+
143+ for (i = 0; i < stack->n; i++) {
144+ if (strcmp(stack->v[i], key) == 0)
145+ return 1;
146+ }
147+ return 0;
148+}
149+
150+static char *
151+subgraphkey(const struct SubGraph *sg)
152+{
153+ size_t i, n;
154+ char *key;
155+
156+ n = strlen(sg->cwd) + 1 + strlen(sg->makefile ? sg->makefile : "") + 1;
157+ for (i = 0; i < sg->assigns.n; i++)
158+ n += strlen(sg->assigns.v[i]) + 1;
159+ key = xmalloc(n + 1);
160+ key[0] = 0;
161+ strcat(key, sg->cwd);
162+ strcat(key, "|");
163+ if (sg->makefile)
164+ strcat(key, sg->makefile);
165+ for (i = 0; i < sg->assigns.n; i++) {
166+ strcat(key, "|");
167+ strcat(key, sg->assigns.v[i]);
168+ }
169+ return key;
170+}
171+
172+static void
173+removerecipe(struct RecipeList *list, size_t idx)
174+{
175+ size_t i;
176+
177+ free(list->v[idx].body);
178+ freesubmake(&list->v[idx].sm);
179+ for (i = idx + 1; i < list->n; i++)
180+ list->v[i - 1] = list->v[i];
181+ list->n--;
182+ if (!list->n) {
183+ free(list->v);
184+ list->v = 0;
185+ return;
186+ }
187+ list->v = xrealloc(list->v, list->n * sizeof(list->v[0]));
188+}
189+
190+static void
191+scopelist(struct StrList *list, const char *prefix)
192+{
193+ size_t i;
194+
195+ for (i = 0; i < list->n; i++) {
196+ char *name;
197+
198+ name = rebaseword(prefix, list->v[i]);
199+ free(list->v[i]);
200+ list->v[i] = name;
201+ }
202+}
203+
204+static void
205+scoperecipes(struct RecipeList *list, const char *prefix)
206+{
207+ size_t i;
208+
209+ for (i = 0; i < list->n; i++) {
210+ char *body;
211+
212+ body = prefixrecipe(prefix, list->v[i].body);
213+ free(list->v[i].body);
214+ list->v[i].body = body;
215+ }
216+}
217+
218+static void
219+scopegraph(struct Graph *graph, const char *prefix)
220+{
221+ size_t i;
222+
223+ if (isempty(prefix))
224+ return;
225+ for (i = 0; i < graph->n; i++) {
226+ char *name;
227+
228+ name = rebaseword(prefix, graph->v[i].name);
229+ free(graph->v[i].name);
230+ graph->v[i].name = name;
231+ free(graph->v[i].owner);
232+ if (graph->v[i].defined || graph->v[i].recipes.n > 0 ||
233+ graph->v[i].prereqs.n > 0 || graph->v[i].order_only.n > 0)
234+ graph->v[i].owner = xstrdup(prefix);
235+ else
236+ graph->v[i].owner = 0;
237+ scopelist(&graph->v[i].prereqs, prefix);
238+ scopelist(&graph->v[i].order_only, prefix);
239+ scoperecipes(&graph->v[i].recipes, prefix);
240+ }
241+}
242+
243+static int
244+mergetarget(struct Graph *graph, const struct Target *src)
245+{
246+ struct Target *dst;
247+
248+ dst = findtarget(graph, src->name);
249+ if (!dst) {
250+ graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
251+ dst = &graph->v[graph->n++];
252+ memset(dst, 0, sizeof(*dst));
253+ dst->name = xstrdup(src->name);
254+ if (src->owner)
255+ dst->owner = xstrdup(src->owner);
256+ } else if (!dst->owner && src->owner) {
257+ dst->owner = xstrdup(src->owner);
258+ } else if (dst->owner && src->owner && !sameprefix(dst->owner, src->owner)) {
259+ fprintf(stderr, "target `%s' crosses subgraph ownership (%s vs %s)\n",
260+ src->name,
261+ dst->owner ? dst->owner : ".",
262+ src->owner ? src->owner : ".");
263+ return -1;
264+ } else if (dst->defined && src->defined && dst->dcolon != src->dcolon) {
265+ fprintf(stderr, "target file `%s' has both : and :: entries, i can't handle that!\n", dst->name);
266+ return -1;
267+ }
268+ if (!dst->defined && src->defined)
269+ dst->dcolon = src->dcolon;
270+ dst->defined |= src->defined;
271+ if (src->defined)
272+ dst->dcolon = src->dcolon;
273+ addwords(&dst->prereqs, &src->prereqs);
274+ addwords(&dst->order_only, &src->order_only);
275+ addrecipes(&dst->recipes, &src->recipes);
276+ return 0;
277+}
278+
279+static int
280+addgraphsub(struct Graph *graph, struct SubGraph *child)
281+{
282+ graph->subs = xrealloc(graph->subs, (graph->nsubs + 1) * sizeof(graph->subs[0]));
283+ graph->subs[graph->nsubs++] = *child;
284+ memset(child, 0, sizeof(*child));
285+ return 0;
286+}
287+
288+/* in make, recursive make calls run sequentially*/
289+static void
290+subgraphorder(struct Graph *graph, const char *owner, const struct StrList *prev)
291+{
292+ size_t i, j, n;
293+
294+ if (!prev->n)
295+ return;
296+ n = owner ? strlen(owner) : 0;
297+ for (i = 0; i < graph->n; i++) {
298+ struct Target *t = &graph->v[i];
299+
300+ if (!t->owner)
301+ continue;
302+ if (strncmp(t->owner, owner, n) != 0)
303+ continue;
304+ if (t->owner[n] != 0 && t->owner[n] != '/')
305+ continue;
306+ for (j = 0; j < prev->n; j++) {
307+ if (!hasword(&t->order_only, prev->v[j]))
308+ addstr(&t->order_only, prev->v[j]);
309+ }
310+ }
311+}
312+
313+static struct SubGraph *
314+findgraphsub(struct Graph *graph, const char *prefix)
315+{
316+ size_t i;
317+
318+ for (i = 0; i < graph->nsubs; i++) {
319+ if (sameprefix(graph->subs[i].prefix, prefix))
320+ return &graph->subs[i];
321+ }
322+ return 0;
323+}
324+
325+static int
326+sameinvocation(const struct SubGraph *a, const struct SubGraph *b)
327+{
328+ if (strcmp(a->cwd, b->cwd) != 0)
329+ return 0;
330+ if (!!a->makefile != !!b->makefile)
331+ return 0;
332+ if (a->makefile && strcmp(a->makefile, b->makefile) != 0)
333+ return 0;
334+ if (!samelist(&a->assigns, &b->assigns))
335+ return 0;
336+ if (!samelist(&a->flags, &b->flags))
337+ return 0;
338+ return 1;
339+}
340+
341+static void
342+mergesubmeta(struct SubGraph *dst, struct SubGraph *src)
343+{
344+ size_t i;
345+
346+ for (i = 0; i < src->parents.n; i++) {
347+ if (!hasword(&dst->parents, src->parents.v[i]))
348+ addstr(&dst->parents, src->parents.v[i]);
349+ }
350+ for (i = 0; i < src->goals.n; i++) {
351+ if (!hasword(&dst->goals, src->goals.v[i]))
352+ addstr(&dst->goals, src->goals.v[i]);
353+ }
354+ freesubgraph(src);
355+ memset(src, 0, sizeof(*src));
356+}
357+
358+static int
359+mergechildgraph(struct SubGraph *parent,
360+ const char *tname,
361+ struct SubGraph *child,
362+ const struct StrList *goals)
363+{
364+ struct SubGraph *known;
365+ struct Target *t;
366+ size_t i;
367+
368+ known = sameprefix(parent->prefix, child->prefix) ? 0 : findgraphsub(&parent->graph, child->prefix);
369+ if (known && !sameinvocation(known, child)) {
370+ fprintf(stderr, "multiple incompatible subgraphs map to %s/build.ninja\n",
371+ child->prefix ? child->prefix : ".");
372+ return -1;
373+ }
374+ if (!known) {
375+ for (i = 0; i < child->graph.n; i++) {
376+ if (mergetarget(&parent->graph, &child->graph.v[i]) < 0)
377+ return -1;
378+ }
379+ }
380+ t = findtarget(&parent->graph, tname);
381+ if (!t) {
382+ fprintf(stderr, "lost parent target `%s' while expanding submake\n", tname);
383+ return -1;
384+ }
385+ for (i = 0; i < goals->n; i++) {
386+ if (!hasword(&t->prereqs, goals->v[i]))
387+ addstr(&t->prereqs, goals->v[i]);
388+ }
389+ if (!sameprefix(parent->prefix, child->prefix)) {
390+ if (known) {
391+ mergesubmeta(known, child);
392+ } else if (addgraphsub(&parent->graph, child) < 0) {
393+ return -1;
394+ }
395+ }
396+ return 0;
397+}
398+
399+static int buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack);
400+
401+static int
402+expandsubgraphs(struct SubGraph *sg, struct SubGraphStack *stack)
403+{
404+ size_t i;
405+
406+ for (i = 0; i < sg->graph.n; i++) {
407+ size_t k;
408+ char *tname;
409+ struct StrList prevgoals;
410+
411+ memset(&prevgoals, 0, sizeof(prevgoals));
412+ tname = xstrdup(sg->graph.v[i].name);
413+ for (k = 0; k < sg->graph.v[i].recipes.n;) {
414+ struct Recipe *r;
415+ struct SubGraph child;
416+ struct StrList goals;
417+
418+ r = &sg->graph.v[i].recipes.v[k];
419+ if (!r->submake) {
420+ k++;
421+ continue;
422+ }
423+ memset(&child, 0, sizeof(child));
424+ child.cwd = r->sm.dir ? joinpath(sg->cwd, r->sm.dir) : xstrdup(sg->cwd);
425+ child.prefix = joinprefix(sg->prefix, r->sm.dir);
426+ addstr(&child.parents, tname);
427+ if (r->sm.makefile)
428+ child.makefile = xstrdup(r->sm.makefile);
429+ addwords(&child.assigns, &r->sm.assigns);
430+ addwords(&child.flags, &r->sm.flags);
431+ addwords(&child.goals, &r->sm.goals);
432+ /* remove the make invocation, we replace it with a graph*/
433+ removerecipe(&sg->graph.v[i].recipes, k);
434+ if (buildsubgraph0(&child, stack) < 0) {
435+ freesubgraph(&child);
436+ freestrs(&prevgoals);
437+ free(tname);
438+ return -1;
439+ }
440+ if (resolvegoals(&child, &goals) < 0) {
441+ freesubgraph(&child);
442+ freestrs(&prevgoals);
443+ free(tname);
444+ return -1;
445+ }
446+ subgraphorder(&child.graph, child.prefix, &prevgoals);
447+ if (mergechildgraph(sg, tname, &child, &goals) < 0) {
448+ freesubgraph(&child);
449+ freestrs(&goals);
450+ freestrs(&prevgoals);
451+ free(tname);
452+ return -1;
453+ }
454+ freestrs(&prevgoals);
455+ prevgoals = goals;
456+ if (!sameprefix(sg->prefix, child.prefix))
457+ continue;
458+ freesubgraph(&child);
459+ }
460+ freestrs(&prevgoals);
461+ free(tname);
462+ }
463+ return 0;
464+}
465+
466+static int
467+buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack)
468+{
469+ struct Ast ast;
470+ struct RuleSet rs;
471+ char *src;
472+ char *path;
473+ char *oldcwd;
474+ char *key;
475+ int rc;
476+ size_t marked;
477+ size_t k;
478+
479+ memset(&ast, 0, sizeof(ast));
480+ memset(&rs, 0, sizeof(rs));
481+ freegraph(&sg->graph);
482+ memset(&sg->graph, 0, sizeof(sg->graph));
483+ src = 0;
484+ path = 0;
485+ oldcwd = getcwddup();
486+ if (chdir(sg->cwd) != 0) {
487+ fprintf(stderr, "failed to chdir to %s\n", sg->cwd);
488+ free(oldcwd);
489+ return -1;
490+ }
491+ free(sg->cwd);
492+ sg->cwd = getcwddup();
493+ if (loadmakefile(sg->makefile, &path, &src) < 0) {
494+ fprintf(stderr, "could not read submakefile in %s\n", sg->cwd);
495+ chdir(oldcwd);
496+ free(oldcwd);
497+ return -1;
498+ }
499+ free(sg->makefile);
500+ sg->makefile = xstrdup(path);
501+ key = subgraphkey(sg);
502+ if (instack(stack, key)) {
503+ fprintf(stderr, "recursive subgraph cycle detected: %s\n", key);
504+ free(key);
505+ free(path);
506+ free(src);
507+ chdir(oldcwd);
508+ free(oldcwd);
509+ return -1;
510+ }
511+ pushstack(stack, key);
512+ free(key);
513+ src = appendassigns(src, &sg->assigns);
514+ rc = parse(path, src, &ast);
515+ if (rc < 0) {
516+ fprintf(stderr, "parse error in %s/%s\n", sg->cwd, path);
517+ goto out;
518+ }
519+ marked = 0;
520+ k = ast.n;
521+ while (k > 0 && marked < sg->assigns.n) {
522+ k--;
523+ if (ast.v[k].kind == NODE_ASSIGN) {
524+ ast.v[k].data.assign.origin = ORIGIN_COMMAND;
525+ marked++;
526+ }
527+ }
528+ rc = eval(&ast, &rs);
529+ if (rc < 0) {
530+ fprintf(stderr, "eval error in %s/%s\n", sg->cwd, path);
531+ goto out;
532+ }
533+ rc = buildgraph(&rs, &sg->graph);
534+ if (rc < 0) {
535+ fprintf(stderr, "graph error in %s/%s\n", sg->cwd, path);
536+ goto out;
537+ }
538+ scopegraph(&sg->graph, sg->prefix);
539+ rc = expandsubgraphs(sg, stack);
540+out:
541+ freeruleset(&rs);
542+ freeast(&ast);
543+ free(src);
544+ free(path);
545+ popstack(stack);
546+ chdir(oldcwd);
547+ free(oldcwd);
548+ return rc;
549+}
550+
551+int
552+buildsubgraph(struct SubGraph *sg)
553+{
554+ struct SubGraphStack stack;
555+
556+ memset(&stack, 0, sizeof(stack));
557+ if (!sg->cwd)
558+ sg->cwd = getcwddup();
559+ if (buildsubgraph0(sg, &stack) < 0) {
560+ while (stack.n)
561+ popstack(&stack);
562+ free(stack.v);
563+ return -1;
564+ }
565+ free(stack.v);
566+ return 0;
567+}
568+
569+void
570+freesubgraph(struct SubGraph *sg)
571+{
572+ if (!sg)
573+ return;
574+ free(sg->cwd);
575+ sg->cwd = 0;
576+ free(sg->prefix);
577+ sg->prefix = 0;
578+ free(sg->makefile);
579+ sg->makefile = 0;
580+ freestrs(&sg->parents);
581+ freestrs(&sg->assigns);
582+ freestrs(&sg->flags);
583+ freestrs(&sg->goals);
584+ freegraph(&sg->graph);
585+}
+11,
-0
1@@ -32,6 +32,17 @@ void *xmalloc(size_t n);
2 void *xrealloc(void *p, size_t n);
3 char *xstrndup(const char *s, size_t n);
4 char *xstrdup(const char *s);
5+char *readfile(const char *path);
6+int loadmakefile(const char *path, char **path_out, char **src_out);
7+char *appendassigns(char *src, const struct StrList *assigns);
8+char *getcwddup(void);
9+char *joinpath(const char *dir, const char *name);
10+char *normpath(const char *path);
11+char *joinstrs(const struct StrList *list, const char *sep);
12+void addstr(struct StrList *list, const char *s);
13+int hasword(const struct StrList *list, const char *word);
14+int targetownedby(const struct Target *t, const char *owner);
15+const struct Target *defaulttarget(const struct Graph *graph, const char *owner);
16 void addnode(struct NodeList *list, struct Node node);
17 void splitwords(struct StrList *out, const char *s, size_t n);
18 char *cat3(const char *a, const char *b, const char *c);
+8,
-58
1@@ -300,40 +300,6 @@ readline(const char **src, int *lineno, struct PreLine *line)
2 return 1;
3 }
4
5-static char *
6-readfile(const char *path)
7-{
8- FILE *fp;
9- long n;
10- char *buf;
11-
12- fp = fopen(path, "rb");
13- if (!fp)
14- return 0;
15- if (fseek(fp, 0, SEEK_END) < 0) {
16- fclose(fp);
17- return 0;
18- }
19- n = ftell(fp);
20- if (n < 0) {
21- fclose(fp);
22- return 0;
23- }
24- if (fseek(fp, 0, SEEK_SET) < 0) {
25- fclose(fp);
26- return 0;
27- }
28- buf = xmalloc((size_t)n + 1);
29- if (fread(buf, 1, (size_t)n, fp) != (size_t)n) {
30- fclose(fp);
31- free(buf);
32- return 0;
33- }
34- buf[n] = 0;
35- fclose(fp);
36- return buf;
37-}
38-
39 static void
40 popinc(struct Inc *inc)
41 {
42@@ -380,22 +346,6 @@ dirpart(const char *path)
43 return xstrndup(path, (size_t)(slash - path));
44 }
45
46-static char *
47-joinpath(const char *dir, const char *name)
48-{
49- size_t ndir, nname;
50- char *out;
51-
52- ndir = strlen(dir);
53- nname = strlen(name);
54- out = xmalloc(ndir + 1 + nname + 1);
55- memcpy(out, dir, ndir);
56- out[ndir] = '/';
57- memcpy(out + ndir + 1, name, nname);
58- out[ndir + 1 + nname] = 0;
59- return out;
60-}
61-
62 static int
63 preprocfile(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc)
64 {
65@@ -433,14 +383,14 @@ preprocfile(const char *path, const char *src_override, struct Pre *pre, struct
66 int rc;
67 size_t kwlen;
68
69- opt = haskw(trim, "-include") || haskw(trim, "sinclude");
70- kwlen = haskw(trim, "-include") || haskw(trim, "sinclude") ? 8 : 7;
71- incarg = trimdup(trim + kwlen, strlen(trim + kwlen));
72- if (isplainpath(incarg)) {
73- full = incarg[0] == '/' ? xstrdup(incarg) : joinpath(dir, incarg);
74- free(line.path);
75- free(line.text);
76- free(trim);
77+ opt = haskw(trim, "-include") || haskw(trim, "sinclude");
78+ kwlen = haskw(trim, "-include") || haskw(trim, "sinclude") ? 8 : 7;
79+ incarg = trimdup(trim + kwlen, strlen(trim + kwlen));
80+ if (isplainpath(incarg)) {
81+ full = joinpath(dir, incarg);
82+ free(line.path);
83+ free(line.text);
84+ free(trim);
85 free(incarg);
86 rc = preprocfile(full, 0, pre, inc);
87 free(full);
+3,
-17
1@@ -99,7 +99,7 @@ pathorargetexists(const struct Graph *graph, const char *name)
2 return access(name, F_OK) == 0;
3 }
4
5-static int
6+int
7 issuf(const char *s, char **from, char **to)
8 {
9 const char *mid;
10@@ -114,7 +114,7 @@ issuf(const char *s, char **from, char **to)
11 return 1;
12 }
13
14-static int
15+int
16 issinglesuf(const char *s, char **from)
17 {
18 if (!s || s[0] != '.')
19@@ -138,20 +138,6 @@ matchsuf(const char *suf, const char *name, char **stem)
20 return 1;
21 }
22
23-static char *
24-joinprereqs(const struct Target *t)
25-{
26- size_t i;
27- char *joined, *next;
28-
29- joined = xstrdup("");
30- for (i = 0; i < t->prereqs.n; i++) {
31- next = joined[0] ? cat3(joined, " ", t->prereqs.v[i]) : xstrdup(t->prereqs.v[i]);
32- free(joined);
33- joined = next;
34- }
35- return joined;
36-}
37
38 /*
39 * expand automatic variables like so:
40@@ -200,7 +186,7 @@ expandauto(const char *s, const struct Target *t, const char *stem)
41 } else if (s[i + 1] == '<') {
42 val = t->prereqs.n > 0 ? t->prereqs.v[0] : "";
43 } else if (s[i + 1] == '^' || s[i + 1] == '+' || s[i + 1] == '?') {
44- val = joinprereqs(t);
45+ val = joinstrs(&t->prereqs, " ");
46 mustfree = 1;
47 } else if (s[i + 1] == '*') {
48 val = stem ? stem : "";
+2,
-0
1@@ -33,6 +33,8 @@ struct SufRules {
2 struct SuffixList active;
3 };
4
5+int issinglesuf(const char *s, char **from);
6+int issuf(const char *s, char **from, char **to);
7 void seedenv(struct Env *env);
8 void imprules(struct SufRules *rules);
9 int collectsufrule(struct SufRules *rules, const struct RuleNode *rule);
+18,
-0
1@@ -161,6 +161,7 @@ struct RuleSet {
2
3 struct Target {
4 char *name;
5+ char *owner;
6 struct StrList prereqs;
7 struct StrList order_only;
8 struct RecipeList recipes;
9@@ -168,9 +169,24 @@ struct Target {
10 int defined;
11 };
12
13+struct SubGraph;
14+
15 struct Graph {
16 struct Target *v;
17 size_t n;
18+ struct SubGraph *subs;
19+ size_t nsubs;
20+};
21+
22+struct SubGraph {
23+ char *cwd;
24+ char *prefix;
25+ char *makefile;
26+ struct StrList parents;
27+ struct StrList assigns;
28+ struct StrList flags;
29+ struct StrList goals;
30+ struct Graph graph;
31 };
32
33 int preproc(const char *path, struct Pre *pre);
34@@ -182,5 +198,7 @@ void freeast(struct Ast *ast);
35 void freeruleset(struct RuleSet *ruleset);
36 int buildgraph(const struct RuleSet *ruleset, struct Graph *graph);
37 void freegraph(struct Graph *graph);
38+int buildsubgraph(struct SubGraph *sg);
39+void freesubgraph(struct SubGraph *sg);
40
41 #endif
+0,
-7
1@@ -65,13 +65,6 @@ flagsarg(const char *s)
2 return 0;
3 }
4
5-static void
6-addstr(struct StrList *list, const char *s)
7-{
8- list->v = xrealloc(list->v, (list->n + 1) * sizeof(list->v[0]));
9- list->v[list->n++] = xstrdup(s);
10-}
11-
12 static void
13 addtok(struct StrList *out, const char *s, size_t n)
14 {
+265,
-0
1@@ -4,6 +4,7 @@
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5+#include <unistd.h>
6
7 /* shared utility functions */
8
9@@ -50,6 +51,270 @@ xstrdup(const char *s)
10 return xstrndup(s, strlen(s));
11 }
12
13+char *
14+readfile(const char *path)
15+{
16+ FILE *fp;
17+ long n;
18+ char *buf;
19+
20+ fp = fopen(path, "rb");
21+ if (!fp)
22+ return 0;
23+ if (fseek(fp, 0, SEEK_END) < 0) {
24+ fclose(fp);
25+ return 0;
26+ }
27+ n = ftell(fp);
28+ if (n < 0) {
29+ fclose(fp);
30+ return 0;
31+ }
32+ if (fseek(fp, 0, SEEK_SET) < 0) {
33+ fclose(fp);
34+ return 0;
35+ }
36+ buf = xmalloc((size_t)n + 1);
37+ if (fread(buf, 1, (size_t)n, fp) != (size_t)n) {
38+ fclose(fp);
39+ free(buf);
40+ return 0;
41+ }
42+ buf[n] = 0;
43+ fclose(fp);
44+ return buf;
45+}
46+
47+int
48+loadmakefile(const char *path, char **path_out, char **src_out)
49+{
50+ static const char *const defaults[] = {
51+ "GNUmakefile",
52+ "makefile",
53+ "Makefile",
54+ 0,
55+ };
56+ size_t i;
57+ char *src;
58+ char *fullpath;
59+
60+ src = 0;
61+ fullpath = 0;
62+ if (path) {
63+ fullpath = xstrdup(path);
64+ src = readfile(fullpath);
65+ } else {
66+ for (i = 0; defaults[i]; i++) {
67+ src = readfile(defaults[i]);
68+ if (!src)
69+ continue;
70+ fullpath = xstrdup(defaults[i]);
71+ break;
72+ }
73+ }
74+ if (!src)
75+ return -1;
76+ *path_out = fullpath;
77+ *src_out = src;
78+ return 0;
79+}
80+
81+char *
82+appendassigns(char *src, const struct StrList *assigns)
83+{
84+ size_t i, len, extra;
85+ char *out;
86+
87+ if (!assigns->n)
88+ return src;
89+ len = strlen(src);
90+ extra = len > 0 && src[len - 1] != '\n' ? 1 : 0;
91+ for (i = 0; i < assigns->n; i++)
92+ extra += strlen(assigns->v[i]) + 1;
93+ out = xmalloc(len + extra + 1);
94+ memcpy(out, src, len);
95+ extra = len;
96+ if (extra > 0 && out[extra - 1] != '\n')
97+ out[extra++] = '\n';
98+ for (i = 0; i < assigns->n; i++) {
99+ size_t n;
100+
101+ n = strlen(assigns->v[i]);
102+ memcpy(out + extra, assigns->v[i], n);
103+ extra += n;
104+ out[extra++] = '\n';
105+ }
106+ out[extra] = 0;
107+ free(src);
108+ return out;
109+}
110+
111+char *
112+getcwddup(void)
113+{
114+ size_t size;
115+ char *buf;
116+
117+ size = 128;
118+ for (;;) {
119+ buf = xmalloc(size);
120+ if (getcwd(buf, size))
121+ return buf;
122+ free(buf);
123+ size *= 2;
124+ }
125+}
126+
127+char *
128+joinpath(const char *dir, const char *name)
129+{
130+ size_t ndir, nname;
131+ char *out;
132+
133+ if (!name || !name[0])
134+ return xstrdup(dir);
135+ if (name[0] == '/')
136+ return xstrdup(name);
137+ ndir = strlen(dir);
138+ nname = strlen(name);
139+ out = xmalloc(ndir + 1 + nname + 1);
140+ memcpy(out, dir, ndir);
141+ out[ndir] = '/';
142+ memcpy(out + ndir + 1, name, nname);
143+ out[ndir + 1 + nname] = 0;
144+ return out;
145+}
146+
147+char *
148+normpath(const char *path)
149+{
150+ size_t i, n, parts_n, outlen;
151+ int absolute;
152+ char **parts;
153+ char *out;
154+
155+ absolute = path[0] == '/';
156+ n = strlen(path);
157+ parts = xmalloc((n + 1) * sizeof(parts[0]));
158+ parts_n = 0;
159+ for (i = 0; i < n;) {
160+ size_t start, len;
161+
162+ while (path[i] == '/')
163+ i++;
164+ start = i;
165+ while (path[i] && path[i] != '/')
166+ i++;
167+ len = i - start;
168+ if (!len)
169+ continue;
170+ if (len == 1 && path[start] == '.')
171+ continue;
172+ if (len == 2 && path[start] == '.' && path[start + 1] == '.') {
173+ if (parts_n > 0 && strcmp(parts[parts_n - 1], "..") != 0) {
174+ free(parts[--parts_n]);
175+ continue;
176+ }
177+ if (!absolute)
178+ parts[parts_n++] = xstrndup(path + start, len);
179+ continue;
180+ }
181+ parts[parts_n++] = xstrndup(path + start, len);
182+ }
183+ if (absolute && parts_n == 0) {
184+ free(parts);
185+ return xstrdup("/");
186+ }
187+ if (!absolute && parts_n == 0) {
188+ free(parts);
189+ return xstrdup(".");
190+ }
191+ outlen = absolute ? 1 : 0;
192+ for (i = 0; i < parts_n; i++)
193+ outlen += strlen(parts[i]) + 1;
194+ out = xmalloc(outlen + 1);
195+ n = 0;
196+ if (absolute)
197+ out[n++] = '/';
198+ for (i = 0; i < parts_n; i++) {
199+ size_t len;
200+
201+ if (n > 0 && out[n - 1] != '/')
202+ out[n++] = '/';
203+ len = strlen(parts[i]);
204+ memcpy(out + n, parts[i], len);
205+ n += len;
206+ free(parts[i]);
207+ }
208+ out[n] = 0;
209+ free(parts);
210+ return out;
211+}
212+
213+char *
214+joinstrs(const struct StrList *list, const char *sep)
215+{
216+ size_t i;
217+ char *s, *next;
218+
219+ s = xstrdup("");
220+ for (i = 0; i < list->n; i++) {
221+ next = s[0] ? cat3(s, sep, list->v[i]) : xstrdup(list->v[i]);
222+ free(s);
223+ s = next;
224+ }
225+ return s;
226+}
227+
228+void
229+addstr(struct StrList *list, const char *s)
230+{
231+ list->v = xrealloc(list->v, (list->n + 1) * sizeof(list->v[0]));
232+ list->v[list->n++] = xstrdup(s);
233+}
234+
235+int
236+hasword(const struct StrList *list, const char *word)
237+{
238+ size_t i;
239+
240+ for (i = 0; i < list->n; i++) {
241+ if (strcmp(list->v[i], word) == 0)
242+ return 1;
243+ }
244+ return 0;
245+}
246+
247+int
248+targetownedby(const struct Target *t, const char *owner)
249+{
250+ if (!t)
251+ return 0;
252+ if (!owner || !owner[0])
253+ return !t->owner || !t->owner[0];
254+ return t->owner && strcmp(t->owner, owner) == 0;
255+}
256+
257+const struct Target *
258+defaulttarget(const struct Graph *graph, const char *owner)
259+{
260+ const struct Target *all;
261+ size_t i;
262+
263+ all = findctarget(graph, "all");
264+ if (targetownedby(all, owner))
265+ return all;
266+ for (i = 0; i < graph->n; i++) {
267+ if (!targetownedby(&graph->v[i], owner))
268+ continue;
269+ if (strcmp(graph->v[i].name, "clean") == 0 || strcmp(graph->v[i].name, "fmt") == 0)
270+ continue;
271+ if (graph->v[i].recipes.n > 0 || graph->v[i].prereqs.n > 0 || graph->v[i].order_only.n > 0)
272+ return &graph->v[i];
273+ }
274+ return 0;
275+}
276+
277 char *
278 cat3(const char *a, const char *b, const char *c)
279 {