main shrubtools / nviz / parse.c
  1#include <stdbool.h>
  2#include <stdio.h>
  3#include <string.h>
  4#include <stdlib.h>
  5#include "env.h"
  6#include "graph.h"
  7#include "parse.h"
  8#include "scan.h"
  9#include "util.h"
 10
 11struct parseoptions parseopts;
 12static struct node **deftarg;
 13static size_t ndeftarg;
 14
 15void
 16parseinit(void)
 17{
 18	free(deftarg);
 19	deftarg = NULL;
 20	ndeftarg = 0;
 21}
 22
 23static void
 24parselet(struct scanner *s, struct evalstring **val)
 25{
 26	scanchar(s, '=');
 27	*val = scanstring(s, false);
 28	scannewline(s);
 29}
 30
 31static void
 32parserule(struct scanner *s, struct environment *env)
 33{
 34	struct rule *r;
 35	char *var;
 36	struct evalstring *val;
 37	bool hascommand = false, hasrspfile = false, hasrspcontent = false;
 38
 39	r = mkrule(scanname(s));
 40	scannewline(s);
 41	while (scanindent(s)) {
 42		var = scanname(s);
 43		parselet(s, &val);
 44		ruleaddvar(r, var, val);
 45		if (!val)
 46			continue;
 47		if (strcmp(var, "command") == 0)
 48			hascommand = true;
 49		else if (strcmp(var, "rspfile") == 0)
 50			hasrspfile = true;
 51		else if (strcmp(var, "rspfile_content") == 0)
 52			hasrspcontent = true;
 53	}
 54	if (!hascommand)
 55		fatal("rule '%s' has no command", r->name);
 56	if (hasrspfile != hasrspcontent)
 57		fatal("rule '%s' has rspfile and no rspfile_content or vice versa", r->name);
 58	envaddrule(env, r);
 59}
 60
 61static void
 62parseedge(struct scanner *s, struct environment *env)
 63{
 64	struct edge *e;
 65	struct evalstring *str, **path;
 66	char *name;
 67	struct string *val;
 68	struct node *n;
 69	size_t i;
 70	int p;
 71
 72	e = mkedge(env);
 73
 74	scanpaths(s);
 75	e->outimpidx = npaths;
 76	if (scanpipe(s, 1))
 77		scanpaths(s);
 78	e->nout = npaths;
 79	if (e->nout == 0)
 80		scanerror(s, "expected output path");
 81	scanchar(s, ':');
 82	name = scanname(s);
 83	e->rule = envrule(env, name);
 84	if (!e->rule)
 85		fatal("undefined rule '%s'", name);
 86	free(name);
 87	scanpaths(s);
 88	e->inimpidx = npaths - e->nout;
 89	p = scanpipe(s, 1 | 2);
 90	if (p == 1) {
 91		scanpaths(s);
 92		p = scanpipe(s, 2);
 93	}
 94	e->inorderidx = npaths - e->nout;
 95	if (p == 2)
 96		scanpaths(s);
 97	e->nin = npaths - e->nout;
 98	scannewline(s);
 99	while (scanindent(s)) {
100		name = scanname(s);
101		parselet(s, &str);
102		val = enveval(env, str);
103		envaddvar(e->env, name, val);
104	}
105
106	e->out = xreallocarray(NULL, e->nout, sizeof(e->out[0]));
107	for (i = 0, path = paths; i < e->nout; ++path) {
108		val = enveval(e->env, *path);
109		canonpath(val);
110		n = mknode(val);
111		if (n->gen) {
112			if (!parseopts.dupbuildwarn)
113				fatal("multiple rules generate '%s'", n->path->s);
114			warn("multiple rules generate '%s'", n->path->s);
115			--e->nout;
116			if (i < e->outimpidx)
117				--e->outimpidx;
118		} else {
119			n->gen = e;
120			e->out[i] = n;
121			++i;
122		}
123	}
124	e->in = xreallocarray(NULL, e->nin, sizeof(e->in[0]));
125	for (i = 0; i < e->nin; ++i, ++path) {
126		val = enveval(e->env, *path);
127		canonpath(val);
128		n = mknode(val);
129		e->in[i] = n;
130		nodeuse(n, e);
131	}
132	npaths = 0;
133
134	val = edgevar(e, "pool", true);
135	if (val)
136		e->pool = poolget(val->s);
137}
138
139static void
140parseinclude(struct scanner *s, struct environment *env, bool newscope)
141{
142	struct evalstring *str;
143	struct string *path;
144
145	str = scanstring(s, true);
146	if (!str)
147		scanerror(s, "expected include path");
148	scannewline(s);
149	path = enveval(env, str);
150
151	if (newscope)
152		env = mkenv(env);
153	parse(path->s, env);
154	free(path);
155}
156
157static void
158parsedefault(struct scanner *s, struct environment *env)
159{
160	struct string *path;
161	struct node *n;
162	size_t i;
163
164	scanpaths(s);
165	deftarg = xreallocarray(deftarg, ndeftarg + npaths, sizeof(*deftarg));
166	for (i = 0; i < npaths; ++i) {
167		path = enveval(env, paths[i]);
168		canonpath(path);
169		n = nodeget(path->s, path->n);
170		if (!n)
171			fatal("unknown target '%s'", path->s);
172		free(path);
173		deftarg[ndeftarg++] = n;
174	}
175	scannewline(s);
176	npaths = 0;
177}
178
179static void
180parsepool(struct scanner *s, struct environment *env)
181{
182	struct pool *p;
183	struct evalstring *val;
184	struct string *str;
185	char *var, *end;
186
187	p = mkpool(scanname(s));
188	scannewline(s);
189	while (scanindent(s)) {
190		var = scanname(s);
191		parselet(s, &val);
192		if (strcmp(var, "depth") == 0) {
193			str = enveval(env, val);
194			p->maxjobs = strtol(str->s, &end, 10);
195			if (*end)
196				fatal("invalid pool depth '%s'", str->s);
197			free(str);
198		} else {
199			fatal("unexpected pool variable '%s'", var);
200		}
201	}
202	if (!p->maxjobs)
203		fatal("pool '%s' has no depth", p->name);
204}
205
206static void
207checkversion(const char *ver)
208{
209	int major, minor = 0;
210
211	if (sscanf(ver, "%d.%d", &major, &minor) < 1)
212		fatal("invalid ninja_required_version");
213	if (major > ninjamajor || (major == ninjamajor && minor > ninjaminor))
214		fatal("ninja_required_version %s is newer than %d.%d", ver, ninjamajor, ninjaminor);
215}
216
217void
218parse(const char *name, struct environment *env)
219{
220	struct scanner s;
221	char *var;
222	struct string *val;
223	struct evalstring *str;
224
225	scaninit(&s, name);
226	for (;;) {
227		switch (scankeyword(&s, &var)) {
228		case RULE:
229			parserule(&s, env);
230			break;
231		case BUILD:
232			parseedge(&s, env);
233			break;
234		case INCLUDE:
235			parseinclude(&s, env, false);
236			break;
237		case SUBNINJA:
238			parseinclude(&s, env, true);
239			break;
240		case DEFAULT:
241			parsedefault(&s, env);
242			break;
243		case POOL:
244			parsepool(&s, env);
245			break;
246		case VARIABLE:
247			parselet(&s, &str);
248			val = enveval(env, str);
249			if (strcmp(var, "ninja_required_version") == 0)
250				checkversion(val->s);
251			envaddvar(env, var, val);
252			break;
253		case EOF:
254			scanclose(&s);
255			return;
256		}
257	}
258}
259
260void
261defaultnodes(void fn(struct node *))
262{
263	struct edge *e;
264	struct node *n;
265	size_t i;
266
267	if (ndeftarg > 0) {
268		for (i = 0; i < ndeftarg; ++i)
269			fn(deftarg[i]);
270	} else {
271		/* by default build all nodes which are not used by any edges */
272		for (e = alledges; e; e = e->allnext) {
273			for (i = 0; i < e->nout; ++i) {
274				n = e->out[i];
275				if (n->nuse == 0)
276					fn(n);
277			}
278		}
279	}
280}