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