command.c revision 3f54fd611f536639ec30dd53c48e5ec1897cc7d9
a79fe3aad8743ea57e473ea5f66a723244cb9c0eMarkus Roggenbach/***********************************************************************
bb83db66bd9b3b4ce67be66419daf29886175276Andy Gimblett* This software is part of the ast package *
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder* Copyright (c) 1984-2012 AT&T Intellectual Property *
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder* and is licensed under the *
98890889ffb2e8f6f722b00e265a211f13b5a861Corneliu-Claudiu Prodescu* Eclipse Public License, Version 1.0 *
a79fe3aad8743ea57e473ea5f66a723244cb9c0eMarkus Roggenbach* by AT&T Intellectual Property *
a79fe3aad8743ea57e473ea5f66a723244cb9c0eMarkus Roggenbach* A copy of the License is available at *
a79fe3aad8743ea57e473ea5f66a723244cb9c0eMarkus Roggenbach* http://www.eclipse.org/org/documents/epl-v10.html *
a79fe3aad8743ea57e473ea5f66a723244cb9c0eMarkus Roggenbach* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
bb83db66bd9b3b4ce67be66419daf29886175276Andy Gimblett* Information and Software Systems Research *
78718c37b1a50086a27e0f031db4cf82bea934aeChristian Maeder* AT&T Research *
a79fe3aad8743ea57e473ea5f66a723244cb9c0eMarkus Roggenbach* Florham Park NJ *
a79fe3aad8743ea57e473ea5f66a723244cb9c0eMarkus Roggenbach* Glenn Fowler <gsf@research.att.com> *
a79fe3aad8743ea57e473ea5f66a723244cb9c0eMarkus Roggenbach***********************************************************************/
5cc369fbceee1b13bd0f06e43620c46541d1d4f8Christian Maeder * Glenn Fowler
5b5db1d788d5240070930175f1322dab56279f99Andy Gimblett * AT&T Research
a79fe3aad8743ea57e473ea5f66a723244cb9c0eMarkus Roggenbach * make command execution routines
d9e78002fb0bf01a9b72d3d3415fdf9790bdfee8Andy Gimblett#define AFTER 0 /* done -- making after prereqs */
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly#define BEFORE 001 /* done -- before after prereqs */
9f93b2a8b552789cd939d599504d39732672dc84Christian Maeder#define BLOCKED 002 /* waiting for prereqs */
1538a6e8d77301d6de757616ffc69ee61f1482e4Andy Gimblett#define INTERMEDIATE 003 /* waiting for parent cancel */
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly#define RUNNING 005 /* job action sent to coshell */
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder#define MAMNAME(r) ((state.mam.dynamic||(r)!=state.frame->target||((r)->property&P_after))?mamname(r):(char*)0)
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maederstatic char* statusname[] =
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder "AFTER", "BEFORE", "BLOCKED", "INTERMEDIATE", "READY", "RUNNING",
e54c5af823b9775dd2c058185ea5bdf7593950faAndy Gimblett#define jobstatus() do { if (state.test & 0x00001000) dumpjobs(2, JOB_status); else if (error_info.trace <= CMDTRACE) dumpjobs(CMDTRACE, JOB_status); } while (0)
12b2ae689353ecbaad720a9af9f9be01c1a3fe2dChristian Maederstruct Context_s; typedef struct Context_s Context_t;
12b2ae689353ecbaad720a9af9f9be01c1a3fe2dChristian Maederstruct Joblist_s; typedef struct Joblist_s Joblist_t;
12b2ae689353ecbaad720a9af9f9be01c1a3fe2dChristian Maeder Frame_t* last; /* last active target frame */
f4a5178450076ee54f3a9adb4f91e241aea3ba75Christian Maeder Frame_t* freeframe; /* free target frames */
90047eafd2de482c67bcd13103c6064e9b0cb254Andy Gimblett Context_t* freecontext; /* free job context headers */
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder Rule_t* triggered; /* triggered but not yet a job */
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder * accept r as up to date
4314e26a12954cb1c9be4dea10aa8103edac5bbbChristian Maeder if (r->active && (r->active->parent->target->property & P_archive) && !(r->property & (P_after|P_before|P_use)))
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder artouch(r->active->parent->target->name, r->name);
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly error(0, "touch %s/%s", r->active->parent->target->name, r->name);
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder if (stat(r->name, &st) || tmxgetmtime(&st) < state.start && tmxtouch(r->name, (Time_t)0, (Time_t)0, (Time_t)0, 0))
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder error(ERROR_SYSTEM|1, "cannot touch %s", r->name);
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly r->status = r->time ? EXISTS : (r->property & P_dontcare) ? IGNORE : FAILED;
70a691ea12f53381209a3709cdd325df5fc0a0c8Christian Maeder * apply operator or action with attributes in r given lhs, rhs and job flags
70a691ea12f53381209a3709cdd325df5fc0a0c8Christian Maeder * blocks until action is complete
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maederapply(register Rule_t* r, char* lhs, char* rhs, char* act, Flags_t flags)
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly x->property = r->property & (P_make|P_operator);
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder if ((r->property & (P_make|P_operator)) == (P_make|P_operator))
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly errors = parse(NiL, act, r->name, NiL) == FAILED;
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly * apply() returning (temporary) CO_DATAFILE pointer to stdout of action
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maederfapply(Rule_t* r, char* lhs, char* rhs, char* act, Flags_t flags)
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder if (!apply(r, lhs, rhs, act, flags|CO_DATAFILE))
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly error(2, "%s: cannot read temporary data output file %s", r->name, state.tmpfile);
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder * call functional r with args
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly * functional return value returned
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly * 0 returned if not functional or empty return
bb83db66bd9b3b4ce67be66419daf29886175276Andy Gimblett * commit target/virtual directory
5858e6262048894b0e933b547852f04aed009b58Andy Gimblett register char* t;
2f35e5f6757968746dbab385be21fcae52378a3fLiam O'Reilly register char* v;
33bdce26495121cdbce30331ef90a1969126a840Liam O'Reilly sfprintf(internal.nam, "%s/%s", state.view[r->view].root, v);
fd4ad12563262ebe380d810df8f7755cfab5fb42Liam O'Reilly if (r || state.targetcontext && (!r || !r->time) && (st.st_mode = (S_IRWXU|S_IRWXG|S_IRWXO)) && tmxsetmtime(&st, state.start))
33bdce26495121cdbce30331ef90a1969126a840Liam O'Reilly * why not mkdir -p here?
816c50f9135a598dfdcfb2af8a80390bc42a9b24Liam O'Reilly if (((job->flags & CO_ALWAYS) || state.exec && state.touch) && (mkdir(s, st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) || stat(s, &st)))
33bdce26495121cdbce30331ef90a1969126a840Liam O'Reilly error(1, "%s: cannot create target directory %s", job->target->name, s);
816c50f9135a598dfdcfb2af8a80390bc42a9b24Liam O'Reilly dumpaction(state.mam.out, MAMNAME(job->target), sfstruse(tmp), NiL);
d04c328b10f17ec78001a94d694f7188ebd8c03cAndy Gimblett * push job context
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder * pop job context
9aeda2b3ae8ce0b018955521e4ca835a8ba8a27bLiam O'Reilly * discard (free) job context
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder * save job context for later execute()
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder * restore action by expanding into buf using original context
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder * coexec attributes placed in att
4314e26a12954cb1c9be4dea10aa8103edac5bbbChristian Maederrestore(register Joblist_t* job, Sfio_t* buf, Sfio_t* att)
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder register char* s;
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder register char* b;
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder state.localview = state.mam.statix && !state.expandview && state.user && !(job->flags & CO_ALWAYS);
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder if ((job->flags & CO_LOCALSTACK) || (job->target->dynamic & D_hasscope))
70a691ea12f53381209a3709cdd325df5fc0a0c8Christian Maeder else if ((r->property & (P_make|P_local|P_use)) == (P_make|P_local) && r->action)
70a691ea12f53381209a3709cdd325df5fc0a0c8Christian Maeder if (state.targetcontext && *(u = unbound(job->target)) != '/' && (s = strrchr(u, '/')))
9f31535736c3d43a98f0157efaa7f87ea73c9be0Liam O'Reilly sfprintf(state.context, "{ cd %s%s", down, sep);
9f31535736c3d43a98f0157efaa7f87ea73c9be0Liam O'Reilly if (*b == '/' || (u = getbound(b)) && *u == '/')
9f31535736c3d43a98f0157efaa7f87ea73c9be0Liam O'Reilly switch (*(b + downlen))
fd4ad12563262ebe380d810df8f7755cfab5fb42Liam O'Reilly if ((v = getvar(CO_ENV_ATTRIBUTES)) && !(v->property & V_import))
7cbbd12f559c5c700f521a52424b098db198f1b4Liam O'Reillystatic int done(Joblist_t* job, int, Cojob_t*);
7cbbd12f559c5c700f521a52424b098db198f1b4Liam O'Reilly * send a job to the coshell for execution
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder if (state.targetcontext || state.maxview && !state.fsview && *job->target->name != '/' && (!(job->target->dynamic & D_regular) || job->target->view))
a731366827a80af216ce6bfd4aa6388260577791Andy Gimblett if ((state.mam.dynamic || state.mam.regress) && state.user && !(job->target->property & (P_after|P_before|P_dontcare|P_make|P_state|P_virtual)))
a731366827a80af216ce6bfd4aa6388260577791Andy Gimblett sfprintf(state.mam.out, "%sinit %s %s\n", state.mam.label, mamname(job->target), timefmt(NiL, CURTIME));
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder if (!(job->target->property & (P_attribute|P_virtual)))
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder if ((job->target->property & (P_joint|P_target)) == (P_joint|P_target))
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder for (p = job->target->prereqs->rule->prereqs; p; p = p->next)
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder else if (*t && (!state.silent || state.mam.regress))
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder dumpaction(state.mam.out ? state.mam.out : sfstdout, NiL, t, NiL);
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder if (state.virtualdot && !notfile(job->target))
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder fcntl(internal.openfd, F_SETFD, FD_CLOEXEC);
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder if (!(state.coshell = coopen(getval(CO_ENV_SHELL, VAL_PRIMARY), flags, sfstruse(sp))))
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder error(ERROR_SYSTEM|3, "coshell open error");
9582375827616730f146b77f9d5a4fd0cc78bc47Andy Gimblett coexport(state.coshell, p->rule->name, sfstruse(exp));
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder coexport(state.coshell, p->rule->name, sfstruse(exp));
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder } while (p = p->next);
bb83db66bd9b3b4ce67be66419daf29886175276Andy Gimblett static char* dot;
c679188b6762edb198e353f724e77c74aa64a7e4Andy Gimblett static char* tmp;
fd4ad12563262ebe380d810df8f7755cfab5fb42Liam O'Reilly#if !_HUH_1992_02_29 /* i386 and ftx m68k dump without this statement -- help */
fd4ad12563262ebe380d810df8f7755cfab5fb42Liam O'Reilly message((-99, "execute: %s: t=0x%08x &t=0x%08x", job->target->name, t, &t));
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder dumpaction(state.mam.out, MAMNAME(job->target), t, NiL);
fd4ad12563262ebe380d810df8f7755cfab5fb42Liam O'Reilly if ((state.test & 0x00020000) && internal.openfile)
fd4ad12563262ebe380d810df8f7755cfab5fb42Liam O'Reilly if (!(job->cojob = coexec(state.coshell, t, job->flags, state.tmpfile, NiL, sfstruse(att))))
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder error(3, "%s: cannot send action to coshell", job->target->name);
c679188b6762edb198e353f724e77c74aa64a7e4Andy Gimblett * grab semaphores
c679188b6762edb198e353f724e77c74aa64a7e4Andy Gimblett if (p->rule->semaphore && --p->rule->semaphore == 1)
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder * check status and sync
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder if (job->flags & (CO_DATAFILE|CO_FOREGROUND))
c679188b6762edb198e353f724e77c74aa64a7e4Andy Gimblett if (job->target->property & (P_functional|P_read))
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder setvar(job->target->name, sfstruse(tmp), 0);
5b5db1d788d5240070930175f1322dab56279f99Andy Gimblett error(2, "%s: cannot read temporary data output file %s", job->target->name, state.tmpfile);
c909c215242232fe78ce335e677e6f22264a0ee9Christian Maeder * check if job for r with completed prereqs p can be cancelled
5cc369fbceee1b13bd0f06e43620c46541d1d4f8Christian Maedercancel(register Rule_t* r, register List_t* p)
5cc369fbceee1b13bd0f06e43620c46541d1d4f8Christian Maeder for (; p; p = p->next)
bb83db66bd9b3b4ce67be66419daf29886175276Andy Gimblett if ((a->dynamic & D_same) && (!s || !(t = staterule(RULE, a, NiL, 0)) || s->event >= t->event))
842ae753ab848a8508c4832ab64296b929167a97Christian Maeder error(state.explain ? 0 : -1, "cancelling %s action", r->name);
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder * all actions on behalf of job are done
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder * with the possible exception of after prereqs
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder * clear!=0 to clear job queue
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder * 0 returned if further blocking required
5b5db1d788d5240070930175f1322dab56279f99Andy Gimblettdone(register Joblist_t* job, int clear, Cojob_t* cojob)
5b5db1d788d5240070930175f1322dab56279f99Andy Gimblett if (clear && jobs.triggered && (((a = jobs.triggered)->property & P_state) || (a = staterule(RULE, a, NiL, 0))) && (a->property & P_force))
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder else if (!clear && job->status == RUNNING && (job->target->dynamic & D_hasafter) && hasafter(job->target, (job->flags & CO_ERRORS) ? P_failure : P_after))
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder for (p = job->target->prereqs; p; p = p->next)
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder if (a->status == MAKING && !a->semaphore || !(a->property & P_make) && a->status == UPDATE)
7f24d24e63854a9a2539c2dac55198f746ad57dbChristian Maeder#if __GNUC__ == 3 && ( sparc || _sparc || __sparc ) && !_HUH_2008_10_25 /* gcc code generation bug -- first hit on solaris -- not sure if for all gcc 3.* */
#ifndef __GNUC_MINOR__
#define __GNUC_MINOR__ 0
#ifndef __GNUC_PATCHLEVEL__
static int warned = 0;
if (!warned)
error(state.mam.regress || state.regress ? -1 : 1, "command.c:%d: gcc %d.%d.%d code generation bug workaround -- pass this on to the vendor", __LINE__, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
return !clear;
job->target->status = (job->flags & CO_ERRORS) ? ((job->target->property & P_dontcare) ? IGNORE : FAILED) : EXISTS;
if (n = cojob && (state.mam.dynamic || state.mam.regress) && state.user && !(job->target->property & (P_after|P_before|P_dontcare|P_make|P_state|P_virtual)))
sfprintf(state.mam.out, "%scode %s %d %s %s%s%s\n", state.mam.label, (job->target != state.frame->target || (job->target->property & P_after)) ? mamname(job->target) : "-", EXIT_CODE(cojob->status), timefmt(NiL, tm), timefmt(NiL, CURTIME), (job->target->dynamic & D_same) ? " same" : null, cojob->status && (job->flags & CO_IGNORE) ? " ignore" : null);
p->rule->status = (job->flags & CO_ERRORS) ? ((p->rule->property & P_dontcare) ? IGNORE : FAILED) : EXISTS;
sfprintf(state.mam.out, "%scode %s %d %s%s%s\n", state.mam.label, mamname(p->rule), EXIT_CODE(cojob->status), timefmt(NiL, tm), (p->rule->dynamic & D_same) ? " same" : null, cojob->status && (job->flags & CO_IGNORE) ? " ignore" : null);
jammed = 0;
case AFTER:
case BEFORE:
case BLOCKED:
case READY:
n = READY;
waiting = 0;
switch (a->status)
case FAILED:
goto another;
case MAKING:
waiting = a;
waiting = 0;
n = BLOCKED;
if (!a->semaphore)
semaphore = 0;
if (waiting)
goto another;
goto after;
goto another;
goto another;
goto again;
case RUNNING:
if (jammed)
#if __GNUC__ >= 4 && !_HUH_2006_01_11 /* gcc code generation bug -- first hit on macos -- not sure if for all gcc 4.* */
#ifndef __GNUC_MINOR__
#define __GNUC_MINOR__ 0
#ifndef __GNUC_PATCHLEVEL__
if (!job)
static int warned = 0;
if (!warned)
error(state.mam.regress || state.regress ? -1 : 1, "command.c:%d: gcc %d.%d.%d code generation bug workaround -- pass this on to the vendor", __LINE__, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
goto another;
jammed = 0;
goto unjam;
return !clear;
Rule_t* r;
int clear = 0;
int resume = 0;
error(state.explain ? 0 : -1, "cancelling %s action -- %s", job->target->name, job->status == INTERMEDIATE ? "intermediate not needed" : "missing intermediates accepted");
if ((cojob = cowait(state.coshell, check ? (Cojob_t*)state.coshell : (Cojob_t*)0, -1)) && (job = (Joblist_t*)cojob->local))
if (trap())
if (!cojob)
if (!cojob)
if (check)
sfprintf(jobs.tmp, "%s %d %s %s", job->target->name, cojob->status, fmtelapsed(cojob->user, CO_QUANT), fmtelapsed(cojob->sys, CO_QUANT));
error(n ? 2 : state.explain ? 0 : -1, "%s%s code %d making %s%s", n ? "*** " : null, ERROR_translate(NiL, NiL, NiL, EXITED_TERM(cojob->status) ? "termination" : "exit"), EXIT_CODE(cojob->status), job->target->name, (job->flags & CO_IGNORE) ? ERROR_translate(NiL, NiL, NiL, " ignored") : null);
message((-3, "job: %s: interrupt=%d clear=%d status=%d flags=%08x", job->target->name, state.interrupt, clear, cojob->status, job->flags));
if (resume)
if (clear)
register int errors = 0;
int check = 0;
int recent;
List_t* q;
p = &tmp;
p->rule = r;
p = p->next;
register Rule_t* x;
register Rule_t* s;
if ((x = q->rule) != r)
errors++;
if (tm)
return errors;
terminate(void)
drop(void)
while (block(0));
message((-1, "jobs %d user %s sys %s", state.coshell->total, fmtelapsed(state.coshell->user, CO_QUANT), fmtelapsed(state.coshell->sys, CO_QUANT)));
register List_t* p;
if (state.exec && !state.touch || (a->property & P_always) && (!state.never || (flags & CO_URGENT)))
if (!state.jobs || (r->property & P_foreground) || (r->property & (P_make|P_functional)) == P_functional || (r->dynamic & D_hasmake))
if (action)
message((-1, "triggering %s action%s%s", r->name, r == a ? null : " using ", r == a ? null : a->name));
if (!*action)
action = 0;
else if ((r->dynamic & (D_hasbefore|D_triggered)) == (D_hasbefore|D_triggered) && (makebefore(r) || complete(NiL, prereqs, NiL, 0)))
if (action)
case EXISTS:
statetime(r, 0);
case FAILED:
case TOUCH:
case UPDATE:
if (p->rule != r)
if ((r->property & P_repeat) && (r->property & (P_before|P_after)) && !(r->dynamic & D_hassemaphore))
n = READY;
switch (a->status)
case FAILED:
if (p->rule != r)
case MAKING:
if (a->active)
n = BLOCKED;
if (n != READY)
if (action)
n = INTERMEDIATE;
if (makebefore(r))
if (p->rule != r)
if (p->rule != r)
if (n == READY)
jobstatus();
#if DEBUG
register List_t* p;
register Rule_t* a;
int indent;
int line;
switch (op)
case JOB_blocked:
case JOB_status:
sfprintf(sfstderr, "JOB STATUS TARGET tot=%d pend=%d done=%d\n", state.coshell->total, copending(state.coshell), cozombie(state.coshell));
sfsprintf(tmpname, MAXNAME, "[%d]%s", job->cojob->id, job->cojob->id > 99 ? null : job->cojob->id > 9 ? " " : " ");
, tmpname