1#include "internal.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <unistd.h>
7
8struct SubGraphStack {
9 char **v;
10 size_t n;
11 struct Arena arena;
12};
13
14static int
15isempty(const char *s)
16{
17 return !s || !s[0];
18}
19
20static int
21sameprefix(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
30static int
31isunderprefix(const char *prefix, const char *name)
32{
33 size_t n;
34
35 if (isempty(prefix))
36 return 1;
37 n = strlen(prefix);
38 if (strncmp(name, prefix, n) != 0)
39 return 0;
40 return name[n] == 0 || name[n] == '/';
41}
42
43static int
44samelist(const struct StrList *a, const struct StrList *b)
45{
46 size_t i;
47
48 if (a->n != b->n)
49 return 0;
50 for (i = 0; i < a->n; i++) {
51 if (strcmp(a->v[i], b->v[i]) != 0)
52 return 0;
53 }
54 return 1;
55}
56
57static void
58addenvassign(struct StrList *list, const char *name, const char *val)
59{
60 char *s;
61
62 s = xmalloc(strlen(name) + 1 + strlen(val) + 1);
63 strcpy(s, name);
64 strcat(s, "=");
65 strcat(s, val);
66 list->v = xrealloc(list->v, (list->n + 1) * sizeof(list->v[0]));
67 list->v[list->n++] = s;
68}
69
70static char *
71joinprefix(const char *parent, const char *child)
72{
73 char *raw, *norm;
74
75 if (isempty(child))
76 return parent ? xstrdup(parent) : 0;
77 if (child[0] == '/')
78 return normpath(child);
79 if (isempty(parent))
80 return normpath(child);
81 raw = cat3(parent, "/", child);
82 norm = normpath(raw);
83 free(raw);
84 return norm;
85}
86
87static char *
88rebaseword(const char *prefix, const char *name)
89{
90 char *raw, *norm;
91
92 if (!name[0] || name[0] == '/')
93 return normpath(name);
94 if (isempty(prefix))
95 return normpath(name);
96 raw = cat3(prefix, "/", name);
97 norm = normpath(raw);
98 free(raw);
99 return norm;
100}
101
102static char *
103prefixrecipe(const char *prefix, const char *body)
104{
105 size_t nprefix, nbody;
106 char *out;
107
108 if (isempty(prefix) || strcmp(prefix, ".") == 0)
109 return xstrdup(body);
110 nprefix = strlen(prefix);
111 nbody = strlen(body);
112 out = xmalloc(5 + nprefix + 4 + nbody + 1);
113 memcpy(out, "cd ", 3);
114 memcpy(out + 3, prefix, nprefix);
115 memcpy(out + 3 + nprefix, " && ", 4);
116 memcpy(out + 7 + nprefix, body, nbody);
117 out[7 + nprefix + nbody] = 0;
118 return out;
119}
120
121static int
122resolvegoals(const struct SubGraph *sg, struct StrList *out)
123{
124 const struct Target *t;
125
126 memset(out, 0, sizeof(*out));
127 if (sg->goals.n) {
128 size_t i;
129
130 for (i = 0; i < sg->goals.n; i++) {
131 char *name;
132
133 name = rebaseword(sg->prefix, sg->goals.v[i]);
134 addstr(out, name);
135 free(name);
136 }
137 return 0;
138 }
139 t = defaulttarget(&sg->graph, sg->prefix);
140 if (t) {
141 addstr(out, t->name);
142 return 0;
143 }
144 fprintf(stderr, "submake has no default target\n");
145 return -1;
146}
147
148/* when expanding a submake, start from a target name and walk the graph backwards
149 * only for that target. if we expand the entire subgraph from the beginning,
150 * we get a lot of cases where the subgraph will recurse and try to create a new
151 * subgraph of itself */
152static void
153markreachable(const struct Graph *graph, const char *name, unsigned char *seen)
154{
155 const struct Target *t;
156 size_t i, idx;
157
158 t = findctarget(graph, name);
159 if (!t)
160 return;
161 idx = (size_t)(t - graph->v);
162 if (seen[idx])
163 return;
164 seen[idx] = 1;
165 for (i = 0; i < t->prereqs.n; i++)
166 markreachable(graph, t->prereqs.v[i], seen);
167 for (i = 0; i < t->impprereqs.n; i++)
168 markreachable(graph, t->impprereqs.v[i], seen);
169 for (i = 0; i < t->order_only.n; i++)
170 markreachable(graph, t->order_only.v[i], seen);
171}
172
173static int
174goalreaches(const struct Graph *graph, const char *goal, const char *name)
175{
176 const struct Target *t;
177 unsigned char *seen;
178 int ok;
179
180 t = findctarget(graph, name);
181 if (!t)
182 return 0;
183 seen = xmalloc(graph->n ? graph->n : 1);
184 memset(seen, 0, graph->n ? graph->n : 1);
185 markreachable(graph, goal, seen);
186 ok = seen[(size_t)(t - graph->v)] != 0;
187 free(seen);
188 return ok;
189}
190
191static int
192allsubmake(const struct Target *t)
193{
194 size_t i;
195
196 if (!t || t->recipes.n == 0)
197 return 0;
198 for (i = 0; i < t->recipes.n; i++) {
199 if (!t->recipes.v[i].submake)
200 return 0;
201 }
202 return 1;
203}
204
205static void
206pushstack(struct SubGraphStack *stack, const char *key)
207{
208 stack->v = xrealloc(stack->v, (stack->n + 1) * sizeof(stack->v[0]));
209 stack->v[stack->n++] = arena_strdup(&stack->arena, key);
210}
211
212static void
213popstack(struct SubGraphStack *stack)
214{
215 if (!stack->n)
216 return;
217 stack->n--;
218}
219
220static int
221instack(const struct SubGraphStack *stack, const char *key)
222{
223 size_t i;
224
225 for (i = 0; i < stack->n; i++) {
226 if (strcmp(stack->v[i], key) == 0)
227 return 1;
228 }
229 return 0;
230}
231
232static char *
233subgraphkey(const struct SubGraph *sg)
234{
235 size_t i, n, pos, len;
236 char *key;
237
238 n = strlen(sg->cwd) + 1 + strlen(sg->makefile ? sg->makefile : "") + 1;
239 for (i = 0; i < sg->envassigns.n; i++)
240 n += strlen(sg->envassigns.v[i]) + 1;
241 for (i = 0; i < sg->assigns.n; i++)
242 n += strlen(sg->assigns.v[i]) + 1;
243 key = xmalloc(n + 1);
244 pos = 0;
245 len = strlen(sg->cwd);
246 memcpy(key + pos, sg->cwd, len);
247 pos += len;
248 key[pos++] = '|';
249 if (sg->makefile) {
250 len = strlen(sg->makefile);
251 memcpy(key + pos, sg->makefile, len);
252 pos += len;
253 }
254 for (i = 0; i < sg->envassigns.n; i++) {
255 key[pos++] = '|';
256 len = strlen(sg->envassigns.v[i]);
257 memcpy(key + pos, sg->envassigns.v[i], len);
258 pos += len;
259 }
260 for (i = 0; i < sg->assigns.n; i++) {
261 key[pos++] = '|';
262 len = strlen(sg->assigns.v[i]);
263 memcpy(key + pos, sg->assigns.v[i], len);
264 pos += len;
265 }
266 key[pos] = 0;
267 return key;
268}
269
270static void
271removerecipe(struct RecipeList *list, size_t idx)
272{
273 size_t i;
274
275 free(list->v[idx].body);
276 freesubmake(&list->v[idx].sm);
277 for (i = idx + 1; i < list->n; i++)
278 list->v[i - 1] = list->v[i];
279 list->n--;
280 if (!list->n) {
281 free(list->v);
282 list->v = 0;
283 return;
284 }
285 list->v = xrealloc(list->v, list->n * sizeof(list->v[0]));
286}
287
288static void
289scopelist(struct StrList *list, const char *prefix)
290{
291 size_t i;
292
293 for (i = 0; i < list->n; i++) {
294 char *name;
295
296 name = rebaseword(prefix, list->v[i]);
297 free(list->v[i]);
298 list->v[i] = name;
299 }
300}
301
302static void
303scoperecipes(struct RecipeList *list, const char *prefix)
304{
305 size_t i;
306
307 for (i = 0; i < list->n; i++) {
308 char *body;
309
310 body = prefixrecipe(prefix, list->v[i].body);
311 free(list->v[i].body);
312 list->v[i].body = body;
313 }
314}
315
316static void
317scopegraph(struct Graph *graph, const char *prefix)
318{
319 size_t i;
320
321 if (isempty(prefix))
322 return;
323 for (i = 0; i < graph->n; i++) {
324 char *name;
325
326 name = rebaseword(prefix, graph->v[i].name);
327 graph->v[i].name = intern(name);
328 free(name);
329 free(graph->v[i].owner);
330 if ((graph->v[i].defined || graph->v[i].recipes.n > 0) &&
331 isunderprefix(prefix, graph->v[i].name))
332 graph->v[i].owner = xstrdup(prefix);
333 else
334 graph->v[i].owner = 0;
335 scopelist(&graph->v[i].prereqs, prefix);
336 scopelist(&graph->v[i].impprereqs, prefix);
337 scopelist(&graph->v[i].order_only, prefix);
338 scoperecipes(&graph->v[i].recipes, prefix);
339 }
340 reindexgraph(graph);
341}
342
343static int
344mergetarget(struct Graph *graph, const struct Target *src)
345{
346 struct Target *dst;
347
348 dst = findtarget(graph, src->name);
349 if (!dst) {
350 dst = gettarget(graph, src->name, 0);
351 if (src->owner)
352 dst->owner = xstrdup(src->owner);
353 } else if (!dst->owner && src->owner) {
354 dst->owner = xstrdup(src->owner);
355 } else if (dst->owner && src->owner && !sameprefix(dst->owner, src->owner)) {
356 /*
357 * shared targets like ../lib/libzstd.a may be referenced from
358 * multiple sibling subgraphs; if that happens, treat the
359 * target as shared instead of attributing it to one owner and erroring.
360 */
361 free(dst->owner);
362 dst->owner = 0;
363 } else if (dst->defined && src->defined && dst->dcolon != src->dcolon) {
364 fprintf(stderr, "target file `%s' has both : and :: entries, i can't handle that!\n", dst->name);
365 return -1;
366 }
367 if (!dst->defined && src->defined)
368 dst->dcolon = src->dcolon;
369 dst->defined |= src->defined;
370 dst->wanted |= src->wanted;
371 if (src->defined)
372 dst->dcolon = src->dcolon;
373 addwords(&dst->prereqs, &src->prereqs);
374 addwords(&dst->impprereqs, &src->impprereqs);
375 addwords(&dst->order_only, &src->order_only);
376 addrecipes(&dst->recipes, &src->recipes);
377 return 0;
378}
379
380static int
381addgraphsub(struct Graph *graph, struct SubGraph *child)
382{
383 graph->subs = xrealloc(graph->subs, (graph->nsubs + 1) * sizeof(graph->subs[0]));
384 graph->subs[graph->nsubs++] = *child;
385 memset(child, 0, sizeof(*child));
386 return 0;
387}
388
389/* in make, recursive make calls run sequentially*/
390static void
391subgraphorder(struct Graph *graph, const char *owner, const struct StrList *prev)
392{
393 size_t i, j, n;
394
395 if (!prev->n)
396 return;
397 n = owner ? strlen(owner) : 0;
398 for (i = 0; i < graph->n; i++) {
399 struct Target *t = &graph->v[i];
400
401 if (!t->owner)
402 continue;
403 if (strncmp(t->owner, owner, n) != 0)
404 continue;
405 if (t->owner[n] != 0 && t->owner[n] != '/')
406 continue;
407 for (j = 0; j < prev->n; j++) {
408 if (!hasword(&t->order_only, prev->v[j]))
409 addstr(&t->order_only, prev->v[j]);
410 }
411 }
412}
413
414static int
415sameinvocation(const struct SubGraph *a, const struct SubGraph *b);
416
417static struct SubGraph *
418findsubgraph(struct Graph *graph, const struct SubGraph *want)
419{
420 size_t i;
421
422 for (i = 0; i < graph->nsubs; i++) {
423 if (!sameprefix(graph->subs[i].prefix, want->prefix))
424 continue;
425 if (sameinvocation(&graph->subs[i], want))
426 return &graph->subs[i];
427 }
428 return 0;
429}
430
431static int
432haswantedclash(const struct Graph *graph, const struct SubGraph *want)
433{
434 size_t i;
435
436 for (i = 0; i < graph->nsubs; i++) {
437 const struct SubGraph *sg;
438
439 sg = &graph->subs[i];
440 if (!sameprefix(sg->prefix, want->prefix))
441 continue;
442 if (sameinvocation(sg, want))
443 continue;
444 if (sg->wanted && want->wanted)
445 return 1;
446 }
447 return 0;
448}
449
450static void
451markwanted(struct SubGraph *sg)
452{
453 struct StrList roots;
454 unsigned char *reachable;
455 size_t i, n;
456
457 n = sg->graph.n;
458 if (!sg->wanted) {
459 for (i = 0; i < n; i++)
460 sg->graph.v[i].wanted = 0;
461 return;
462 }
463 if (resolvegoals(sg, &roots) < 0)
464 return;
465 reachable = xmalloc(n ? n : 1);
466 memset(reachable, 0, n ? n : 1);
467 for (i = 0; i < roots.n; i++)
468 markreachable(&sg->graph, roots.v[i], reachable);
469 for (i = 0; i < n; i++)
470 sg->graph.v[i].wanted = reachable[i] != 0;
471 free(reachable);
472 freestrs(&roots);
473}
474
475static void
476markwantedtree(struct SubGraph *sg)
477{
478 size_t i;
479
480 markwanted(sg);
481 for (i = 0; i < sg->graph.nsubs; i++)
482 markwantedtree(&sg->graph.subs[i]);
483}
484
485static int
486sameinvocation(const struct SubGraph *a, const struct SubGraph *b)
487{
488 if (strcmp(a->cwd, b->cwd) != 0)
489 return 0;
490 if (!!a->makefile != !!b->makefile)
491 return 0;
492 if (a->makefile && strcmp(a->makefile, b->makefile) != 0)
493 return 0;
494 if (!samelist(&a->envassigns, &b->envassigns))
495 return 0;
496 if (!samelist(&a->assigns, &b->assigns))
497 return 0;
498 if (!samelist(&a->flags, &b->flags))
499 return 0;
500 return 1;
501}
502
503static void
504mergesubmeta(struct SubGraph *dst, struct SubGraph *src)
505{
506 size_t i;
507
508 for (i = 0; i < src->parents.n; i++) {
509 if (!hasword(&dst->parents, src->parents.v[i]))
510 addstr(&dst->parents, src->parents.v[i]);
511 }
512 for (i = 0; i < src->goals.n; i++) {
513 if (!hasword(&dst->goals, src->goals.v[i]))
514 addstr(&dst->goals, src->goals.v[i]);
515 }
516 freesubgraph(src);
517 memset(src, 0, sizeof(*src));
518}
519
520static int
521mergechildgraph(struct SubGraph *parent,
522 const char *tname,
523 struct SubGraph *child,
524 const struct StrList *goals)
525{
526 struct SubGraph *known;
527 unsigned char *reachable;
528 struct Target *t;
529 size_t i;
530
531 known = sameprefix(parent->prefix, child->prefix) ? 0 : findsubgraph(&parent->graph, child);
532 if (!sameprefix(parent->prefix, child->prefix) && haswantedclash(&parent->graph, child)) {
533 fprintf(stderr, "multiple incompatible subgraphs map to %s/build.ninja\n",
534 child->prefix ? child->prefix : ".");
535 return -1;
536 }
537 if (!known && child->wanted) {
538 reachable = xmalloc(child->graph.n ? child->graph.n : 1);
539 memset(reachable, 0, child->graph.n ? child->graph.n : 1);
540 for (i = 0; i < goals->n; i++)
541 markreachable(&child->graph, goals->v[i], reachable);
542 for (i = 0; i < child->graph.n; i++) {
543 if (!reachable[i])
544 continue;
545 if (mergetarget(&parent->graph, &child->graph.v[i]) < 0) {
546 free(reachable);
547 return -1;
548 }
549 }
550 free(reachable);
551 }
552 t = findtarget(&parent->graph, tname);
553 if (!t) {
554 fprintf(stderr, "lost parent target `%s' while expanding submake\n", tname);
555 return -1;
556 }
557 for (i = 0; i < goals->n; i++) {
558 /* the child graph might have a default goal like "lib/all" to build
559 * the parent. if we add that goal back as a prerequisite of
560 * the parent target would create a synthetic cycle; dont do that. */
561 if (goalreaches(&child->graph, goals->v[i], tname))
562 continue;
563 if (!hasword(&t->prereqs, goals->v[i]))
564 addstr(&t->prereqs, goals->v[i]);
565 }
566 if (child->prefix && child->prefix[0] && isunderprefix(child->prefix, tname)) {
567 size_t n;
568 const char *local;
569
570 n = strlen(child->prefix);
571 local = tname + n;
572 if (*local == '/')
573 local++;
574 if (*local && !hasword(&t->prereqs, local))
575 addstr(&t->prereqs, local);
576 }
577 if (!sameprefix(parent->prefix, child->prefix)) {
578 if (known) {
579 mergesubmeta(known, child);
580 } else if (addgraphsub(&parent->graph, child) < 0) {
581 return -1;
582 }
583 }
584 return 0;
585}
586
587static int buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack);
588
589static int
590expandsubgraphs(struct SubGraph *sg, struct SubGraphStack *stack)
591{
592 struct StrList roots;
593 unsigned char *reachable;
594 size_t i, n;
595
596 markwanted(sg);
597 if (resolvegoals(sg, &roots) < 0)
598 return -1;
599 for (i = 0; i < sg->graph.n; i++) {
600 if (!targetownedby(&sg->graph.v[i], sg->prefix))
601 continue;
602 if (!sg->graph.v[i].phony && !allsubmake(&sg->graph.v[i]))
603 continue;
604 if (!hasword(&roots, sg->graph.v[i].name))
605 addstr(&roots, sg->graph.v[i].name);
606 }
607 n = sg->graph.n;
608 reachable = xmalloc(n ? n : 1);
609 memset(reachable, 0, n ? n : 1);
610 for (i = 0; i < roots.n; i++)
611 markreachable(&sg->graph, roots.v[i], reachable);
612 freestrs(&roots);
613
614 for (i = 0; i < n; i++) {
615 size_t k;
616 const char *tname;
617 struct StrList prevgoals;
618
619 if (!reachable[i])
620 continue;
621
622 memset(&prevgoals, 0, sizeof(prevgoals));
623 tname = sg->graph.v[i].name;
624 for (k = 0; k < sg->graph.v[i].recipes.n;) {
625 struct Recipe *r;
626 struct SubGraph child;
627 struct StrList goals;
628 int rc;
629
630 r = &sg->graph.v[i].recipes.v[k];
631 if (!r->submake) {
632 k++;
633 continue;
634 }
635 memset(&child, 0, sizeof(child));
636 child.cwd = r->sm.dir ? joinpath(sg->cwd, r->sm.dir) : xstrdup(sg->cwd);
637 child.prefix = joinprefix(sg->prefix, r->sm.dir);
638 child.mode = sg->mode;
639 child.wanted = sg->graph.v[i].wanted;
640 addstr(&child.parents, tname);
641 if (r->sm.makefile)
642 child.makefile = xstrdup(r->sm.makefile);
643 {
644 size_t ei;
645
646 for (ei = 0; ei < sg->graph.v[i].env.n; ei++) {
647 const struct Var *v = &sg->graph.v[i].env.v[ei];
648
649 if (!v->exported)
650 continue;
651 addenvassign(&child.envassigns, v->name, v->val);
652 }
653 }
654 addwords(&child.assigns, &r->sm.assigns);
655 addwords(&child.flags, &r->sm.flags);
656 addwords(&child.goals, &r->sm.goals);
657 if (strcmp(child.cwd, sg->cwd) == 0 &&
658 (!child.makefile || strcmp(child.makefile, sg->makefile) == 0)) {
659 freesubgraph(&child);
660 k++;
661 continue;
662 }
663 rc = buildsubgraph0(&child, stack);
664 if (rc > 0) {
665 struct SubGraph *known;
666
667 known = sameprefix(sg->prefix, child.prefix) ? 0 : findsubgraph(&sg->graph, &child);
668 if (known && sameinvocation(known, &child) && child.goals.n > 0) {
669 size_t gi;
670 struct Target *pt;
671
672 removerecipe(&sg->graph.v[i].recipes, k);
673 pt = findtarget(&sg->graph, tname);
674 for (gi = 0; pt && gi < child.goals.n; gi++) {
675 char *gname;
676
677 gname = rebaseword(child.prefix, child.goals.v[gi]);
678 if (!hasword(&pt->prereqs, gname))
679 addstr(&pt->prereqs, gname);
680 free(gname);
681 }
682 freesubgraph(&child);
683 continue;
684 }
685 freesubgraph(&child);
686 k++;
687 continue;
688 }
689 if (rc < 0) {
690 freesubgraph(&child);
691 freestrs(&prevgoals);
692 free(reachable);
693 return -1;
694 }
695 /* remove the make invocation, we replace it with a graph*/
696 removerecipe(&sg->graph.v[i].recipes, k);
697 if (resolvegoals(&child, &goals) < 0) {
698 freesubgraph(&child);
699 freestrs(&prevgoals);
700 free(reachable);
701 return -1;
702 }
703 subgraphorder(&child.graph, child.prefix, &prevgoals);
704 if (mergechildgraph(sg, tname, &child, &goals) < 0) {
705 freesubgraph(&child);
706 freestrs(&goals);
707 freestrs(&prevgoals);
708 free(reachable);
709 return -1;
710 }
711 freestrs(&prevgoals);
712 prevgoals = goals;
713 if (!sameprefix(sg->prefix, child.prefix))
714 continue;
715 freesubgraph(&child);
716 }
717 freestrs(&prevgoals);
718 }
719 free(reachable);
720 return 0;
721}
722
723static int
724buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack)
725{
726 struct Ast ast;
727 struct Ast pre;
728 struct RuleSet rs;
729 char *src;
730 char *path;
731 char *oldcwd;
732 char *key;
733 int rc;
734 size_t i;
735 int envoverride;
736 size_t nenvassigns;
737
738 memset(&ast, 0, sizeof(ast));
739 memset(&pre, 0, sizeof(pre));
740 memset(&rs, 0, sizeof(rs));
741 freegraph(&sg->graph);
742 memset(&sg->graph, 0, sizeof(sg->graph));
743 src = 0;
744 path = 0;
745 oldcwd = getcwddup();
746 if (chdir(sg->cwd) != 0) {
747 free(oldcwd);
748 return 1;
749 }
750 free(sg->cwd);
751 sg->cwd = getcwddup();
752 if (loadmakefile(sg->makefile, &path, &src) < 0) {
753 chdir(oldcwd);
754 free(oldcwd);
755 return 1;
756 }
757 free(sg->makefile);
758 sg->makefile = xstrdup(path);
759 key = subgraphkey(sg);
760 if (instack(stack, key)) {
761 free(key);
762 free(path);
763 free(src);
764 chdir(oldcwd);
765 free(oldcwd);
766 return 1;
767 }
768 pushstack(stack, key);
769 free(key);
770 envoverride = 0;
771 for (i = 0; i < sg->flags.n; i++) {
772 if (strcmp(sg->flags.v[i], "-e") == 0) {
773 envoverride = 1;
774 break;
775 }
776 }
777 nenvassigns = sg->envassigns.n;
778 if (sg->envassigns.n || sg->assigns.n) {
779 char *assignsrc;
780 struct StrList allassigns;
781
782 memset(&allassigns, 0, sizeof(allassigns));
783 addwords(&allassigns, &sg->envassigns);
784 addwords(&allassigns, &sg->assigns);
785 assignsrc = appendassigns(xstrdup(""), &allassigns);
786 freestrs(&allassigns);
787 rc = parse("<command line>", assignsrc, &pre, sg->mode);
788 free(assignsrc);
789 if (rc < 0)
790 goto out;
791 for (i = 0; i < pre.n; i++) {
792 if (pre.v[i].kind != NODE_ASSIGN)
793 continue;
794 if (nenvassigns > 0) {
795 pre.v[i].data.assign.origin = envoverride ? ORIGIN_ENV_OVERRIDE : ORIGIN_ENV;
796 nenvassigns--;
797 } else {
798 pre.v[i].data.assign.origin = ORIGIN_COMMAND;
799 }
800 }
801 }
802 rc = parse(path, src, &ast, sg->mode);
803 if (rc < 0)
804 goto out;
805 rc = eval(path, &ast, sg->assigns.n ? &pre : 0, envoverride, sg->mode, &rs);
806 if (rc < 0)
807 goto out;
808 rc = buildgraph(&rs, sg->wanted ? &sg->goals : 0, &sg->graph, sg->mode);
809 if (rc < 0)
810 goto out;
811 rc = expandgraph(&sg->graph, sg->mode);
812 if (rc < 0)
813 goto out;
814 scopegraph(&sg->graph, sg->prefix);
815 rc = expandsubgraphs(sg, stack);
816 if (rc >= 0)
817 markwantedtree(sg);
818out:
819 freeruleset(&rs);
820 freeast(&pre);
821 freeast(&ast);
822 free(src);
823 free(path);
824 popstack(stack);
825 chdir(oldcwd);
826 free(oldcwd);
827 return rc;
828}
829
830int
831buildsubgraph(struct SubGraph *sg)
832{
833 struct SubGraphStack stack;
834 int rc;
835
836 memset(&stack, 0, sizeof(stack));
837 arena_init(&stack.arena, 0);
838 if (!sg->cwd)
839 sg->cwd = getcwddup();
840 rc = buildsubgraph0(sg, &stack);
841 if (rc < 0) {
842 while (stack.n)
843 popstack(&stack);
844 arena_free(&stack.arena);
845 free(stack.v);
846 return rc;
847 }
848 arena_free(&stack.arena);
849 free(stack.v);
850 return 0;
851}
852
853void
854freesubgraph(struct SubGraph *sg)
855{
856 if (!sg)
857 return;
858 free(sg->cwd);
859 sg->cwd = 0;
860 free(sg->prefix);
861 sg->prefix = 0;
862 free(sg->makefile);
863 sg->makefile = 0;
864 freestrs(&sg->parents);
865 freestrs(&sg->envassigns);
866 freestrs(&sg->assigns);
867 freestrs(&sg->flags);
868 freestrs(&sg->goals);
869 freegraph(&sg->graph);
870}