1#include "ninqu.h"
2#include "wexec.h"
3
4#include <errno.h>
5#include <libgen.h>
6#include <limits.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/wait.h>
11#include <unistd.h>
12
13int jobs_n = 1;
14int summary_mode;
15int failed_any;
16
17/* child _exit on error so parent never sees a stale pid */
18pid_t
19spawn_inst(struct Inst *inst, struct StrList *argv)
20{
21 pid_t pid = fork();
22 if (pid < 0)
23 eprintf("fork:");
24 if (pid != 0)
25 return pid;
26
27 if (inst->workdir[0] && chdir(inst->workdir) != 0)
28 eprintf("chdir %s:", inst->workdir);
29 if (inst->redirect) {
30 FILE *f = fopen(inst->out, "w");
31 if (!f)
32 eprintf("open %s:", inst->out);
33 if (dup2(fileno(f), STDOUT_FILENO) < 0)
34 _exit(127);
35 fclose(f);
36 }
37 child_execvp(sl_argv(argv));
38}
39
40/* parent waits on supervisor so pipeline has one exit status */
41pid_t
42spawn_pipe(struct Inst *inst)
43{
44 pid_t pid = fork();
45 if (pid < 0)
46 eprintf("fork:");
47 if (pid != 0)
48 return pid;
49
50 if (inst->workdir[0] && chdir(inst->workdir) != 0)
51 eprintf("chdir %s:", inst->workdir);
52
53 {
54 int k, prev_read = -1;
55 for (k = 0; k < inst->cmd.nstages; k++) {
56 int p[2] = {-1, -1};
57 pid_t sp;
58 if (k < inst->cmd.nstages - 1) {
59 if (pipe(p) != 0)
60 _exit(127);
61 }
62 sp = fork();
63 if (sp < 0)
64 _exit(127);
65 if (sp == 0) {
66 if (prev_read >= 0) {
67 if (dup2(prev_read, STDIN_FILENO) < 0)
68 _exit(127);
69 close(prev_read);
70 }
71 if (k < inst->cmd.nstages - 1) {
72 close(p[0]);
73 if (dup2(p[1], STDOUT_FILENO) < 0)
74 _exit(127);
75 close(p[1]);
76 }
77 child_execvp(sl_argv(inst->cmd.stages[k]));
78 }
79 if (prev_read >= 0)
80 close(prev_read);
81 if (k < inst->cmd.nstages - 1) {
82 close(p[1]);
83 prev_read = p[0];
84 }
85 }
86 for (k = 0; k < inst->cmd.nstages; k++)
87 wait(NULL);
88 _exit(0);
89 }
90}
91
92/* mkdir -p the directory an instance writes into, so a rule (out) can point at a path that does not
93 * exist yet */
94void
95mk_out_dir(const char *out)
96{
97 char tmp[PATH_MAX];
98 char *dir;
99
100 if (!out || !out[0])
101 return;
102 estrlcpy(tmp, out, sizeof tmp);
103 dir = dirname(tmp);
104 if (strcmp(dir, ".") == 0 || strcmp(dir, "/") == 0)
105 return;
106 mkdirp(dir, 0777, 0777);
107}
108
109/* flush before child runs so announcement appears before its output */
110void
111announce_inst(struct Inst *inst)
112{
113 mk_out_dir(inst->out);
114 printf(" %-10s %s\n", rules[inst->rule_idx].name, inst->out[0] ? inst->out : "(phony)");
115 fflush(stdout);
116}
117
118/* exits on failure since a missing producer is unrecoverable */
119void
120run_inst_sync(int idx)
121{
122 struct Inst *inst = &insts[idx];
123 pid_t pid;
124 int status;
125
126 if (!inst_stale(inst))
127 return;
128 announce_inst(inst);
129 pid = inst->cmd.is_pipe ? spawn_pipe(inst) : spawn_inst(inst, &inst->cmd.argv);
130 if (waitpid(pid, &status, 0) < 0)
131 eprintf("waitpid:");
132 if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0))
133 eprintf("manifest: producer failed (rule %s)\n", rules[inst->rule_idx].name);
134}
135
136/* all instances in the batch are at the same kahn level */
137void
138run_batch(int *idxs, int n)
139{
140 int next = 0, running = 0;
141 pid_t *pids = emalloc((size_t)(n > 0 ? n : 1) * sizeof *pids);
142 int *slot = emalloc((size_t)(n > 0 ? n : 1) * sizeof *slot);
143
144 while (next < n || running > 0) {
145 while (running < jobs_n && next < n) {
146 struct Inst *inst = &insts[idxs[next]];
147 pid_t pid;
148
149 if (!inst_stale(inst)) {
150 next++;
151 continue;
152 }
153 announce_inst(inst);
154
155 pid = inst->cmd.is_pipe ? spawn_pipe(inst) : spawn_inst(inst, &inst->cmd.argv);
156 pids[running] = pid;
157 slot[running] = idxs[next];
158 running++;
159 next++;
160 }
161 if (running > 0) {
162 int status;
163 pid_t done = wait(&status);
164 int i;
165 if (done < 0)
166 eprintf("wait:");
167 for (i = 0; i < running; i++) {
168 if (pids[i] == done) {
169 if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) {
170 char *cmd = sl_join(&insts[slot[i]].cmd.argv);
171 weprintf("rule %s failed: %s\n", rules[insts[slot[i]].rule_idx].name, cmd);
172 free(cmd);
173 failed_any = 1;
174 }
175 pids[i] = pids[running - 1];
176 slot[i] = slot[running - 1];
177 running--;
178 break;
179 }
180 }
181 }
182 }
183 free(pids);
184 free(slot);
185}
186
187/* run transitive deps in order so a producer does not archive uncompiled files */
188void
189run_inst_with_deps(int idx)
190{
191 struct Inst *inst = &insts[idx];
192 int i;
193
194 for (i = 0; i < inst->dep_rule_names.n; i++) {
195 struct Rule *dep = rule_find(inst->dep_rule_names.v[i]);
196 int k;
197 if (!dep)
198 continue;
199 for (k = 0; k < dep->n_inst; k++)
200 dep_push(inst, dep->inst_idx[k]);
201 }
202 for (i = 0; i < inst->n_dep; i++)
203 run_inst_with_deps(inst->dep_inst[i]);
204 run_inst_sync(idx);
205}
206
207/* run producer for path only if path does not exist */
208void
209materialize_if_missing(const char *path)
210{
211 struct Rule *r;
212
213 if (file_exists(path))
214 return;
215 r = rule_find_output(path);
216 if (!r)
217 r = rule_find_produces(base_of(path));
218 if (!r)
219 return;
220 expand_rule(r->name);
221 if (r->n_inst > 0) {
222 int k;
223 for (k = 0; k < r->n_inst; k++)
224 run_inst_with_deps(r->inst_idx[k]);
225 }
226}
227
228/* used by (set NAME (capture (exec ...))) at parse time */
229char *
230capture_argv(struct StrList *argv)
231{
232 int fds[2];
233 pid_t pid;
234 char *buf;
235 size_t cap, len;
236 int status;
237
238 if (argv->n == 0)
239 eprintf("capture: empty argv\n");
240 if (pipe(fds) != 0)
241 eprintf("capture: pipe:");
242
243 pid = fork();
244 if (pid < 0)
245 eprintf("capture: fork:");
246 if (pid == 0) {
247 close(fds[0]);
248 if (dup2(fds[1], STDOUT_FILENO) < 0)
249 _exit(127);
250 close(fds[1]);
251 child_execvp(sl_argv(argv));
252 }
253 close(fds[1]);
254
255 cap = 4096;
256 len = 0;
257 buf = emalloc(cap);
258 for (;;) {
259 ssize_t n;
260 if (len + 4096 > cap) {
261 cap *= 2;
262 buf = erealloc(buf, cap);
263 }
264 n = read(fds[0], buf + len, cap - len - 1);
265 if (n < 0) {
266 if (errno == EINTR)
267 continue;
268 eprintf("capture: read:");
269 }
270 if (n == 0)
271 break;
272 len += (size_t)n;
273 }
274 buf[len] = '\0';
275 close(fds[0]);
276
277 while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
278 buf[--len] = '\0';
279
280 if (waitpid(pid, &status, 0) < 0)
281 eprintf("capture: waitpid:");
282 if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0))
283 eprintf("capture: command failed\n");
284 return buf;
285}