3 * $Id: sw_build.c,v 1.4 1999/09/16 12:53:46 mdw Exp $
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 /*----- Revision history --------------------------------------------------*
31 * $Log: sw_build.c,v $
32 * Revision 1.4 1999/09/16 12:53:46 mdw
33 * Some systems have uname(2) return > 0.
35 * Revision 1.3 1999/09/10 15:27:33 mdw
36 * Include `%'-escape substitution.
38 * Revision 1.2 1999/07/16 12:50:24 mdw
39 * Improve exit status display. New interface from `doto' project.
41 * Revision 1.1.1.1 1999/06/02 16:53:34 mdw
46 /*----- Header files ------------------------------------------------------*/
58 #include <sys/types.h>
60 #include <sys/select.h>
65 #include <sys/utsname.h>
68 extern char **environ
;
71 #include <mLib/alloc.h>
72 #include <mLib/dstr.h>
74 #include <mLib/quis.h>
75 #include <mLib/report.h>
84 #define PRES_DEFAULT 0
85 #define PRES_PREFERENCE 0
86 #include "pres_plain.h"
87 #include "pres_curses.h"
89 /*----- Data structures ---------------------------------------------------*/
91 /*----- Static variables --------------------------------------------------*/
93 static pres
*preslist
= PRES_LINK
;
95 /*----- Main code ---------------------------------------------------------*/
97 /* --- @swbuild_archlist@ --- *
99 * Arguments: @swinfo *sw@ = pointer to the build information block
101 * Returns: A list of architectures which are to be built.
103 * Use: Decides which architectures need building, and returns them
107 archcons
*swbuild_archlist(swinfo
*sw
)
109 archcons
*a
= arch_readtab();
110 const char *only
= 0;
111 unsigned and = 0, xor = 0;
113 /* --- Restrict the architecture list appropriately --- */
115 only
= opt_arch ? opt_arch
: sw
->only_arch
;
117 /* --- Apply the built flags --- */
119 if (!(opt_flags
& optFlag_force
) && sw
->arch
) {
120 archcons
*aa
= arch_filter(a
, sw
->arch
, 0, 0);
122 for (c
= aa
; c
; c
= c
->cdr
)
123 c
->car
->flags
|= archFlag_built
;
125 and |= archFlag_built
;
128 return (arch_filter(a
, only
, and, xor));
131 /*----- Main build command ------------------------------------------------*/
133 /* --- @sw_run@ --- *
135 * Arguments: @int argc@ = number of command line arguments
136 * @char *argv[]@ = array of command line arguments
138 * Returns: Zero on success (all builds OK) or nonzero for failure.
140 * Use: Runs a multi-architecture build.
143 int sw_run(int argc
, char *argv
[])
147 pres
*p
= PRES_DEFAULT
;
153 /* --- Handle help on output styles --- */
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
);
160 printf("The default presentation style is %s\n", (PRES_DEFAULT
)->name
);
164 /* --- Validate arguments --- */
167 die(1, "Usage: run COMMAND [ARG...]");
169 /* --- Choose an output presentation style --- */
173 size_t sz
= strlen(opt_output
);
176 for (q
= preslist
; q
; q
= q
->next
) {
177 if (strncmp(opt_output
, q
->name
, sz
) == 0) {
178 if (q
->name
[sz
] == 0) {
182 die(1, "ambiguous output style `%s'", opt_output
);
189 die(1, "unknown output style `%s'", opt_output
);
192 if (p
->ok
&& !p
->ok()) {
193 moan("output style `%s' can't run; using `plain' instead", p
->name
);
197 /* --- Decide on an architecture --- */
199 if (swinfo_fetch(&sw
)) {
200 die(1, "couldn't read build status: %s (try running setup)",
204 a
= swbuild_archlist(&sw
);
207 moan("All desired architectures already built OK.");
208 moan("(Perhaps you forgot `--force', or want to say `%s reset'.)", QUIS
);
212 /* --- Tie on remote context blocks, and crank up the presentation --- */
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
));
223 signal(SIGINT
, SIG_IGN
);
224 signal(SIGQUIT
, SIG_IGN
);
226 /* --- Trap any exceptions coming this way --- *
228 * It's important, for example, that a curses-based presentation system
229 * reset the terminal flags appropriately.
233 /* --- Run remote build processes on the remote hosts --- */
241 /* --- Fill in the hostname --- */
244 strcpy(u
.nodename
, "<unknown>");
246 /* --- If necessary, set up the output @argv@ array --- */
248 if (opt_flags
& optFlag_percent
)
249 av
= xmalloc(argc
* sizeof(char *));
253 /* --- Run through the target build hosts --- */
256 for (aa
= a
; aa
; aa
= aa
->cdr
) {
257 archent
*e
= aa
->car
;
260 /* --- If necessary, translate `%'-escapes --- */
262 if (opt_flags
& optFlag_percent
) {
265 for (pp
= argv
+ 1, qq
= av
; *pp
; pp
++, qq
++) {
266 if (strchr(*pp
, '%') == 0)
271 for (p
= *pp
; *p
; p
++) {
283 dstr_puts(&d
, e
->arch
);
286 dstr_puts(&d
, e
->flags
& archFlag_home ?
287 u
.nodename
: e
->host
);
290 dstr_puts(&d
, PREFIX
);
293 dstr_puts(&d
, sw
.package
);
296 dstr_puts(&d
, sw
.version
);
299 dstr_puts(&d
, sw
.maintainer
);
312 *qq
= xstrdup(d
.buf
);
319 /* --- Start a new process off --- */
321 if (swrsh(r
, e
->flags
& archFlag_home ?
0 : e
->host
,
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
);
338 /* --- Free up the argument array --- */
340 if (opt_flags
& optFlag_percent
) {
343 for (pp
= argv
+ 1, qq
= av
; *pp
; pp
++, qq
++) {
350 if (opt_flags
& optFlag_percent
)
354 /* --- Watch the builds until they do something interesting --- */
362 /* --- Find out what interesting things are happening --- */
364 memcpy(&f
, &fdin
, sizeof(f
));
365 n
= select(maxfd
, &f
, 0, 0, 0);
367 if (errno
== EINTR
|| errno
== EAGAIN
)
370 THROW(EXC_ERRNO
, errno
);
373 /* --- Scan through for jobs which need attention --- */
375 for (aa
= a
; aa
; aa
= aa
->cdr
) {
376 archent
*e
= aa
->car
;
380 if (!FD_ISSET(r
->fdin
, &f
))
383 switch (t
= pkrecv(r
)) {
386 p
->output(e
, r
->buf
, r
->sz
);
389 case PKTYPE_STATUS
: {
394 dstr_putf(&d
, "failed (%s)", r
->buf
);
397 } else if (r
->buf
[0]) {
398 dstr_putf(&d
, "failed (status %u)", (unsigned char)r
->buf
[0]);
402 dstr_puts(&d
, "finished");
403 if (opt_flags
& optFlag_install
)
404 e
->flags
|= archFlag_built
;
407 p
->close(e
, ok
, d
.buf
);
409 FD_CLR(r
->fdin
, &fdin
);
416 p
->close(e
, 0, "unexpected exit");
418 FD_CLR(r
->fdin
, &fdin
);
424 const static char msg
[] = "\n[Unexpected packet, type %i]\n";
425 p
->output(e
, msg
, sizeof(msg
) - 1);
432 /* --- Handle any exceptions coming this way --- *
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.
443 die(1, "unexpected error: %s", strerror(exc_i
));
451 /* --- Tell the presentation that everything's done --- */
455 else if (opt_flags
& optFlag_beep
)
458 /* --- Clean up the unwanted remote contexts --- */
462 for (aa
= a
; aa
; aa
= aa
->cdr
)
466 /* --- Tidy away the architecture list --- */
470 /* --- Mark built architectures as having been completed now --- */
472 if (opt_flags
& optFlag_install
) {
476 arch_toText(&d
, arch_readtab(), archFlag_built
, archFlag_built
);
478 swinfo_update(&sw
, &skel
);
481 die(1, "error writing build status: %s", strerror(errno
));
486 /*----- Main remote entry point -------------------------------------------*/
490 * Arguments: @sw_remote *r@ = pointer to remote context
491 * @FILE *fp@ = log file handle
492 * @const char *fmt@ = format string
493 * @...@ = other arguments
497 * Use: Reports a string to the log file and the remote controller
501 static void putf(sw_remote
*r
, FILE *fp
, const char *fmt
, ...)
506 dstr_vputf(&d
, fmt
, ap
);
509 pksend(r
, PKTYPE_DATA
, d
.buf
, d
.len
);
511 fwrite(d
.buf
, 1, d
.len
, fp
);
515 /* --- @swrsh_build@ --- *
517 * Arguments: @sw_remote *r@ = pointer to remote context
518 * @char *argv[]@ = pointer to argument list
519 * @char *env[]@ = pointer to environment list
523 * Use: Runs a remote build command.
526 void swrsh_build(sw_remote
*r
, char *argv
[], char *env
[])
532 /* --- Validate the arguments --- */
535 swdie(r
, 1, "Usage: build COMMAND [ARG...]");
537 /* --- Change into architecture directory --- */
539 if (mkdir(ARCH
, 0775) && errno
!= EEXIST
)
540 swdie(r
, 1, "mkdir(`%s') failed: %s", ARCH
, strerror(errno
));
542 swdie(r
, 1, "couldn't change directory to `%s': %s",
543 ARCH
, strerror(errno
));
546 swdie(r
, 1, "couldn't create pipe: %s", strerror(errno
));
548 /* --- Open the log file --- */
551 int logfd
= open(".build-log", O_WRONLY
| O_APPEND
| O_CREAT
, 0664);
559 swdie(r
, 1, "couldn't get hostname: %s", strerror(errno
));
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",
568 strftime(buf
, sizeof(buf
), "%Y-%m-%d %H:%M:%S", tm
);
569 fprintf(logfp
, "\n\n*** %s: %s started build: %s",
570 buf
, u
.nodename
, argv
[0]);
571 for (p
= argv
+ 1; *p
; p
++)
572 fprintf(logfp
, " %s", *p
);
573 fputs("\n\n", logfp
);
576 /* --- Start off the child process --- */
587 nullfd
= open("/dev/null", O_RDONLY
);
593 execvp(argv
[0], argv
);
594 fprintf(stderr
, "failed to start `%s': %s\n", argv
[0], strerror(errno
));
598 /* --- Read from the pipe, and write to the socket and logfile --- */
602 ssize_t n
= read(fd
[0], r
->buf
, PKMAX
);
606 putf(r
, logfp
, "\n*** error reading from pipe: %s\n", strerror(errno
));
610 fwrite(r
->buf
, 1, n
, logfp
);
611 pksend(r
, PKTYPE_DATA
, r
->buf
, n
);
615 /* --- Reap exit status and produce final report --- */
619 if (waitpid(kid
, &status
, 0) < 0) {
620 putf(r
, logfp
, "\n*** error reading exit status: %s\n",
623 if (WIFSIGNALED(status
))
624 fprintf(logfp
, "\n*** exited on signal %i\n", WTERMSIG(status
));
625 else if (WIFEXITED(status
))
626 fprintf(logfp
, "\n*** exited with status %i\n", WEXITSTATUS(status
));
628 fprintf(logfp
, "\n*** reaped, but didn't exit. Strange\n");
635 /*----- Syntactic sugar ---------------------------------------------------*/
638 * `Syntactic sugar causes cancer of the semicolon.'
639 * -- Alan Perlis, `Epigrams in Programming'
644 * Arguments: @char **u@ = first segment
645 * @char **v@ = second segment
647 * Returns: Return code from the build.
649 * Use: Combines two @argv@-style arrays and then runs a build on the
653 static int build(char **u
, char **v
)
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
));
667 /* --- @sw_make@ --- */
669 int sw_make(int argc
, char *argv
[])
671 static char *mk
[] = { 0, 0 };
674 if ((m
= getenv("SW_MAKE")) == 0 &&
675 (m
= getenv("MAKE")) == 0)
679 return (build(mk
, argv
+ 1));
682 /* --- @sw_conf@ --- */
684 int sw_conf(int argc
, char *argv
[])
686 static char *cf
[] = { "../configure", "--prefix=" PREFIX
, 0 };
687 return (build(cf
, argv
+ 1));
690 /*----- Other subcommands -------------------------------------------------*/
692 /* --- @sw_reset@ --- */
694 int sw_reset(int argc
, char *argv
[])
698 die(1, "Usage: reset");
699 if (swinfo_fetch(&sw
)) {
700 die(1, "couldn't read build status: %s (try running setup)",
706 swinfo_update(&sw
, &skel
);
708 die(1, "couldn't write build status: %s", strerror(errno
));
712 /*----- That's all, folks -------------------------------------------------*/