5 * Management of build processes
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of sw-tools.
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.
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.
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.
29 /*----- Header files ------------------------------------------------------*/
42 #include <sys/types.h>
44 #include <sys/select.h>
49 #include <sys/utsname.h>
52 extern char **environ;
55 #include <mLib/alloc.h>
56 #include <mLib/dstr.h>
58 #include <mLib/quis.h>
59 #include <mLib/report.h>
68 #define PRES_DEFAULT 0
69 #define PRES_PREFERENCE 0
70 #include "pres_plain.h"
71 #include "pres_curses.h"
73 /*----- Data structures ---------------------------------------------------*/
75 /*----- Static variables --------------------------------------------------*/
77 static pres *preslist = PRES_LINK;
79 /*----- Main code ---------------------------------------------------------*/
81 /* --- @swbuild_archlist@ --- *
83 * Arguments: @swinfo *sw@ = pointer to the build information block
85 * Returns: A list of architectures which are to be built.
87 * Use: Decides which architectures need building, and returns them
91 archcons *swbuild_archlist(swinfo *sw)
93 archcons *a = arch_readtab();
95 unsigned and = 0, xor = 0;
97 /* --- Restrict the architecture list appropriately --- */
99 only = opt_arch ? opt_arch : sw->only_arch;
101 /* --- Apply the built flags --- */
103 if (!(opt_flags & optFlag_force) && sw->arch) {
104 archcons *aa = arch_filter(a, sw->arch, 0, 0);
106 for (c = aa; c; c = c->cdr)
107 c->car->flags |= archFlag_built;
109 and |= archFlag_built;
112 return (arch_filter(a, only, and, xor));
115 /*----- Main build command ------------------------------------------------*/
117 /* --- @sw_run@ --- *
119 * Arguments: @int argc@ = number of command line arguments
120 * @char *argv[]@ = array of command line arguments
122 * Returns: Zero on success (all builds OK) or nonzero for failure.
124 * Use: Runs a multi-architecture build.
127 int sw_run(int argc, char *argv[])
131 pres *p = PRES_DEFAULT;
137 /* --- Handle help on output styles --- */
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);
144 printf("The default presentation style is %s\n", (PRES_DEFAULT)->name);
148 /* --- Validate arguments --- */
151 die(1, "Usage: run COMMAND [ARG...]");
153 /* --- Choose an output presentation style --- */
157 size_t sz = strlen(opt_output);
160 for (q = preslist; q; q = q->next) {
161 if (strncmp(opt_output, q->name, sz) == 0) {
162 if (q->name[sz] == 0) {
166 die(1, "ambiguous output style `%s'", opt_output);
173 die(1, "unknown output style `%s'", opt_output);
176 if (p->ok && !p->ok()) {
177 moan("output style `%s' can't run; using `plain' instead", p->name);
181 /* --- Decide on an architecture --- */
183 if (swinfo_fetch(&sw)) {
184 die(1, "couldn't read build status: %s (try running setup)",
188 a = swbuild_archlist(&sw);
191 moan("All desired architectures already built OK.");
192 moan("(Perhaps you forgot `--force', or want to say `%s reset'.)", QUIS);
196 /* --- Tie on remote context blocks, and crank up the presentation --- */
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));
207 signal(SIGINT, SIG_IGN);
208 signal(SIGQUIT, SIG_IGN);
210 /* --- Trap any exceptions coming this way --- *
212 * It's important, for example, that a curses-based presentation system
213 * reset the terminal flags appropriately.
217 /* --- Run remote build processes on the remote hosts --- */
225 /* --- Fill in the hostname --- */
228 strcpy(u.nodename, "<unknown>");
230 /* --- If necessary, set up the output @argv@ array --- */
232 if (opt_flags & optFlag_percent)
233 av = xmalloc(argc * sizeof(char *));
237 /* --- Run through the target build hosts --- */
240 for (aa = a; aa; aa = aa->cdr) {
241 archent *e = aa->car;
244 /* --- If necessary, translate `%'-escapes --- */
246 if (opt_flags & optFlag_percent) {
249 for (pp = argv + 1, qq = av; *pp; pp++, qq++) {
250 if (strchr(*pp, '%') == 0)
255 for (p = *pp; *p; p++) {
267 dstr_puts(&d, e->arch);
270 dstr_puts(&d, e->flags & archFlag_home ?
271 u.nodename : e->host);
274 dstr_puts(&d, PREFIX);
277 dstr_puts(&d, sw.package);
280 dstr_puts(&d, sw.version);
283 dstr_puts(&d, sw.maintainer);
296 *qq = xstrdup(d.buf);
303 /* --- Start a new process off --- */
305 if (swrsh(r, e->flags & archFlag_home ? 0 : e->host,
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);
322 /* --- Free up the argument array --- */
324 if (opt_flags & optFlag_percent) {
327 for (pp = argv + 1, qq = av; *pp; pp++, qq++) {
334 if (opt_flags & optFlag_percent)
338 /* --- Watch the builds until they do something interesting --- */
346 /* --- Find out what interesting things are happening --- */
348 memcpy(&f, &fdin, sizeof(f));
349 n = select(maxfd, &f, 0, 0, 0);
351 if (errno == EINTR || errno == EAGAIN)
354 THROW(EXC_ERRNO, errno);
357 /* --- Scan through for jobs which need attention --- */
359 for (aa = a; aa; aa = aa->cdr) {
360 archent *e = aa->car;
364 if (!FD_ISSET(r->fdin, &f))
367 switch (t = pkrecv(r)) {
370 p->output(e, r->buf, r->sz);
373 case PKTYPE_STATUS: {
378 dstr_putf(&d, "failed (%s)", r->buf);
381 } else if (r->buf[0]) {
382 dstr_putf(&d, "failed (status %u)", (unsigned char)r->buf[0]);
386 dstr_puts(&d, "finished");
387 if (opt_flags & optFlag_install)
388 e->flags |= archFlag_built;
391 p->close(e, ok, d.buf);
393 FD_CLR(r->fdin, &fdin);
400 p->close(e, 0, "unexpected exit");
402 FD_CLR(r->fdin, &fdin);
408 const static char msg[] = "\n[Unexpected packet, type %i]\n";
409 p->output(e, msg, sizeof(msg) - 1);
416 /* --- Handle any exceptions coming this way --- *
418 * I could do more cleanup here (freeing buffers and so) but it's not worth
419 * it. Nobody's bothering to catch exceptions anyway.
427 die(1, "unexpected error: %s", strerror(exc_i));
435 /* --- Tell the presentation that everything's done --- */
439 else if (opt_flags & optFlag_beep)
442 /* --- Clean up the unwanted remote contexts --- */
446 for (aa = a; aa; aa = aa->cdr)
450 /* --- Tidy away the architecture list --- */
454 /* --- Mark built architectures as having been completed now --- */
456 if (opt_flags & optFlag_install) {
460 arch_toText(&d, arch_readtab(), archFlag_built, archFlag_built);
462 swinfo_update(&sw, &skel);
465 die(1, "error writing build status: %s", strerror(errno));
470 /*----- Main remote entry point -------------------------------------------*/
474 * Arguments: @sw_remote *r@ = pointer to remote context
475 * @FILE *fp@ = log file handle
476 * @const char *fmt@ = format string
477 * @...@ = other arguments
481 * Use: Reports a string to the log file and the remote controller
485 static void putf(sw_remote *r, FILE *fp, const char *fmt, ...)
490 dstr_vputf(&d, fmt, &ap);
493 pksend(r, PKTYPE_DATA, d.buf, d.len);
495 fwrite(d.buf, 1, d.len, fp);
499 /* --- @swrsh_build@ --- *
501 * Arguments: @sw_remote *r@ = pointer to remote context
502 * @char *argv[]@ = pointer to argument list
503 * @char *env[]@ = pointer to environment list
507 * Use: Runs a remote build command.
510 void swrsh_build(sw_remote *r, char *argv[], char *env[])
516 /* --- Validate the arguments --- */
519 swdie(r, 1, "Usage: build COMMAND [ARG...]");
521 /* --- Change into architecture directory --- */
523 if (mkdir(ARCH, 0775) && errno != EEXIST)
524 swdie(r, 1, "mkdir(`%s') failed: %s", ARCH, strerror(errno));
526 swdie(r, 1, "couldn't change directory to `%s': %s",
527 ARCH, strerror(errno));
530 swdie(r, 1, "couldn't create pipe: %s", strerror(errno));
532 /* --- Open the log file --- */
535 int logfd = open(".build-log", O_WRONLY | O_APPEND | O_CREAT, 0664);
543 swdie(r, 1, "couldn't get hostname: %s", strerror(errno));
545 swdie(r, 1, "couldn't open `.build-log' file: %s", strerror(errno));
546 if ((logfp = fdopen(logfd, "a")) == 0) {
547 swdie(r, 1, "couldn't open stream on `.build-log' file: %s",
552 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
553 fprintf(logfp, "\n\n*** %s: %s started build: %s",
554 buf, u.nodename, argv[0]);
555 for (p = argv + 1; *p; p++)
556 fprintf(logfp, " %s", *p);
557 fputs("\n\n", logfp);
560 /* --- Start off the child process --- */
571 nullfd = open("/dev/null", O_RDONLY);
577 execvp(argv[0], argv);
578 fprintf(stderr, "failed to start `%s': %s\n", argv[0], strerror(errno));
582 /* --- Read from the pipe, and write to the socket and logfile --- */
586 ssize_t n = read(fd[0], r->buf, PKMAX);
590 putf(r, logfp, "\n*** error reading from pipe: %s\n", strerror(errno));
594 fwrite(r->buf, 1, n, logfp);
595 pksend(r, PKTYPE_DATA, r->buf, n);
599 /* --- Reap exit status and produce final report --- */
603 if (waitpid(kid, &status, 0) < 0) {
604 putf(r, logfp, "\n*** error reading exit status: %s\n",
607 if (WIFSIGNALED(status))
608 fprintf(logfp, "\n*** exited on signal %i\n", WTERMSIG(status));
609 else if (WIFEXITED(status))
610 fprintf(logfp, "\n*** exited with status %i\n", WEXITSTATUS(status));
612 fprintf(logfp, "\n*** reaped, but didn't exit. Strange\n");
619 /*----- Syntactic sugar ---------------------------------------------------*/
622 * `Syntactic sugar causes cancer of the semicolon.'
623 * -- Alan Perlis, `Epigrams in Programming'
628 * Arguments: @char **u@ = first segment
629 * @char **v@ = second segment
631 * Returns: Return code from the build.
633 * Use: Combines two @argv@-style arrays and then runs a build on the
637 static int build(char **u, char **v)
642 for (i = 0, p = u; *p; p++, i++) ;
643 for (j = 0, p = v; *p; p++, j++) ;
644 p = xmalloc((i + j + 2) * sizeof(char **));
645 memcpy(p + 1, u, i * sizeof(char **));
646 memcpy(p + i + 1, v, j * sizeof(char **));
647 p[0] = p[i + j + 1] = 0;
648 return (sw_run(i + j + 1, p));
651 /* --- @sw_make@ --- */
653 int sw_make(int argc, char *argv[])
655 static char *mk[] = { 0, 0 };
658 if ((m = getenv("SW_MAKE")) == 0 &&
659 (m = getenv("MAKE")) == 0)
663 return (build(mk, argv + 1));
666 /* --- @sw_conf@ --- */
668 int sw_conf(int argc, char *argv[])
670 static char *cf[] = { "../configure", "--prefix=" PREFIX, 0 };
671 return (build(cf, argv + 1));
674 /*----- Other subcommands -------------------------------------------------*/
676 /* --- @sw_reset@ --- */
678 int sw_reset(int argc, char *argv[])
682 die(1, "Usage: reset");
683 if (swinfo_fetch(&sw)) {
684 die(1, "couldn't read build status: %s (try running setup)",
690 swinfo_update(&sw, &skel);
692 die(1, "couldn't write build status: %s", strerror(errno));
696 /*----- That's all, folks -------------------------------------------------*/