shithub: mc

ref: 5fcdb860c1c14604c276bc98a49f162d6fd367c2
dir: /deps.myr/

View raw version
use std
use regex
use bio

use "config.use"
use "opts.use"
use "types.use"
use "util.use"

pkg bld =
	const myrdeps	: (dg : depgraph#, targ : byte[:], srcs : byte[:][:], islib : bool	-> bool)


	/* a bit ugly: initialized from main() */
	var usepat	: regex.regex#

;;
var usepat	: regex.regex#

type dep = union
	`Local	byte[:]
	`Lib byte[:]
;;

const myrdeps = {dg, targ, srcs, islib
	var seentab, donetab
	var out, useout
	var objs, uses
	var i

	dg.deps = std.mkht(std.strhash, std.streq)
	dg.libs = 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", targ)
		useout = std.sldup(targ)
	else
		out = std.sldup(targ)
		useout = ""
	;;
	objs = swapall(srcs, ".o")
	uses = swapall(srcs, ".use")
	for i = 0; i < srcs.len; i++
		std.htput(dg.sources, objs[i], srcs[i])
		pushdep(dg, srcs[i], objs[i])
		if std.hassuffix(srcs[i], ".myr")
			std.htput(dg.sources, 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++
		srcdeps(dg, seentab, donetab, srcs[i], objs[i], uses[i])
	;;
	dumpgraph(dg)
	std.htfree(seentab)
	std.htfree(donetab)
	-> 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 = {g, seen, done, path, obj, usefile
	var deps

	if std.hthas(done, path)
		->
	elif std.htgetv(seen, path, false)
		std.fput(1, "dependency loop involving %s\n", path)
		std.exit(1)
	;;
	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)
			;;
			if obj.len != 0
				pushdep(g, l, obj)
			;;
			if usefile.len != 0
				pushdep(g, l, usefile)
			;;
			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)
		if opt_debug
			std.put("already loaded deps for %s\n", usefile)
		;;
		->
	;;
	match std.htget(g.sources, usefile)
	| `std.Some path:
		src = std.sldup(path)
	| `std.None:
		src = swapsuffix(usefile, ".use", ".myr")
	;;
	pushdep(g, src, usefile)
	std.htput(g.sources, usefile, src)
	srcdeps(g, seen, done, src, "", usefile)
	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.pathjoin([p, "/lib/myr", 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 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]))
		;;
	| `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 : 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
}