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