1#include "ninqu.h"
2
3#include <fnmatch.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8void
9dep_push(struct Inst *inst, int idx)
10{
11 int i;
12 for (i = 0; i < inst->n_dep; i++)
13 if (inst->dep_inst[i] == idx)
14 return;
15 if (inst->n_dep >= inst->cap_dep) {
16 inst->cap_dep = inst->cap_dep ? inst->cap_dep * 2 : 4;
17 inst->dep_inst = erealloc(inst->dep_inst, (size_t)inst->cap_dep * sizeof *inst->dep_inst);
18 }
19 inst->dep_inst[inst->n_dep++] = idx;
20}
21
22void
23resolve_deps(void)
24{
25 int i, j, k;
26 for (i = 0; i < ninsts; i++) {
27 struct Inst *inst = &insts[i];
28 for (j = 0; j < inst->dep_rule_names.n; j++) {
29 struct Rule *dep = rule_find(inst->dep_rule_names.v[j]);
30 if (!dep)
31 continue;
32 for (k = 0; k < dep->n_inst; k++)
33 dep_push(inst, dep->inst_idx[k]);
34 }
35 }
36}
37
38int
39inst_stale(struct Inst *inst)
40{
41 int i;
42 if (inst->phony)
43 return 1;
44 if (!file_exists(inst->out))
45 return 1;
46 for (i = 0; i < inst->in.n; i++)
47 if (mtime_of(inst->in.v[i]) > mtime_of(inst->out))
48 return 1;
49 for (i = 0; i < inst->stale_extra.n; i++)
50 if (file_exists(inst->stale_extra.v[i])
51 && mtime_of(inst->stale_extra.v[i]) > mtime_of(inst->out))
52 return 1;
53 return 0;
54}
55
56/* pure $(NAME) splits on whitespace so $(CFLAGS) becomes many flags, -I$(INCDIR) stays one token */
57static int
58is_pure_ref(const char *tok)
59{
60 size_t len;
61 int depth;
62 const char *p;
63
64 if (tok[0] != '$' || tok[1] != '(')
65 return 0;
66 len = strlen(tok);
67 if (tok[len - 1] != ')')
68 return 0;
69 depth = 1;
70 for (p = tok + 2; *p; p++) {
71 if (p[0] == '$' && p[1] == '(') {
72 depth++;
73 p++;
74 continue;
75 }
76 if (*p == ')') {
77 depth--;
78 if (depth == 0)
79 return (size_t)(p - tok) == len - 1;
80 }
81 }
82 return 0;
83}
84
85static void
86expand_cmd_token(struct StrList *out, const char *raw)
87{
88 char *exp = kv_expand(raw);
89 if (is_pure_ref(raw)) {
90 struct StrList pieces = {0};
91 int j;
92 sl_split(&pieces, exp);
93 for (j = 0; j < pieces.n; j++)
94 sl_push(out, pieces.v[j]);
95 } else {
96 sl_push(out, exp);
97 }
98 free(exp);
99}
100
101static void
102build_cmd(struct Inst *inst, struct Rule *r)
103{
104 int i;
105 if (r->cmd.is_pipe) {
106 inst->cmd.is_pipe = 1;
107 inst->cmd.nstages = r->cmd.nstages;
108 inst->cmd.stages = emalloc((size_t)r->cmd.nstages * sizeof *inst->cmd.stages);
109 for (i = 0; i < r->cmd.nstages; i++) {
110 int j;
111 struct StrList *slot = emalloc(sizeof *slot);
112 memset(slot, 0, sizeof *slot);
113 for (j = 0; j < r->cmd.stages[i]->n; j++)
114 expand_cmd_token(slot, r->cmd.stages[i]->v[j]);
115 inst->cmd.stages[i] = slot;
116 }
117 for (i = 0; i < inst->cmd.nstages; i++) {
118 char *joined = sl_join(inst->cmd.stages[i]);
119 if (i > 0)
120 sl_push(&inst->cmd.argv, "|");
121 sl_push(&inst->cmd.argv, joined);
122 free(joined);
123 }
124 } else {
125 for (i = 0; i < r->cmd.argv.n; i++)
126 expand_cmd_token(&inst->cmd.argv, r->cmd.argv.v[i]);
127 }
128}
129
130/* if path does not exist, try to find a producer and run it first */
131static void
132resolve_input_token(int inst_idx, const char *raw, int only_dep)
133{
134 char *exp = kv_expand(raw);
135
136 if (exp[0] == '@') {
137 const char *rulename = exp + 1;
138 struct Rule *dep;
139 struct KvStore *saved = local_overlay;
140 struct Inst *inst;
141 int i;
142
143 /* expand_rule may set/clear local_overlay for its own loop */
144 local_overlay = NULL;
145 expand_rule(rulename);
146 local_overlay = saved;
147
148 /* insts[] may have moved during expand_rule */
149 inst = &insts[inst_idx];
150 dep = rule_find(rulename);
151 if (!dep)
152 eprintf(
153 "manifest: '%s' references unknown rule '@%s'\n", rules[inst->rule_idx].name, rulename
154 );
155 if (!sl_has(&inst->dep_rule_names, rulename))
156 sl_push(&inst->dep_rule_names, rulename);
157 if (!only_dep)
158 for (i = 0; i < dep->n_inst; i++)
159 sl_push(&inst->in, insts[dep->inst_idx[i]].out);
160 } else if (strncmp(exp, "glob:", 5) == 0) {
161 struct StrList matches = {0};
162 int i;
163 glob_expand(exp + 5, &matches);
164 for (i = 0; i < matches.n; i++) {
165 if (only_dep)
166 sl_push(&insts[inst_idx].stale_extra, matches.v[i]);
167 else
168 sl_push(&insts[inst_idx].in, matches.v[i]);
169 }
170 } else {
171 struct StrList pieces = {0};
172 int i;
173 sl_split(&pieces, exp);
174 for (i = 0; i < pieces.n; i++) {
175 const char *path = pieces.v[i];
176 if (!file_exists(path))
177 materialize_if_missing(path);
178 if (only_dep)
179 sl_push(&insts[inst_idx].stale_extra, path);
180 else
181 sl_push(&insts[inst_idx].in, path);
182 }
183 }
184 free(exp);
185}
186
187static void
188setup_inst(struct Inst *inst, struct Rule *r)
189{
190 inst->phony = r->phony;
191 inst->redirect = r->redirect;
192 if (r->workdir[0]) {
193 char *w = kv_expand(r->workdir);
194 strlcpy(inst->workdir, w, sizeof inst->workdir);
195 free(w);
196 }
197}
198
199/* caller must have local_overlay set if needed */
200static void
201finalize_inst(int idx, struct Rule *r, struct KvStore *ov)
202{
203 struct Inst *inst = &insts[idx];
204 int k;
205
206 for (k = 0; k < r->in.n; k++)
207 resolve_input_token(idx, r->in.v[k], 0);
208 for (k = 0; k < r->extra_deps.n; k++)
209 resolve_input_token(idx, r->extra_deps.v[k], 1);
210
211 inst = &insts[idx]; /* may have moved */
212 {
213 char *o = kv_expand(r->out);
214 strlcpy(inst->out, o, sizeof inst->out);
215 free(o);
216 }
217 kv_set_raw(ov, "IN", sl_join(&inst->in));
218 kv_set_raw(ov, "OUT", inst->out);
219 build_cmd(inst, r);
220}
221
222/* true when deps (out) matches selfs glob. glob_all() needs bytes on disk
223 * so dep must run synchronously before enumerating instances */
224static int
225dep_feeds_glob(struct Rule *self, struct Rule *dep)
226{
227 int j;
228
229 if (!dep->out[0])
230 return 0;
231 for (j = 0; j < self->globs.n; j++) {
232 char *pat = kv_expand(self->globs.v[j]);
233 char *out = kv_expand(dep->out);
234 int hit = fnmatch(pat, out, 0) == 0;
235 free(pat);
236 free(out);
237 if (hit)
238 return 1;
239 }
240 return 0;
241}
242
243void
244expand_rule(const char *name)
245{
246 struct Rule *r = rule_find(name);
247 int i;
248
249 if (!r)
250 eprintf("manifest: unknown rule '%s'\n", name);
251 if (r->expanded)
252 return;
253 r->expanded = 1;
254
255 /* static gate checked once, if false rule produces zero instances */
256 if (r->gate && !gate_has_dyn(r->gate) && !gate_eval(r->gate))
257 return;
258
259 if (r->require_file[0] && r->globs.n == 0) {
260 char *rf = kv_expand(r->require_file);
261 int ok = file_exists(rf);
262 free(rf);
263 if (!ok)
264 return;
265 }
266
267 if (r->globs.n > 0) {
268 struct StrList matches = {0};
269 int j;
270 int r_literal = rule_is_literal(r);
271
272 /* run a codegen producer before globbing only when its own
273 * output feeds this rules glob, so a step like POSIX_BC_C
274 * (produces cmd/posix/bc.c) finishes before glob_all() looks
275 * for cmd/posix/bc.c. anything else in extra_deps (a library to
276 * link, a header some other rule includes) never needs to run
277 * here, expand_rule() on it just registers its instances so
278 * the resolve_input_token() pass in finalize_inst() can wire up
279 * dep_rule_names for backend scheduling. skipped in
280 * summary mode since -S must not execute */
281 if (!summary_mode) {
282 for (j = 0; j < r->extra_deps.n; j++) {
283 char *tok = kv_expand(r->extra_deps.v[j]);
284 if (tok[0] == '@') {
285 struct Rule *dep = rule_find(tok + 1);
286 if (dep) {
287 expand_rule(dep->name);
288 if (dep_feeds_glob(r, dep) && dep->n_inst > 0) {
289 int k;
290 for (k = 0; k < dep->n_inst; k++)
291 run_inst_with_deps(dep->inst_idx[k]);
292 }
293 }
294 }
295 free(tok);
296 }
297 }
298
299 glob_all(r, &matches);
300
301 for (i = 0; i < matches.n; i++) {
302 const char *match = matches.v[i];
303 const char *base = base_of(match);
304 char dir[1024], stem[512], basestem[512], ext[64], unused[64];
305 char ident[512];
306 struct KvStore ov;
307 int idx;
308 struct Inst *inst;
309
310 if (sl_has(&r->skip, base))
311 continue;
312 if (!r_literal && path_claimed_elsewhere(match, r))
313 continue;
314 if (r->require_marker[0] && !file_contains(match, r->require_marker))
315 continue;
316
317 if (strrchr(match, '/')) {
318 size_t dl = (size_t)(strrchr(match, '/') - match);
319 if (dl >= sizeof dir)
320 dl = sizeof dir - 1;
321 memcpy(dir, match, dl);
322 dir[dl] = '\0';
323 } else {
324 strlcpy(dir, ".", sizeof dir);
325 }
326 splitext(base, basestem, sizeof basestem, ext, sizeof ext);
327 splitext(match, stem, sizeof stem, unused, sizeof unused);
328 to_ident(basestem, ident, sizeof ident);
329
330 memset(&ov, 0, sizeof ov);
331 kv_set_raw(&ov, "MATCH", match);
332 kv_set_raw(&ov, "DIR", dir);
333 kv_set_raw(&ov, "BASE", base);
334 kv_set_raw(&ov, "EXT", ext);
335 kv_set_raw(&ov, "STEM", stem);
336 kv_set_raw(&ov, "BASESTEM", basestem);
337 kv_set_raw(&ov, "IDENT", ident);
338 local_overlay = &ov;
339
340 if (r->gate && gate_has_dyn(r->gate)) {
341 struct Gate *mg = gate_materialize(r->gate);
342 int ok = gate_eval(mg);
343 gate_free(mg);
344 if (!ok) {
345 local_overlay = NULL;
346 continue;
347 }
348 }
349 if (r->require_file[0]) {
350 char *rf = kv_expand(r->require_file);
351 int ok = file_exists(rf);
352 free(rf);
353 if (!ok) {
354 local_overlay = NULL;
355 continue;
356 }
357 }
358
359 idx = inst_new((int)(r - rules));
360 inst = &insts[idx];
361 setup_inst(inst, r);
362 finalize_inst(idx, r, &ov);
363 local_overlay = NULL;
364 }
365 } else {
366 int idx = inst_new((int)(r - rules));
367 struct Inst *inst = &insts[idx];
368 struct KvStore ov;
369
370 /* non-glob rules have no MATCH/DIR/BASE to expose, so the
371 * overlay only carries IN/OUT for build_cmd */
372 setup_inst(inst, r);
373 memset(&ov, 0, sizeof ov);
374 local_overlay = &ov;
375 finalize_inst(idx, r, &ov);
376 local_overlay = NULL;
377 }
378}