static int cancel(register Rule_t* r, register List_t* p) { register Rule_t* a; register Rule_t* s; register Rule_t* t; if (r->must) { s = staterule(RULE, r, NiL, 0); for (; p; p = p->next) { if ((a = p->rule)->dynamic & D_alias) a = makerule(a->name); if ((a->dynamic & D_same) && (!s || !(t = staterule(RULE, a, NiL, 0)) || s->event >= t->event)) r->must--; } if (!r->must) { if (error_info.trace || state.explain) error(state.explain ? 0 : -1, "cancelling %s action", r->name); r->status = EXISTS; r->dynamic |= D_same; r->dynamic &= ~D_triggered; if (t = staterule(PREREQS, r, NiL, 0)) t->time = CURTIME; return 1; } } return 0; }
static void pop(Joblist_t* job) { register Context_t* z; register Frame_t* p; register Rule_t* r; int n; Time_t tm; if (z = job->context) { n = state.targetview; state.targetview = z->targetview; z->targetview = n; p = state.frame; state.frame = z->frame; z->frame = p; for (;;) { if (!(r = getrule(p->context.name))) r = makerule(p->context.name); r->active = p->context.frame; tm = r->time; r->time = p->context.time; p->context.time = tm; if (p == p->parent) break; p = p->parent; } } job->status &= ~PUSHED; }
void initwakeup(int repeat) { register Alarms_t* a; NoP(repeat); for (a = trap.alarms; a; a = a->next) a->rule = makerule(a->rule->name); }
static void commit(Joblist_t* job, register char* s) { register char* t; register char* v; register Rule_t* r; Stat_t st; if (t = strrchr(s, '/')) { *t = 0; if (r = bindfile(NiL, s, 0)) { if (!r->view) { *t = '/'; return; } if (*(v = r->name) != '/') { sfprintf(internal.nam, "%s/%s", state.view[r->view].root, v); v = sfstruse(internal.nam); } if (stat(v, &st)) r = 0; } if (r || state.targetcontext && (!r || !r->time) && (st.st_mode = (S_IRWXU|S_IRWXG|S_IRWXO)) && tmxsetmtime(&st, state.start)) { /* * why not mkdir -p here? */ commit(job, s); if (((job->flags & CO_ALWAYS) || state.exec && state.touch) && (mkdir(s, st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) || stat(s, &st))) error(1, "%s: cannot create target directory %s", job->target->name, s); if (state.mam.out) { Sfio_t* tmp = sfstropen(); sfprintf(tmp, "mkdir %s", s); dumpaction(state.mam.out, MAMNAME(job->target), sfstruse(tmp), NiL); sfstrclose(tmp); } r = makerule(s); if (r->dynamic & D_alias) oldname(r); r->view = 0; r->time = tmxgetmtime(&st); if (r->dynamic & D_scanned) unbind(NiL, (char*)r, NiL); } *t = '/'; } }
int main(int argc, char** argv) { register char* s; register Rule_t* r; register List_t* p; int i; int args; int trace; char* t; char* buf; char* tok; Var_t* v; Stat_t st; Stat_t ds; Sfio_t* tmp; /* * initialize dynamic globals */ version = strdup(fmtident(version)); setlocale(LC_ALL, ""); error_info.id = idname; error_info.version = version; error_info.exit = finish; error_info.auxilliary = intercept; if (pathcheck(PATHCHECK, error_info.id, NiL)) return 1; error(-99, "startup"); settypes("*?[]", C_MATCH); settypes("+-|=", C_OPTVAL); settypes(" \t\n", C_SEP); settype(0, C_SEP); settypes(" \t\v\n:+&=;\"\\", C_TERMINAL); settype(0, C_TERMINAL); settypes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", C_ID1|C_ID2|C_VARIABLE1|C_VARIABLE2); settypes(".", C_VARIABLE1|C_VARIABLE2); settypes("0123456789", C_ID2|C_VARIABLE2); /* * close garbage fd's -- we'll be tidy from this point on * 3 may be /dev/tty on some systems * 0..9 for user redirection in shell * 10..19 left open by bugs in some shells * error_info.fd interited from parent * any close-on-exec fd's must have been done on our behalf */ i = 3; if (isatty(i)) i++; for (; i < 20; i++) if (i != error_info.fd && !fcntl(i, F_GETFD, 0)) close(i); /* * allocate the very temporary buffer streams */ internal.met = sfstropen(); internal.nam = sfstropen(); internal.tmp = sfstropen(); internal.val = sfstropen(); internal.wrk = sfstropen(); tmp = sfstropen(); sfstrrsrv(tmp, 2 * MAXNAME); /* * initialize the code and hash tables */ initcode(); inithash(); /* * set the default state */ state.alias = 1; state.exec = 1; state.global = 1; state.init = 1; #if DEBUG state.intermediate = 1; #endif state.io[0] = sfstdin; state.io[1] = sfstdout; state.io[2] = sfstderr; state.jobs = 1; state.pid = getpid(); state.readstate = MAXVIEW; state.scan = 1; state.start = CURTIME; state.stateview = -1; state.tabstops = 8; state.targetview = -1; #if BINDINDEX state.view[0].path = makerule("."); #else state.view[0].path = "."; #endif state.view[0].pathlen = 1; state.writeobject = state.writestate = "-"; /* * pwd initialization * * for project management, if . is group|other writeable * then change umask() for similar write protections */ buf = sfstrbase(tmp); internal.pwd = (s = getcwd(buf, MAXNAME)) ? strdup(s) : strdup("."); internal.pwdlen = strlen(internal.pwd); if (stat(".", &st)) error(3, "cannot stat ."); if (S_ISDIR(st.st_mode) && (st.st_mode & (S_IWGRP|S_IWOTH))) umask(umask(0) & ~(st.st_mode & (S_IWGRP|S_IWOTH))); /* * set some variable default values */ hashclear(table.var, HASH_ALLOCATE); setvar(external.make, argv[0], V_import); t = "lib/make"; setvar(external.lib, strdup((s = pathpath(t, argv[0], PATH_EXECUTE, buf, SF_BUFSIZE)) ? s : t), V_import); setvar(external.pwd, internal.pwd, V_import); setvar(external.version, version, V_import); hashset(table.var, HASH_ALLOCATE); /* * read the environment */ readenv(); if (v = getvar(external.nproc)) state.jobs = (int)strtol(v->value, NiL, 0); if ((v = getvar(external.pwd)) && !streq(v->value, internal.pwd)) { if (!stat(v->value, &st) && !stat(internal.pwd, &ds) && st.st_ino == ds.st_ino && st.st_dev == ds.st_dev) { free(internal.pwd); internal.pwd = strdup(v->value); internal.pwdlen = strlen(v->value); } else { v->property &= ~V_import; v->property |= V_free; v->value = strdup(internal.pwd); } } /* * initialize the internal rule pointers */ initrule(); /* * read the static initialization script */ sfputr(tmp, initstatic, -1); parse(NiL, sfstruse(tmp), "initstatic", NiL); /* * check and read the args file */ if (s = colonlist(tmp, external.args, 1, ' ')) { i = fs3d(0); tok = tokopen(s, 1); while (s = tokread(tok)) if (vecargs(vecfile(s), &argc, &argv) >= 0) break; else if (errno != ENOENT) error(1, "cannot read args file %s", s); tokclose(tok); fs3d(i); } state.argf = newof(0, int, argc, 0); /* * set the command line options * read the command line assignments * mark the command line scripts and targets */ state.init = 0; state.readonly = 1; state.argv = argv; state.argc = argc; if ((args = scanargs(state.argc, state.argv, state.argf)) < 0) return 1; state.readonly = 0; state.init = 1; if (state.base) state.readstate = 0; if (state.compileonly) { state.forceread = 1; state.virtualdot = 0; } /* * tone down the bootstrap noise */ if ((trace = error_info.trace) == -1) error_info.trace = 0; /* * check explicit environment overrides */ if (s = colonlist(tmp, external.import, 1, ' ')) { tok = tokopen(s, 1); while (s = tokread(tok)) { if (i = *s == '!') s++; if (v = getvar(s)) { if (i) v->property &= ~V_import; else v->property |= V_readonly; } } tokclose(tok); } /* * set up the traps */ inittrap(); /* * announce the version */ if (error_info.trace < 0) { errno = 0; error(error_info.trace, "%s [%d %s]", version, state.pid, timestr(state.start)); } /* * initialize the views */ state.global = 0; state.user = 1; initview(); /* * check for mam */ if (state.mam.out) { if (!state.mam.statix || *state.mam.label) error_info.write = mamerror; if (state.mam.regress || state.regress) { sfprintf(state.mam.out, "%sinfo mam %s %05d\n", state.mam.label, state.mam.type, state.mam.parent); if (state.mam.regress) sfprintf(state.mam.out, "%sinfo start regression\n", state.mam.label); } else { sfprintf(state.mam.out, "%sinfo mam %s %05d 1994-07-17 %s\n", state.mam.label, state.mam.type, state.mam.parent, version); if (!state.mam.statix || *state.mam.label) { sfprintf(state.mam.out, "%sinfo start %lu\n", state.mam.label, CURTIME); if (!state.mam.root || streq(state.mam.root, internal.pwd)) sfprintf(state.mam.out, "%sinfo pwd %s\n", state.mam.label, internal.pwd); else sfprintf(state.mam.out, "%sinfo pwd %s %s\n", state.mam.label, state.mam.root, mamname(makerule(internal.pwd))); buf = sfstrbase(tmp); if (state.fsview && !mount(NiL, buf, FS3D_GET|FS3D_ALL|FS3D_SIZE(sfstrsize(tmp)), NiL)) sfprintf(state.mam.out, "%sinfo view %s\n", state.mam.label, buf); } } } /* * read the dynamic initialization script */ if ((i = error_info.trace) > -20) error_info.trace = 0; sfputr(tmp, initdynamic, -1); parse(NiL, sfstruse(tmp), "initdynamic", NiL); error_info.trace = i; state.user = 0; state.init = 0; /* * read the explicit makefiles * readfile() handles the base and global rules * * NOTE: internal.tmplist is used to handle the effects of * load() on internal list pointers */ compref(NiL, 0); if (p = internal.makefiles->prereqs) { p = internal.tmplist->prereqs = listcopy(p); for (; p; p = p->next) readfile(p->rule->name, COMP_FILE, NiL); freelist(internal.tmplist->prereqs); internal.tmplist->prereqs = 0; } /* * if no explicit makefiles then try external.{convert,files} */ if (!state.makefile) { int sep; Sfio_t* exp; Sfio_t* imp; exp = 0; imp = sfstropen(); sep = 0; s = 0; if (*(t = getval(external.convert, VAL_PRIMARY))) { sfputr(tmp, t, 0); sfstrrsrv(tmp, MAXNAME); tok = tokopen(sfstrbase(tmp), 0); while (s = tokread(tok)) { if (!exp) exp = sfstropen(); if (t = colonlist(exp, s, 0, ' ')) { t = tokopen(t, 0); while (s = tokread(t)) { if (readfile(s, COMP_INCLUDE|COMP_DONTCARE, NiL)) break; if (sep) sfputc(imp, ','); else sep = 1; sfputr(imp, s, -1); } tokclose(t); if (s) break; } if (!(s = tokread(tok))) break; } tokclose(tok); sfstrseek(tmp, 0, SEEK_SET); } if (!s && (s = colonlist(tmp, external.files, 1, ' '))) { tok = tokopen(s, 1); while (s = tokread(tok)) { if (readfile(s, COMP_INCLUDE|COMP_DONTCARE, NiL)) break; if (sep) sfputc(imp, ','); else sep = 1; sfputr(imp, s, -1); } tokclose(tok); } if (!s) { /* * this readfile() pulls in the default base rules * that might resolve any delayed self-documenting * options in optcheck() */ if (readfile("-", COMP_FILE, NiL)) optcheck(1); if (*(s = sfstruse(imp))) error(state.errorid ? 1 : 3, "a makefile must be specified when %s omitted", s); else error(state.errorid ? 1 : 3, "a makefile must be specified"); } sfstrclose(imp); if (exp) sfstrclose(exp); } /* * validate external command line options */ optcheck(1); /* * check for listing of variable and rule definitions */ if (state.list) { dump(sfstdout, 0); return 0; } /* * check if makefiles to be compiled */ if (state.compile && !state.virtualdot && state.writeobject) { /* * make the compinit trap */ if (r = getrule(external.compinit)) { state.reading = 1; maketop(r, P_dontcare|P_foreground, NiL); state.reading = 0; } if (state.exec && state.objectfile) { message((-2, "compiling makefile input into %s", state.objectfile)); compile(state.objectfile, NiL); } /* * make the compdone trap */ if (r = getrule(external.compdone)) { state.reading = 1; maketop(r, P_dontcare|P_foreground, NiL); state.reading = 0; } } /* * makefile read cleanup */ if (state.compileonly) return 0; compref(NiL, 0); sfstrclose(tmp); state.compile = COMPILED; if (state.believe) { if (!state.maxview) state.believe = 0; else if (state.fsview) error(3, "%s: option currently works in 2d only", optflag(OPT_believe)->name); } /* * read the state file */ readstate(); /* * place the command line targets in internal.args */ if (internal.main->dynamic & D_dynamic) dynamic(internal.main); internal.args->prereqs = p = 0; for (i = args; i < state.argc; i++) if (state.argf[i] & ARG_TARGET) { List_t* q; q = cons(makerule(state.argv[i]), NiL); if (p) p = p->next = q; else internal.args->prereqs = p = q; } /* * the engine bootstrap is complete -- start user activities */ state.user = 1; /* * make the makeinit trap */ if (r = getrule(external.makeinit)) maketop(r, P_dontcare|P_foreground, NiL); /* * read the command line scripts */ for (i = args; i < state.argc; i++) if (state.argf[i] & ARG_SCRIPT) { state.reading = 1; parse(NiL, state.argv[i], "command line script", NiL); state.reading = 0; } /* * freeze the parameter files and candidate state variables */ state.user = 2; candidates(); /* * make the init trap */ if (r = getrule(external.init)) maketop(r, P_dontcare|P_foreground, NiL); /* * internal.args default to internal.main */ if (!internal.args->prereqs && internal.main->prereqs) internal.args->prereqs = listcopy(internal.main->prereqs); /* * turn up the volume again */ if (!error_info.trace) error_info.trace = trace; /* * make the prerequisites of internal.args */ if (internal.args->prereqs) while (internal.args->prereqs) { /* * we explicitly allow internal.args modifications */ r = internal.args->prereqs->rule; internal.making->prereqs = internal.args->prereqs; internal.args->prereqs = internal.args->prereqs->next; internal.making->prereqs->next = 0; maketop(r, 0, NiL); } else if (state.makefile) error(3, "%s: a main target must be specified", state.makefile); /* * finish up */ finish(0); return 0; }
static int done(register Joblist_t* job, int clear, Cojob_t* cojob) { register List_t* p; register Rule_t* a; Time_t tm; int n; int semaphore; Rule_t* jammed; Rule_t* waiting; if (clear && jobs.triggered && (((a = jobs.triggered)->property & P_state) || (a = staterule(RULE, a, NiL, 0))) && (a->property & P_force)) { a->time = 0; state.savestate = 1; } another: jobstatus(); if (job->status == INTERMEDIATE) state.intermediate--; else if (!clear && job->status == RUNNING && (job->target->dynamic & D_hasafter) && hasafter(job->target, (job->flags & CO_ERRORS) ? P_failure : P_after)) { job->status = BEFORE; for (p = job->target->prereqs; p; p = p->next) { if ((a = p->rule)->dynamic & D_alias) a = makerule(a->name); if (a->status == MAKING && !a->semaphore || !(a->property & P_make) && a->status == UPDATE) return 0; } after: #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 #endif #ifndef __GNUC_PATCHLEVEL__ #define __GNUC_PATCHLEVEL__ 1 #endif if (!jobs.firstjob) { static int warned = 0; if (!warned) { warned = 1; 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; } #endif push(job); n = makeafter(job->target, (job->flags & CO_ERRORS) ? P_failure : P_after); pop(job); if (n) job->flags |= CO_ERRORS; else { job->flags &= ~CO_ERRORS; for (p = job->prereqs; p; p = p->next) { if ((a = p->rule)->dynamic & D_alias) a = makerule(a->name); if (!a->semaphore && a->status == MAKING) { job->status = AFTER; return !state.coshell || cojobs(state.coshell) < state.jobs; } } } } /* * update rule times and status */ if (job->target->status != TOUCH) job->target->status = (job->flags & CO_ERRORS) ? ((job->target->property & P_dontcare) ? IGNORE : FAILED) : EXISTS; tm = statetime(job->target, 0); 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); if ((job->target->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = job->target->prereqs->rule->prereqs; p; p = p->next) if (p->rule != job->target) { if (p->rule->status != TOUCH) p->rule->status = (job->flags & CO_ERRORS) ? ((p->rule->property & P_dontcare) ? IGNORE : FAILED) : EXISTS; tm = statetime(p->rule, 0); if (n) 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); } /* * update the job list */ discard(job); again: jammed = 0; if (job = jobs.firstjob) for (;;) { switch (job->status) { case AFTER: case BEFORE: case BLOCKED: case READY: n = READY; semaphore = 1; waiting = 0; for (p = job->prereqs; p; p = p->next) { if ((a = p->rule)->dynamic & D_alias) a = makerule(a->name); if ((a->property & P_after) && job->status != BEFORE && job->status != AFTER) continue; switch (a->status) { case FAILED: if (a->property & P_repeat) continue; job->flags |= CO_ERRORS; goto another; case MAKING: if (!jammed && (a->mark & M_waiting) && !(a->property & P_archive)) { waiting = a; continue; } waiting = 0; n = BLOCKED; if (!a->semaphore) semaphore = 0; break; default: continue; } break; } if (waiting) jammed = waiting; else if (!clear && job->status == AFTER) { if (n == READY || semaphore) goto another; } else if (!clear && job->status == BEFORE) { if (n == READY || semaphore) goto after; } else if ((job->status = n) == READY) { unjam: if (clear || cancel(job->target, job->prereqs)) goto another; if ((job->target->dynamic & D_intermediate) && job->target->must == 1) { job->status = INTERMEDIATE; jobs.intermediate++; } else if ((job->target->dynamic & (D_hasbefore|D_triggered)) == (D_hasbefore|D_triggered)) { push(job); n = makebefore(job->target); pop(job); if (n) { job->flags |= CO_ERRORS; goto another; } } else if (!state.coshell || cojobs(state.coshell) < state.jobs) { execute(job); goto again; } } break; case RUNNING: if (clear && job->cojob && (job->cojob->flags & CO_SERVICE)) { job->status = FAILED; job->flags |= CO_ERRORS; cokill(state.coshell, job->cojob, 0); } break; } if (!(job = job->next)) { /* * jammed is the first discovered member * of a possible deadlock and we arbitrarily * break it here */ if (jammed) { if (error_info.trace || state.explain) error(state.explain ? 0 : -1, "breaking possible job deadlock at %s", jammed->name); for (job = jobs.firstjob; job; job = job->next) #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 #endif #ifndef __GNUC_PATCHLEVEL__ #define __GNUC_PATCHLEVEL__ 1 #endif if (!job) { static int warned = 0; if (!warned) { warned = 1; 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__); } break; } else #endif if (job->target == jammed) { if (job->status == AFTER) goto another; if (job->status != RUNNING) { jammed = 0; job->status = READY; state.jobs++; goto unjam; } } } break; } } return !clear; }
void dumpjobs(int level, int op) { register Joblist_t* job; register List_t* p; register Rule_t* a; int indent; int line; if (state.coshell && error_info.trace <= level) { indent = error_info.indent; error_info.indent = 0; line = error_info.line; error_info.line = 0; switch (op) { case JOB_blocked: for (job = jobs.firstjob; job; job = job->next) if (job->status == BLOCKED) { sfprintf(sfstderr, "%8s %s :", statusname[job->status & STATUS], job->target->name); for (p = job->prereqs; p; p = p->next) { if ((a = p->rule)->dynamic & D_alias) a = makerule(a->name); if ((a->property & P_after) && job->status != BEFORE && job->status != AFTER) continue; if (a->status == MAKING) sfprintf(sfstderr, " %s", a->name); } sfprintf(sfstderr, "\n"); } else if (job->status == READY) sfprintf(sfstderr, "%8s %s\n", statusname[job->status & STATUS], job->target->name); break; case JOB_status: sfprintf(sfstderr, "JOB STATUS TARGET tot=%d pend=%d done=%d\n", state.coshell->total, copending(state.coshell), cozombie(state.coshell)); for (job = jobs.firstjob; job; job = job->next) { if (job->cojob) sfsprintf(tmpname, MAXNAME, "[%d]%s", job->cojob->id, job->cojob->id > 99 ? null : job->cojob->id > 9 ? " " : " "); else sfsprintf(tmpname, MAXNAME, "[-] "); sfprintf(sfstderr, "%s %-13s%s\t%s%s%s\n" , tmpname , job->status == RUNNING && !job->cojob ? "DONE" : statusname[job->status & STATUS] , job->target->name , (job->target->must > ((unsigned int)(job->target->dynamic & D_intermediate) != 0)) ? " [must]" : null , job->context ? null : " [popped]" , (job->target->mark & M_waiting) ? " [waiting]" : null ); } break; default: error(1, "%d: unknown op index", op); break; } error_info.indent = indent; error_info.line = line; } }
void trigger(register Rule_t* r, Rule_t* a, char* action, Flags_t flags) { register Joblist_t* job; register List_t* p; List_t* prereqs; int n; /* * update flags */ if (!a) a = r; if (state.exec && !state.touch || (a->property & P_always) && (!state.never || (flags & CO_URGENT))) flags |= CO_ALWAYS; if ((a->property | r->property) & P_local) flags |= CO_LOCAL; if (!state.jobs || (r->property & P_foreground) || (r->property & (P_make|P_functional)) == P_functional || (r->dynamic & D_hasmake)) flags |= CO_FOREGROUND|CO_LOCAL; if (state.keepgoing || state.unwind) flags |= CO_KEEPGOING; if (state.silent) flags |= CO_SILENT; if (state.ignore) flags |= CO_IGNORE; if (r->property & (P_functional|P_read)) flags |= CO_DATAFILE; if (action) { message((-1, "triggering %s action%s%s", r->name, r == a ? null : " using ", r == a ? null : a->name)); if (state.exec) jobs.triggered = r; r->dynamic |= D_triggered; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) p->rule->dynamic |= D_triggered; if (!*action) action = 0; } if (state.coshell && (action && !(r->property & P_make) || (flags & CO_FOREGROUND))) { /* * the make thread blocks when too many jobs are outstanding */ n = (flags & CO_FOREGROUND) ? 0 : (state.jobs - 1); while ((cozombie(state.coshell) || cojobs(state.coshell) > n) && block(0)); if ((flags & CO_FOREGROUND) && r->active && r->active->parent && r->active->parent->prereqs && copending(state.coshell) > cojobs(state.coshell)) serial(r, r->active->parent->prereqs); } prereqs = r->prereqs; if (r->active && r->active->primary) { prereqs = cons(getrule(r->active->primary), prereqs); flags |= CO_PRIMARY; } if (r->property & P_make) { if (r->property & P_local) { r->status = EXISTS; return; } /* * make actions are done immediately, bypassing the job queue */ if (prereqs && complete(NiL, prereqs, NiL, 0)) r->status = (r->property & P_dontcare) ? IGNORE : FAILED; else { if (action && cancel(r, prereqs)) r->status = EXISTS; else if ((r->dynamic & (D_hasbefore|D_triggered)) == (D_hasbefore|D_triggered) && (makebefore(r) || complete(NiL, prereqs, NiL, 0))) r->status = (r->property & P_dontcare) ? IGNORE : FAILED; else { if (r->property & P_functional) setvar(r->name, null, 0); if (action) switch (parse(NiL, action, r->name, NiL)) { case EXISTS: if (!(r->property & (P_state|P_virtual))) statetime(r, 0); break; case FAILED: r->status = (r->property & P_dontcare) ? IGNORE : FAILED; break; case TOUCH: r->time = internal.internal->time; break; case UPDATE: if ((r->property & (P_state|P_virtual)) != (P_state|P_virtual)) r->time = CURTIME; break; } if (r->status == UPDATE) r->status = EXISTS; } } if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) if (p->rule != r) { p->rule->status = r->status; p->rule->time = r->time; } if ((r->dynamic & (D_hasafter|D_triggered)) == (D_hasafter|D_triggered)) { if (r->status == FAILED) { if (hasafter(r, P_failure) && !makeafter(r, P_failure) && !complete(NiL, prereqs, NiL, 0)) r->status = EXISTS; } else if (hasafter(r, P_after) && (makeafter(r, P_after) || complete(NiL, prereqs, NiL, 0))) r->status = (r->property & P_dontcare) ? IGNORE : FAILED; } } else { /* * only one repeat action at a time */ if ((r->property & P_repeat) && (r->property & (P_before|P_after)) && !(r->dynamic & D_hassemaphore)) { a = catrule(internal.semaphore->name, ".", r->name, 1); a->semaphore = 2; r->prereqs = append(r->prereqs, cons(a, NiL)); r->dynamic |= D_hassemaphore; } /* * check if any prerequisites are blocking execution * FAILED prerequisites cause the target to fail too */ n = READY; for (;;) { for (p = prereqs; p; p = p->next) { if ((a = p->rule)->dynamic & D_alias) a = makerule(a->name); if (a->property & P_after) continue; switch (a->status) { case FAILED: if (a->property & P_repeat) continue; r->status = (r->property & P_dontcare) ? IGNORE : FAILED; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) if (p->rule != r) p->rule->status = (p->rule->property & P_dontcare) ? IGNORE : FAILED; return; case MAKING: if (a->active) error(1, "%s: prerequisite %s is active", r->name, a->name); else n = BLOCKED; break; } } if (n != READY) break; if (action) { if (cancel(r, prereqs)) return; if ((r->dynamic & D_intermediate) && r->must == 1) { n = INTERMEDIATE; jobs.intermediate++; break; } } if ((r->dynamic & (D_hasbefore|D_triggered)) != (D_hasbefore|D_triggered)) break; if (makebefore(r)) { r->status = (r->property & P_dontcare) ? IGNORE : FAILED; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) if (p->rule != r) p->rule->status = (p->rule->property & P_dontcare) ? IGNORE : FAILED; return; } } if (action || n != READY) { /* * allocate a job cell and add to job list * the first READY job from the top is executed next */ if (job = jobs.freejob) jobs.freejob = jobs.freejob->next; else job = newof(0, Joblist_t, 1, 0); if (flags & CO_URGENT) { job->prev = 0; if (job->next = jobs.firstjob) jobs.firstjob->prev = job; else jobs.lastjob = job; jobs.firstjob = job; } else { job->next = 0; if (job->prev = jobs.lastjob) jobs.lastjob->next = job; else jobs.firstjob = job; jobs.lastjob = job; } /* * fill in the info */ job->target = r; job->prereqs = prereqs; job->status = n; job->flags = flags; job->action = action; r->status = MAKING; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) if (p->rule != r) p->rule->status = r->status; if (n == READY) { execute(job); if (r->dynamic & D_hasafter) save(job); } else save(job); jobstatus(); } else { if (r->status == UPDATE) r->status = EXISTS; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) if (p->rule->status == UPDATE) p->rule->status = EXISTS; if ((r->dynamic & (D_hasafter|D_triggered)) == (D_hasafter|D_triggered)) { if (r->status == FAILED) { if (hasafter(r, P_failure) && !makeafter(r, P_failure) && !complete(NiL, prereqs, NiL, 0)) r->status = EXISTS; } else if (hasafter(r, P_after) && (makeafter(r, P_after) || complete(NiL, prereqs, NiL, 0))) r->status = (r->property & P_dontcare) ? IGNORE : FAILED; if (r->status == EXISTS) { char* t; Sfio_t* tmp; tmp = sfstropen(); edit(tmp, r->name, KEEP, DELETE, DELETE); if (*(t = sfstruse(tmp))) newfile(r, t, r->time); sfstrclose(tmp); } } } if (r->dynamic & D_triggered) { r->time = CURTIME; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) p->rule->time = r->time; } } }
int complete(register Rule_t* r, register List_t* p, Time_t* tm, Flags_t flags) { register int errors = 0; int check = 0; int recent; List_t tmp; List_t* q; Time_t tprereqs; if (r) { tmp.next = p; p = &tmp; p->rule = r; } else { if (p && p->rule == internal.serialize) { p = p->next; check = 1; } if (!p) { while (block(check)); return 0; } } for (tprereqs = 0; p; p = p->next) { if ((r = p->rule)->dynamic & D_alias) r = makerule(r->name); if (recent = r->status == MAKING) { message((-1, "waiting for %s%s", r->semaphore ? "semaphore " : null, r->name)); r->mark |= M_waiting; do { if (!block(check)) { if (recent = r->status == MAKING) { r->status = (r->property & P_dontcare) ? IGNORE : FAILED; error(1, "%s did not complete", r->name); } break; } } while (r->status == MAKING); r->mark &= ~M_waiting; } if (r->status == UPDATE && !(r->property & P_make) && !(flags & P_implicit)) r->status = EXISTS; if (recent && (r->property & (P_joint|P_target)) == (P_joint|P_target)) { register Rule_t* x; register Rule_t* s; s = staterule(RULE, r, NiL, 1); for (q = r->prereqs->rule->prereqs; q; q = q->next) if ((x = q->rule) != r) { if (x->status != r->status) { x->status = r->status; x->time = r->time; } if (s && (x = staterule(RULE, x, NiL, 1))) { x->dynamic |= D_built; x->action = s->action; x->prereqs = s->prereqs; } } } if (r->status == FAILED) errors++; if (!(r->property & (P_after|P_before|P_ignore)) && r->time > tprereqs) tprereqs = r->time; } if (tm) *tm = tprereqs; return errors; }