shithub: mc

Download patch

ref: 52585564bf5a46db0ae6c9fe4e20307e014de848
parent: 2f5a88d9b79271f78ec71cf7f796ca5fb74de89e
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Apr 12 21:25:54 EDT 2015

Move mbld to subdirectory of itself for merging.

--- a/Makefile
+++ /dev/null
@@ -1,20 +1,0 @@
-MYRBIN=mbld
-
-MYRSRC= \
-	build.myr \
-	clean.myr \
-	config.myr \
-	deps.myr \
-	fsel.myr \
-	install.myr \
-	main.myr \
-	opts.myr \
-	parse.myr \
-	subdir.myr \
-	util.myr \
-	test.myr \
-	types.myr
-
-include config.mk
-include mk/myr.mk
-
--- a/bldfile
+++ /dev/null
@@ -1,22 +1,0 @@
-# the mbld binary
-bin mbld =
-	build.myr
-	clean.myr
-	config.myr	# config, as generated by the ./configure script
-	config+plan9-x64.myr	# config, as hardcoded for Plan 9
-	deps.myr
-	fsel.myr
-	install.myr
-	main.myr
-	opts.myr
-	parse.myr
-	subdir.myr
-	test.myr
-        types.myr
-	util.myr
-;;
-
-gen config.myr {durable} = ./configure ;;
-
-man = mbld.1;;
-
--- a/build.myr
+++ /dev/null
@@ -1,412 +1,0 @@
-use std
-
-use "config.use"
-use "deps.use"
-use "opts.use"
-use "parse.use"
-use "types.use"
-use "util.use"
-
-pkg bld =
-	const buildall	: (b : build# -> bool)
-	const genall	: (b : build# -> bool)
-	const build	: (b : build#, target : byte[:] -> bool)
-	const buildbin	: (b : build#, bt : myrtarg#, addsrc : bool -> void)
-	const buildlib	: (b : build#, lt : myrtarg# -> void)
-;;
-
-const buildall = {b
-	for tn in b.all
-		match gettarg(b.targs, tn)
-		| `Bin bt:	buildbin(b, bt, false)
-		| `Lib lt:	buildlib(b, lt)
-		| `Test tt:	/* build on 'mbld test' by default */
-		| `Gen gt:	genfiles(b, gt)
-		| `Man m:	/* nothing needed */
-		;;
-	;;
-	-> true
-}
-
-const genall = {b
-	for tn in b.all
-		match gettarg(b.targs, tn)
-		| `Gen gt: run(gt.cmd)
-		| _:	/* skip */
-		;;
-	;;
-	/* genfiles will exit if the build fails; always return true */
-	-> true
-}
-
-const build = {b, targ
-	match std.htget(b.targs, targ)
-	| `std.Some (`Bin bt):	buildbin(b, bt, false)
-	| `std.Some (`Lib lt):	buildlib(b, lt)
-	| `std.Some (`Test tt):	buildbin(b, tt, false)
-	| `std.Some (`Gen gt):	run(gt.cmd)
-	| `std.Some (`Man m):	/* nothing needed */
-	| `std.None:	std.fatal(1, "invalid target %s\n", targ)
-	;;
-	-> true
-}
-
-const buildbin = {b, targ, addsrc
-	var dg, src
-
-	if targ.built
-		->
-	;;
-	setdir(b, targ.dir)
-	addincludes(b, targ)
-	buildlibdeps(b, targ)
-	std.put("%s...\n", targ.name)
-	if !myrdeps(b, targ, false, false, addsrc, &dg)
-		std.fatal(1, "Could not load dependencies for %s\n", targ.name)
-	;;
-	if !std.hthas(dg.deps, targ.name)
-		std.fatal(1, "no input files for %s\n", targ.name)
-	;;
-	if builddep(b, &dg, targ.name, targ.incpath) || !freshlibs(targ, dg.libs)
-		src = std.htkeys(dg.sources)
-		linkbin(&dg, targ.name, src, targ.ldscript, targ.runtime, targ.incpath, targ.libdeps)
-		std.slfree(src)
-	;;
-	targ.built = true
-}
-
-const buildlib = {b, targ
-	var archive
-	var u, l
-	var dg
-	var lib, src
-
-	if targ.built
-		->
-	;;
-	setdir(b, targ.dir)
-	addincludes(b, targ)
-	buildlibdeps(b, targ)
-	lib = targ.name
-	std.put("lib%s.a...\n", lib)
-	archive = std.fmt("lib%s.a", lib)
-	if !myrdeps(b, targ, true, false, false, &dg)
-		std.fatal(1, "Could not load dependencies for %s\n", lib)
-	;;
-	if !std.hthas(dg.deps, lib)
-		std.fatal(1, "no target declared for %s\n", lib)
-	;;
-	u = builddep(b, &dg, targ.name, targ.incpath)
-	l = builddep(b, &dg, archive, targ.incpath)
-	if  u || l || !freshlibs(targ, dg.libs)
-		src = std.htkeys(dg.sources)
-		mergeuse(&dg, lib, src, targ.incpath)
-		archivelib(&dg, lib, src, targ.incpath)
-		std.slfree(src)
-	;;
-	std.slfree(archive)
-	targ.built = true
-}
-
-const genfiles = {b, gt
-	for f in gt.out
-		if !std.fexists(f)
-			run(gt.cmd)
-			break
-		;;
-	;;
-}
-
-const addincludes = {b, targ
-	for (inc, lib, subtarg) in targ.libdeps
-		if !hasinc(targ.incpath, inc)
-			targ.incpath = std.slpush(targ.incpath, inc)
-		;;
-	;;
-}
-
-const buildlibdeps = {b, targ
-	for (inc, lib, subtarg) in targ.libdeps
-		build(b, subtarg)
-	;;
-}
-
-const hasinc = {path, t
-	for e in path
-		if std.sleq(e, t)
-			-> true
-		;;
-	;;
-	-> false
-}
-
-
-const builddep = {b, dg, out, incs
-	var stale
-
-	stale = false
-	/* short circuit walking the dep tree if we've already built this. */
-	if std.htgetv(dg.updated, out, false)
-		-> false
-	;;
-
-	match std.htget(dg.deps, out)
-	| `std.Some deps:
-		for d in deps
-			if builddep(b, dg, d, incs)
-				stale = true
-			;;
-			if !std.fexists(d)
-				match std.htget(b.gensrc, d)
-				| `std.Some gt:	run(gt.cmd)
-				| `std.None:	std.fatal(1, "no input file %s\n", d)
-				;;
-			;;
-			if !isfresh(d, out)
-				stale = true
-			;;
-		;;
-	| `std.None:
-	;;
-
-	match std.htget(dg.input, out)
-	| `std.Some src:
-		if stale
-			compile(src, incs)
-		;;
-		std.htput(dg.updated, out, true)
-	| `std.None:
-	;;
-	-> stale
-}
-
-const compile = {src, incs
-	var o
-	var cmd
-
-	cmd = [][:]
-	if std.hassuffix(src, ".myr")
-		cmd = std.slpush(cmd, opt_mc)
-		for inc in incs 
-			cmd = std.slpush(cmd, "-I")
-			cmd = std.slpush(cmd, inc)
-		;;
-		if opt_genasm
-			cmd = std.slpush(cmd, "-S")
-		;;
-		cmd = std.slpush(cmd, src)
-		run(cmd)
-		std.slfree(cmd)
-	elif std.hassuffix(src, ".s")
-		o = srcswapsuffix(src, config.Objsuffix)
-		for c in config.Ascmd
-			cmd = std.slpush(cmd, c)
-		;;
-		cmd = std.slpush(cmd,"-o")
-		cmd = std.slpush(cmd, o)
-		cmd = std.slpush(cmd, src)
-		run(cmd)
-		std.slfree(o)
-	else
-		std.fatal(1, "Unknown file type for %s\n", src)
-	;;
-}
-
-const linkbin = {dg, bin, srcfiles, ldscript, rt, incs, extralibs
-	var cmd
-
-	cmd = [][:]
-
-	/* ld -o bin */
-	for c in config.Linkcmd
-		cmd = std.slpush(cmd, std.sldup(c))
-	;;
-	cmd = std.slpush(cmd, std.sldup(bin))
-
-	/* [-T script] */
-	if ldscript.len > 0
-		cmd = std.slpush(cmd, std.sldup("-T"))
-		cmd = std.slpush(cmd, std.sldup(ldscript))
-	;;
-
-	if rt.len != 0
-		cmd = std.slpush(cmd, std.sldup(rt))
-	else
-		cmd = std.slpush(cmd, std.sldup(opt_runtime))
-	;;
-
-	/* input.o list.o... */
-	for f in srcfiles
-		cmd = std.slpush(cmd, srcswapsuffix(f, config.Objsuffix))
-	;;
-
-	/* -L path -l lib... */
-	cmd = addlibs(cmd, dg.libs, incs)
-	for (d, l, t) in extralibs
-		cmd = std.slpush(cmd, std.fmt("-l%s", l))
-	;;
-
-
-	/* special for OSX: it warns if we don't add this */
-	if std.sleq(opt_sys, "osx")
-		cmd = std.slpush(cmd, std.sldup("-macosx_version_min"))
-		cmd = std.slpush(cmd, std.sldup("10.6"))
-	;;
-
-	run(cmd)
-	strlistfree(cmd)
-}
-
-const archivelib = {dg, lib, files, incs
-	var cmd
-	var obj
-
-	cmd = [][:]
-	for c in config.Arcmd
-		cmd = std.slpush(cmd, std.sldup(c))
-	;;
-	cmd = std.slpush(cmd, std.fmt("lib%s.a", lib))
-	for f in files
-		obj = srcswapsuffix(f, config.Objsuffix)
-		cmd = std.slpush(cmd, obj)
-	;;
-	run(cmd)
-	strlistfree(cmd)
-}
-
-const mergeuse = {dg, lib, files, incs
-	var cmd
-
-	cmd = [][:]
-	cmd = std.slpush(cmd, std.sldup(opt_muse))
-	cmd = std.slpush(cmd, std.sldup("-o"))
-	cmd = std.slpush(cmd, std.sldup(lib))
-	for f in files
-		if std.hassuffix(f, ".myr")
-			cmd = std.slpush(cmd, srcswapsuffix(f, ".use"))
-		elif !std.hassuffix(f, ".s")
-			std.fatal(1, "unknown file type for %s\n", f)
-		;;
-	;;
-	run(cmd)
-	strlistfree(cmd)
-}
-
-const addlibs = {cmd, libgraph, incs
-	var looped : std.htab(byte[:], bool)#
-	var marked : std.htab(byte[:], bool)#
-	var libs
-	var head
-
-	/* -L incpath... */
-	if !config.Directlib
-		for inc in incs
-			cmd = std.slpush(cmd, std.fmt("-L%s", inc))
-		;;
-		cmd = std.slpush(cmd, std.fmt("-L%s%s", opt_instroot, "/lib/myr"))
-	;;
-
-	libs = std.htkeys(libgraph)
-	looped = std.mkht(std.strhash, std.streq)
-	marked = std.mkht(std.strhash, std.streq)
-	head = cmd.len
-
-	for lib in libs
-		cmd = visit(cmd, head, libgraph, lib, looped, marked, incs)
-	;;
-
-	-> cmd
-}
-
-const visit = {cmd, head, g, lib, looped, marked, incs
-	if std.hthas(looped, lib)
-		std.fatal(1, "cycle in library graph involving \"%s\"\n", lib)
-	elif std.hthas(marked, lib)
-		-> cmd
-	;;
-
-	std.htput(looped, lib, true)
-	for dep in std.htgetv(g, lib, [][:])
-		cmd = visit(cmd, head, g, dep, looped, marked, incs)
-	;;
-	std.htdel(looped, lib)
-	std.htput(marked, lib, true)
-	-> putlib(cmd, head, lib, incs)
-}
-
-const putlib = {cmd, head, lib, incs
-	if !config.Directlib
-		-> std.slput(cmd, head, std.fmt("-l%s", lib))
-	else
-		match findlib(lib, incs)
-		| `std.None:
-			std.fatal(1, "could not find library lib%s.a", lib)
-		| `std.Some p:
-			-> std.slput(cmd, head, p)
-		;;
-	;;
-}
-
-const findlib = {lib, incs
-	var buf : byte[512]
-	var sl, p
-
-	sl = std.bfmt(buf[:], "lib%s.a", lib)
-	for i in incs
-		p = std.pathjoin([i, sl][:])
-		if std.fexists(p)
-			-> `std.Some p
-		;;
-		std.slfree(p)
-	;;
-	p = std.pathjoin([opt_instroot, "lib/myr", sl][:])
-	if std.fexists(p)
-		-> `std.Some p
-	;;
-	std.slfree(p)
-	-> `std.None
-}
-
-const freshlibs = {targ, libgraph
-	var libs
-
-	libs = std.htkeys(libgraph)
-	for l in libs
-		match findlib(l, targ.incpath)
-		| `std.Some lib:
-			if !isfresh(lib, targ.name)
-				std.slfree(lib)
-				-> false
-			;;
-			std.slfree(lib)
-		| `std.None:
-			std.fatal(1, "%s: could not find library lib%s.a", l)
-		;;
-	;;
-	std.slfree(libs)
-	-> true
-}
-
-const isfresh = {src, dst
-	var srcmt, dstmt
-
-	/*
-	OSX only has single second resolution on modification
-	times. Since most builds happen within one second of each
-	other, if we treat equal times as outdated, we do a lot of
-	spurious rebuilding.
-
-	So, we treat times where both secs and nsecs are equal as
-	up to date.
-	*/
-	match std.fmtime(src)
-	| `std.Some mt:	srcmt = mt
-	| `std.None:	std.fatal(1, "could not stat %s\n", src)
-	;;
-	match std.fmtime(dst)
-	| `std.Some mt:	dstmt = mt
-	| `std.None:	-> false
-	;;
-	-> srcmt <= dstmt
-}
-
--- a/clean.myr
+++ /dev/null
@@ -1,89 +1,0 @@
-use std
-
-use "config.use"
-use "deps.use"
-use "opts.use"
-use "parse.use"
-use "subdir.use"
-use "types.use"
-use "util.use"
-
-pkg bld =
-	const cleanall	: (b : build# -> bool)
-	const clean	: (b : build#, targ : byte[:] -> bool)
-;;
-
-const cleanall = {b
-	for tn in b.all
-		match gettarg(b.targs, tn)
-		| `Bin bt:
-			cleanup(b, bt, bt.inputs, true)
-		| `Lib lt:
-			cleanup(b, lt, lt.inputs, true)
-		| `Test tt:
-			cleanup(b, tt, tt.inputs, true)
-		| `Gen gt:
-			for f in gt.out
-				if !gt.durable && std.remove(f)
-					std.put("\tclean %s\n", f)
-				;;
-			;;
-		| `Man m:
-		;;
-	;;
-	-> true
-}
-
-const clean = {b, targ
-	for tn in b.all
-		match gettarg(b.targs, tn)
-		| `Bin bt:
-			if std.sleq(bt.name, targ)
-				cleanup(b, bt, bt.inputs, true)
-			;;
-		| `Lib lt:
-			if std.sleq(lt.name, targ)
-				cleanup(b, lt, lt.inputs, true)
-			;;
-		| `Test tt:
-			if std.sleq(tt.name, targ)
-				cleanup(b, tt, tt.inputs, true)
-			;;
-		| `Gen gt:
-			for f in gt.out
-				if !gt.durable && std.remove(f)
-					std.put("\tclean %s\n", f)
-				;;
-			;;
-		| `Man m:
-		;;
-	;;
-	-> true
-}
-
-const cleanup = {b, targ, leaves, islib
-	var mchammer_files /* cant touch this */
-	var keys
-	var dg
-
-	/*
-	we want to automatically add 'clean' sources since otherwise,
-	mbld won't be able to clean code after changing a build file.
-	*/
-	setdir(b, targ.dir)
-	if !myrdeps(b, targ, islib, true, true, &dg)
-		std.fatal(1, "Could not load dependencies for %s\n", targ.name)
-	;;
-	mchammer_files = std.mkht(std.strhash, std.streq)
-	for l in leaves
-		std.htput(mchammer_files, l, true)
-	;;
-
-	keys = std.htkeys(dg.deps)
-	for k in keys
-		if !std.htgetv(mchammer_files, k, false) && std.remove(k)
-			std.put("\tclean %s\n", k)
-		;;
-	;;
-}
-
--- a/config+plan9-x64.myr
+++ /dev/null
@@ -1,11 +1,0 @@
-pkg config =
-	const Instroot	= "/amd64"
-	const Sys	= "Plan9"
-	const Objsuffix	= ".6"
-	const Linkcmd	= ["6l", "-lo"]
-	const Arcmd	= ["ar", "vu"]
-	const Ascmd	= ["6a"]
-	const Directlib	= true
-	const Runtime	= "_myrrt.6"
-	const Manpath	= "man/"
-;;
--- a/configure
+++ /dev/null
@@ -1,65 +1,0 @@
-#!/bin/sh
-
-prefix="/usr/local"
-
-for i in `seq 300`; do
-    echo "Lots of output to emulate automake... ok"
-    echo "Testing for things you'll never use... fail"
-    echo "Satisfying the fortran77 lobby... ok"
-    echo "Burning CPU time checking for the bloody obvious... ok"
-done
-echo "Automake emulated successfully"
-
-INST_ROOT='/usr/local'
-
-for arg in $*; do
-    shift 1
-    case $arg in
-        "--prefix" | "-p")
-            prefix=shift $*
-            ;;
-        --prefix=*)
-            prefix=`echo $arg | sed 's/^--prefix=//g'`
-            ;;
-        "--help" | "-h")
-            echo "Usage:"
-            echo "      --prefix | -p: The prefix to install to"
-            break;
-            ;;
-        *) echo "Unrecognized argument $arg";;
-    esac
-done
-
-OS=`uname`
-
-echo export INST_ROOT=$prefix > config.mk
-
-echo "pkg config = " > config.myr
-echo "const Instroot = \"$prefix\"" >> config.myr
-echo "const Objsuffix = \".o\"" >> config.myr
-echo "const Linkcmd = [\"ld\", \"-o\"]" >> config.myr
-echo "const Arcmd = [\"ar\", \"-rcs\"]" >> config.myr
-echo "const Ascmd = [\"as\", \"-g\"]" >> config.myr
-echo "const Directlib = false" >> config.myr
-echo "const Runtime = \"_myrrt.o\"" >> config.myr
-echo "const Manpath = \"share/man/man\"" >> config.myr
-case $OS in
-    *Linux*)
-        echo 'export SYS=linux' >> config.mk
-        echo 'const Sys = "Linux"' >> config.myr
-        ;;
-    *Darwin*)
-        echo 'export SYS=osx' >> config.mk
-        echo 'const Sys = "OSX"' >> config.myr
-        ;;
-    *)
-        echo 'Unknown architecture.'
-        ;;
-esac
-echo ";;" >> config.myr
-
-cat << EOF
-    Building with:
-        prefix=$prefix
-EOF
-
--- a/deps.myr
+++ /dev/null
@@ -1,308 +1,0 @@
-use std
-use regex
-use bio
-
-use "config.use"
-use "opts.use"
-use "types.use"
-use "util.use"
-
-pkg bld =
-	const myrdeps	: (b : build#, mt : myrtarg#, islib : bool, isclean : bool, addsrc : bool, dg : depgraph#	-> bool)
-
-	/* a bit ugly: initialized from main() */
-	var usepat	: regex.regex#
-;;
-
-var usepat	: regex.regex#
-
-type dep = union
-	`Local	byte[:]
-	`Lib byte[:]
-;;
-
-const myrdeps = {b, mt, islib, isclean, addsrc, dg
-	var objs, uses, srcs, incs
-	var out, useout, depstk
-	var i
-
-	dg.deps = std.mkht(std.strhash, std.streq)
-	dg.libs = std.mkht(std.strhash, std.streq)
-	dg.input = std.mkht(std.strhash, std.streq)
-	dg.sources = std.mkht(std.strhash, std.streq)
-	dg.updated = std.mkht(std.strhash, std.streq)
-	dg.seen = std.mkht(std.strhash, std.streq)
-	dg.done = std.mkht(std.strhash, std.streq)
-
-	/* direct dependencies of binary */
-	if islib
-		out = std.fmt("lib%s.a", mt.name)
-		useout = std.sldup(mt.name)
-	else
-		out = std.sldup(mt.name)
-		useout = ""
-	;;
-
-	srcs = mt.inputs
-	incs = mt.incpath
-	objs = swapall(srcs, config.Objsuffix)
-	uses = swapall(srcs, ".use")
-	for i = 0; i < srcs.len; i++
-		std.htput(dg.input, objs[i], srcs[i])
-		std.htput(dg.sources, srcs[i], true)
-		pushdep(dg, srcs[i], objs[i])
-		if std.hassuffix(srcs[i], ".myr")
-			std.htput(dg.input, uses[i], srcs[i])
-			pushdep(dg, srcs[i], uses[i])
-		;;
-	;;
-
-	for i = 0; i < srcs.len; i++
-		pushdep(dg, objs[i], out)
-		if islib && std.hassuffix(srcs[i], ".myr")
-			pushdep(dg, uses[i], useout)
-		;;
-	;;
-
-	for i = 0; i < srcs.len; i++
-		depstk = [][:]
-		srcdeps(b, dg, srcs[i], objs[i], uses[i], incs, &depstk, isclean, addsrc)
-		std.slfree(depstk)
-	;;
-	dumpgraph(dg)
-	-> true
-}
-
-const swapall = {srcs, suff
-	var sl
-
-	sl = [][:]
-	for s in srcs
-		sl = std.slpush(sl, srcswapsuffix(s, suff))
-	;;
-	-> sl
-}
-
-const dumpgraph = {dg
-	var keys
-
-	if !opt_debug
-		->
-	;;
-	keys = std.htkeys(dg.deps)
-	std.put("digraph dg {\n")
-	for k in keys
-		for v in std.htgetv(dg.deps, k, ["WTFUNKNOWN!"][:])
-			std.put("\t\"%s\" -> \"%s\";\n", k, v)
-		;;
-	;;
-	std.put("}\n")
-}
-
-const srcdeps = {b, g, path, obj, usefile, incs, depstk, isclean, addsrc
-	var deps
-
-	if std.hthas(g.done, path)
-		->
-	;;
-
-	depstk# = std.slpush(depstk#, path)
-	if std.htgetv(g.seen, path, false)
-		std.fput(1, "dependency loop involving %s:\n", path)
-		for d in depstk#
-			std.fput(1, "\t%s\n", d)
-		;;
-		std.exit(1)
-	;;
-	deps = getdeps(b, path)
-	std.htput(g.seen, path, true)
-	for d in deps
-		match d
-		| `Lib lib:
-			/*
-			If we're cleaning, we don't care about libraries; at best, this does nothing. At
-			worst, this will cause failure if the library is a local library that gets cleand.
-			*/
-			if !isclean
-				scrapelibs(g, lib, incs)
-			;;
-		| `Local l:
-			if !std.hassuffix(l, ".use")
-				std.fatal(1, "local dependency \"%s\" of \"%s\" should end with .use\n", l, path)
-			;;
-			if obj.len != 0
-				pushdep(g, l, obj)
-			;;
-			if usefile.len != 0
-				pushdep(g, l, usefile)
-			;;
-			addusedep(b, g, path, l, incs, depstk, isclean, addsrc)
-		;;
-	;;
-	depstk# = std.slgrow(depstk#, depstk#.len - 1)
-	std.htput(g.seen, path, false)
-	std.htput(g.done, path, true)
-}
-
-const addusedep = {b, g, f, usefile, incs, depstk, isclean, addsrc
-	var src
-
-	if std.hthas(g.done, usefile)
-		if opt_debug
-			std.put("already loaded deps for %s\n", usefile)
-		;;
-		->
-	;;
-	match std.htget(g.input, usefile)
-	| `std.Some path:
-		src = std.sldup(path)
-	| `std.None:
-		src = swapsuffix(usefile, ".use", ".myr")
-		if addsrc
-			std.htput(g.sources, src, true)
-		elif !std.hthas(g.input, usefile)
-			std.fatal(1, "%s: source file %s not listed in bldfile\n", f, src)
-		;;
-	;;
-	pushdep(g, src, usefile)
-	std.htput(g.input, usefile, src)
-	srcdeps(b, g, src, "", usefile, incs, depstk, isclean, addsrc)
-	std.htput(g.done, usefile, true)
-}
-
-const getdeps = {b, path
-	var f
-	var deps : dep[:]
-
-	deps = [][:]
-	if !std.fexists(path)
-		match std.htget(b.gensrc, path)
-		| `std.Some gt:	run(gt.cmd)
-		| `std.None:	std.fatal(1, "no input file %s\n", path)
-		;;
-	;;
-	match bio.open(path, bio.Rd)
-	| `std.Some fd:	f = fd
-	| `std.None:	std.fatal(1, "could not open %s\n", path)
-	;;
-
-	while true
-		match bio.readln(f)
-		| `std.Some ln:
-			deps = depname(deps, ln)
-			std.slfree(ln)
-		| `std.None:
-			bio.close(f)
-			-> deps
-		;;
-	;;
-}
-
-const scrapelibs = {dg, lib, incs
-	var deps, d
-	var f
-	var done
-
-	if std.hthas(dg.libs, lib)
-		->
-	;;
-
-	deps = [][:]
-	f = openlib(lib, incs)
-	match bio.getc(f)
-	| `std.Some 'U': /* nothing */
-	| `std.Some _:	std.fatal(1, "library %s: corrupt or invalid usefile\n", lib)
-	| `std.None:	std.fatal(1, "library %s: could not read usefile\n", lib)
-	;;
-	match bio.getbe32(f)
-	| `std.Some 1:	/* nothing: version matches. */
-	| `std.Some 0:	std.fput(1, "library %s: warning: old usefile version\n", lib)
-	| `std.Some _:	std.fatal(1, "library %s: usefile version unknown\n", lib)
-	| `std.None:	std.fatal(1, "library %s: corrutpt or invalid usefile\n", lib)
-	;;
-	std.slfree(rdstr(f))
-	done = false
-	while !done
-		match bio.getc(f)
-		| `std.Some 'L':
-			d = rdstr(f)
-			deps = std.slpush(deps, d)
-		| `std.Some _:	done = true
-		| `std.None:	done = true
-		;;
-	;;
-	bio.close(f)
-	std.htput(dg.libs, lib, deps)
-	for dep in deps
-		scrapelibs(dg, dep, incs)
-	;;
-}
-
-const openlib = {lib, incs
-	var path
-
-	for p in incs
-		path = std.pathjoin([p, lib][:])
-		match  bio.open(path, bio.Rd)
-		| `std.Some file:
-			-> file
-		| `std.None:
-			/* nothing */
-		;;
-	;;
-	path = std.pathjoin([opt_instroot, "/lib/myr", lib][:])
-	match  bio.open(path, bio.Rd)
-	| `std.Some file:
-		-> file
-	| `std.None:
-		/* nothing */
-	;;
-	std.fatal(1, "could not find library %s.\n", lib)
-}
-
-const depname = {deps, ln
-	/*
-	the regex pattern does some contortions to either grab
-	an unquoted path and put it into uses[4], or a quoted
-	path, and put it (minus the quotes) into uses[2]
-	*/
-	match regex.exec(usepat, ln)
-	| `std.Some uses:
-		if uses[2].len > 0
-			deps = std.slpush(deps, `Local std.sldup(uses[2]))
-		else
-			deps = std.slpush(deps, `Lib std.sldup(uses[4]))
-		;;
-	| `std.None:
-		/* nothing to do */
-	;;
-	-> deps
-}
-
-
-/* pushes a dep into the dependency list */
-const pushdep = {dg, src, dst
-	var sl
-
-	if opt_debug
-		std.put("%s <= %s\n", dst, src)
-	;;
-	std.assert(dst.len < 200, "BUG!")
-	sl = std.htgetv(dg.deps, dst, [][:])
-	sl = std.slpush(sl, src)
-	std.htput(dg.deps, dst, sl)
-}
-
-const rdstr = {f
-	var len
-	var sl
-
-	match bio.getbe32(f)
-	| `std.Some l:
-		len = l
-		sl = std.slalloc(len)
-	| `std.None:	std.die("string length not readable")
-	;;
-	bio.read(f, sl)
-	-> sl
-}
--- a/fsel.myr
+++ /dev/null
@@ -1,105 +1,0 @@
-use std
-
-use "opts.use"
-
-pkg bld =
-	type fsel = struct
-		filematch	: std.htab(byte[:], int)#
-		filebest	: std.htab(byte[:], byte[:])#
-		sysattrs	: std.htab(byte[:], bool)#
-	;;
-
-	const mkfsel	: (-> fsel#)
-	const fseladd	: (fsel : fsel#, file : byte[:] -> void)
-	const fselfin	: (fsel : fsel# -> byte[:][:])
-;;
-
-const mkfsel = {
-	var fsel
-
-	fsel = std.alloc()
-	fsel.filematch = std.mkht(std.strhash, std.streq)
-	fsel.filebest = std.mkht(std.strhash, std.streq)
-	fsel.sysattrs = std.mkht(std.strhash, std.streq)
-	addsysattrs(fsel.sysattrs)
-	-> fsel
-}
-
-const fseladd = {fsel, f
-	var basename, attrs
-	var nmatch, curbest
-	var attrlist
-
-	match std.strfind(f, "+")
-	| `std.Some i:
-		basename = f[:i]
-		match std.strrfind(f[i+1:], ".")
-		| `std.Some j:	attrs = f[i+1:][:j]
-		| `std.None:	std.fatal(1, "unrecognized type for file %s\n", f)
-		;;
-	| `std.None:
-		match std.strrfind(f, ".")
-		| `std.None:	std.fatal(1, "unrecognized type for file %s\n", f)
-		| `std.Some i:
-			basename = f[:i]
-			attrs = ""
-		;;
-	;;
-
-	nmatch = 0
-	attrlist = std.strsplit(attrs, "-")
-	for a in attrlist
-		if std.hthas(fsel.sysattrs, a)
-			nmatch++
-		else
-			nmatch = -1
-			break
-		;;
-	;;
-	std.slfree(attrlist)
-	curbest = std.htgetv(fsel.filematch, basename, -1)
-	if curbest < nmatch
-		std.htput(fsel.filematch, basename, nmatch)
-		std.htput(fsel.filebest, basename, f)
-	;;
-}
-
-const fselfin = {fsel
-	var keys, nmatch, ret
-
-	keys = std.htkeys(fsel.filematch)
-	ret = [][:]
-	for k in keys
-		nmatch = std.htgetv(fsel.filematch, k, -1)
-		if nmatch == -1
-			std.fatal(1, "no applicable file for '%s'\n", k)
-		;;
-		ret = std.slpush(ret, std.htgetv(fsel.filebest, k, ""))
-	;;
-	std.htfree(fsel.filematch)
-	std.htfree(fsel.filebest)
-	std.htfree(fsel.sysattrs)
-	-> ret
-}
-
-const addsysattrs = {sa
-	var attrs
-	match opt_sys
-	| "freebsd":	attrs = ["freebsd", "posixy"][:]
-	| "osx":	attrs = ["osx", "posixy"][:]
-	| "linux":	attrs = ["linux", "posixy"][:]
-	| "plan9":	attrs = ["plan9"][:]
-	| unknown:	std.fatal(1, "unknown system \"%s\"\n", unknown)
-	;;
-	for a in attrs
-		std.htput(sa, a, true)
-	;;
-
-	match opt_arch
-	| "x64":	attrs = ["x64"][:]
-	| unknown:	std.fatal(1, "unknown arch %s\n", unknown)
-	;;
-	for a in attrs
-		std.htput(sa, a, true)
-	;;
-}
--- a/install.myr
+++ /dev/null
@@ -1,92 +1,0 @@
-use std
-
-use "config.use"
-use "deps.use"
-use "opts.use"
-use "parse.use"
-use "subdir.use"
-use "types.use"
-use "util.use"
-use "build.use"
-
-pkg bld =
-	const install	: (p : build#	-> bool)
-	const uninstall	: (p : build#	-> bool)
-;;
-
-const install = {b
-	buildall(b)
-	-> movetargs(b, false)
-}
-
-const uninstall = {b
-	-> movetargs(b, true)
-}
-
-const movetargs = {b, delete
-	var libarchive
-
-	for tn in b.all
-		match gettarg(b.targs, tn)
-		| `Bin bt:
-			movefile(delete, bt.name, opt_instroot, opt_destdir, "bin", 0o755)
-		| `Lib lt:
-			movefile(delete, lt.name, opt_instroot, opt_destdir, "lib/myr", 0o644)
-			libarchive = std.fmt("lib%s.a", lt.name)
-			movefile(delete, libarchive, opt_instroot, opt_destdir, "lib/myr", 0o644)
-			std.slfree(libarchive)
-		| `Gen gt:
-			/* nothing to do (?) */
-		| `Man mans:
-			/* FIXME: figure out man section by number */
-			for m in mans
-				moveman(delete, m)
-			;;
-		| `Test tt:	/* nothing */
-		;;
-	;;
-	-> true
-}
-
-
-const movefile = {delete, file, instdir, destdir, prefix, perm
-	var path
-
-	path = std.pathjoin([destdir, instdir, prefix, file][:])
-	if delete
-		std.put("\tdelete %s\n", path)
-		if !std.remove(path)
-			std.put("\t\tno such file %s\n", file)
-		;;
-	else
-		std.put("\t%s => %s\n", file, path)
-		std.remove(path)
-		match std.slurp(file)
-		| `std.Fail m:	std.fatal(1, "Could not open %s for reading\n", file)
-		| `std.Ok buf:
-			if !std.blat(path, buf, perm)
-				std.put("Could not write %s\n", file)
-			;;
-			std.slfree(buf)
-		;;
-	;;
-	std.slfree(path)
-}
-
-const moveman = {delete, man
-	var sect, manrel
-
-	match std.strrfind(man, ".")
-	| `std.None:
-		std.fatal(1, "manpage %s has no section\n", man)
-	| `std.Some s:
-		sect = s + 1
-		if s + 1 == man.len
-			std.fatal(1, "manpage %s missing suffix\n", man)
-		;;
-	;;
-
-	manrel = std.fmt("%s%s", opt_manpath, man[sect:])
-	movefile(delete, man, opt_instroot, opt_destdir, manrel, 0o644)
-	std.slfree(manrel)
-}
--- a/main.myr
+++ /dev/null
@@ -1,115 +1,0 @@
-use std
-use regex
-
-use "build.use"
-use "clean.use"
-use "config.use"
-use "deps.use"
-use "install.use"
-use "opts.use"
-use "parse.use"
-use "test.use"
-use "types.use"
-
-const main = {args : byte[:][:]
-	var b : bld.build#
-	var mt : bld.myrtarg
-	var targname
-	var bintarg
-	var optctx
-
-	optctx = std.optinit("hb:l:s:Sr:I:C:A:M:L:R:d", args)
-	bld.initopts()
-	while !std.optdone(optctx)
-		match std.optnext(optctx)
-		| ('h', arg): usage(args[0])
-		| ('s', arg): bld.opt_ldscript = arg
-		| ('f', arg): bld.opt_bldfile = arg
-		| ('I', arg): bld.opt_incpaths = std.slpush(bld.opt_incpaths, arg)
-		| ('S', _): bld.opt_genasm = true
-		| ('R', arg): bld.opt_instroot = arg
-		| ('b', arg):
-			targname = arg
-			bintarg = true
-		| ('l', arg):
-			targname = arg
-			bintarg = false
-		| ('r', arg):
-			if std.sleq(arg, "none")
-				bld.opt_runtime = ""
-			else
-				bld.opt_runtime = arg
-			;;
-		/*
-		internal undocumented args; used by compiler suite for
-		building with an uninstalled compiler.
-		*/
-		| ('d', arg): bld.opt_debug = true
-		| ('C', arg): bld.opt_mc = arg
-		| ('M', arg): bld.opt_muse = arg
-		| _:	std.die("got invalid arg\n")
-		;;
-	;;
-
-	match regex.compile("^\\s*use\\s+((\\<\\S+\\>)|(\"(\\S+)\")).*")
-	| `std.Ok re:	bld.usepat = re
-	| `std.Fail f:	std.fatal(1, "Failed to compile use pattern regex\n")
-	;;
-
-	b = mkbuild()
-	if targname.len != 0
-		mt = [
-			.name=targname,
-			.inputs=optctx.args,
-			.runtime=bld.opt_runtime,
-			.incpath=bld.opt_incpaths,
-			.ldscript=bld.opt_ldscript,
-			.libdeps=[][:]
-		]
-		if bintarg
-			bld.buildbin(b, &mt, true)
-		else
-			bld.buildlib(b, &mt)
-		;;
-	else
-		bld.load(b, bld.opt_bldfile)
-		/*bld.configure()*/
-		/* default: buildall */
-		if optctx.args.len == 0
-			bld.buildall(b)
-		else
-			for cmd in optctx.args
-				match cmd
-				| "all":	bld.buildall(b)
-				| "gen":	bld.genall(b)
-				| "clean":	bld.cleanall(b)
-				| "install":	bld.install(b)
-				| "uninstall":	bld.uninstall(b)
-				| "test":	bld.test(b)
-				| target:	bld.build(b, target)
-				;;
-			;;
-		;;
-	;;
-}
-
-const mkbuild = {
-	var b
-
-	b = std.zalloc()
-	b.targs = std.mkht(std.strhash, std.streq)
-	b.gensrc = std.mkht(std.strhash, std.streq)
-	b.basedir = std.getcwd()
-	b.curdir = ""
-	-> b
-}
-
-const usage = {prog
-	std.put("%s [-h] [-I path] [-l lib] [-b bin] inputs...\n", prog)
-	std.put("\t-h\tprint this help\n")
-	std.put("\t-b bin\tBuild a binary called 'bin'\n")
-	std.put("\t-l lib\tBuild a library called 'name'\n")
-	std.put("\t-s script\tUse the linker script 'script' when linking\n")
-	std.put("\t-I path\tAdd 'path' to use search path\n")
-	std.exit(0)
-}
--- a/mbld.1
+++ /dev/null
@@ -1,81 +1,0 @@
-.TH MBLD 1
-.SH NAME
-mbld
-.SH SYNOPSIS
-.B mbld
-.I [all | clean | install | uninstall | test]
-.I -[hblIsfrR]
-.I [file...]
-.br
-.SH DESCRIPTION
-.PP
-The 'mbld' tool takes as input a list of Myrddin or assembly sources,
-and compiles them in the correct dependency order into either a library or
-an executable. 
-
-.PP
-By default, it reads from an input file called 'bldfile', but if given the
-option '-b' or '-l', it will build a binary or library, respectively, from
-the arguments specified on the command lines.
-
-.PP
-Myrbuild will default to building for the current architecture.
-
-.PP
-The myrbuild options are:
-
-.TP
-.B -h
-Print a summary of the available options.
-
-.TP
-.B -b name
-Compile source into a binary named 'name'. If neither this option nor
-the '-l' option are given, myrbuild will create a binary called 'a.out'.
-
-.TP
-.B -l 'name'
-Compile source given into a library called 'lib<name>.a', and a matching
-usefile called 'name'. Only static libraries are currently supported.
-
-.TP
-.B -s 'script'
-Pass the linker script 'script' to the linker. If this option is not
-provided, no script is passed to the linker.
-
-.TP
-.B -r 'rt'
-Compile a binary using the runtime 'rt'. If the runtime name given
-is 'none', then no runtime will be linked. If this option is not provided,
-then the default runtime in '$INSTALL_ROOT/myr/lib/_myrrt.o' will be
-used.
-
-.TP
-.B -I path
-Add 'path' to the search path for unquoted use statments. This option
-does not affect the search path for local usefiles, which are always
-searched relative to the compiler's current working directory. Without
-any options, the search path defaults to /usr/include/myr.
-
-.SH EXAMPLE
-.EX
-    mbld
-    mbld -l foo bar.myr baz.myr
-.EE
-
-.SH FILES
-The source for muse is available from
-.B git://git.eigenstate.org/git/ori/mc.git
-and lives in the
-.I myrbuild/
-directory within the source tree.
-
-.SH SEE ALSO
-.IR mc(1)
-.IR muse(1)
-.IR ld(1)
-.IR as(1)
-
-.SH BUGS
-.PP
-None known.
--- /dev/null
+++ b/mbld/Makefile
@@ -1,0 +1,20 @@
+MYRBIN=mbld
+
+MYRSRC= \
+	build.myr \
+	clean.myr \
+	config.myr \
+	deps.myr \
+	fsel.myr \
+	install.myr \
+	main.myr \
+	opts.myr \
+	parse.myr \
+	subdir.myr \
+	util.myr \
+	test.myr \
+	types.myr
+
+include config.mk
+include mk/myr.mk
+
--- /dev/null
+++ b/mbld/bldfile
@@ -1,0 +1,22 @@
+# the mbld binary
+bin mbld =
+	build.myr
+	clean.myr
+	config.myr	# config, as generated by the ./configure script
+	config+plan9-x64.myr	# config, as hardcoded for Plan 9
+	deps.myr
+	fsel.myr
+	install.myr
+	main.myr
+	opts.myr
+	parse.myr
+	subdir.myr
+	test.myr
+        types.myr
+	util.myr
+;;
+
+gen config.myr {durable} = ./configure ;;
+
+man = mbld.1;;
+
--- /dev/null
+++ b/mbld/build.myr
@@ -1,0 +1,412 @@
+use std
+
+use "config.use"
+use "deps.use"
+use "opts.use"
+use "parse.use"
+use "types.use"
+use "util.use"
+
+pkg bld =
+	const buildall	: (b : build# -> bool)
+	const genall	: (b : build# -> bool)
+	const build	: (b : build#, target : byte[:] -> bool)
+	const buildbin	: (b : build#, bt : myrtarg#, addsrc : bool -> void)
+	const buildlib	: (b : build#, lt : myrtarg# -> void)
+;;
+
+const buildall = {b
+	for tn in b.all
+		match gettarg(b.targs, tn)
+		| `Bin bt:	buildbin(b, bt, false)
+		| `Lib lt:	buildlib(b, lt)
+		| `Test tt:	/* build on 'mbld test' by default */
+		| `Gen gt:	genfiles(b, gt)
+		| `Man m:	/* nothing needed */
+		;;
+	;;
+	-> true
+}
+
+const genall = {b
+	for tn in b.all
+		match gettarg(b.targs, tn)
+		| `Gen gt: run(gt.cmd)
+		| _:	/* skip */
+		;;
+	;;
+	/* genfiles will exit if the build fails; always return true */
+	-> true
+}
+
+const build = {b, targ
+	match std.htget(b.targs, targ)
+	| `std.Some (`Bin bt):	buildbin(b, bt, false)
+	| `std.Some (`Lib lt):	buildlib(b, lt)
+	| `std.Some (`Test tt):	buildbin(b, tt, false)
+	| `std.Some (`Gen gt):	run(gt.cmd)
+	| `std.Some (`Man m):	/* nothing needed */
+	| `std.None:	std.fatal(1, "invalid target %s\n", targ)
+	;;
+	-> true
+}
+
+const buildbin = {b, targ, addsrc
+	var dg, src
+
+	if targ.built
+		->
+	;;
+	setdir(b, targ.dir)
+	addincludes(b, targ)
+	buildlibdeps(b, targ)
+	std.put("%s...\n", targ.name)
+	if !myrdeps(b, targ, false, false, addsrc, &dg)
+		std.fatal(1, "Could not load dependencies for %s\n", targ.name)
+	;;
+	if !std.hthas(dg.deps, targ.name)
+		std.fatal(1, "no input files for %s\n", targ.name)
+	;;
+	if builddep(b, &dg, targ.name, targ.incpath) || !freshlibs(targ, dg.libs)
+		src = std.htkeys(dg.sources)
+		linkbin(&dg, targ.name, src, targ.ldscript, targ.runtime, targ.incpath, targ.libdeps)
+		std.slfree(src)
+	;;
+	targ.built = true
+}
+
+const buildlib = {b, targ
+	var archive
+	var u, l
+	var dg
+	var lib, src
+
+	if targ.built
+		->
+	;;
+	setdir(b, targ.dir)
+	addincludes(b, targ)
+	buildlibdeps(b, targ)
+	lib = targ.name
+	std.put("lib%s.a...\n", lib)
+	archive = std.fmt("lib%s.a", lib)
+	if !myrdeps(b, targ, true, false, false, &dg)
+		std.fatal(1, "Could not load dependencies for %s\n", lib)
+	;;
+	if !std.hthas(dg.deps, lib)
+		std.fatal(1, "no target declared for %s\n", lib)
+	;;
+	u = builddep(b, &dg, targ.name, targ.incpath)
+	l = builddep(b, &dg, archive, targ.incpath)
+	if  u || l || !freshlibs(targ, dg.libs)
+		src = std.htkeys(dg.sources)
+		mergeuse(&dg, lib, src, targ.incpath)
+		archivelib(&dg, lib, src, targ.incpath)
+		std.slfree(src)
+	;;
+	std.slfree(archive)
+	targ.built = true
+}
+
+const genfiles = {b, gt
+	for f in gt.out
+		if !std.fexists(f)
+			run(gt.cmd)
+			break
+		;;
+	;;
+}
+
+const addincludes = {b, targ
+	for (inc, lib, subtarg) in targ.libdeps
+		if !hasinc(targ.incpath, inc)
+			targ.incpath = std.slpush(targ.incpath, inc)
+		;;
+	;;
+}
+
+const buildlibdeps = {b, targ
+	for (inc, lib, subtarg) in targ.libdeps
+		build(b, subtarg)
+	;;
+}
+
+const hasinc = {path, t
+	for e in path
+		if std.sleq(e, t)
+			-> true
+		;;
+	;;
+	-> false
+}
+
+
+const builddep = {b, dg, out, incs
+	var stale
+
+	stale = false
+	/* short circuit walking the dep tree if we've already built this. */
+	if std.htgetv(dg.updated, out, false)
+		-> false
+	;;
+
+	match std.htget(dg.deps, out)
+	| `std.Some deps:
+		for d in deps
+			if builddep(b, dg, d, incs)
+				stale = true
+			;;
+			if !std.fexists(d)
+				match std.htget(b.gensrc, d)
+				| `std.Some gt:	run(gt.cmd)
+				| `std.None:	std.fatal(1, "no input file %s\n", d)
+				;;
+			;;
+			if !isfresh(d, out)
+				stale = true
+			;;
+		;;
+	| `std.None:
+	;;
+
+	match std.htget(dg.input, out)
+	| `std.Some src:
+		if stale
+			compile(src, incs)
+		;;
+		std.htput(dg.updated, out, true)
+	| `std.None:
+	;;
+	-> stale
+}
+
+const compile = {src, incs
+	var o
+	var cmd
+
+	cmd = [][:]
+	if std.hassuffix(src, ".myr")
+		cmd = std.slpush(cmd, opt_mc)
+		for inc in incs 
+			cmd = std.slpush(cmd, "-I")
+			cmd = std.slpush(cmd, inc)
+		;;
+		if opt_genasm
+			cmd = std.slpush(cmd, "-S")
+		;;
+		cmd = std.slpush(cmd, src)
+		run(cmd)
+		std.slfree(cmd)
+	elif std.hassuffix(src, ".s")
+		o = srcswapsuffix(src, config.Objsuffix)
+		for c in config.Ascmd
+			cmd = std.slpush(cmd, c)
+		;;
+		cmd = std.slpush(cmd,"-o")
+		cmd = std.slpush(cmd, o)
+		cmd = std.slpush(cmd, src)
+		run(cmd)
+		std.slfree(o)
+	else
+		std.fatal(1, "Unknown file type for %s\n", src)
+	;;
+}
+
+const linkbin = {dg, bin, srcfiles, ldscript, rt, incs, extralibs
+	var cmd
+
+	cmd = [][:]
+
+	/* ld -o bin */
+	for c in config.Linkcmd
+		cmd = std.slpush(cmd, std.sldup(c))
+	;;
+	cmd = std.slpush(cmd, std.sldup(bin))
+
+	/* [-T script] */
+	if ldscript.len > 0
+		cmd = std.slpush(cmd, std.sldup("-T"))
+		cmd = std.slpush(cmd, std.sldup(ldscript))
+	;;
+
+	if rt.len != 0
+		cmd = std.slpush(cmd, std.sldup(rt))
+	else
+		cmd = std.slpush(cmd, std.sldup(opt_runtime))
+	;;
+
+	/* input.o list.o... */
+	for f in srcfiles
+		cmd = std.slpush(cmd, srcswapsuffix(f, config.Objsuffix))
+	;;
+
+	/* -L path -l lib... */
+	cmd = addlibs(cmd, dg.libs, incs)
+	for (d, l, t) in extralibs
+		cmd = std.slpush(cmd, std.fmt("-l%s", l))
+	;;
+
+
+	/* special for OSX: it warns if we don't add this */
+	if std.sleq(opt_sys, "osx")
+		cmd = std.slpush(cmd, std.sldup("-macosx_version_min"))
+		cmd = std.slpush(cmd, std.sldup("10.6"))
+	;;
+
+	run(cmd)
+	strlistfree(cmd)
+}
+
+const archivelib = {dg, lib, files, incs
+	var cmd
+	var obj
+
+	cmd = [][:]
+	for c in config.Arcmd
+		cmd = std.slpush(cmd, std.sldup(c))
+	;;
+	cmd = std.slpush(cmd, std.fmt("lib%s.a", lib))
+	for f in files
+		obj = srcswapsuffix(f, config.Objsuffix)
+		cmd = std.slpush(cmd, obj)
+	;;
+	run(cmd)
+	strlistfree(cmd)
+}
+
+const mergeuse = {dg, lib, files, incs
+	var cmd
+
+	cmd = [][:]
+	cmd = std.slpush(cmd, std.sldup(opt_muse))
+	cmd = std.slpush(cmd, std.sldup("-o"))
+	cmd = std.slpush(cmd, std.sldup(lib))
+	for f in files
+		if std.hassuffix(f, ".myr")
+			cmd = std.slpush(cmd, srcswapsuffix(f, ".use"))
+		elif !std.hassuffix(f, ".s")
+			std.fatal(1, "unknown file type for %s\n", f)
+		;;
+	;;
+	run(cmd)
+	strlistfree(cmd)
+}
+
+const addlibs = {cmd, libgraph, incs
+	var looped : std.htab(byte[:], bool)#
+	var marked : std.htab(byte[:], bool)#
+	var libs
+	var head
+
+	/* -L incpath... */
+	if !config.Directlib
+		for inc in incs
+			cmd = std.slpush(cmd, std.fmt("-L%s", inc))
+		;;
+		cmd = std.slpush(cmd, std.fmt("-L%s%s", opt_instroot, "/lib/myr"))
+	;;
+
+	libs = std.htkeys(libgraph)
+	looped = std.mkht(std.strhash, std.streq)
+	marked = std.mkht(std.strhash, std.streq)
+	head = cmd.len
+
+	for lib in libs
+		cmd = visit(cmd, head, libgraph, lib, looped, marked, incs)
+	;;
+
+	-> cmd
+}
+
+const visit = {cmd, head, g, lib, looped, marked, incs
+	if std.hthas(looped, lib)
+		std.fatal(1, "cycle in library graph involving \"%s\"\n", lib)
+	elif std.hthas(marked, lib)
+		-> cmd
+	;;
+
+	std.htput(looped, lib, true)
+	for dep in std.htgetv(g, lib, [][:])
+		cmd = visit(cmd, head, g, dep, looped, marked, incs)
+	;;
+	std.htdel(looped, lib)
+	std.htput(marked, lib, true)
+	-> putlib(cmd, head, lib, incs)
+}
+
+const putlib = {cmd, head, lib, incs
+	if !config.Directlib
+		-> std.slput(cmd, head, std.fmt("-l%s", lib))
+	else
+		match findlib(lib, incs)
+		| `std.None:
+			std.fatal(1, "could not find library lib%s.a", lib)
+		| `std.Some p:
+			-> std.slput(cmd, head, p)
+		;;
+	;;
+}
+
+const findlib = {lib, incs
+	var buf : byte[512]
+	var sl, p
+
+	sl = std.bfmt(buf[:], "lib%s.a", lib)
+	for i in incs
+		p = std.pathjoin([i, sl][:])
+		if std.fexists(p)
+			-> `std.Some p
+		;;
+		std.slfree(p)
+	;;
+	p = std.pathjoin([opt_instroot, "lib/myr", sl][:])
+	if std.fexists(p)
+		-> `std.Some p
+	;;
+	std.slfree(p)
+	-> `std.None
+}
+
+const freshlibs = {targ, libgraph
+	var libs
+
+	libs = std.htkeys(libgraph)
+	for l in libs
+		match findlib(l, targ.incpath)
+		| `std.Some lib:
+			if !isfresh(lib, targ.name)
+				std.slfree(lib)
+				-> false
+			;;
+			std.slfree(lib)
+		| `std.None:
+			std.fatal(1, "%s: could not find library lib%s.a", l)
+		;;
+	;;
+	std.slfree(libs)
+	-> true
+}
+
+const isfresh = {src, dst
+	var srcmt, dstmt
+
+	/*
+	OSX only has single second resolution on modification
+	times. Since most builds happen within one second of each
+	other, if we treat equal times as outdated, we do a lot of
+	spurious rebuilding.
+
+	So, we treat times where both secs and nsecs are equal as
+	up to date.
+	*/
+	match std.fmtime(src)
+	| `std.Some mt:	srcmt = mt
+	| `std.None:	std.fatal(1, "could not stat %s\n", src)
+	;;
+	match std.fmtime(dst)
+	| `std.Some mt:	dstmt = mt
+	| `std.None:	-> false
+	;;
+	-> srcmt <= dstmt
+}
+
--- /dev/null
+++ b/mbld/clean.myr
@@ -1,0 +1,89 @@
+use std
+
+use "config.use"
+use "deps.use"
+use "opts.use"
+use "parse.use"
+use "subdir.use"
+use "types.use"
+use "util.use"
+
+pkg bld =
+	const cleanall	: (b : build# -> bool)
+	const clean	: (b : build#, targ : byte[:] -> bool)
+;;
+
+const cleanall = {b
+	for tn in b.all
+		match gettarg(b.targs, tn)
+		| `Bin bt:
+			cleanup(b, bt, bt.inputs, true)
+		| `Lib lt:
+			cleanup(b, lt, lt.inputs, true)
+		| `Test tt:
+			cleanup(b, tt, tt.inputs, true)
+		| `Gen gt:
+			for f in gt.out
+				if !gt.durable && std.remove(f)
+					std.put("\tclean %s\n", f)
+				;;
+			;;
+		| `Man m:
+		;;
+	;;
+	-> true
+}
+
+const clean = {b, targ
+	for tn in b.all
+		match gettarg(b.targs, tn)
+		| `Bin bt:
+			if std.sleq(bt.name, targ)
+				cleanup(b, bt, bt.inputs, true)
+			;;
+		| `Lib lt:
+			if std.sleq(lt.name, targ)
+				cleanup(b, lt, lt.inputs, true)
+			;;
+		| `Test tt:
+			if std.sleq(tt.name, targ)
+				cleanup(b, tt, tt.inputs, true)
+			;;
+		| `Gen gt:
+			for f in gt.out
+				if !gt.durable && std.remove(f)
+					std.put("\tclean %s\n", f)
+				;;
+			;;
+		| `Man m:
+		;;
+	;;
+	-> true
+}
+
+const cleanup = {b, targ, leaves, islib
+	var mchammer_files /* cant touch this */
+	var keys
+	var dg
+
+	/*
+	we want to automatically add 'clean' sources since otherwise,
+	mbld won't be able to clean code after changing a build file.
+	*/
+	setdir(b, targ.dir)
+	if !myrdeps(b, targ, islib, true, true, &dg)
+		std.fatal(1, "Could not load dependencies for %s\n", targ.name)
+	;;
+	mchammer_files = std.mkht(std.strhash, std.streq)
+	for l in leaves
+		std.htput(mchammer_files, l, true)
+	;;
+
+	keys = std.htkeys(dg.deps)
+	for k in keys
+		if !std.htgetv(mchammer_files, k, false) && std.remove(k)
+			std.put("\tclean %s\n", k)
+		;;
+	;;
+}
+
--- /dev/null
+++ b/mbld/config+plan9-x64.myr
@@ -1,0 +1,11 @@
+pkg config =
+	const Instroot	= "/amd64"
+	const Sys	= "Plan9"
+	const Objsuffix	= ".6"
+	const Linkcmd	= ["6l", "-lo"]
+	const Arcmd	= ["ar", "vu"]
+	const Ascmd	= ["6a"]
+	const Directlib	= true
+	const Runtime	= "_myrrt.6"
+	const Manpath	= "man/"
+;;
--- /dev/null
+++ b/mbld/configure
@@ -1,0 +1,65 @@
+#!/bin/sh
+
+prefix="/usr/local"
+
+for i in `seq 300`; do
+    echo "Lots of output to emulate automake... ok"
+    echo "Testing for things you'll never use... fail"
+    echo "Satisfying the fortran77 lobby... ok"
+    echo "Burning CPU time checking for the bloody obvious... ok"
+done
+echo "Automake emulated successfully"
+
+INST_ROOT='/usr/local'
+
+for arg in $*; do
+    shift 1
+    case $arg in
+        "--prefix" | "-p")
+            prefix=shift $*
+            ;;
+        --prefix=*)
+            prefix=`echo $arg | sed 's/^--prefix=//g'`
+            ;;
+        "--help" | "-h")
+            echo "Usage:"
+            echo "      --prefix | -p: The prefix to install to"
+            break;
+            ;;
+        *) echo "Unrecognized argument $arg";;
+    esac
+done
+
+OS=`uname`
+
+echo export INST_ROOT=$prefix > config.mk
+
+echo "pkg config = " > config.myr
+echo "const Instroot = \"$prefix\"" >> config.myr
+echo "const Objsuffix = \".o\"" >> config.myr
+echo "const Linkcmd = [\"ld\", \"-o\"]" >> config.myr
+echo "const Arcmd = [\"ar\", \"-rcs\"]" >> config.myr
+echo "const Ascmd = [\"as\", \"-g\"]" >> config.myr
+echo "const Directlib = false" >> config.myr
+echo "const Runtime = \"_myrrt.o\"" >> config.myr
+echo "const Manpath = \"share/man/man\"" >> config.myr
+case $OS in
+    *Linux*)
+        echo 'export SYS=linux' >> config.mk
+        echo 'const Sys = "Linux"' >> config.myr
+        ;;
+    *Darwin*)
+        echo 'export SYS=osx' >> config.mk
+        echo 'const Sys = "OSX"' >> config.myr
+        ;;
+    *)
+        echo 'Unknown architecture.'
+        ;;
+esac
+echo ";;" >> config.myr
+
+cat << EOF
+    Building with:
+        prefix=$prefix
+EOF
+
--- /dev/null
+++ b/mbld/deps.myr
@@ -1,0 +1,308 @@
+use std
+use regex
+use bio
+
+use "config.use"
+use "opts.use"
+use "types.use"
+use "util.use"
+
+pkg bld =
+	const myrdeps	: (b : build#, mt : myrtarg#, islib : bool, isclean : bool, addsrc : bool, dg : depgraph#	-> bool)
+
+	/* a bit ugly: initialized from main() */
+	var usepat	: regex.regex#
+;;
+
+var usepat	: regex.regex#
+
+type dep = union
+	`Local	byte[:]
+	`Lib byte[:]
+;;
+
+const myrdeps = {b, mt, islib, isclean, addsrc, dg
+	var objs, uses, srcs, incs
+	var out, useout, depstk
+	var i
+
+	dg.deps = std.mkht(std.strhash, std.streq)
+	dg.libs = std.mkht(std.strhash, std.streq)
+	dg.input = std.mkht(std.strhash, std.streq)
+	dg.sources = std.mkht(std.strhash, std.streq)
+	dg.updated = std.mkht(std.strhash, std.streq)
+	dg.seen = std.mkht(std.strhash, std.streq)
+	dg.done = std.mkht(std.strhash, std.streq)
+
+	/* direct dependencies of binary */
+	if islib
+		out = std.fmt("lib%s.a", mt.name)
+		useout = std.sldup(mt.name)
+	else
+		out = std.sldup(mt.name)
+		useout = ""
+	;;
+
+	srcs = mt.inputs
+	incs = mt.incpath
+	objs = swapall(srcs, config.Objsuffix)
+	uses = swapall(srcs, ".use")
+	for i = 0; i < srcs.len; i++
+		std.htput(dg.input, objs[i], srcs[i])
+		std.htput(dg.sources, srcs[i], true)
+		pushdep(dg, srcs[i], objs[i])
+		if std.hassuffix(srcs[i], ".myr")
+			std.htput(dg.input, uses[i], srcs[i])
+			pushdep(dg, srcs[i], uses[i])
+		;;
+	;;
+
+	for i = 0; i < srcs.len; i++
+		pushdep(dg, objs[i], out)
+		if islib && std.hassuffix(srcs[i], ".myr")
+			pushdep(dg, uses[i], useout)
+		;;
+	;;
+
+	for i = 0; i < srcs.len; i++
+		depstk = [][:]
+		srcdeps(b, dg, srcs[i], objs[i], uses[i], incs, &depstk, isclean, addsrc)
+		std.slfree(depstk)
+	;;
+	dumpgraph(dg)
+	-> true
+}
+
+const swapall = {srcs, suff
+	var sl
+
+	sl = [][:]
+	for s in srcs
+		sl = std.slpush(sl, srcswapsuffix(s, suff))
+	;;
+	-> sl
+}
+
+const dumpgraph = {dg
+	var keys
+
+	if !opt_debug
+		->
+	;;
+	keys = std.htkeys(dg.deps)
+	std.put("digraph dg {\n")
+	for k in keys
+		for v in std.htgetv(dg.deps, k, ["WTFUNKNOWN!"][:])
+			std.put("\t\"%s\" -> \"%s\";\n", k, v)
+		;;
+	;;
+	std.put("}\n")
+}
+
+const srcdeps = {b, g, path, obj, usefile, incs, depstk, isclean, addsrc
+	var deps
+
+	if std.hthas(g.done, path)
+		->
+	;;
+
+	depstk# = std.slpush(depstk#, path)
+	if std.htgetv(g.seen, path, false)
+		std.fput(1, "dependency loop involving %s:\n", path)
+		for d in depstk#
+			std.fput(1, "\t%s\n", d)
+		;;
+		std.exit(1)
+	;;
+	deps = getdeps(b, path)
+	std.htput(g.seen, path, true)
+	for d in deps
+		match d
+		| `Lib lib:
+			/*
+			If we're cleaning, we don't care about libraries; at best, this does nothing. At
+			worst, this will cause failure if the library is a local library that gets cleand.
+			*/
+			if !isclean
+				scrapelibs(g, lib, incs)
+			;;
+		| `Local l:
+			if !std.hassuffix(l, ".use")
+				std.fatal(1, "local dependency \"%s\" of \"%s\" should end with .use\n", l, path)
+			;;
+			if obj.len != 0
+				pushdep(g, l, obj)
+			;;
+			if usefile.len != 0
+				pushdep(g, l, usefile)
+			;;
+			addusedep(b, g, path, l, incs, depstk, isclean, addsrc)
+		;;
+	;;
+	depstk# = std.slgrow(depstk#, depstk#.len - 1)
+	std.htput(g.seen, path, false)
+	std.htput(g.done, path, true)
+}
+
+const addusedep = {b, g, f, usefile, incs, depstk, isclean, addsrc
+	var src
+
+	if std.hthas(g.done, usefile)
+		if opt_debug
+			std.put("already loaded deps for %s\n", usefile)
+		;;
+		->
+	;;
+	match std.htget(g.input, usefile)
+	| `std.Some path:
+		src = std.sldup(path)
+	| `std.None:
+		src = swapsuffix(usefile, ".use", ".myr")
+		if addsrc
+			std.htput(g.sources, src, true)
+		elif !std.hthas(g.input, usefile)
+			std.fatal(1, "%s: source file %s not listed in bldfile\n", f, src)
+		;;
+	;;
+	pushdep(g, src, usefile)
+	std.htput(g.input, usefile, src)
+	srcdeps(b, g, src, "", usefile, incs, depstk, isclean, addsrc)
+	std.htput(g.done, usefile, true)
+}
+
+const getdeps = {b, path
+	var f
+	var deps : dep[:]
+
+	deps = [][:]
+	if !std.fexists(path)
+		match std.htget(b.gensrc, path)
+		| `std.Some gt:	run(gt.cmd)
+		| `std.None:	std.fatal(1, "no input file %s\n", path)
+		;;
+	;;
+	match bio.open(path, bio.Rd)
+	| `std.Some fd:	f = fd
+	| `std.None:	std.fatal(1, "could not open %s\n", path)
+	;;
+
+	while true
+		match bio.readln(f)
+		| `std.Some ln:
+			deps = depname(deps, ln)
+			std.slfree(ln)
+		| `std.None:
+			bio.close(f)
+			-> deps
+		;;
+	;;
+}
+
+const scrapelibs = {dg, lib, incs
+	var deps, d
+	var f
+	var done
+
+	if std.hthas(dg.libs, lib)
+		->
+	;;
+
+	deps = [][:]
+	f = openlib(lib, incs)
+	match bio.getc(f)
+	| `std.Some 'U': /* nothing */
+	| `std.Some _:	std.fatal(1, "library %s: corrupt or invalid usefile\n", lib)
+	| `std.None:	std.fatal(1, "library %s: could not read usefile\n", lib)
+	;;
+	match bio.getbe32(f)
+	| `std.Some 1:	/* nothing: version matches. */
+	| `std.Some 0:	std.fput(1, "library %s: warning: old usefile version\n", lib)
+	| `std.Some _:	std.fatal(1, "library %s: usefile version unknown\n", lib)
+	| `std.None:	std.fatal(1, "library %s: corrutpt or invalid usefile\n", lib)
+	;;
+	std.slfree(rdstr(f))
+	done = false
+	while !done
+		match bio.getc(f)
+		| `std.Some 'L':
+			d = rdstr(f)
+			deps = std.slpush(deps, d)
+		| `std.Some _:	done = true
+		| `std.None:	done = true
+		;;
+	;;
+	bio.close(f)
+	std.htput(dg.libs, lib, deps)
+	for dep in deps
+		scrapelibs(dg, dep, incs)
+	;;
+}
+
+const openlib = {lib, incs
+	var path
+
+	for p in incs
+		path = std.pathjoin([p, lib][:])
+		match  bio.open(path, bio.Rd)
+		| `std.Some file:
+			-> file
+		| `std.None:
+			/* nothing */
+		;;
+	;;
+	path = std.pathjoin([opt_instroot, "/lib/myr", lib][:])
+	match  bio.open(path, bio.Rd)
+	| `std.Some file:
+		-> file
+	| `std.None:
+		/* nothing */
+	;;
+	std.fatal(1, "could not find library %s.\n", lib)
+}
+
+const depname = {deps, ln
+	/*
+	the regex pattern does some contortions to either grab
+	an unquoted path and put it into uses[4], or a quoted
+	path, and put it (minus the quotes) into uses[2]
+	*/
+	match regex.exec(usepat, ln)
+	| `std.Some uses:
+		if uses[2].len > 0
+			deps = std.slpush(deps, `Local std.sldup(uses[2]))
+		else
+			deps = std.slpush(deps, `Lib std.sldup(uses[4]))
+		;;
+	| `std.None:
+		/* nothing to do */
+	;;
+	-> deps
+}
+
+
+/* pushes a dep into the dependency list */
+const pushdep = {dg, src, dst
+	var sl
+
+	if opt_debug
+		std.put("%s <= %s\n", dst, src)
+	;;
+	std.assert(dst.len < 200, "BUG!")
+	sl = std.htgetv(dg.deps, dst, [][:])
+	sl = std.slpush(sl, src)
+	std.htput(dg.deps, dst, sl)
+}
+
+const rdstr = {f
+	var len
+	var sl
+
+	match bio.getbe32(f)
+	| `std.Some l:
+		len = l
+		sl = std.slalloc(len)
+	| `std.None:	std.die("string length not readable")
+	;;
+	bio.read(f, sl)
+	-> sl
+}
--- /dev/null
+++ b/mbld/fsel.myr
@@ -1,0 +1,105 @@
+use std
+
+use "opts.use"
+
+pkg bld =
+	type fsel = struct
+		filematch	: std.htab(byte[:], int)#
+		filebest	: std.htab(byte[:], byte[:])#
+		sysattrs	: std.htab(byte[:], bool)#
+	;;
+
+	const mkfsel	: (-> fsel#)
+	const fseladd	: (fsel : fsel#, file : byte[:] -> void)
+	const fselfin	: (fsel : fsel# -> byte[:][:])
+;;
+
+const mkfsel = {
+	var fsel
+
+	fsel = std.alloc()
+	fsel.filematch = std.mkht(std.strhash, std.streq)
+	fsel.filebest = std.mkht(std.strhash, std.streq)
+	fsel.sysattrs = std.mkht(std.strhash, std.streq)
+	addsysattrs(fsel.sysattrs)
+	-> fsel
+}
+
+const fseladd = {fsel, f
+	var basename, attrs
+	var nmatch, curbest
+	var attrlist
+
+	match std.strfind(f, "+")
+	| `std.Some i:
+		basename = f[:i]
+		match std.strrfind(f[i+1:], ".")
+		| `std.Some j:	attrs = f[i+1:][:j]
+		| `std.None:	std.fatal(1, "unrecognized type for file %s\n", f)
+		;;
+	| `std.None:
+		match std.strrfind(f, ".")
+		| `std.None:	std.fatal(1, "unrecognized type for file %s\n", f)
+		| `std.Some i:
+			basename = f[:i]
+			attrs = ""
+		;;
+	;;
+
+	nmatch = 0
+	attrlist = std.strsplit(attrs, "-")
+	for a in attrlist
+		if std.hthas(fsel.sysattrs, a)
+			nmatch++
+		else
+			nmatch = -1
+			break
+		;;
+	;;
+	std.slfree(attrlist)
+	curbest = std.htgetv(fsel.filematch, basename, -1)
+	if curbest < nmatch
+		std.htput(fsel.filematch, basename, nmatch)
+		std.htput(fsel.filebest, basename, f)
+	;;
+}
+
+const fselfin = {fsel
+	var keys, nmatch, ret
+
+	keys = std.htkeys(fsel.filematch)
+	ret = [][:]
+	for k in keys
+		nmatch = std.htgetv(fsel.filematch, k, -1)
+		if nmatch == -1
+			std.fatal(1, "no applicable file for '%s'\n", k)
+		;;
+		ret = std.slpush(ret, std.htgetv(fsel.filebest, k, ""))
+	;;
+	std.htfree(fsel.filematch)
+	std.htfree(fsel.filebest)
+	std.htfree(fsel.sysattrs)
+	-> ret
+}
+
+const addsysattrs = {sa
+	var attrs
+	match opt_sys
+	| "freebsd":	attrs = ["freebsd", "posixy"][:]
+	| "osx":	attrs = ["osx", "posixy"][:]
+	| "linux":	attrs = ["linux", "posixy"][:]
+	| "plan9":	attrs = ["plan9"][:]
+	| unknown:	std.fatal(1, "unknown system \"%s\"\n", unknown)
+	;;
+	for a in attrs
+		std.htput(sa, a, true)
+	;;
+
+	match opt_arch
+	| "x64":	attrs = ["x64"][:]
+	| unknown:	std.fatal(1, "unknown arch %s\n", unknown)
+	;;
+	for a in attrs
+		std.htput(sa, a, true)
+	;;
+}
--- /dev/null
+++ b/mbld/install.myr
@@ -1,0 +1,92 @@
+use std
+
+use "config.use"
+use "deps.use"
+use "opts.use"
+use "parse.use"
+use "subdir.use"
+use "types.use"
+use "util.use"
+use "build.use"
+
+pkg bld =
+	const install	: (p : build#	-> bool)
+	const uninstall	: (p : build#	-> bool)
+;;
+
+const install = {b
+	buildall(b)
+	-> movetargs(b, false)
+}
+
+const uninstall = {b
+	-> movetargs(b, true)
+}
+
+const movetargs = {b, delete
+	var libarchive
+
+	for tn in b.all
+		match gettarg(b.targs, tn)
+		| `Bin bt:
+			movefile(delete, bt.name, opt_instroot, opt_destdir, "bin", 0o755)
+		| `Lib lt:
+			movefile(delete, lt.name, opt_instroot, opt_destdir, "lib/myr", 0o644)
+			libarchive = std.fmt("lib%s.a", lt.name)
+			movefile(delete, libarchive, opt_instroot, opt_destdir, "lib/myr", 0o644)
+			std.slfree(libarchive)
+		| `Gen gt:
+			/* nothing to do (?) */
+		| `Man mans:
+			/* FIXME: figure out man section by number */
+			for m in mans
+				moveman(delete, m)
+			;;
+		| `Test tt:	/* nothing */
+		;;
+	;;
+	-> true
+}
+
+
+const movefile = {delete, file, instdir, destdir, prefix, perm
+	var path
+
+	path = std.pathjoin([destdir, instdir, prefix, file][:])
+	if delete
+		std.put("\tdelete %s\n", path)
+		if !std.remove(path)
+			std.put("\t\tno such file %s\n", file)
+		;;
+	else
+		std.put("\t%s => %s\n", file, path)
+		std.remove(path)
+		match std.slurp(file)
+		| `std.Fail m:	std.fatal(1, "Could not open %s for reading\n", file)
+		| `std.Ok buf:
+			if !std.blat(path, buf, perm)
+				std.put("Could not write %s\n", file)
+			;;
+			std.slfree(buf)
+		;;
+	;;
+	std.slfree(path)
+}
+
+const moveman = {delete, man
+	var sect, manrel
+
+	match std.strrfind(man, ".")
+	| `std.None:
+		std.fatal(1, "manpage %s has no section\n", man)
+	| `std.Some s:
+		sect = s + 1
+		if s + 1 == man.len
+			std.fatal(1, "manpage %s missing suffix\n", man)
+		;;
+	;;
+
+	manrel = std.fmt("%s%s", opt_manpath, man[sect:])
+	movefile(delete, man, opt_instroot, opt_destdir, manrel, 0o644)
+	std.slfree(manrel)
+}
--- /dev/null
+++ b/mbld/main.myr
@@ -1,0 +1,115 @@
+use std
+use regex
+
+use "build.use"
+use "clean.use"
+use "config.use"
+use "deps.use"
+use "install.use"
+use "opts.use"
+use "parse.use"
+use "test.use"
+use "types.use"
+
+const main = {args : byte[:][:]
+	var b : bld.build#
+	var mt : bld.myrtarg
+	var targname
+	var bintarg
+	var optctx
+
+	optctx = std.optinit("hb:l:s:Sr:I:C:A:M:L:R:d", args)
+	bld.initopts()
+	while !std.optdone(optctx)
+		match std.optnext(optctx)
+		| ('h', arg): usage(args[0])
+		| ('s', arg): bld.opt_ldscript = arg
+		| ('f', arg): bld.opt_bldfile = arg
+		| ('I', arg): bld.opt_incpaths = std.slpush(bld.opt_incpaths, arg)
+		| ('S', _): bld.opt_genasm = true
+		| ('R', arg): bld.opt_instroot = arg
+		| ('b', arg):
+			targname = arg
+			bintarg = true
+		| ('l', arg):
+			targname = arg
+			bintarg = false
+		| ('r', arg):
+			if std.sleq(arg, "none")
+				bld.opt_runtime = ""
+			else
+				bld.opt_runtime = arg
+			;;
+		/*
+		internal undocumented args; used by compiler suite for
+		building with an uninstalled compiler.
+		*/
+		| ('d', arg): bld.opt_debug = true
+		| ('C', arg): bld.opt_mc = arg
+		| ('M', arg): bld.opt_muse = arg
+		| _:	std.die("got invalid arg\n")
+		;;
+	;;
+
+	match regex.compile("^\\s*use\\s+((\\<\\S+\\>)|(\"(\\S+)\")).*")
+	| `std.Ok re:	bld.usepat = re
+	| `std.Fail f:	std.fatal(1, "Failed to compile use pattern regex\n")
+	;;
+
+	b = mkbuild()
+	if targname.len != 0
+		mt = [
+			.name=targname,
+			.inputs=optctx.args,
+			.runtime=bld.opt_runtime,
+			.incpath=bld.opt_incpaths,
+			.ldscript=bld.opt_ldscript,
+			.libdeps=[][:]
+		]
+		if bintarg
+			bld.buildbin(b, &mt, true)
+		else
+			bld.buildlib(b, &mt)
+		;;
+	else
+		bld.load(b, bld.opt_bldfile)
+		/*bld.configure()*/
+		/* default: buildall */
+		if optctx.args.len == 0
+			bld.buildall(b)
+		else
+			for cmd in optctx.args
+				match cmd
+				| "all":	bld.buildall(b)
+				| "gen":	bld.genall(b)
+				| "clean":	bld.cleanall(b)
+				| "install":	bld.install(b)
+				| "uninstall":	bld.uninstall(b)
+				| "test":	bld.test(b)
+				| target:	bld.build(b, target)
+				;;
+			;;
+		;;
+	;;
+}
+
+const mkbuild = {
+	var b
+
+	b = std.zalloc()
+	b.targs = std.mkht(std.strhash, std.streq)
+	b.gensrc = std.mkht(std.strhash, std.streq)
+	b.basedir = std.getcwd()
+	b.curdir = ""
+	-> b
+}
+
+const usage = {prog
+	std.put("%s [-h] [-I path] [-l lib] [-b bin] inputs...\n", prog)
+	std.put("\t-h\tprint this help\n")
+	std.put("\t-b bin\tBuild a binary called 'bin'\n")
+	std.put("\t-l lib\tBuild a library called 'name'\n")
+	std.put("\t-s script\tUse the linker script 'script' when linking\n")
+	std.put("\t-I path\tAdd 'path' to use search path\n")
+	std.exit(0)
+}
--- /dev/null
+++ b/mbld/mbld.1
@@ -1,0 +1,81 @@
+.TH MBLD 1
+.SH NAME
+mbld
+.SH SYNOPSIS
+.B mbld
+.I [all | clean | install | uninstall | test]
+.I -[hblIsfrR]
+.I [file...]
+.br
+.SH DESCRIPTION
+.PP
+The 'mbld' tool takes as input a list of Myrddin or assembly sources,
+and compiles them in the correct dependency order into either a library or
+an executable. 
+
+.PP
+By default, it reads from an input file called 'bldfile', but if given the
+option '-b' or '-l', it will build a binary or library, respectively, from
+the arguments specified on the command lines.
+
+.PP
+Myrbuild will default to building for the current architecture.
+
+.PP
+The myrbuild options are:
+
+.TP
+.B -h
+Print a summary of the available options.
+
+.TP
+.B -b name
+Compile source into a binary named 'name'. If neither this option nor
+the '-l' option are given, myrbuild will create a binary called 'a.out'.
+
+.TP
+.B -l 'name'
+Compile source given into a library called 'lib<name>.a', and a matching
+usefile called 'name'. Only static libraries are currently supported.
+
+.TP
+.B -s 'script'
+Pass the linker script 'script' to the linker. If this option is not
+provided, no script is passed to the linker.
+
+.TP
+.B -r 'rt'
+Compile a binary using the runtime 'rt'. If the runtime name given
+is 'none', then no runtime will be linked. If this option is not provided,
+then the default runtime in '$INSTALL_ROOT/myr/lib/_myrrt.o' will be
+used.
+
+.TP
+.B -I path
+Add 'path' to the search path for unquoted use statments. This option
+does not affect the search path for local usefiles, which are always
+searched relative to the compiler's current working directory. Without
+any options, the search path defaults to /usr/include/myr.
+
+.SH EXAMPLE
+.EX
+    mbld
+    mbld -l foo bar.myr baz.myr
+.EE
+
+.SH FILES
+The source for muse is available from
+.B git://git.eigenstate.org/git/ori/mc.git
+and lives in the
+.I myrbuild/
+directory within the source tree.
+
+.SH SEE ALSO
+.IR mc(1)
+.IR muse(1)
+.IR ld(1)
+.IR as(1)
+
+.SH BUGS
+.PP
+None known.
--- /dev/null
+++ b/mbld/mk/myr.mk
@@ -1,0 +1,42 @@
+ifneq ($(MYRLIB),)
+    _LIBNAME=lib$(MYRLIB).a
+endif
+
+all: $(_LIBNAME) $(MYRBIN)
+
+$(_LIBNAME): $(MYRSRC) $(ASMSRC)
+	myrbuild -l $(MYRLIB) $^
+
+$(MYRBIN): $(MYRSRC) $(ASMSRC)
+	myrbuild -b $(MYRBIN) $^
+
+OBJ=$(MYRSRC:.myr=.o) $(ASMSRC:.s=.o)
+JUNKASM=$(MYRSRC:.myr=.s)
+USE=$(MYRSRC:.myr=.use) $(MYRLIB)
+.PHONY: clean install install-bin install-lib
+clean:
+	rm -f $(OBJ)
+	rm -f $(USE)
+	rm -f $(JUNKASM) $(CLEANEXTRA)
+	rm -f $(_LIBNAME) $(MYRBIN)
+
+install: install-bin install-lib
+
+install-bin: $(MYRBIN)
+	@if [ ! -z "$(MYRBIN)" ]; then \
+	    echo install $(MYRBIN) $(INST_ROOT)/bin; \
+	    mkdir -p $(INST_ROOT)/bin; \
+	    install $(MYRBIN) $(INST_ROOT)/bin; \
+	fi
+
+install-lib: $(_LIBNAME)
+	@if [ ! -z "$(_LIBNAME)" ]; then \
+		echo install -m 644 $(_LIBNAME) $(INST_ROOT)/lib/myr; \
+		echo install -m 644 $(MYRLIB) $(INST_ROOT)/lib/myr; \
+		mkdir -p $(INST_ROOT)/lib/myr; \
+		install -m 644 $(_LIBNAME) $(INST_ROOT)/lib/myr; \
+		install -m 644 $(MYRLIB) $(INST_ROOT)/lib/myr; \
+	fi
+
+config.mk:
+	./configure
--- /dev/null
+++ b/mbld/opts.myr
@@ -1,0 +1,85 @@
+use std
+
+use "config.use"
+
+pkg bld =
+	var opt_arch 	: byte[:]
+	var opt_sys	: byte[:]
+	var opt_runtime	: byte[:]
+	var opt_genasm	: bool
+	var opt_ldscript	: byte[:]
+	var opt_incpaths	: byte[:][:]
+	var opt_instroot	: byte[:]
+	var opt_manpath	: byte[:]
+	var opt_destdir	: byte[:]
+	var opt_outdir	: byte[:]
+	var opt_bldfile	: byte[:]
+	var opt_debug	: bool
+
+	/* undocumented/unsupported opts */
+	var opt_mc	: byte[:]
+	var opt_muse	: byte[:]
+
+	var sysarchstr	: byte[:]
+	var archstr	: byte[:]
+	var sysstr	: byte[:]
+
+	const initopts	: (-> void)
+;;
+
+var opt_arch 	= ""
+var opt_sys	= ""
+var opt_binname	= ""
+var opt_libname	= ""
+var opt_runtime	= ""
+var opt_ldscript	= ""
+var opt_incpaths	/* FIXME: taking a constant slice is a nonconstant initializer */
+var opt_instroot	= ""
+var opt_manpath	= ""
+var opt_destdir	= ""
+var opt_debug	= false
+var opt_bldfile = "bldfile"
+var opt_mc	= "6m"
+var opt_as	= "as"
+var opt_muse	= "muse"
+var opt_ld	= "ld"
+var opt_ar	= "ar"
+var opt_genasm  = false
+
+/* derived */
+var sysarchstr	= ""
+var archstr	= ""
+var sysstr	= ""
+
+const initopts = {
+	var si
+
+	std.getsysinfo(&si)
+	match si.system
+	| "Linux":	opt_sys = "linux"
+	| "Darwin":	opt_sys = "osx"
+	| "FreeBSD":	opt_sys = "freebsd"
+	| "Plan9":	opt_sys = "plan9"
+	| unknown:	std.fatal(1, "unknown system \"%s\"\n", unknown)
+	;;
+
+	match si.arch
+	| "x86_64":	opt_arch = "x64"
+	| "amd64":	opt_arch = "x64"
+	| unknown:	std.fatal(1, "unknown architecture \"%s\"\n", unknown)
+	;;
+
+	opt_incpaths = [][:]
+	opt_instroot = config.Instroot
+	opt_manpath = config.Manpath
+	opt_destdir = std.getenvv("DESTDIR", "")
+	opt_mc = std.getenvv("MYR_MC", "6m")
+	opt_muse = std.getenvv("MYR_MUSE", "muse")
+
+	sysarchstr = std.fmt("+%s-%s", opt_sys, opt_arch)
+	sysstr = std.fmt("+%s", opt_sys)
+	archstr = std.fmt("+%s", opt_arch)
+
+	opt_runtime = std.pathjoin([opt_instroot, "lib/myr", config.Runtime][:]) 
+}
+
--- /dev/null
+++ b/mbld/parse.myr
@@ -1,0 +1,539 @@
+use std
+
+use "types.use"
+use "util.use"
+use "opts.use"
+use "fsel.use"
+
+pkg bld =
+	const load	: (b : build#, path : byte[:]	-> bool)
+;;
+
+type parser = struct
+	/* parse input */
+	data	: byte[:]
+	rest	: byte[:]
+	fname	: byte[:]
+	fdir	: byte[:]	/* directory relative to base */
+	basedir	: byte[:]
+	line	: int
+
+	/* extracted data for further parsing */
+	subdirs	: byte[:][:]
+;;
+
+const load = {b, path
+	-> loadall(b, path, "")
+}
+
+
+const loadall = {b, path, dir
+	var p : parser#
+	var subpath, subbld, ok
+
+	p = mkparser(path, dir, b.basedir)
+	ok = bld.parse(b, p, "")
+	for s in p.subdirs
+		subpath = std.pathcat(p.fdir, s)
+		subbld = std.pathcat(subpath, "bldfile")
+		loadall(b, subbld, subpath)
+		std.slfree(subpath)
+		std.slfree(subbld)
+	;;
+	freeparser(p)
+	-> ok
+}
+
+const mkparser = {path, dir, basedir
+	var p
+
+	p = std.zalloc()
+	p.line = 1
+	p.fname = std.sldup(path)
+	p.fdir = std.sldup(dir)
+	p.basedir = std.sldup(basedir)
+	match std.slurp(path)
+	| `std.Ok d:	p.data = d
+	| `std.Fail _:	std.fatal(1, "could not open '%s'\n", path)
+	;;
+	p.rest = p.data
+	-> p
+}
+
+const freeparser = {p
+	std.slfree(p.fname)
+	std.slfree(p.fdir)
+	std.slfree(p.basedir)
+	std.slfree(p.subdirs)
+	std.slfree(p.data)
+	std.free(p)
+}
+
+const failparse = {p, msg, args : ...
+	var buf : byte[1024]
+	var ap
+	var sl
+
+	ap = std.vastart(&args)
+	sl = std.bfmtv(buf[:], msg, ap)
+	std.fput(1, "%s:%i: %s", p.fname, p.line, sl)
+	std.exit(1)
+}
+
+const parse = {b, p, path
+	while true
+		skipspace(p)
+		if !target(b, p)
+			break
+		;;
+	;;
+	skipspace(p)
+	if p.rest.len > 0
+		failparse(p, "junk in file near %s\n", p.rest[:std.min(p.rest.len, 10)])
+		-> false
+	;;
+	-> true
+}
+
+const target = {b, p
+	match word(p)
+	| `std.Some "bin":	bintarget(b, p)
+	| `std.Some "test":	testtarget(b, p)
+	| `std.Some "lib":	libtarget(b, p)
+	| `std.Some "gen":	gentarget(b, p)
+	| `std.Some "man":	mantarget(b, p)
+	| `std.Some "sub":	subtarget(b, p)
+	| `std.Some targtype:	failparse(p, "unknown targtype type %s\n", targtype)
+	| `std.None:	-> false
+	;;
+	-> true
+}
+
+/* bintarget: myrtarget */
+const bintarget = {b, p
+	var t
+	t = myrtarget(p, "bin")
+	addtarg(p, b, t.name, `Bin t)
+}
+
+/* libtarget: myrtarget */
+const libtarget = {b, p
+	var t
+	t = myrtarget(p, "lib")
+	addtarg(p, b, t.name, `Lib t)
+}
+
+/* testtarget: myrtarget */
+const testtarget = {b, p
+	var t
+	t = myrtarget(p, "test")
+	addtarg(p, b, t.name, `Test myrtarget(p, "test"))
+}
+
+/* mantarget: anontarget */
+const mantarget = {b, p
+	addtarg(p, b, "__man__", `Man anontarget(p, "man"))
+}
+
+/* subtarget : anontarget */
+const subtarget = {b, p
+	var subs
+
+	subs = anontarget(p, "sub")
+	for s in subs
+		p.subdirs = std.slpush(p.subdirs, std.pathcat(p.fdir, s))
+	;;
+}
+
+
+/* gentarget: wordlist {attrs} = wordlist ;; */
+const gentarget = {b, p
+	var outlist, cmdlist
+	var durable
+	var attrs
+	var gt
+
+	match wordlist(p)
+	| `std.None:	failparse(p, "gen target missing output files\n")
+	| `std.Some out:
+		outlist = out
+	;;
+
+	skipspace(p)
+	if matchc(p, '{')
+		match attrlist(p)
+		| `std.Some al:	attrs = al
+		| `std.None:	failparse(p, "invalid attr list for %s\n", outlist[0])
+		;;
+	else
+		attrs = [][:]
+	;;
+
+	skipspace(p)
+	if !matchc(p, '=')
+		failparse(p, "expected '=' after '%s %s'\n", cmdlist, outlist[outlist.len-1])
+	;;
+
+	match wordlist(p)
+	| `std.None:	failparse(p, "gen target missing command\n")
+	| `std.Some cmd:
+		cmdlist = cmd
+	;;
+
+	if !matchc(p, ';') || !matchc(p, ';')
+		failparse(p, "expected ';;' terminating genfile command, got %c\n", peekc(p))
+	;;
+
+	durable = false
+	for elt in attrs
+		match elt
+		| ("durable", ""):	durable = true
+		| ("durable", val):	failparse(p, "durable attr does not take argument\n")
+		;;
+	;;
+
+	gt = std.mk([
+		.dir=std.sldup(p.fdir),
+		.out=outlist,
+		.durable=durable,
+		.cmd=cmdlist
+	])
+	for o in outlist
+		std.htput(b.gensrc, o, gt)
+		addtarg(p, b, o, `Gen gt)
+	;;
+}
+
+/*
+myrtarget: name '=' inputlist ';;'
+	| name attrlist = inputlist ';;'
+*/
+const myrtarget = {p, targ
+	var ldscript, runtime, inst, incpath
+	var name, inputs, libdeps, attrs
+	var fsel
+
+	match word(p)
+	| `std.Some n:	name = n
+	| `std.None:	failparse(p, "expected target name after '%s'\n", targ)
+	;;
+
+	skipspace(p)
+	if matchc(p, '{')
+		match attrlist(p)
+		| `std.Some al:	attrs = al
+		| `std.None:	failparse(p, "invalid attr list for %s %s\n", targ, name)
+		;;
+	else
+		attrs = [][:]
+	;;
+
+	skipspace(p)
+	if !matchc(p, '=')
+		failparse(p, "expected '=' after '%s %s'\n", targ, name)
+	;;
+
+	match inputlist(p)
+	| `std.Some (wl, libs): 
+		fsel = mkfsel()
+		libdeps = libs
+		for w in wl
+			fseladd(fsel, w)
+		;;
+		inputs = fselfin(fsel)
+		std.slfree(wl)
+	| `std.None: failparse(p, "expected list of file names after '%s %s'\n", targ, name)
+	;;
+
+	skipspace(p)
+	if !matchc(p, ';') || !matchc(p, ';')
+		failparse(p, "expected ';;' terminating input list, got %c\n", peekc(p))
+	;;
+
+	inst = true
+	ldscript = ""
+	runtime = ""
+	incpath = [][:]
+	for elt in attrs
+		match elt
+		| ("ldscript", lds):	ldscript = std.sldup(lds)
+		| ("runtime", rt):	runtime = std.sldup(rt)
+		| ("inc", path):	incpath = std.slpush(incpath, std.sldup(path))
+		| ("noinst", ""):	inst = false
+		| ("noinst", val):	failparse(p, "noinst attr does not take argument\n")
+		| (invalid, _):
+			std.fatal(1, "%s: got invalid attr '%s'\n", targ, invalid)
+		;;
+	;;
+	-> std.mk([
+		/* target */
+		.dir=std.sldup(p.fdir),
+		.name=name,
+		.inputs=inputs,
+		.libdeps=libdeps,
+		/* attrs */
+		.install=inst,
+		.ldscript=ldscript,
+		.runtime=runtime,
+		.incpath=incpath,
+		.built=false
+	])
+}
+
+/* anontarget: '=' wordlist ';;' */
+const anontarget = {p, targ
+	var inputs
+
+	inputs = [][:]
+	skipspace(p)
+	if !matchc(p, '=')
+		failparse(p, "expected '=' after '%s' target\n", targ)
+	;;
+
+	match wordlist(p)
+	| `std.None:	failparse(p, "expected list of file names after '%s' target\n", targ)
+	| `std.Some wl:	inputs = wl
+	;;
+	skipspace(p)
+	if !matchc(p, ';') || !matchc(p, ';')
+		failparse(p, "expected ';;' terminating input list\n")
+	;;
+	-> inputs
+}
+
+/*
+attrlist: attrs '}'
+
+attrs	: EMPTY
+	| attrs attr
+
+attr	: name
+	| name '=' name
+*/
+const attrlist = {p
+	var al
+
+	al = [][:]
+	while true
+		match word(p)
+		| `std.Some k:
+			skipspace(p)
+			if matchc(p, '=')
+				match word(p)
+				| `std.Some v:
+					al = std.slpush(al, (k, v))
+				| `std.None:
+					failparse(p, "invalid attr in attribute list\n")
+				;;
+			else
+				al = std.slpush(al, (k, [][:]))
+			;;
+			if !matchc(p, ',')
+				break
+			;;
+		| `std.None:	break
+		;;
+	;;
+	if !matchc(p, '}')
+		failparse(p, "expected '}' at end of attr list, got '%c'\n", peekc(p))
+	;;
+	if al.len == 0
+		-> `std.None
+	else
+		-> `std.Some al
+	;;
+}
+
+/*
+inputlist: EMPTY
+	| inputlist input
+
+input	: word
+	| "lib" word
+*/
+const inputlist = {p
+	var dir, lib, targ
+	var wl, libs
+
+	wl = [][:]
+	libs = [][:]
+	while true
+		match word(p)
+		| `std.Some "lib":
+			match word(p)
+			| `std.Some l:
+				(dir, lib, targ) = libpath(p, l)
+				libs = std.slpush(libs, (dir, lib, targ))
+			| `std.None:
+				failparse(p, "expected lib name after 'lib'\n")
+			;;
+		| `std.Some w:	wl = std.slpush(wl, w)
+		| `std.None:	break
+		;;
+	;;
+	if wl.len == 0
+		-> `std.None
+	else
+		-> `std.Some (wl, libs)
+	;;
+}
+
+/* wordlist: EMPTY | wordlist word */
+const wordlist = {p
+	var wl
+
+	wl = [][:]
+	while true
+		match word(p)
+		| `std.Some w:	wl = std.slpush(wl, w)
+		| `std.None:	break
+		;;
+	;;
+	if wl.len == 0
+		-> `std.None
+	else
+		-> `std.Some wl
+	;;
+}
+
+/* word: /wordchar*/
+const word = {p
+	var c, n
+	var start
+
+	skipspace(p)
+
+	c = peekc(p)
+	if c == '"'
+		n = 0
+		getc(p)
+		start = p.rest
+		while p.rest.len > 0
+			c = peekc(p)
+			if c == '"'
+				getc(p)
+				goto done
+			elif c == '\\'
+				c = getc(p)
+			;;
+			getc(p)
+			n += std.charlen(c)
+		;;
+		failparse(p, "input ended within quoted word\n")
+	else
+		n = 0
+		start = p.rest
+		while p.rest.len > 0
+			c = peekc(p)
+			if wordchar(c)
+				getc(p)
+				n += std.charlen(c)
+			else
+				break
+			;;
+		;;
+	;;
+:done
+	if n > 0
+		-> `std.Some std.sldup(start[:n])
+	else
+		-> `std.None
+	;;
+}
+
+const wordchar = {c
+	-> std.isalnum(c) || \
+		c == '.' || c == '_' || c == '$' || c == '-' || \
+		c == '/' || c == ':' || c == '!' || c == '~' || \
+		c == '+'
+}
+
+const skipspace = {p
+	var c, r
+
+	r = p.rest
+	while r.len > 0
+		c = peekc(p)
+		match c
+		| ' ':	getc(p)
+		| '\t':	getc(p)
+		| '\n':
+			getc(p)
+			p.line++
+		| '#':
+			while p.rest.len > 0 && peekc(p) != '\n'
+				getc(p)
+			;;
+		| _:
+			break
+		;;
+	;;
+}
+
+const matchc = {p, c
+	var chr, s
+
+	if p.rest.len == 0
+		-> false
+	;;
+	(chr, s) = std.striter(p.rest)
+	if c == chr
+		p.rest = s
+		-> true
+	else
+		-> false
+	;;
+}
+
+const peekc = {p
+	-> std.decode(p.rest)
+}
+
+const getc = {p
+	var c, s
+
+	(c, s) = std.striter(p.rest)
+	p.rest = s
+	-> c
+}
+
+const addtarg = {p, b, name, targ
+	var tn
+
+	tn = std.fmt("%s:%s", p.fdir, name)
+	if std.hthas(b.targs, tn)
+		failparse(p, "duplicate target %s\n", tn)
+	;;
+	b.all = std.slpush(b.all, tn)
+	std.htput(b.targs, tn, targ)
+}
+
+const libpath = {p, libpath
+	var dir, lib, targ
+
+	match(std.strrfind(libpath, ":"))
+	| `std.None:
+		dir = std.sldup(".")
+		lib = std.sldup(libpath)
+		targ = std.fmt("%s:%s", p.fdir, lib)
+	| `std.Some idx:
+		if idx == libpath.len
+			std.fatal(1, "libdep %s missing library after ':'\n")
+		;;
+		/* absolute path */
+		if std.hasprefix(libpath, "@/") || std.hasprefix(libpath, "@:")
+			dir = std.pathcat(p.basedir, libpath[2:idx])
+			lib = std.sldup(libpath[idx+1:])
+			targ = std.sldup(libpath[2:])
+		/* relative path */
+		else
+			dir = std.sldup(libpath[:idx])
+			lib = std.sldup(libpath[idx+1:])
+			targ = std.pathcat(p.fdir, libpath)
+			if std.hasprefix(targ, "../")
+				std.fatal(1, "library %s outside of project\n", libpath)
+			;;
+		;;
+	;;
+	-> (dir, lib, targ)
+}
--- /dev/null
+++ b/mbld/subdir.myr
@@ -1,0 +1,23 @@
+use std
+
+use "types.use"
+use "util.use"
+
+pkg bld =
+	const subdirs	: (p : build#, subs : byte[:][:], targ : std.option(byte[:]) -> void)
+;;
+
+const subdirs = {p, subs, targ
+	for s in subs
+		std.put("Entering directory '%s'\n", s)
+		if !std.chdir(s)
+			std.fatal(1, "unable to enter directory %s\n", s)
+		;;
+		run(p.cmd)
+		std.put("Leaving directory '%s'\n", s)
+		if !std.chdir("..")
+			std.fatal(1, "unable to leave directory %s\n", s)
+		;;
+	;;
+}
+
--- /dev/null
+++ b/mbld/test.myr
@@ -1,0 +1,150 @@
+use std
+
+use "build.use"
+use "clean.use"
+use "deps.use"
+use "opts.use"
+use "parse.use"
+use "types.use"
+use "util.use"
+use "subdir.use"
+
+use "config.use"
+
+pkg bld =
+	const test	: (b : build# -> void)
+;;
+
+const test = {b
+	var ok/*, bin */
+
+	/* no implicit tests to run */
+	ok = true
+	if std.fexists("test")
+		for tn in b.all
+			match gettarg(b.targs, tn)
+			| `Bin bt:
+				if !dotest(b, bt, ok)
+					ok = false
+				;;
+			| `Lib lt:
+				if !dotest(b, lt, ok)
+					ok = false
+				;;
+			| _:	/* ignore */
+			;;
+		;;
+	;;
+	/*
+	FIXME: reenable test binaries
+	for `Test t in targs
+		for s in t.incpath
+			if std.sleq(".", s)
+				goto founddot
+			;;
+		;;
+		t.incpath = std.slpush(t.incpath, std.sldup("."))
+
+:founddot
+		buildbin(b, t, false)
+		bin = std.strcat("./", t.name)
+		if !runtest(bin)
+			ok = false
+		;;
+		std.slfree(bin)
+	;;
+	*/
+	if ok
+		std.put("TESTS PASSED\n")
+	else
+		std.put("TESTS FAILED\n")
+		std.exit(1)
+	;;
+}
+
+const dotest = {b, targ, ok
+	var tt, bin ,path, tests
+
+	tests = [][:]
+	setdir(b, targ.dir)
+	for s in targ.inputs
+		path = std.pathcat("./test", s)
+		if std.fexists(path)
+			bin = srcswapsuffix(path, "")
+			tt = [
+				.name = bin,
+				.dir = targ.dir,
+				.inputs = [path, s][:],
+				.install = false,
+				.libdeps = targ.libdeps,
+				.incpath = targ.incpath,
+				.built = false,
+			]
+
+			cleantest(b, path)
+			buildbin(b, &tt, true)
+			tests = std.slpush(tests, bin)
+		;;
+		std.slfree(path)
+	;;
+
+	ok = true
+	for t in tests
+		if !runtest(t)
+			ok = false
+		;;
+		std.slfree(t)
+	;;
+	std.slfree(tests)
+	-> ok
+}
+
+const cleantest = {b, src
+	var obj, bin, log, usef
+
+	obj = srcswapsuffix(src, config.Objsuffix)
+	log = srcswapsuffix(src, ".log")
+	usef = srcswapsuffix(src, ".use")
+	bin = srcswapsuffix(src, "")
+
+	std.remove(obj)
+	std.remove(usef)
+	std.remove(log)
+	std.remove(bin)
+
+	std.slfree(obj)
+	std.slfree(usef)
+	std.slfree(log)
+	std.slfree(bin)
+}
+
+const runtest = {bin
+	var r, log
+
+	std.put("run %s:\t", bin)
+	log = std.strcat(bin, ".log")
+	match std.spork([bin][:])
+	| `std.Fail m:
+		std.fatal(1, "unable to run test: %s\n", m)
+	| `std.Ok (pid, infd, outfd):
+		match std.fslurp(outfd)
+		| `std.Ok "":	/* empty output; nothing to log */
+		| `std.Ok buf:
+			std.blat(log, buf, 0o644)
+		| `std.Fail m:
+		;;
+		std.slfree(log)
+
+		r = false
+		match std.wait(pid)
+		| `std.Wfailure:	std.put("FAIL\n")
+		| `std.Wsignalled:	std.put("CRASH\n")
+		| `std.Wsuccess:
+			std.put("PASS\n")
+			r = true
+		| _:	std.put("???\n")
+		;;
+	;;
+	-> r
+}
+
--- /dev/null
+++ b/mbld/types.myr
@@ -1,0 +1,59 @@
+use std
+
+pkg bld =
+	type build = struct
+		cmd	: byte[:][:]	/* command that we ran */
+		/* build state */
+		basedir	: byte[:]
+		curdir	: byte[:]
+		/* build params */
+		all	: byte[:][:]
+		targs	: std.htab(byte[:], targ)#	/* dir => target mapping */
+		tdeps	: std.htab(byte[:], byte[:][:])	/* targname => depname[:] mapping */
+		gensrc	: std.htab(byte[:], gentarg#)#	/* generated src => generating target mapping */
+		prefix	: byte[:]
+		system	: byte[:]
+		arch	: byte[:]
+	;;
+
+
+	type depgraph = struct
+		roots	: byte[:][:]
+		deps	: std.htab(byte[:], byte[:][:])#
+		libs	: std.htab(byte[:], byte[:][:])#
+		input	: std.htab(byte[:], byte[:])#
+		sources	: std.htab(byte[:], bool)#
+		updated	: std.htab(byte[:], bool)#
+		seen	: std.htab(byte[:], bool)#
+		done	: std.htab(byte[:], bool)#
+	;;
+
+	type myrtarg = struct
+		dir	: byte[:]
+		name	: byte[:]
+		inputs	: byte[:][:]
+		libdeps	: (byte[:], byte[:], byte[:])[:]	/* dir, lib pairs */
+		built	: bool
+		install	: bool
+		runtime	: byte[:]
+		incpath	: byte[:][:]
+		ldscript	: byte[:]
+	;;
+
+	type gentarg = struct
+		dir	: byte[:]
+		out	: byte[:][:]
+		cmd	: byte[:][:] 
+		durable	: bool
+		/* we can have multiple outputs, but we only want to run once for each */
+		done	: bool
+	;;
+
+	type targ = union
+		`Bin	myrtarg#
+		`Lib	myrtarg#
+		`Test	myrtarg#
+		`Gen	gentarg#
+		`Man	byte[:][:]
+	;;
+;;
--- /dev/null
+++ b/mbld/util.myr
@@ -1,0 +1,123 @@
+use std
+
+use "opts.use"
+use "types.use"
+
+pkg bld =
+	const run	: (cmd : byte[:][:] -> void)
+	const printcmd
+	const srcsplit	: (src : byte[:] -> (byte[:], byte[:], byte[:]))
+	const swapsuffix	: (f : byte[:], suff : byte[:], newsuff : byte[:] -> byte[:])
+	const srcswapsuffix	: (f : byte[:], newsuff : byte[:] -> byte[:])
+	const strlistfree	: (sl : byte[:][:] -> void)
+	const gettarg	: (tab : std.htab(byte[:], targ)#, n : byte[:] -> targ)
+	const setdir	: (b : build#, dir : byte[:] -> void)
+;;
+
+const run = {cmd
+	var pid
+
+	printcmd(cmd)
+	pid = std.fork()
+	if pid == -1
+		std.fatal(1, "could not fork command\n")
+	elif pid == 0
+		if std.execvp(cmd[0], cmd) < 0
+			std.fatal(1, "failed to exec %s\n", cmd[0])
+		;;
+	else
+		match std.wait(pid)
+		| `std.Wsuccess:	/* nothing */
+		| `std.Wfailure:	std.fatal(1, "FAIL: \"%s\"\n", std.strjoin(cmd, " "))
+		| `std.Wsignalled:	std.fatal(1, "CRASH: \"%s\"\n", std.strjoin(cmd, " "))
+		| `std.Waiterror:	std.fatal(1, "WAT: \"%s\"\n", std.strjoin(cmd, " "))
+		;;
+	;;
+}
+
+const printcmd = {lst
+	if lst.len > 0
+		std.put("\t")
+		std.put("%s\t", lst[0])
+		for l in lst[1:]
+			std.put("%s ", l)
+		;;
+	;;
+	std.put("\n")
+}
+
+const srcsplit = {src
+	var platf, suff
+
+	platf = ""
+	suff = ""
+	match std.strrfind(src, ".")
+	| `std.Some i:
+		suff = src[i:]
+		src = src[:i]
+	| `std.None:
+		/* no suffix to trim */
+	;;
+
+	match std.strrfind(src, "+")
+	| `std.Some i:
+		platf = src[i:]
+		src = src[:i]
+	| `std.None:
+		/* no platform to trim */
+	;;
+	-> (src, platf, suff)
+}
+
+const swapsuffix = {f, suff, newsuff
+	if std.hassuffix(f, suff)
+		f = f[:f.len - suff.len]
+	;;
+	-> std.fmt("%s%s", f, newsuff)
+}
+
+const srcswapsuffix = {src, new
+	var base, platf, suff
+
+	(base, platf, suff) = srcsplit(src)
+	if std.sleq(suff, ".myr")
+		-> std.strcat(base, new)
+	elif std.sleq(suff, ".s")
+		-> std.strcat(base, new)
+	else
+		std.fatal(1, "unrecognized source %s\n", src)
+	;;
+}
+
+const strlistfree = {sl
+	for s in sl
+		std.slfree(s)
+	;;
+	std.slfree(sl)
+}
+
+const gettarg = {tab, n
+	match std.htget(tab, n)
+	| `std.None:	std.fatal(1, "internal: nonexistent %s\n", n)
+	| `std.Some t:	-> t
+	;;
+}
+
+const setdir = {b, dir
+	var p
+
+	if !std.sleq(b.curdir, dir)
+		p = std.pathcat(b.basedir, dir)
+		if b.curdir.len != 0
+			std.put("Leaving directory %s\n", b.curdir)
+		;;
+
+		std.put("Entering directory '%s'\n", dir)
+		if !std.chdir(p)
+			std.fatal(1, "could not cd into %s\n")
+		;;
+		b.curdir = dir
+		std.slfree(p)
+	;;
+}
+
--- a/mk/myr.mk
+++ /dev/null
@@ -1,42 +1,0 @@
-ifneq ($(MYRLIB),)
-    _LIBNAME=lib$(MYRLIB).a
-endif
-
-all: $(_LIBNAME) $(MYRBIN)
-
-$(_LIBNAME): $(MYRSRC) $(ASMSRC)
-	myrbuild -l $(MYRLIB) $^
-
-$(MYRBIN): $(MYRSRC) $(ASMSRC)
-	myrbuild -b $(MYRBIN) $^
-
-OBJ=$(MYRSRC:.myr=.o) $(ASMSRC:.s=.o)
-JUNKASM=$(MYRSRC:.myr=.s)
-USE=$(MYRSRC:.myr=.use) $(MYRLIB)
-.PHONY: clean install install-bin install-lib
-clean:
-	rm -f $(OBJ)
-	rm -f $(USE)
-	rm -f $(JUNKASM) $(CLEANEXTRA)
-	rm -f $(_LIBNAME) $(MYRBIN)
-
-install: install-bin install-lib
-
-install-bin: $(MYRBIN)
-	@if [ ! -z "$(MYRBIN)" ]; then \
-	    echo install $(MYRBIN) $(INST_ROOT)/bin; \
-	    mkdir -p $(INST_ROOT)/bin; \
-	    install $(MYRBIN) $(INST_ROOT)/bin; \
-	fi
-
-install-lib: $(_LIBNAME)
-	@if [ ! -z "$(_LIBNAME)" ]; then \
-		echo install -m 644 $(_LIBNAME) $(INST_ROOT)/lib/myr; \
-		echo install -m 644 $(MYRLIB) $(INST_ROOT)/lib/myr; \
-		mkdir -p $(INST_ROOT)/lib/myr; \
-		install -m 644 $(_LIBNAME) $(INST_ROOT)/lib/myr; \
-		install -m 644 $(MYRLIB) $(INST_ROOT)/lib/myr; \
-	fi
-
-config.mk:
-	./configure
--- a/opts.myr
+++ /dev/null
@@ -1,85 +1,0 @@
-use std
-
-use "config.use"
-
-pkg bld =
-	var opt_arch 	: byte[:]
-	var opt_sys	: byte[:]
-	var opt_runtime	: byte[:]
-	var opt_genasm	: bool
-	var opt_ldscript	: byte[:]
-	var opt_incpaths	: byte[:][:]
-	var opt_instroot	: byte[:]
-	var opt_manpath	: byte[:]
-	var opt_destdir	: byte[:]
-	var opt_outdir	: byte[:]
-	var opt_bldfile	: byte[:]
-	var opt_debug	: bool
-
-	/* undocumented/unsupported opts */
-	var opt_mc	: byte[:]
-	var opt_muse	: byte[:]
-
-	var sysarchstr	: byte[:]
-	var archstr	: byte[:]
-	var sysstr	: byte[:]
-
-	const initopts	: (-> void)
-;;
-
-var opt_arch 	= ""
-var opt_sys	= ""
-var opt_binname	= ""
-var opt_libname	= ""
-var opt_runtime	= ""
-var opt_ldscript	= ""
-var opt_incpaths	/* FIXME: taking a constant slice is a nonconstant initializer */
-var opt_instroot	= ""
-var opt_manpath	= ""
-var opt_destdir	= ""
-var opt_debug	= false
-var opt_bldfile = "bldfile"
-var opt_mc	= "6m"
-var opt_as	= "as"
-var opt_muse	= "muse"
-var opt_ld	= "ld"
-var opt_ar	= "ar"
-var opt_genasm  = false
-
-/* derived */
-var sysarchstr	= ""
-var archstr	= ""
-var sysstr	= ""
-
-const initopts = {
-	var si
-
-	std.getsysinfo(&si)
-	match si.system
-	| "Linux":	opt_sys = "linux"
-	| "Darwin":	opt_sys = "osx"
-	| "FreeBSD":	opt_sys = "freebsd"
-	| "Plan9":	opt_sys = "plan9"
-	| unknown:	std.fatal(1, "unknown system \"%s\"\n", unknown)
-	;;
-
-	match si.arch
-	| "x86_64":	opt_arch = "x64"
-	| "amd64":	opt_arch = "x64"
-	| unknown:	std.fatal(1, "unknown architecture \"%s\"\n", unknown)
-	;;
-
-	opt_incpaths = [][:]
-	opt_instroot = config.Instroot
-	opt_manpath = config.Manpath
-	opt_destdir = std.getenvv("DESTDIR", "")
-	opt_mc = std.getenvv("MYR_MC", "6m")
-	opt_muse = std.getenvv("MYR_MUSE", "muse")
-
-	sysarchstr = std.fmt("+%s-%s", opt_sys, opt_arch)
-	sysstr = std.fmt("+%s", opt_sys)
-	archstr = std.fmt("+%s", opt_arch)
-
-	opt_runtime = std.pathjoin([opt_instroot, "lib/myr", config.Runtime][:]) 
-}
-
--- a/parse.myr
+++ /dev/null
@@ -1,539 +1,0 @@
-use std
-
-use "types.use"
-use "util.use"
-use "opts.use"
-use "fsel.use"
-
-pkg bld =
-	const load	: (b : build#, path : byte[:]	-> bool)
-;;
-
-type parser = struct
-	/* parse input */
-	data	: byte[:]
-	rest	: byte[:]
-	fname	: byte[:]
-	fdir	: byte[:]	/* directory relative to base */
-	basedir	: byte[:]
-	line	: int
-
-	/* extracted data for further parsing */
-	subdirs	: byte[:][:]
-;;
-
-const load = {b, path
-	-> loadall(b, path, "")
-}
-
-
-const loadall = {b, path, dir
-	var p : parser#
-	var subpath, subbld, ok
-
-	p = mkparser(path, dir, b.basedir)
-	ok = bld.parse(b, p, "")
-	for s in p.subdirs
-		subpath = std.pathcat(p.fdir, s)
-		subbld = std.pathcat(subpath, "bldfile")
-		loadall(b, subbld, subpath)
-		std.slfree(subpath)
-		std.slfree(subbld)
-	;;
-	freeparser(p)
-	-> ok
-}
-
-const mkparser = {path, dir, basedir
-	var p
-
-	p = std.zalloc()
-	p.line = 1
-	p.fname = std.sldup(path)
-	p.fdir = std.sldup(dir)
-	p.basedir = std.sldup(basedir)
-	match std.slurp(path)
-	| `std.Ok d:	p.data = d
-	| `std.Fail _:	std.fatal(1, "could not open '%s'\n", path)
-	;;
-	p.rest = p.data
-	-> p
-}
-
-const freeparser = {p
-	std.slfree(p.fname)
-	std.slfree(p.fdir)
-	std.slfree(p.basedir)
-	std.slfree(p.subdirs)
-	std.slfree(p.data)
-	std.free(p)
-}
-
-const failparse = {p, msg, args : ...
-	var buf : byte[1024]
-	var ap
-	var sl
-
-	ap = std.vastart(&args)
-	sl = std.bfmtv(buf[:], msg, ap)
-	std.fput(1, "%s:%i: %s", p.fname, p.line, sl)
-	std.exit(1)
-}
-
-const parse = {b, p, path
-	while true
-		skipspace(p)
-		if !target(b, p)
-			break
-		;;
-	;;
-	skipspace(p)
-	if p.rest.len > 0
-		failparse(p, "junk in file near %s\n", p.rest[:std.min(p.rest.len, 10)])
-		-> false
-	;;
-	-> true
-}
-
-const target = {b, p
-	match word(p)
-	| `std.Some "bin":	bintarget(b, p)
-	| `std.Some "test":	testtarget(b, p)
-	| `std.Some "lib":	libtarget(b, p)
-	| `std.Some "gen":	gentarget(b, p)
-	| `std.Some "man":	mantarget(b, p)
-	| `std.Some "sub":	subtarget(b, p)
-	| `std.Some targtype:	failparse(p, "unknown targtype type %s\n", targtype)
-	| `std.None:	-> false
-	;;
-	-> true
-}
-
-/* bintarget: myrtarget */
-const bintarget = {b, p
-	var t
-	t = myrtarget(p, "bin")
-	addtarg(p, b, t.name, `Bin t)
-}
-
-/* libtarget: myrtarget */
-const libtarget = {b, p
-	var t
-	t = myrtarget(p, "lib")
-	addtarg(p, b, t.name, `Lib t)
-}
-
-/* testtarget: myrtarget */
-const testtarget = {b, p
-	var t
-	t = myrtarget(p, "test")
-	addtarg(p, b, t.name, `Test myrtarget(p, "test"))
-}
-
-/* mantarget: anontarget */
-const mantarget = {b, p
-	addtarg(p, b, "__man__", `Man anontarget(p, "man"))
-}
-
-/* subtarget : anontarget */
-const subtarget = {b, p
-	var subs
-
-	subs = anontarget(p, "sub")
-	for s in subs
-		p.subdirs = std.slpush(p.subdirs, std.pathcat(p.fdir, s))
-	;;
-}
-
-
-/* gentarget: wordlist {attrs} = wordlist ;; */
-const gentarget = {b, p
-	var outlist, cmdlist
-	var durable
-	var attrs
-	var gt
-
-	match wordlist(p)
-	| `std.None:	failparse(p, "gen target missing output files\n")
-	| `std.Some out:
-		outlist = out
-	;;
-
-	skipspace(p)
-	if matchc(p, '{')
-		match attrlist(p)
-		| `std.Some al:	attrs = al
-		| `std.None:	failparse(p, "invalid attr list for %s\n", outlist[0])
-		;;
-	else
-		attrs = [][:]
-	;;
-
-	skipspace(p)
-	if !matchc(p, '=')
-		failparse(p, "expected '=' after '%s %s'\n", cmdlist, outlist[outlist.len-1])
-	;;
-
-	match wordlist(p)
-	| `std.None:	failparse(p, "gen target missing command\n")
-	| `std.Some cmd:
-		cmdlist = cmd
-	;;
-
-	if !matchc(p, ';') || !matchc(p, ';')
-		failparse(p, "expected ';;' terminating genfile command, got %c\n", peekc(p))
-	;;
-
-	durable = false
-	for elt in attrs
-		match elt
-		| ("durable", ""):	durable = true
-		| ("durable", val):	failparse(p, "durable attr does not take argument\n")
-		;;
-	;;
-
-	gt = std.mk([
-		.dir=std.sldup(p.fdir),
-		.out=outlist,
-		.durable=durable,
-		.cmd=cmdlist
-	])
-	for o in outlist
-		std.htput(b.gensrc, o, gt)
-		addtarg(p, b, o, `Gen gt)
-	;;
-}
-
-/*
-myrtarget: name '=' inputlist ';;'
-	| name attrlist = inputlist ';;'
-*/
-const myrtarget = {p, targ
-	var ldscript, runtime, inst, incpath
-	var name, inputs, libdeps, attrs
-	var fsel
-
-	match word(p)
-	| `std.Some n:	name = n
-	| `std.None:	failparse(p, "expected target name after '%s'\n", targ)
-	;;
-
-	skipspace(p)
-	if matchc(p, '{')
-		match attrlist(p)
-		| `std.Some al:	attrs = al
-		| `std.None:	failparse(p, "invalid attr list for %s %s\n", targ, name)
-		;;
-	else
-		attrs = [][:]
-	;;
-
-	skipspace(p)
-	if !matchc(p, '=')
-		failparse(p, "expected '=' after '%s %s'\n", targ, name)
-	;;
-
-	match inputlist(p)
-	| `std.Some (wl, libs): 
-		fsel = mkfsel()
-		libdeps = libs
-		for w in wl
-			fseladd(fsel, w)
-		;;
-		inputs = fselfin(fsel)
-		std.slfree(wl)
-	| `std.None: failparse(p, "expected list of file names after '%s %s'\n", targ, name)
-	;;
-
-	skipspace(p)
-	if !matchc(p, ';') || !matchc(p, ';')
-		failparse(p, "expected ';;' terminating input list, got %c\n", peekc(p))
-	;;
-
-	inst = true
-	ldscript = ""
-	runtime = ""
-	incpath = [][:]
-	for elt in attrs
-		match elt
-		| ("ldscript", lds):	ldscript = std.sldup(lds)
-		| ("runtime", rt):	runtime = std.sldup(rt)
-		| ("inc", path):	incpath = std.slpush(incpath, std.sldup(path))
-		| ("noinst", ""):	inst = false
-		| ("noinst", val):	failparse(p, "noinst attr does not take argument\n")
-		| (invalid, _):
-			std.fatal(1, "%s: got invalid attr '%s'\n", targ, invalid)
-		;;
-	;;
-	-> std.mk([
-		/* target */
-		.dir=std.sldup(p.fdir),
-		.name=name,
-		.inputs=inputs,
-		.libdeps=libdeps,
-		/* attrs */
-		.install=inst,
-		.ldscript=ldscript,
-		.runtime=runtime,
-		.incpath=incpath,
-		.built=false
-	])
-}
-
-/* anontarget: '=' wordlist ';;' */
-const anontarget = {p, targ
-	var inputs
-
-	inputs = [][:]
-	skipspace(p)
-	if !matchc(p, '=')
-		failparse(p, "expected '=' after '%s' target\n", targ)
-	;;
-
-	match wordlist(p)
-	| `std.None:	failparse(p, "expected list of file names after '%s' target\n", targ)
-	| `std.Some wl:	inputs = wl
-	;;
-	skipspace(p)
-	if !matchc(p, ';') || !matchc(p, ';')
-		failparse(p, "expected ';;' terminating input list\n")
-	;;
-	-> inputs
-}
-
-/*
-attrlist: attrs '}'
-
-attrs	: EMPTY
-	| attrs attr
-
-attr	: name
-	| name '=' name
-*/
-const attrlist = {p
-	var al
-
-	al = [][:]
-	while true
-		match word(p)
-		| `std.Some k:
-			skipspace(p)
-			if matchc(p, '=')
-				match word(p)
-				| `std.Some v:
-					al = std.slpush(al, (k, v))
-				| `std.None:
-					failparse(p, "invalid attr in attribute list\n")
-				;;
-			else
-				al = std.slpush(al, (k, [][:]))
-			;;
-			if !matchc(p, ',')
-				break
-			;;
-		| `std.None:	break
-		;;
-	;;
-	if !matchc(p, '}')
-		failparse(p, "expected '}' at end of attr list, got '%c'\n", peekc(p))
-	;;
-	if al.len == 0
-		-> `std.None
-	else
-		-> `std.Some al
-	;;
-}
-
-/*
-inputlist: EMPTY
-	| inputlist input
-
-input	: word
-	| "lib" word
-*/
-const inputlist = {p
-	var dir, lib, targ
-	var wl, libs
-
-	wl = [][:]
-	libs = [][:]
-	while true
-		match word(p)
-		| `std.Some "lib":
-			match word(p)
-			| `std.Some l:
-				(dir, lib, targ) = libpath(p, l)
-				libs = std.slpush(libs, (dir, lib, targ))
-			| `std.None:
-				failparse(p, "expected lib name after 'lib'\n")
-			;;
-		| `std.Some w:	wl = std.slpush(wl, w)
-		| `std.None:	break
-		;;
-	;;
-	if wl.len == 0
-		-> `std.None
-	else
-		-> `std.Some (wl, libs)
-	;;
-}
-
-/* wordlist: EMPTY | wordlist word */
-const wordlist = {p
-	var wl
-
-	wl = [][:]
-	while true
-		match word(p)
-		| `std.Some w:	wl = std.slpush(wl, w)
-		| `std.None:	break
-		;;
-	;;
-	if wl.len == 0
-		-> `std.None
-	else
-		-> `std.Some wl
-	;;
-}
-
-/* word: /wordchar*/
-const word = {p
-	var c, n
-	var start
-
-	skipspace(p)
-
-	c = peekc(p)
-	if c == '"'
-		n = 0
-		getc(p)
-		start = p.rest
-		while p.rest.len > 0
-			c = peekc(p)
-			if c == '"'
-				getc(p)
-				goto done
-			elif c == '\\'
-				c = getc(p)
-			;;
-			getc(p)
-			n += std.charlen(c)
-		;;
-		failparse(p, "input ended within quoted word\n")
-	else
-		n = 0
-		start = p.rest
-		while p.rest.len > 0
-			c = peekc(p)
-			if wordchar(c)
-				getc(p)
-				n += std.charlen(c)
-			else
-				break
-			;;
-		;;
-	;;
-:done
-	if n > 0
-		-> `std.Some std.sldup(start[:n])
-	else
-		-> `std.None
-	;;
-}
-
-const wordchar = {c
-	-> std.isalnum(c) || \
-		c == '.' || c == '_' || c == '$' || c == '-' || \
-		c == '/' || c == ':' || c == '!' || c == '~' || \
-		c == '+'
-}
-
-const skipspace = {p
-	var c, r
-
-	r = p.rest
-	while r.len > 0
-		c = peekc(p)
-		match c
-		| ' ':	getc(p)
-		| '\t':	getc(p)
-		| '\n':
-			getc(p)
-			p.line++
-		| '#':
-			while p.rest.len > 0 && peekc(p) != '\n'
-				getc(p)
-			;;
-		| _:
-			break
-		;;
-	;;
-}
-
-const matchc = {p, c
-	var chr, s
-
-	if p.rest.len == 0
-		-> false
-	;;
-	(chr, s) = std.striter(p.rest)
-	if c == chr
-		p.rest = s
-		-> true
-	else
-		-> false
-	;;
-}
-
-const peekc = {p
-	-> std.decode(p.rest)
-}
-
-const getc = {p
-	var c, s
-
-	(c, s) = std.striter(p.rest)
-	p.rest = s
-	-> c
-}
-
-const addtarg = {p, b, name, targ
-	var tn
-
-	tn = std.fmt("%s:%s", p.fdir, name)
-	if std.hthas(b.targs, tn)
-		failparse(p, "duplicate target %s\n", tn)
-	;;
-	b.all = std.slpush(b.all, tn)
-	std.htput(b.targs, tn, targ)
-}
-
-const libpath = {p, libpath
-	var dir, lib, targ
-
-	match(std.strrfind(libpath, ":"))
-	| `std.None:
-		dir = std.sldup(".")
-		lib = std.sldup(libpath)
-		targ = std.fmt("%s:%s", p.fdir, lib)
-	| `std.Some idx:
-		if idx == libpath.len
-			std.fatal(1, "libdep %s missing library after ':'\n")
-		;;
-		/* absolute path */
-		if std.hasprefix(libpath, "@/") || std.hasprefix(libpath, "@:")
-			dir = std.pathcat(p.basedir, libpath[2:idx])
-			lib = std.sldup(libpath[idx+1:])
-			targ = std.sldup(libpath[2:])
-		/* relative path */
-		else
-			dir = std.sldup(libpath[:idx])
-			lib = std.sldup(libpath[idx+1:])
-			targ = std.pathcat(p.fdir, libpath)
-			if std.hasprefix(targ, "../")
-				std.fatal(1, "library %s outside of project\n", libpath)
-			;;
-		;;
-	;;
-	-> (dir, lib, targ)
-}
--- a/subdir.myr
+++ /dev/null
@@ -1,23 +1,0 @@
-use std
-
-use "types.use"
-use "util.use"
-
-pkg bld =
-	const subdirs	: (p : build#, subs : byte[:][:], targ : std.option(byte[:]) -> void)
-;;
-
-const subdirs = {p, subs, targ
-	for s in subs
-		std.put("Entering directory '%s'\n", s)
-		if !std.chdir(s)
-			std.fatal(1, "unable to enter directory %s\n", s)
-		;;
-		run(p.cmd)
-		std.put("Leaving directory '%s'\n", s)
-		if !std.chdir("..")
-			std.fatal(1, "unable to leave directory %s\n", s)
-		;;
-	;;
-}
-
--- a/test.myr
+++ /dev/null
@@ -1,150 +1,0 @@
-use std
-
-use "build.use"
-use "clean.use"
-use "deps.use"
-use "opts.use"
-use "parse.use"
-use "types.use"
-use "util.use"
-use "subdir.use"
-
-use "config.use"
-
-pkg bld =
-	const test	: (b : build# -> void)
-;;
-
-const test = {b
-	var ok/*, bin */
-
-	/* no implicit tests to run */
-	ok = true
-	if std.fexists("test")
-		for tn in b.all
-			match gettarg(b.targs, tn)
-			| `Bin bt:
-				if !dotest(b, bt, ok)
-					ok = false
-				;;
-			| `Lib lt:
-				if !dotest(b, lt, ok)
-					ok = false
-				;;
-			| _:	/* ignore */
-			;;
-		;;
-	;;
-	/*
-	FIXME: reenable test binaries
-	for `Test t in targs
-		for s in t.incpath
-			if std.sleq(".", s)
-				goto founddot
-			;;
-		;;
-		t.incpath = std.slpush(t.incpath, std.sldup("."))
-
-:founddot
-		buildbin(b, t, false)
-		bin = std.strcat("./", t.name)
-		if !runtest(bin)
-			ok = false
-		;;
-		std.slfree(bin)
-	;;
-	*/
-	if ok
-		std.put("TESTS PASSED\n")
-	else
-		std.put("TESTS FAILED\n")
-		std.exit(1)
-	;;
-}
-
-const dotest = {b, targ, ok
-	var tt, bin ,path, tests
-
-	tests = [][:]
-	setdir(b, targ.dir)
-	for s in targ.inputs
-		path = std.pathcat("./test", s)
-		if std.fexists(path)
-			bin = srcswapsuffix(path, "")
-			tt = [
-				.name = bin,
-				.dir = targ.dir,
-				.inputs = [path, s][:],
-				.install = false,
-				.libdeps = targ.libdeps,
-				.incpath = targ.incpath,
-				.built = false,
-			]
-
-			cleantest(b, path)
-			buildbin(b, &tt, true)
-			tests = std.slpush(tests, bin)
-		;;
-		std.slfree(path)
-	;;
-
-	ok = true
-	for t in tests
-		if !runtest(t)
-			ok = false
-		;;
-		std.slfree(t)
-	;;
-	std.slfree(tests)
-	-> ok
-}
-
-const cleantest = {b, src
-	var obj, bin, log, usef
-
-	obj = srcswapsuffix(src, config.Objsuffix)
-	log = srcswapsuffix(src, ".log")
-	usef = srcswapsuffix(src, ".use")
-	bin = srcswapsuffix(src, "")
-
-	std.remove(obj)
-	std.remove(usef)
-	std.remove(log)
-	std.remove(bin)
-
-	std.slfree(obj)
-	std.slfree(usef)
-	std.slfree(log)
-	std.slfree(bin)
-}
-
-const runtest = {bin
-	var r, log
-
-	std.put("run %s:\t", bin)
-	log = std.strcat(bin, ".log")
-	match std.spork([bin][:])
-	| `std.Fail m:
-		std.fatal(1, "unable to run test: %s\n", m)
-	| `std.Ok (pid, infd, outfd):
-		match std.fslurp(outfd)
-		| `std.Ok "":	/* empty output; nothing to log */
-		| `std.Ok buf:
-			std.blat(log, buf, 0o644)
-		| `std.Fail m:
-		;;
-		std.slfree(log)
-
-		r = false
-		match std.wait(pid)
-		| `std.Wfailure:	std.put("FAIL\n")
-		| `std.Wsignalled:	std.put("CRASH\n")
-		| `std.Wsuccess:
-			std.put("PASS\n")
-			r = true
-		| _:	std.put("???\n")
-		;;
-	;;
-	-> r
-}
-
--- a/types.myr
+++ /dev/null
@@ -1,59 +1,0 @@
-use std
-
-pkg bld =
-	type build = struct
-		cmd	: byte[:][:]	/* command that we ran */
-		/* build state */
-		basedir	: byte[:]
-		curdir	: byte[:]
-		/* build params */
-		all	: byte[:][:]
-		targs	: std.htab(byte[:], targ)#	/* dir => target mapping */
-		tdeps	: std.htab(byte[:], byte[:][:])	/* targname => depname[:] mapping */
-		gensrc	: std.htab(byte[:], gentarg#)#	/* generated src => generating target mapping */
-		prefix	: byte[:]
-		system	: byte[:]
-		arch	: byte[:]
-	;;
-
-
-	type depgraph = struct
-		roots	: byte[:][:]
-		deps	: std.htab(byte[:], byte[:][:])#
-		libs	: std.htab(byte[:], byte[:][:])#
-		input	: std.htab(byte[:], byte[:])#
-		sources	: std.htab(byte[:], bool)#
-		updated	: std.htab(byte[:], bool)#
-		seen	: std.htab(byte[:], bool)#
-		done	: std.htab(byte[:], bool)#
-	;;
-
-	type myrtarg = struct
-		dir	: byte[:]
-		name	: byte[:]
-		inputs	: byte[:][:]
-		libdeps	: (byte[:], byte[:], byte[:])[:]	/* dir, lib pairs */
-		built	: bool
-		install	: bool
-		runtime	: byte[:]
-		incpath	: byte[:][:]
-		ldscript	: byte[:]
-	;;
-
-	type gentarg = struct
-		dir	: byte[:]
-		out	: byte[:][:]
-		cmd	: byte[:][:] 
-		durable	: bool
-		/* we can have multiple outputs, but we only want to run once for each */
-		done	: bool
-	;;
-
-	type targ = union
-		`Bin	myrtarg#
-		`Lib	myrtarg#
-		`Test	myrtarg#
-		`Gen	gentarg#
-		`Man	byte[:][:]
-	;;
-;;
--- a/util.myr
+++ /dev/null
@@ -1,123 +1,0 @@
-use std
-
-use "opts.use"
-use "types.use"
-
-pkg bld =
-	const run	: (cmd : byte[:][:] -> void)
-	const printcmd
-	const srcsplit	: (src : byte[:] -> (byte[:], byte[:], byte[:]))
-	const swapsuffix	: (f : byte[:], suff : byte[:], newsuff : byte[:] -> byte[:])
-	const srcswapsuffix	: (f : byte[:], newsuff : byte[:] -> byte[:])
-	const strlistfree	: (sl : byte[:][:] -> void)
-	const gettarg	: (tab : std.htab(byte[:], targ)#, n : byte[:] -> targ)
-	const setdir	: (b : build#, dir : byte[:] -> void)
-;;
-
-const run = {cmd
-	var pid
-
-	printcmd(cmd)
-	pid = std.fork()
-	if pid == -1
-		std.fatal(1, "could not fork command\n")
-	elif pid == 0
-		if std.execvp(cmd[0], cmd) < 0
-			std.fatal(1, "failed to exec %s\n", cmd[0])
-		;;
-	else
-		match std.wait(pid)
-		| `std.Wsuccess:	/* nothing */
-		| `std.Wfailure:	std.fatal(1, "FAIL: \"%s\"\n", std.strjoin(cmd, " "))
-		| `std.Wsignalled:	std.fatal(1, "CRASH: \"%s\"\n", std.strjoin(cmd, " "))
-		| `std.Waiterror:	std.fatal(1, "WAT: \"%s\"\n", std.strjoin(cmd, " "))
-		;;
-	;;
-}
-
-const printcmd = {lst
-	if lst.len > 0
-		std.put("\t")
-		std.put("%s\t", lst[0])
-		for l in lst[1:]
-			std.put("%s ", l)
-		;;
-	;;
-	std.put("\n")
-}
-
-const srcsplit = {src
-	var platf, suff
-
-	platf = ""
-	suff = ""
-	match std.strrfind(src, ".")
-	| `std.Some i:
-		suff = src[i:]
-		src = src[:i]
-	| `std.None:
-		/* no suffix to trim */
-	;;
-
-	match std.strrfind(src, "+")
-	| `std.Some i:
-		platf = src[i:]
-		src = src[:i]
-	| `std.None:
-		/* no platform to trim */
-	;;
-	-> (src, platf, suff)
-}
-
-const swapsuffix = {f, suff, newsuff
-	if std.hassuffix(f, suff)
-		f = f[:f.len - suff.len]
-	;;
-	-> std.fmt("%s%s", f, newsuff)
-}
-
-const srcswapsuffix = {src, new
-	var base, platf, suff
-
-	(base, platf, suff) = srcsplit(src)
-	if std.sleq(suff, ".myr")
-		-> std.strcat(base, new)
-	elif std.sleq(suff, ".s")
-		-> std.strcat(base, new)
-	else
-		std.fatal(1, "unrecognized source %s\n", src)
-	;;
-}
-
-const strlistfree = {sl
-	for s in sl
-		std.slfree(s)
-	;;
-	std.slfree(sl)
-}
-
-const gettarg = {tab, n
-	match std.htget(tab, n)
-	| `std.None:	std.fatal(1, "internal: nonexistent %s\n", n)
-	| `std.Some t:	-> t
-	;;
-}
-
-const setdir = {b, dir
-	var p
-
-	if !std.sleq(b.curdir, dir)
-		p = std.pathcat(b.basedir, dir)
-		if b.curdir.len != 0
-			std.put("Leaving directory %s\n", b.curdir)
-		;;
-
-		std.put("Entering directory '%s'\n", dir)
-		if !std.chdir(p)
-			std.fatal(1, "could not cd into %s\n")
-		;;
-		b.curdir = dir
-		std.slfree(p)
-	;;
-}
-