9a3026042dacda4bf990eac9a6b6f77ae36cb3ae
[sw-tools] / src / sw_build.c
1 /* -*-c-*-
2 *
3 * $Id: sw_build.c,v 1.5 2004/04/08 01:52:19 mdw Exp $
4 *
5 * Management of build processes
6 *
7 * (c) 1999 EBI
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of sw-tools.
13 *
14 * sw-tools is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * sw-tools is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with sw-tools; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #include "config.h"
32
33 #include <ctype.h>
34 #include <errno.h>
35 #include <signal.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include <sys/types.h>
42 #include <sys/time.h>
43 #include <sys/select.h>
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #include <fcntl.h>
47 #include <sys/wait.h>
48 #include <sys/utsname.h>
49
50 #ifndef DECL_ENVIRON
51 extern char **environ;
52 #endif
53
54 #include <mLib/alloc.h>
55 #include <mLib/dstr.h>
56 #include <mLib/exc.h>
57 #include <mLib/quis.h>
58 #include <mLib/report.h>
59
60 #include "sw.h"
61 #include "sw_arch.h"
62 #include "sw_build.h"
63 #include "sw_info.h"
64 #include "sw_rsh.h"
65
66 #define PRES_LINK 0
67 #define PRES_DEFAULT 0
68 #define PRES_PREFERENCE 0
69 #include "pres_plain.h"
70 #include "pres_curses.h"
71
72 /*----- Data structures ---------------------------------------------------*/
73
74 /*----- Static variables --------------------------------------------------*/
75
76 static pres *preslist = PRES_LINK;
77
78 /*----- Main code ---------------------------------------------------------*/
79
80 /* --- @swbuild_archlist@ --- *
81 *
82 * Arguments: @swinfo *sw@ = pointer to the build information block
83 *
84 * Returns: A list of architectures which are to be built.
85 *
86 * Use: Decides which architectures need building, and returns them
87 * in a list.
88 */
89
90 archcons *swbuild_archlist(swinfo *sw)
91 {
92 archcons *a = arch_readtab();
93 const char *only = 0;
94 unsigned and = 0, xor = 0;
95
96 /* --- Restrict the architecture list appropriately --- */
97
98 only = opt_arch ? opt_arch : sw->only_arch;
99
100 /* --- Apply the built flags --- */
101
102 if (!(opt_flags & optFlag_force) && sw->arch) {
103 archcons *aa = arch_filter(a, sw->arch, 0, 0);
104 archcons *c;
105 for (c = aa; c; c = c->cdr)
106 c->car->flags |= archFlag_built;
107 arch_free(aa);
108 and |= archFlag_built;
109 }
110
111 return (arch_filter(a, only, and, xor));
112 }
113
114 /*----- Main build command ------------------------------------------------*/
115
116 /* --- @sw_run@ --- *
117 *
118 * Arguments: @int argc@ = number of command line arguments
119 * @char *argv[]@ = array of command line arguments
120 *
121 * Returns: Zero on success (all builds OK) or nonzero for failure.
122 *
123 * Use: Runs a multi-architecture build.
124 */
125
126 int sw_run(int argc, char *argv[])
127 {
128 swinfo sw;
129 archcons *a;
130 pres *p = PRES_DEFAULT;
131 fd_set fdin;
132 int active = 0;
133 int maxfd = 0;
134 int rc = 0;
135
136 /* --- Handle help on output styles --- */
137
138 if (opt_output && strcmp(opt_output, "help") == 0) {
139 printf("Presentation styles supported:");
140 for (p = preslist; p; p = p->next)
141 printf(" %s", p->name);
142 putc('\n', stdout);
143 printf("The default presentation style is %s\n", (PRES_DEFAULT)->name);
144 exit(0);
145 }
146
147 /* --- Validate arguments --- */
148
149 if (!argv[1])
150 die(1, "Usage: run COMMAND [ARG...]");
151
152 /* --- Choose an output presentation style --- */
153
154 if (opt_output) {
155 pres *q;
156 size_t sz = strlen(opt_output);
157 p = 0;
158
159 for (q = preslist; q; q = q->next) {
160 if (strncmp(opt_output, q->name, sz) == 0) {
161 if (q->name[sz] == 0) {
162 p = q;
163 break;
164 } else if (p)
165 die(1, "ambiguous output style `%s'", opt_output);
166 else
167 p = q;
168 }
169 }
170
171 if (!p)
172 die(1, "unknown output style `%s'", opt_output);
173 }
174
175 if (p->ok && !p->ok()) {
176 moan("output style `%s' can't run; using `plain' instead", p->name);
177 p = &pres_plain;
178 }
179
180 /* --- Decide on an architecture --- */
181
182 if (swinfo_fetch(&sw)) {
183 die(1, "couldn't read build status: %s (try running setup)",
184 strerror(errno));
185 }
186 swinfo_sanity(&sw);
187 a = swbuild_archlist(&sw);
188
189 if (!a) {
190 moan("All desired architectures already built OK.");
191 moan("(Perhaps you forgot `--force', or want to say `%s reset'.)", QUIS);
192 return (0);
193 }
194
195 /* --- Tie on remote context blocks, and crank up the presentation --- */
196
197 {
198 archcons *aa;
199
200 for (aa = a; aa; aa = aa->cdr)
201 aa->car->r = xmalloc(sizeof(sw_remote));
202 if (p->init && p->init(a))
203 die(1, "presentation style refused to start: %s", strerror(errno));
204 }
205
206 signal(SIGINT, SIG_IGN);
207 signal(SIGQUIT, SIG_IGN);
208
209 /* --- Trap any exceptions coming this way --- *
210 *
211 * It's important, for example, that a curses-based presentation system
212 * reset the terminal flags appropriately.
213 */
214
215 TRY {
216 /* --- Run remote build processes on the remote hosts --- */
217
218 {
219 archcons *aa;
220 dstr d = DSTR_INIT;
221 char **av;
222 struct utsname u;
223
224 /* --- Fill in the hostname --- */
225
226 if (uname(&u) < 0)
227 strcpy(u.nodename, "<unknown>");
228
229 /* --- If necessary, set up the output @argv@ array --- */
230
231 if (opt_flags & optFlag_percent)
232 av = xmalloc(argc * sizeof(char *));
233 else
234 av = argv + 1;
235
236 /* --- Run through the target build hosts --- */
237
238 FD_ZERO(&fdin);
239 for (aa = a; aa; aa = aa->cdr) {
240 archent *e = aa->car;
241 sw_remote *r = e->r;
242
243 /* --- If necessary, translate `%'-escapes --- */
244
245 if (opt_flags & optFlag_percent) {
246 char **pp, **qq;
247
248 for (pp = argv + 1, qq = av; *pp; pp++, qq++) {
249 if (strchr(*pp, '%') == 0)
250 *qq = *pp;
251 else {
252 char *p;
253 char *q = *pp;
254 for (p = *pp; *p; p++) {
255 if (*p == '%') {
256 DPUTM(&d, q, p - q);
257 p++;
258 switch (*p) {
259 case 0:
260 DPUTC(&d, '%');
261 goto done_arg;
262 case '%':
263 DPUTC(&d, '%');
264 break;
265 case 'a':
266 dstr_puts(&d, e->arch);
267 break;
268 case 'h':
269 dstr_puts(&d, e->flags & archFlag_home ?
270 u.nodename : e->host);
271 break;
272 case 'P':
273 dstr_puts(&d, PREFIX);
274 break;
275 case 'p':
276 dstr_puts(&d, sw.package);
277 break;
278 case 'v':
279 dstr_puts(&d, sw.version);
280 break;
281 case 'u':
282 dstr_puts(&d, sw.maintainer);
283 break;
284 default:
285 DPUTC(&d, '%');
286 DPUTC(&d, *p);
287 break;
288 }
289 q = p + 1;
290 }
291 }
292 DPUTM(&d, q, p - q);
293 done_arg:
294 DPUTZ(&d);
295 *qq = xstrdup(d.buf);
296 DRESET(&d);
297 }
298 }
299 *qq++ = 0;
300 }
301
302 /* --- Start a new process off --- */
303
304 if (swrsh(r, e->flags & archFlag_home ? 0 : e->host,
305 "build", av)) {
306 dstr d = DSTR_INIT;
307 dstr_putf(&d, "%s: couldn't start build for architecture `%s': %s",
308 QUIS, e->arch, strerror(errno));
309 p->output(e, d.buf, d.len);
310 free(r);
311 e->r = 0;
312 dstr_destroy(&d);
313 } else {
314 int fd = r->fdin;
315 if (fd > maxfd)
316 maxfd = fd;
317 active++;
318 FD_SET(fd, &fdin);
319 }
320
321 /* --- Free up the argument array --- */
322
323 if (opt_flags & optFlag_percent) {
324 char **pp, **qq;
325
326 for (pp = argv + 1, qq = av; *pp; pp++, qq++) {
327 if (*pp != *qq)
328 free(*qq);
329 }
330 }
331 }
332
333 if (opt_flags & optFlag_percent)
334 free(av);
335 }
336
337 /* --- Watch the builds until they do something interesting --- */
338
339 maxfd++;
340 while (active) {
341 fd_set f;
342 int n;
343 archcons *aa;
344
345 /* --- Find out what interesting things are happening --- */
346
347 memcpy(&f, &fdin, sizeof(f));
348 n = select(maxfd, &f, 0, 0, 0);
349 if (n < 0) {
350 if (errno == EINTR || errno == EAGAIN)
351 continue;
352 else
353 THROW(EXC_ERRNO, errno);
354 }
355
356 /* --- Scan through for jobs which need attention --- */
357
358 for (aa = a; aa; aa = aa->cdr) {
359 archent *e = aa->car;
360 sw_remote *r = e->r;
361 int t;
362
363 if (!FD_ISSET(r->fdin, &f))
364 continue;
365
366 switch (t = pkrecv(r)) {
367
368 case PKTYPE_DATA:
369 p->output(e, r->buf, r->sz);
370 break;
371
372 case PKTYPE_STATUS: {
373 dstr d = DSTR_INIT;
374 int ok = 1;
375 if (r->sz != 1) {
376 r->buf[r->sz] = 0;
377 dstr_putf(&d, "failed (%s)", r->buf);
378 ok = 0;
379 rc = 1;
380 } else if (r->buf[0]) {
381 dstr_putf(&d, "failed (status %u)", (unsigned char)r->buf[0]);
382 ok = 0;
383 rc = 1;
384 } else {
385 dstr_puts(&d, "finished");
386 if (opt_flags & optFlag_install)
387 e->flags |= archFlag_built;
388 }
389 if (p->close)
390 p->close(e, ok, d.buf);
391 dstr_destroy(&d);
392 FD_CLR(r->fdin, &fdin);
393 close(r->fdin);
394 active--;
395 } break;
396
397 case PKTYPE_EOF: {
398 if (p->close)
399 p->close(e, 0, "unexpected exit");
400 rc = 1;
401 FD_CLR(r->fdin, &fdin);
402 close(r->fdin);
403 active--;
404 } break;
405
406 default: {
407 const static char msg[] = "\n[Unexpected packet, type %i]\n";
408 p->output(e, msg, sizeof(msg) - 1);
409 } break;
410 }
411 }
412 }
413 }
414
415 /* --- Handle any exceptions coming this way --- *
416 *
417 * I could do more cleanup here (freeing buffers and so) but it's not worth
418 * it. Nobody's bothering to catch exceptions anyway.
419 */
420
421 CATCH {
422 if (p->abort)
423 p->abort(a);
424 switch (exc_type) {
425 case EXC_ERRNO:
426 die(1, "unexpected error: %s", strerror(exc_i));
427 break;
428 default:
429 RETHROW;
430 break;
431 }
432 } END_TRY;
433
434 /* --- Tell the presentation that everything's done --- */
435
436 if (p->done)
437 p->done(a);
438 else if (opt_flags & optFlag_beep)
439 putchar('\a');
440
441 /* --- Clean up the unwanted remote contexts --- */
442
443 {
444 archcons *aa;
445 for (aa = a; aa; aa = aa->cdr)
446 free(a->car->r);
447 }
448
449 /* --- Tidy away the architecture list --- */
450
451 arch_free(a);
452
453 /* --- Mark built architectures as having been completed now --- */
454
455 if (opt_flags & optFlag_install) {
456 swinfo skel;
457 dstr d = DSTR_INIT;
458 swinfo_clear(&skel);
459 arch_toText(&d, arch_readtab(), archFlag_built, archFlag_built);
460 skel.arch = d.buf;
461 swinfo_update(&sw, &skel);
462 dstr_destroy(&d);
463 if (swinfo_put(&sw))
464 die(1, "error writing build status: %s", strerror(errno));
465 }
466 return (rc);
467 }
468
469 /*----- Main remote entry point -------------------------------------------*/
470
471 /* --- @putf@ --- *
472 *
473 * Arguments: @sw_remote *r@ = pointer to remote context
474 * @FILE *fp@ = log file handle
475 * @const char *fmt@ = format string
476 * @...@ = other arguments
477 *
478 * Returns: ---
479 *
480 * Use: Reports a string to the log file and the remote controller
481 * process.
482 */
483
484 static void putf(sw_remote *r, FILE *fp, const char *fmt, ...)
485 {
486 va_list ap;
487 dstr d = DSTR_INIT;
488 va_start(ap, fmt);
489 dstr_vputf(&d, fmt, ap);
490 va_end(ap);
491 if (r)
492 pksend(r, PKTYPE_DATA, d.buf, d.len);
493 if (fp)
494 fwrite(d.buf, 1, d.len, fp);
495 dstr_destroy(&d);
496 }
497
498 /* --- @swrsh_build@ --- *
499 *
500 * Arguments: @sw_remote *r@ = pointer to remote context
501 * @char *argv[]@ = pointer to argument list
502 * @char *env[]@ = pointer to environment list
503 *
504 * Returns: Doesn't.
505 *
506 * Use: Runs a remote build command.
507 */
508
509 void swrsh_build(sw_remote *r, char *argv[], char *env[])
510 {
511 FILE *logfp;
512 int fd[2];
513 pid_t kid;
514
515 /* --- Validate the arguments --- */
516
517 if (!argv[0])
518 swdie(r, 1, "Usage: build COMMAND [ARG...]");
519
520 /* --- Change into architecture directory --- */
521
522 if (mkdir(ARCH, 0775) && errno != EEXIST)
523 swdie(r, 1, "mkdir(`%s') failed: %s", ARCH, strerror(errno));
524 if (chdir(ARCH)) {
525 swdie(r, 1, "couldn't change directory to `%s': %s",
526 ARCH, strerror(errno));
527 }
528 if (pipe(fd))
529 swdie(r, 1, "couldn't create pipe: %s", strerror(errno));
530
531 /* --- Open the log file --- */
532
533 {
534 int logfd = open(".build-log", O_WRONLY | O_APPEND | O_CREAT, 0664);
535 time_t t;
536 struct tm *tm;
537 char buf[64];
538 char **p;
539 struct utsname u;
540
541 if (uname(&u) < 0)
542 swdie(r, 1, "couldn't get hostname: %s", strerror(errno));
543 if (logfd < 0)
544 swdie(r, 1, "couldn't open `.build-log' file: %s", strerror(errno));
545 if ((logfp = fdopen(logfd, "a")) == 0) {
546 swdie(r, 1, "couldn't open stream on `.build-log' file: %s",
547 strerror(errno));
548 }
549 t = time(0);
550 tm = localtime(&t);
551 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
552 fprintf(logfp, "\n\n*** %s: %s started build: %s",
553 buf, u.nodename, argv[0]);
554 for (p = argv + 1; *p; p++)
555 fprintf(logfp, " %s", *p);
556 fputs("\n\n", logfp);
557 }
558
559 /* --- Start off the child process --- */
560
561 kid = fork();
562 if (kid == 0) {
563 int nullfd;
564 close(fd[0]);
565 dup2(fd[1], 1);
566 dup2(fd[1], 2);
567 if (fd[1] > 2)
568 close(fd[1]);
569 close(0);
570 nullfd = open("/dev/null", O_RDONLY);
571 if (nullfd > 0) {
572 dup2(nullfd, 0);
573 close(nullfd);
574 }
575 environ = env;
576 execvp(argv[0], argv);
577 fprintf(stderr, "failed to start `%s': %s\n", argv[0], strerror(errno));
578 _exit(127);
579 }
580
581 /* --- Read from the pipe, and write to the socket and logfile --- */
582
583 close(fd[1]);
584 for (;;) {
585 ssize_t n = read(fd[0], r->buf, PKMAX);
586 if (!n)
587 break;
588 if (n < 0) {
589 putf(r, logfp, "\n*** error reading from pipe: %s\n", strerror(errno));
590 kill(kid, SIGTERM);
591 break;
592 }
593 fwrite(r->buf, 1, n, logfp);
594 pksend(r, PKTYPE_DATA, r->buf, n);
595 }
596 close(fd[0]);
597
598 /* --- Reap exit status and produce final report --- */
599
600 {
601 int status;
602 if (waitpid(kid, &status, 0) < 0) {
603 putf(r, logfp, "\n*** error reading exit status: %s\n",
604 strerror(errno));
605 } else {
606 if (WIFSIGNALED(status))
607 fprintf(logfp, "\n*** exited on signal %i\n", WTERMSIG(status));
608 else if (WIFEXITED(status))
609 fprintf(logfp, "\n*** exited with status %i\n", WEXITSTATUS(status));
610 else
611 fprintf(logfp, "\n*** reaped, but didn't exit. Strange\n");
612 }
613 fclose(logfp);
614 swwait(r, status);
615 }
616 }
617
618 /*----- Syntactic sugar ---------------------------------------------------*/
619
620 /*
621 * `Syntactic sugar causes cancer of the semicolon.'
622 * -- Alan Perlis, `Epigrams in Programming'
623 */
624
625 /* --- @build@ --- *
626 *
627 * Arguments: @char **u@ = first segment
628 * @char **v@ = second segment
629 *
630 * Returns: Return code from the build.
631 *
632 * Use: Combines two @argv@-style arrays and then runs a build on the
633 * result.
634 */
635
636 static int build(char **u, char **v)
637 {
638 size_t i, j;
639 char **p;
640
641 for (i = 0, p = u; *p; p++, i++) ;
642 for (j = 0, p = v; *p; p++, j++) ;
643 p = xmalloc((i + j + 2) * sizeof(char **));
644 memcpy(p + 1, u, i * sizeof(char **));
645 memcpy(p + i + 1, v, j * sizeof(char **));
646 p[0] = p[i + j + 1] = 0;
647 return (sw_run(i + j + 1, p));
648 }
649
650 /* --- @sw_make@ --- */
651
652 int sw_make(int argc, char *argv[])
653 {
654 static char *mk[] = { 0, 0 };
655 if (!mk[0]) {
656 char *m;
657 if ((m = getenv("SW_MAKE")) == 0 &&
658 (m = getenv("MAKE")) == 0)
659 m = "make";
660 mk[0] = m;
661 }
662 return (build(mk, argv + 1));
663 }
664
665 /* --- @sw_conf@ --- */
666
667 int sw_conf(int argc, char *argv[])
668 {
669 static char *cf[] = { "../configure", "--prefix=" PREFIX, 0 };
670 return (build(cf, argv + 1));
671 }
672
673 /*----- Other subcommands -------------------------------------------------*/
674
675 /* --- @sw_reset@ --- */
676
677 int sw_reset(int argc, char *argv[])
678 {
679 swinfo sw, skel;
680 if (argc != 1)
681 die(1, "Usage: reset");
682 if (swinfo_fetch(&sw)) {
683 die(1, "couldn't read build status: %s (try running setup)",
684 strerror(errno));
685 }
686 swinfo_sanity(&sw);
687 swinfo_clear(&skel);
688 skel.arch = "";
689 swinfo_update(&sw, &skel);
690 if (swinfo_put(&sw))
691 die(1, "couldn't write build status: %s", strerror(errno));
692 return (0);
693 }
694
695 /*----- That's all, folks -------------------------------------------------*/