main shrub/shinobi / src / graph / subgraph.c
  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}