forsyth20121205-174063a80cc13 years agoHistory
723 lines · 12.3 KB · Brainfuck
RawDownload
implement Mashbuiltin; ##	"make" builtin, defines:##	depends	- print dependencies#	make		- make-like command#	match	- print details of rule matches#	rules		- print rules# include	"mash.m";include	"mashparse.m"; verbose:	con 0;	# debug output mashlib:	Mashlib; Cmd, Env, Item, Stab:	import mashlib;Depend, Rule, Target:	import mashlib;sys, bufio, hash:		import mashlib; Iobuf:	import bufio; ##	Interface to catch the use as a command.#init(nil: ref Draw->Context, args: list of string){	raise "fail: " + hd args + " not loaded";} ##	Used by whatis.#name(): string{	return "make";} ##	Install commands.#mashinit(nil: list of string, lib: Mashlib, this: Mashbuiltin, e: ref Env){	mashlib = lib;	e.defbuiltin("depends", this);	e.defbuiltin("make", this);	e.defbuiltin("match", this);	e.defbuiltin("rules", this);} ##	Execute a builtin.#mashcmd(e: ref Env, l: list of string){	s := hd l;	l = tl l;	case s {	"depends" =>		out := e.outfile();		if (out == nil)			return;		if (l == nil)			alldeps(out);		else			depends(out, l);		out.close();	"make" =>		domake(e, l);	"match" =>		domatch(e, l);	"rules" =>		out := e.outfile();		if (out == nil)			return;		if (l == nil)			allrules(out);		else			rules(out, l);		out.close();	}} ##	Node states.#SUnknown, SNoexist, SExist, SStale, SMade, SDir, SDirload	: con iota; ##	Node flags.##	FMark	- marked as in progress#FMark	: con 1 << iota; Node: adt{	name:	string;	state:		int;	flags:		int;	mtime:	int;}; ##	Step in implicit chain.#Step:	type (ref Rule, array of string, ref Node); ##	Implicit match.#Match: adt{	node:	ref Node;	path:		list of Step;}; NSIZE:	con 127;	# node hash sizeDSIZE:	con 32;	# number of dir entries for read ntab:		array of list of ref Node;	# node hash table initnodes(){	ntab = array[NSIZE] of list of ref Node;} ##	Find node for a pathname.#getnode(s: string): ref Node{	h := hash->fun1(s, NSIZE);	for (l := ntab[h]; l != nil; l = tl l) {		n := hd l;		if (n.name == s)			return n;	}	r := ref Node(s, SUnknown, 0, 0);	ntab[h] = r :: ntab[h];	return r;} ##	Make a pathname from a dir and an entry.#mkpath(d, s: string): string{	if (d == ".")		return s;	else if (d == "/")		return "/" + s;	else		return d + "/" + s;} ##	Load a directory.#loaddir(s: string){	if (verbose)		sys->print("loaddir %s\n", s);	fd := sys->open(s, Sys->OREAD);	if (fd == nil)		return;	for (;;) {		(c, dbuf) := sys->dirread(fd);		if(c <= 0)			break;		for (i := 0; i < c; i++) {			n := getnode(mkpath(s, dbuf[i].name));			if (dbuf[i].mode & Sys->DMDIR)				n.state = SDir;			else				n.state = SExist;			n.mtime = dbuf[i].mtime;		}	}} ##	Load a file.  Get its node, maybe stat it or loaddir.#loadfile(s: string): ref Node{	n := getnode(s);	if (n.state == SUnknown) {		if (verbose)			sys->print("stat %s\n", s);		(ok, d) := sys->stat(s);		if (ok >= 0) {			n.mtime = d.mtime;			if (d.mode & Sys->DMDIR) {				loaddir(s);				n.state = SDirload;			} else				n.state = SExist;		} else			n.state = SNoexist;	} else if (n.state == SDir) {		loaddir(s);		n.state = SDirload;	}	return n;} ##	Get the node for a file and load the directories in its path.#getfile(s: string): ref Node{	d: string;	n := len s;	while (n >= 2 && s[0:2] == "./") {		n -= 2;		s = s[2:];	}	if (n > 0 && s[0] == '/') {		d = "/";		s = s[1:];	} else		d = ".";	(nil, l) := sys->tokenize(s, "/");	for (;;) {		w := loadfile(d);		if (l == nil)			return w;		s = hd l;		l = tl l;		d = mkpath(d, s);	}} ##	If a dependency rule makes more than one target propogate SMade.#propagate(l: list of string){	if (tl l == nil)		return ;	while (l != nil) {		s := hd l;		if (verbose)			sys->print("propogate to %s\n", s);		getfile(s).state = SMade;		l = tl l;	}} ##	Try to make a node, or mark it as stale.#	Return -1 on (reported) error, 0 on fail, 1 on success.#explicit(e: ref Env, t: ref Target, n: ref Node): int{	d: ref Depend;	for (l := t.depends; l != nil ; l = tl l) {		if ((hd l).op != Cnop) {			if (d != nil) {				e.report(sys->sprint("make: too many rules for %s", t.target));				return -1;			}			d = hd l;		}	}	for (l = t.depends; l != nil ; l = tl l) {		for (u := (hd l).depends; u != nil; u = tl u) {			s := hd u;			m := getfile(s);			x := make(e, m, s);			if (x < 0) {				sys->print("don't know how to make %s\n", s);				return x;			}			if (m.state == SMade || m.mtime > n.mtime) {				if (verbose)					sys->print("%s makes %s stale\n", s, t.target);				n.state = SStale;			}		}	}	if (d != nil) {		if (n.state == SNoexist || n.state == SStale) {			if (verbose)				sys->print("build %s with explicit rule\n", t.target);			e = e.copy();			e.flags |= mashlib->EEcho | Mashlib->ERaise;			e.flags &= ~mashlib->EInter;			d.cmd.xeq(e);			propagate(d.targets);			n.state = SMade;		} else if (verbose)			sys->print("%s up to date\n", t.target);		return 1;	}	return 0;} ##	Report multiple implicit chains of equal length.#multimatch(e: ref Env, n: ref Node, l: list of Match){	e.report(sys->sprint("%d rules match for %s", len l, n.name));	f := e.stderr;	while (l != nil) {		m := hd l;		sys->fprint(f, "%s", m.node.name);		for (p := m.path; p != nil; p = tl p) {			(nil, nil, t) := hd p;			sys->fprint(f, " -> %s", t.name);		}		sys->fprint(f, "\n");		l = tl l;	}} cycle(e: ref Env, n: ref Node){	e.report(sys->sprint("make: cycle in dependencies for target %s", n.name));} ##	Mark the nodes in an implicit chain.#markchain(e: ref Env, l: list of Step): int{	while (tl l != nil) {		(nil, nil, n) := hd l;		if (n.flags & FMark) {			cycle(e, n);			return 0;		}		n.flags |= FMark;		l = tl l;	}	return 1;} ##	Unmark the nodes in an implicit chain.#unmarkchain(l: list of Step): int{	while (tl l != nil) {		(nil, nil, n) := hd l;		n.flags &= ~FMark;		l = tl l;	}	return 1;} ##	Execute an implicit rule chain.#xeqmatch(e: ref Env, b, n: ref Node, l: list of Step): int{	if (!markchain(e, l))		return -1;	if (verbose)		sys->print("making %s for implicit rule chain\n", n.name);	e.args = nil;	x := make(e, n, n.name);	if (x < 0) {		sys->print("don't know how to make %s\n", n.name);		return x;	}	if (n.state == SMade || n.mtime > b.mtime || b.state == SStale) {		e = e.copy();		e.flags |= mashlib->EEcho | Mashlib->ERaise;		e.flags &= ~mashlib->EInter;		for (;;) {			(r, a, t) := hd l;			if (verbose)				sys->print("making %s with implicit rule\n", t.name);			e.args = a;			r.cmd.xeq(e);			t.state = SMade;			l = tl l;			if (l == nil)				break;			t.flags &= ~FMark;		}	} else		unmarkchain(l);	return 1;} ##	Find the shortest implicit rule chain.#implicit(e: ref Env, base: ref Node): int{	win, lose: list of Match;	l: list of ref Rule;	cand := Match(base, nil) :: nil;	do {		# cand - list of candidate chains		# lose - list of extended chains that lose		# win	 - list of extended chains that win		lose = nil;	match:		# for each candidate		for (c := cand; c != nil; c = tl c) {			(b, x) := hd c;			s := b.name;			# find rules that match end of chain			m := mashlib->rulematch(s);			l = nil;			# exclude rules already in the chain		exclude:			for (n := m; n != nil; n = tl n) {				r := hd n;				for (y := x; y != nil; y = tl y) {					(u, nil, nil) := hd y;					if (u == r)						continue exclude;				}				l = r :: l;			}			if (l == nil)				continue match;			(nil, t) := sys->tokenize(s, "/");			# for each new rule that matched			for (n = l; n != nil; n = tl n) {				r := hd n;				a := r.matches(t);				if (a == nil) {					e.report("rule match cock up");					return -1;				}				a[0] = s;				e.args = a;				# eval rhs				(v, nil, nil) := r.rhs.ieval2(e);				if (v == nil)					continue;				y := (r, a, b) :: x;				z := getfile(v);				# winner or loser				if (z.state != SNoexist || Target.find(v) != nil)					win = (z, y) :: win;				else					lose = (z, y) :: lose;			}		}		# winner should be unique		if (win != nil) {			if (tl win != nil) {				multimatch(e, base, win);				return -1;			} else {				(a, p) := hd win;				return xeqmatch(e, base, a, p);			}		}		# losers are candidates in next round		cand = lose;	} while (cand != nil);	return 0;} ##	Make a node (recursive).#	Return -1 on (reported) error, 0 on fail, 1 on success.#make(e: ref Env, n: ref Node, s: string): int{	if (n == nil)		n = getfile(s);	if (verbose)		sys->print("making %s\n", n.name);	if (n.state == SMade)		return 1;	if (n.flags & FMark) {		cycle(e, n);		return -1;	}	n.flags |= FMark;	t := Target.find(s);	if (t != nil) {		x := explicit(e, t, n);		if (x != 0) {			n.flags &= ~FMark;			return x;		}	}	x := implicit(e, n);	n.flags &= ~FMark;	if (x != 0)		return x;	if (n.state == SExist)		return 0;	return -1;}

Lines 1–500 of 723.