chiark / gitweb /
usage: Print metavariables in SHOUTY letters.
[sw-tools] / src / sw_build.c
1 /* -*-c-*-
2  *
3  * $Id$
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 #include <time.h>
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>
49 #include <sys/utsname.h>
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
77 static 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
91 archcons *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
127 int 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;
221       dstr d = DSTR_INIT;
222       char **av;
223       struct utsname u;
224
225       /* --- Fill in the hostname --- */
226
227       if (uname(&u) < 0)
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 --- */
238
239       FD_ZERO(&fdin);
240       for (aa = a; aa; aa = aa->cdr) {
241         archent *e = aa->car;
242         sw_remote *r = e->r;
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
305         if (swrsh(r, e->flags & archFlag_home ? 0 : e->host,
306                   "build", av)) {
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         }
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         }
332       }
333
334       if (opt_flags & optFlag_percent)
335         free(av);
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;
378               dstr_putf(&d, "failed (%s)", r->buf);
379               ok = 0;
380               rc = 1;
381             } else if (r->buf[0]) {
382               dstr_putf(&d, "failed (status %u)", (unsigned char)r->buf[0]);
383               ok = 0;
384               rc = 1;
385             } else {
386               dstr_puts(&d, "finished");
387               if (opt_flags & optFlag_install)
388                 e->flags |= archFlag_built;
389             }
390             if (p->close)
391               p->close(e, ok, d.buf);
392             dstr_destroy(&d);
393             FD_CLR(r->fdin, &fdin);
394             close(r->fdin);
395             active--;
396           } break;
397
398           case PKTYPE_EOF: {
399             if (p->close)
400               p->close(e, 0, "unexpected exit");
401             rc = 1;
402             FD_CLR(r->fdin, &fdin);
403             close(r->fdin);
404             active--;
405           } break;
406
407           default: {
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);
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:
429         die(1, "unexpected error: %s", strerror(exc_i));
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)
449       free(aa->car->r);
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
487 static 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);
492   dstr_vputf(&d, fmt, &ap);
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
512 void swrsh_build(sw_remote *r, char *argv[], char *env[])
513 {
514   FILE *logfp;
515   int fd[2];
516   pid_t kid;
517
518   /* --- Validate the arguments --- */
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;
542     struct utsname u;
543
544     if (uname(&u) < 0)
545       swdie(r, 1, "couldn't get hostname: %s", strerror(errno));
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);
555     fprintf(logfp, "\n\n*** %s: %s started build: %s",
556             buf, u.nodename, argv[0]);
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) {
592       putf(r, logfp, "\n*** error reading from pipe: %s\n", strerror(errno));
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;
605     if (waitpid(kid, &status, 0) < 0) {
606       putf(r, logfp, "\n*** error reading exit status: %s\n",
607            strerror(errno));
608     } else {
609       if (WIFSIGNALED(status))
610         fprintf(logfp, "\n*** exited on signal %i\n", WTERMSIG(status));
611       else if (WIFEXITED(status))
612         fprintf(logfp, "\n*** exited with status %i\n", WEXITSTATUS(status));
613       else
614         fprintf(logfp, "\n*** reaped, but didn't exit.  Strange\n");
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
639 static 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
655 int 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
670 int 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
680 int 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 -------------------------------------------------*/