shithub: mc

Download patch

ref: d9e2c6c7cf64dab43e65c2f4ec2c13c301297f48
author: Ori Bernstein <ori@eigenstate.org>
date: Mon Sep 22 18:23:45 EDT 2014

It compiles itself!

--- /dev/null
+++ b/bldfile
@@ -1,0 +1,2 @@
+# comment
+bin mbld2 = main.myr build.myr parse.myr deps.myr config.myr opts.myr; # comment
--- /dev/null
+++ b/build.myr
@@ -1,0 +1,225 @@
+use std
+
+use "config.use"
+use "deps.use"
+use "opts.use"
+use "parse.use"
+
+pkg bld =
+	const build	: (p : parser# -> bool)
+;;
+
+const build = {p
+	var dg
+
+	for t in p.targs
+		match t
+		| `Bin (b, leaves):
+			if !myrdeps(&dg, b, leaves, false)
+				std.fatal(1, "Could not load dependencies for %s\n", b)
+			;;
+			buildbin(&dg, b, leaves)
+		| `Lib (l, leaves):
+			if !myrdeps(&dg, l, leaves, true)
+				std.fatal(1, "Could not load dependencies for %s\n", l)
+			;;
+			buildlib(&dg, l, leaves)
+		| `Sub sub:
+			dosub(sub)
+		| `Man m:
+			/* nothing needed */
+		;;
+	;;
+	-> true
+}
+
+const buildbin = {dg, b, inputs
+	match std.htget(dg.deps, b)
+	| `std.Some deps:
+		for d in deps
+			builddep(dg, d, b)
+		;;
+		linkbin(dg, b, inputs, "")
+	| `std.None:
+	;;
+}
+
+const buildlib = {dt, b, dep
+	std.put("building %s:\n", b)
+	for d in dep
+		std.put("\t%s\n", d)
+	;;
+}
+
+const dosub = {subs
+	for s in subs
+		std.put("subdir %s\n", s)
+	;;
+}
+
+const builddep = {dg, dep, out
+	match std.htget(dg.deps, dep)
+	| `std.Some deps:
+		for d in deps
+			builddep(dg, d, dep)
+		;;
+	| `std.None:
+	;;
+
+	if isfresh(dep, out)
+		->
+	;;
+	if std.hassuffix(dep, ".myr")
+		run(["6m", dep][:])
+	;;
+}
+
+const linkbin = {dg, binname, srcfiles, ldscript
+	var cmd
+	var obj
+
+	cmd = [][:]
+
+	/* ld -o bin */
+	cmd = std.slpush(cmd, std.sldup("ld"))
+	cmd = std.slpush(cmd, std.sldup("-o"))
+	cmd = std.slpush(cmd, std.sldup(binname))
+
+	/* [-T script] */
+	if ldscript.len > 0
+		cmd = std.slpush(cmd, std.sldup("-T"))
+		cmd = std.slpush(cmd, std.sldup(ldscript))
+	;;
+
+	cmd = std.slpush(cmd, std.fmt("%s/%s", config.Instroot, "/lib/myr/_myrrt.o"))
+
+	/* input.o list.o... */
+	for f in srcfiles
+		if std.hassuffix(f, ".myr")
+			obj = swapsuffix(f, ".myr", ".o")
+		elif std.hassuffix(f, ".s")
+			obj = swapsuffix(f, ".s", ".o")
+		else
+			std.fatal(1, "unknown file type for %s\n", f)
+			;;
+		cmd = std.slpush(cmd, obj)
+	;;
+
+	/* -l lib... */
+	cmd = addlibs(cmd, dg.libs)
+
+	/* -L incpath... */
+	for inc in opt_incpaths
+		cmd = std.slpush(cmd, std.fmt("-L%s", inc))
+	;;
+	cmd = std.slpush(cmd, std.fmt("-L%s%s", config.Instroot, "/lib/myr"))
+
+	/*
+	/* special for OSX: it warns if we don't add this */
+	if std.sleq(opt_sysname, "Darwin")
+	cmd = std.slpush(cmd, std.sldup("-macosx_version_min"))
+	cmd = std.slpush(cmd, std.sldup("10.6"))
+	;;
+	*/
+
+	run(cmd)
+	strlistfree(cmd)
+}
+
+
+const addlibs = {cmd, libgraph
+	var looped : std.htab(byte[:], bool)#
+	var marked : std.htab(byte[:], bool)#
+	var libs
+	var head
+
+	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)
+	;;
+	-> cmd
+}
+
+const visit = {cmd, head, g, lib, looped, marked -> byte[:][:]
+	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)
+	;;
+	std.htdel(looped, lib)
+	std.htput(marked, lib, true)
+	-> std.slput(cmd, head, std.fmt("-l%s", lib))
+}
+const isfresh = {src, dst
+	var srcsb, dstsb
+
+	if std.stat(src, &srcsb) != 0
+		std.fatal(1, "could not stat %s\n", src)
+	;;
+	if std.stat(dst, &dstsb) != 0
+		-> false
+	;;
+	if srcsb.mtime.sec != dstsb.mtime.sec
+		-> srcsb.mtime.sec <= dstsb.mtime.sec
+	else
+		-> srcsb.mtime.nsec <= dstsb.mtime.nsec
+	;;
+}
+
+const run = {cmd
+	var pid
+	var status
+
+	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
+		std.waitpid(pid, &status, 0)
+	;;
+	match std.waitstatus(status)
+	| `std.Waitexit estatus:
+		if estatus != 0
+			std.exit(estatus castto(int))
+		;;
+	| `std.Waitsig sig:
+		std.fatal(1, "%s exited with signal %i\n", cmd[0], sig)
+	;;
+}
+
+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 swapsuffix = {f, suff, newsuff
+	if std.hassuffix(f, suff)
+		f = f[:f.len - suff.len]
+	;;
+	-> std.fmt("%s%s", f, newsuff)
+}
+
+const strlistfree = {sl
+	for s in sl
+		std.slfree(s)
+	;;
+	std.slfree(sl)
+}
--- /dev/null
+++ b/config.myr
@@ -1,0 +1,4 @@
+pkg config = 
+const Instroot = "/home/ori/bin"
+const Sys = "Linux"
+;;
--- /dev/null
+++ b/deps.myr
@@ -1,0 +1,249 @@
+use std
+use regex
+use bio
+
+use "config.use"
+use "opts.use"
+
+pkg bld =
+	type depgraph = struct
+		roots	: byte[:][:]
+		deps	: std.htab(byte[:], byte[:][:])#
+		libs	: std.htab(byte[:], byte[:][:])#
+	;;
+
+	const myrdeps	: (dg : depgraph#, targ : byte[:], leaves : byte[:][:], islib : bool	-> bool)
+;;
+
+type dep = union
+	`Local	byte[:]
+	`Lib byte[:]
+;;
+
+var usepat	: regex.regex#
+
+const myrdeps = {dg, targ, leaves, islib
+	var seentab, donetab
+	var obj, usefile, out, useout
+
+	match regex.compile("^\\s*use\\s+(([^\"]\\S+[^\"])|(\"(\\S+)\"))")
+	| `std.Ok re:	usepat = re
+	| `std.Fail f:	std.fatal(1, "Failed to compile use pattern regex\n")
+	;;
+
+	dg.deps = std.mkht(std.strhash, std.streq)
+	dg.libs = std.mkht(std.strhash, std.streq)
+	seentab = std.mkht(std.strhash, std.streq)
+	donetab = std.mkht(std.strhash, std.streq)
+	/* direct dependencies of binary */
+	if islib
+		out = std.fmt("lib%s.a", targ)
+		useout = std.sldup(targ)
+	else
+		out = std.sldup(targ)
+		useout = ""
+	;;
+	for l in leaves
+		obj = srcswapsuffix(l, ".o")
+		pushdep(dg, obj, out)
+		pushdep(dg, l, obj)
+		if islib
+			usefile = srcswapsuffix(l, ".use")
+			pushdep(dg, usefile, useout)
+			pushdep(dg, l, usefile)
+		;;
+		srcdeps(dg, seentab, donetab, l)
+	;;
+	dumpgraph(dg)
+	std.htfree(seentab)
+	std.htfree(donetab)
+	-> true
+}
+
+const dumpgraph = {dg
+	var keys
+
+	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 = {g, seen, done, path
+	var deps
+
+	if std.hthas(done, path)
+		std.put("already know about %s\n", path)
+		->
+	elif std.htgetv(seen, path, false)
+		std.fatal(1, "dependency loop involving %s\n", path)
+	;;
+	deps = getdeps(path)
+	std.htput(seen, path, true)
+	for d in deps
+		match d
+		| `Lib lib:
+			scrapelibs(g, lib)
+		| `Local l:
+			if !std.hassuffix(l, ".use")
+				std.fatal(1, "usefile dependency \"%s\" of \"%s\" is not a usefile\n", l, path)
+			;;
+			pushdep(g, l, path)
+			addusedep(g, seen, done, l)
+		;;
+	;;
+	std.htput(seen, path, false)
+	std.htput(done, path, true)
+}
+
+const addusedep = {g, seen, done, usefile
+	var src
+
+	if std.hthas(done, usefile)
+		std.put("already know about %s\n", usefile)
+		->
+	;;
+	std.put("adding use dep for %s\n", usefile)
+	src = swapsuffix(usefile, ".use", ".myr")
+	pushdep(g, src, usefile)
+	srcdeps(g, seen, done, src)
+	std.htput(done, usefile, true)
+}
+
+const getdeps = {path
+	var f
+	var deps : dep[:]
+
+	deps = [][:]
+	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
+	var deps, d
+	var f
+	var done
+
+	if std.hthas(dg.libs, lib)
+		->
+	;;
+
+	deps = [][:]
+	f = openlib(lib)
+	match bio.getc(f)
+	| `std.Some 'U': /* nothing */
+	| `std.Some _:	std.fatal(1, "library %s is not usefile\n", lib)
+	| `std.None:	std.fatal(1, "library %s is not 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)
+	;;
+}
+
+const openlib = {lib
+	var path
+
+	for p in opt_incpaths
+		path = std.fmt("%s/%s/%s", p, "/lib/myr", lib)
+		match  bio.open(path, bio.Rd)
+		| `std.Some file:
+			-> file
+		;;
+	;;
+	path = std.fmt("%s/%s/%s", config.Instroot, "/lib/myr", lib)
+	match  bio.open(path, bio.Rd)
+	| `std.Some file:
+		-> file
+	;;
+	std.fatal(1, "could not find library %s in %s\n", lib, path)
+}
+
+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]))
+		;;
+	;;
+	-> deps
+}
+
+
+/* pushes a dep into the dependency list */
+const pushdep = {dg, to, from
+	var sl
+
+	std.put("%s ==> %s\n", from, to)
+	sl = std.htgetv(dg.deps, from, [][:])
+	sl = std.slpush(sl, to)
+	std.htput(dg.deps, from, sl)
+}
+
+const srcswapsuffix = {s, new
+	if std.hassuffix(s, ".myr")
+		-> swapsuffix(s, ".myr", new)
+	elif std.hassuffix(s, ".s")
+		-> swapsuffix(s, ".s", new)
+	else
+		std.fatal(1, "unrecognized source %s\n", s)
+	;;
+}
+
+const swapsuffix = {s, suffix, new
+	if !std.hassuffix(s, suffix)
+		std.die("swapping suffix on string without appropriate suffix\n")
+	;;
+	s = s[:s.len - suffix.len]
+	-> std.strcat(s, new)
+}
+
+const rdstr = {f
+	var len : uint32
+	var sl
+
+	match bio.getbe(f)
+	| `std.Some l:
+		len = l
+		sl = std.slalloc(len castto(std.size))
+	| `std.None:	std.die("string length not readable")
+	;;
+	bio.read(f, sl)
+	-> sl
+}
--- /dev/null
+++ b/main.myr
@@ -1,0 +1,19 @@
+use std
+use "parse.use"
+use "build.use"
+
+const main = {args : byte[:][:]
+	var p : bld.parser#
+
+	p = std.alloc()
+	p.line = 1
+	p.fname = "bldfile"
+	match std.slurp("bldfile")
+	| `std.Ok d:	p.data = d
+	| `std.Fail _:	std.fatal(1, "could not open file 'bldfile'\n")
+	;;
+	p.rest = p.data
+
+	bld.parse(p)
+	bld.build(p)
+}
--- /dev/null
+++ b/opts.myr
@@ -1,0 +1,11 @@
+pkg bld =
+	var opt_arch 	: byte[:]
+	var opt_sys	: byte[:]
+	var opt_targ	: byte[:]
+	var opt_incpaths	: byte[:][:]
+;;
+
+var opt_arch 	: byte[:]
+var opt_sys	: byte[:]
+var opt_targ	: byte[:]
+var opt_incpaths	: byte[:][:]
--- /dev/null
+++ b/parse.myr
@@ -1,0 +1,216 @@
+use std
+
+pkg bld =
+	type parser = struct
+		/* parse input */
+		data	: byte[:]
+		rest	: byte[:]
+		fname	: byte[:]
+		line	: int
+
+		/* build params */
+		targs	: target[:]
+		prefix	: byte[:]
+		system	: byte[:]
+		arch	: byte[:]
+	;;
+
+	type target = union
+		`Bin	(byte[:], byte[:][:])
+		`Lib	(byte[:], byte[:][:])
+		`Sub	byte[:][:]
+		`Man	byte[:][:]
+	;;
+
+	const parse	: (p : parser#	-> bool)
+;;
+
+const failparse = {p : parser#, msg, args : ...
+	var buf : byte[1024]
+	var ap
+	var n
+
+	ap = std.vastart(&args)
+	n = std.bfmtv(buf[:], msg, ap)
+	std.fput(1, "%s:%i: %s", p.fname, p.line, buf[:n])
+	std.exit(1)
+}
+
+const parse = {p
+	while true
+		skipspace(p)
+		if !target(p)
+			break
+		;;
+	;;
+	skipspace(p)
+	if p.rest.len > 0
+		failparse(p, "junk in file near %s", p.rest[:std.min(p.rest.len, 10)])
+		-> false
+	else
+		-> true
+	;;
+}
+
+const target = {p : parser#
+	match word(p)
+	| `std.Some "bin":	bintarget(p)
+	| `std.Some "lib":	libtarget(p)
+	| `std.Some "sub":	subtarget(p)
+	| `std.Some "man":	mantarget(p)
+	| `std.Some targtype:	failparse(p, "unknown targtype type %s\n", targtype)
+	| `std.None:	-> false
+	;;
+	-> true
+}
+
+const bintarget = {p
+	p.targs = std.slpush(p.targs, `Bin nametarget(p, "bin"))
+}
+
+const libtarget = {p
+	p.targs = std.slpush(p.targs, `Lib nametarget(p, "lib"))
+}
+
+const subtarget = {p
+	p.targs = std.slpush(p.targs, `Sub anontarget(p, "sub"))
+}
+
+const mantarget = {p
+	p.targs = std.slpush(p.targs, `Man anontarget(p, "man"))
+
+}
+const nametarget = {p, targ
+	var name, inputs
+
+	match word(p)
+	| `std.Some n:	name = n
+	| `std.None:	failparse(p, "expected target name after '%s'\n", targ)
+	;;
+
+	skipspace(p)
+	if !matchc(p, '=')
+		failparse(p, "expected '=' after '%s %s'", targ, name)
+	;;
+	match wordlist(p)
+	| `std.Some wl: inputs = wl
+	| `std.None: failparse(p, "expected list of file names after %s target\n", targ)
+	;;
+	skipspace(p)
+	if !matchc(p, ';')
+		failparse(p, "expected ';' terminating input list, got %c\n", peekc(p))
+	;;
+	-> (name, inputs)
+}
+
+const anontarget = {p, targ
+	var inputs
+
+	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, ';')
+		failparse(p, "expected ';' terminating input list\n")
+	;;
+	-> inputs
+}
+
+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
+	;;
+}
+
+const word = {p : parser#
+	var c, r, n
+	var start
+
+	skipspace(p)
+
+	r = p.rest
+	start = r
+	n = 0
+	while r.len > 0
+		c = peekc(p)
+		if wordchar(c)
+			nextc(p)
+			n += std.charlen(c)
+		else
+			break
+		;;
+	;;
+	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 == '~'
+}
+
+const skipspace = {p : parser#
+	var c, r
+
+	r = p.rest
+	while r.len > 0
+		c = peekc(p)
+		match c
+		| ' ':	nextc(p)
+		| '\t':	nextc(p)
+		| '\n':
+			nextc(p)
+			p.line++
+		| '#':
+			while p.rest.len > 0 && peekc(p) != '\n'
+				nextc(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 nextc = {p
+	var c, s
+
+	(c, s) = std.striter(p.rest)
+	p.rest = s
+	-> c
+}
+