Exemple #1
0
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;
}
Exemple #2
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;
}
Exemple #3
0
void
initwakeup(int repeat)
{
	register Alarms_t*	a;

	NoP(repeat);
	for (a = trap.alarms; a; a = a->next)
		a->rule = makerule(a->rule->name);
}
Exemple #4
0
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 = '/';
	}
}
Exemple #5
0
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;
}
Exemple #6
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;
}
Exemple #7
0
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;
	}
}
Exemple #8
0
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;
		}
	}
}
Exemple #9
0
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;
}