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