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}