shithub: mc

Download patch

ref: d1c72c34e6fd566250e4e8c750d89c3631bf9668
parent: aff5b4b72e11a8d31e45a0a99d6a02bf1b2f2ed1
author: Ori Bernstein <ori@eigenstate.org>
date: Sat Feb 14 07:36:37 EST 2015

Add initial support for implicit tests.

--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,7 @@
 	parse.myr \
 	subdir.myr \
 	util.myr \
+	test.myr \
 	types.myr
 
 include config.mk
--- a/bldfile
+++ b/bldfile
@@ -11,6 +11,7 @@
 	opts.myr
 	parse.myr
 	subdir.myr
+	test.myr
         types.myr
 	util.myr
 ;;
--- a/build.myr
+++ b/build.myr
@@ -11,9 +11,8 @@
 pkg bld =
 	const buildall	: (p : parser# -> bool)
 	const genall	: (p : parser# -> bool)
-	const test	: (p : parser# -> bool)
 	const build	: (p : parser#, target : byte[:] -> bool)
-	const buildbin	: (p : parser#, bt : myrtarg# -> void)
+	const buildbin	: (p : parser#, bt : myrtarg#, addsrc : bool -> void)
 	const buildlib	: (p : parser#, lt : myrtarg# -> void)
 ;;
 
@@ -21,7 +20,7 @@
 	for t in p.targs
 		match t
 		| `Bin bt:
-			buildbin(p, bt)
+			buildbin(p, bt, false)
 		| `Lib lt:
 			buildlib(p, lt)
 		| `Gen gt:
@@ -46,11 +45,6 @@
 	-> true
 }
 
-const test = {p
-	std.fatal(1, "testing not yet supported\n")
-	-> false
-}
-
 const build = {p, targ
 	var found
 
@@ -59,7 +53,7 @@
 		match t
 		| `Bin bt:
 			if std.sleq(bt.name, targ)
-				buildbin(p, bt)
+				buildbin(p, bt, false)
 			;;
 		| `Lib lt:
 			if std.sleq(lt.name, targ)
@@ -86,8 +80,8 @@
 	-> found
 }
 
-const buildbin = {p, targ
-	var dg
+const buildbin = {p, targ, addsrc
+	var dg, src
 
 	if targ.built
 		->
@@ -101,7 +95,7 @@
 		;;
 	;;
 	std.put("%s...\n", targ.name)
-	if !myrdeps(p, targ, false, false, &dg)
+	if !myrdeps(p, targ, false, false, addsrc, &dg)
 		std.fatal(1, "Could not load dependencies for %s\n", targ.name)
 	;;
 	if !std.hthas(dg.deps, targ.name)
@@ -108,7 +102,9 @@
 		std.fatal(1, "no input files for %s\n", targ.name)
 	;;
 	if builddep(p, &dg, targ.name, targ.incpath) || !freshlibs(targ, dg.libs)
-		linkbin(&dg, targ.name, targ.inputs, targ.ldscript, targ.runtime, targ.incpath, targ.libdeps)
+		src = std.htkeys(dg.sources)
+		linkbin(&dg, targ.name, src, targ.ldscript, targ.runtime, targ.incpath, targ.libdeps)
+		std.slfree(src)
 	;;
 	targ.built = true
 }
@@ -126,7 +122,7 @@
 	var archive
 	var u, l
 	var dg
-	var lib
+	var lib, src
 
 	if targ.built
 		->
@@ -134,7 +130,7 @@
 	lib = targ.name
 	std.put("lib%s.a...\n", lib)
 	archive = std.fmt("lib%s.a", lib)
-	if !myrdeps(p, targ, true, false, &dg)
+	if !myrdeps(p, targ, true, false, false, &dg)
 		std.fatal(1, "Could not load dependencies for %s\n", lib)
 	;;
 	if !std.hthas(dg.deps, lib)
@@ -143,8 +139,10 @@
 	u = builddep(p, &dg, targ.name, targ.incpath)
 	l = builddep(p, &dg, archive, targ.incpath)
 	if  u || l || !freshlibs(targ, dg.libs)
-		mergeuse(&dg, lib, targ.inputs, targ.incpath)
-		archivelib(&dg, lib, targ.inputs, targ.incpath)
+		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
@@ -187,7 +185,7 @@
 	| `std.None:
 	;;
 
-	match std.htget(dg.sources, out)
+	match std.htget(dg.input, out)
 	| `std.Some src:
 		if stale
 			compile(src, incs)
--- a/clean.myr
+++ b/clean.myr
@@ -58,7 +58,11 @@
 	var keys
 	var dg
 
-	if !myrdeps(p, targ, islib, true, &dg)
+	/*
+	we want to automatically add 'clean' sources since otherwise,
+	mbld won't be able to clean code after changing a build file.
+	*/
+	if !myrdeps(p, 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)
--- a/deps.myr
+++ b/deps.myr
@@ -8,7 +8,7 @@
 use "util.use"
 
 pkg bld =
-	const myrdeps	: (p : parser#, mt : myrtarg#, islib : bool, isclean : bool, dg : depgraph#	-> bool)
+	const myrdeps	: (p : parser#, mt : myrtarg#, islib : bool, isclean : bool, addsrc : bool, dg : depgraph#	-> bool)
 
 	/* a bit ugly: initialized from main() */
 	var usepat	: regex.regex#
@@ -21,7 +21,7 @@
 	`Lib byte[:]
 ;;
 
-const myrdeps = {p, mt, islib, isclean, dg
+const myrdeps = {p, mt, islib, isclean, addsrc, dg
 	var objs, uses, srcs, incs
 	var seentab, donetab
 	var out, useout
@@ -29,10 +29,12 @@
 
 	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)
 	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", mt.name)
@@ -41,18 +43,21 @@
 		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.sources, objs[i], srcs[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.sources, uses[i], srcs[i])
+			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")
@@ -59,8 +64,9 @@
 			pushdep(dg, uses[i], useout)
 		;;
 	;;
+
 	for i = 0; i < srcs.len; i++
-		srcdeps(p, dg, seentab, donetab, srcs[i], objs[i], uses[i], incs, isclean)
+		srcdeps(p, dg, seentab, donetab, srcs[i], objs[i], uses[i], incs, isclean, addsrc)
 	;;
 	dumpgraph(dg)
 	std.htfree(seentab)
@@ -94,7 +100,7 @@
 	std.put("}\n")
 }
 
-const srcdeps = {p, g, seen, done, path, obj, usefile, incs, isclean
+const srcdeps = {p, g, seen, done, path, obj, usefile, incs, isclean, addsrc
 	var deps
 
 	if std.hthas(done, path)
@@ -125,7 +131,7 @@
 			if usefile.len != 0
 				pushdep(g, l, usefile)
 			;;
-			addusedep(p, g, seen, done, l, incs, isclean)
+			addusedep(p, g, seen, done, l, incs, isclean, addsrc)
 		;;
 	;;
 	std.htput(seen, path, false)
@@ -132,7 +138,7 @@
 	std.htput(done, path, true)
 }
 
-const addusedep = {p, g, seen, done, usefile, incs, isclean
+const addusedep = {p, g, seen, done, usefile, incs, isclean, addsrc
 	var src
 
 	if std.hthas(done, usefile)
@@ -141,15 +147,20 @@
 		;;
 		->
 	;;
-	match std.htget(g.sources, 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, src)
+			std.fatal(1, "source file %s not listed in bldfile\n", src)
+		;;
 	;;
 	pushdep(g, src, usefile)
-	std.htput(g.sources, usefile, src)
-	srcdeps(p, g, seen, done, src, "", usefile, incs, isclean)
+	std.htput(g.input, usefile, src)
+	srcdeps(p, g, seen, done, src, "", usefile, incs, isclean, addsrc)
 	std.htput(done, usefile, true)
 }
 
--- a/main.myr
+++ b/main.myr
@@ -8,6 +8,7 @@
 use "install.use"
 use "opts.use"
 use "parse.use"
+use "test.use"
 use "types.use"
 
 const main = {args : byte[:][:]
@@ -66,7 +67,7 @@
 		]
 		p = mkparser("cli")
 		if bintarg
-			bld.buildbin(p, &mt)
+			bld.buildbin(p, &mt, true)
 		else
 			bld.buildlib(p, &mt)
 		;;
--- /dev/null
+++ b/test.myr
@@ -1,0 +1,118 @@
+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	: (p : parser# -> bool)
+;;
+
+const test = {p
+	var hasdir, ok
+
+	/* no implicit tests to run */
+	ok = true
+	hasdir = std.fexists("test")
+	if hasdir
+		for it in p.targs
+			match it
+			| `Bin bt: ok = dotest(p, bt, ok)
+			| `Lib lt: ok = dotest(p, lt, ok)
+			| _:	/* ignore */
+			;;
+		;;
+	;;
+	-> true
+}
+
+const dotest = {p, targ, ok
+	var tt, bin ,path, tests
+
+	tests = [][:]
+	for s in targ.inputs
+		path = std.pathcat("test", s)
+		if std.fexists(path)
+			bin = srcswapsuffix(path, "")
+			tt = [
+				.name = bin,
+				.inputs = [path, s][:],
+				.install = false,
+				.libdeps = targ.libdeps,
+				.incpath = targ.incpath,
+				.built = false,
+			]
+
+			cleantest(p, path)
+			buildbin(p, &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 = {p, 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 buf:
+			std.blat(log, buf)
+		| `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
+++ b/types.myr
@@ -21,7 +21,8 @@
 		roots	: byte[:][:]
 		deps	: std.htab(byte[:], byte[:][:])#
 		libs	: std.htab(byte[:], byte[:][:])#
-		sources	: std.htab(byte[:], byte[:])#
+		input	: std.htab(byte[:], byte[:])#
+		sources	: std.htab(byte[:], bool)#
 		updated	: std.htab(byte[:], bool)#
 	;;