shithub: mc

ref: d8e7409cd405de9a996e44ac184e5b8cd83c65c6
dir: /build.myr/

View raw version
use std
use sys

use "config.use"
use "deps.use"
use "opts.use"
use "parse.use"
use "types.use"
use "util.use"
use "subdir.use"

pkg bld =
	const buildall	: (p : parser# -> bool)
	const test	: (p : parser# -> bool)
	const build	: (p : parser#, target : byte[:] -> bool)
	const buildbin	: (p : parser#, bt : myrtarg# -> void)
	const buildlib	: (p : parser#, lt : myrtarg# -> void)
;;

const buildall = {p
	for t in p.targs
		match t
		| `Bin bt:
			buildbin(p, bt)
		| `Lib lt:
			buildlib(p, lt)
		| `Sub subs:
			subdirs(p, subs, `std.None)
		| `Man m:
			/* nothing needed */
		;;
	;;
	-> true
}

const test = {p
	std.fatal(1, "testing not yet supported\n")
	-> false
}

const build = {p, targ
	var found

	found = false
	for t in p.targs
		match t
		| `Bin bt:
			if std.sleq(bt.name, targ)
				buildbin(p, bt)
			;;
		| `Lib lt:
			if std.sleq(lt.name, targ)
				buildlib(p, lt)
				found = true
			;;
		| `Sub subs:
			found = true
			subdirs(p, subs, `std.Some targ)
		| `Man m:
			found = true
			/* nothing needed */
		;;
	;;
	if !found
		std.fatal(1, "%s: no such target\n", targ)
	;;
	-> found
}

const buildbin = {p, targ
	var dg

	if targ.built
		->
	;;
	if targ.libdeps.len > 0
		targ.incpath = std.slpush(targ.incpath, ".")
		for l in targ.libdeps
			build(p, l)
		;;
	;;
	std.put("%s...\n", targ.name)
	if !myrdeps(&dg, targ.name, targ.inputs, false)
		std.fatal(1, "Could not load dependencies for %s\n", targ.name)
	;;
	if !std.hthas(dg.deps, targ.name)
		std.fatal(1, "no target declared for %s\n", targ.name)
	;;
	if builddep(&dg, targ.name, targ.incpath)
		linkbin(&dg, targ.name, targ.inputs, targ.ldscript, targ.runtime, targ.incpath, targ.libdeps)
	;;
	targ.built = true
}

const buildlib = {lib, targ
	var archive
	var u, l
	var dg
	var lib

	if targ.built
		->
	;;
	lib = targ.name
	std.put("lib%s.a...\n", lib)
	archive = std.fmt("lib%s.a", lib)
	if !myrdeps(&dg, lib, targ.inputs, true)
		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(&dg, targ.name, targ.incpath)
	l = builddep(&dg, archive, targ.incpath)
	if  u || l
		mergeuse(&dg, lib, targ.inputs, targ.incpath)
		archivelib(&dg, lib, targ.inputs, targ.incpath)
	;;
	std.slfree(archive)
	targ.built = true
}

const builddep = {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(dg, d, incs)
				stale = true
			;;
			if !isfresh(d, out)
				stale = true
			;;
		;;
	| `std.None:
	;;

	match std.htget(dg.sources, 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

	if std.hassuffix(src, ".myr")
		cmd = [][:]
		cmd = std.slpush(cmd, "6m")
		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, ".o")
		run(["as", src, "-o", o][:], "")
		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 */
	cmd = std.slpush(cmd, std.sldup("ld"))
	cmd = std.slpush(cmd, std.sldup("-o"))
	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, ".o"))
	;;

	/* -l lib... */
	cmd = addlibs(cmd, dg.libs)
	for l in extralibs
		cmd = std.slpush(cmd, std.fmt("-l%s", l))
	;;

	/* -L incpath... */
	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"))

	/* 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 = [][:]
	cmd = std.slpush(cmd, std.sldup(opt_ar))
	cmd = std.slpush(cmd, std.sldup("-rcs"))
	cmd = std.slpush(cmd, std.fmt("lib%s.a", lib))
	for f in files
		obj = srcswapsuffix(f, ".o")
		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("-mo"))
	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
	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 sys.stat(src, &srcsb) != 0
		std.fatal(1, "could not stat %s\n", src)
	;;
	if sys.stat(dst, &dstsb) != 0
		-> false
	;;
	/*
	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.
	*/
	if srcsb.mtime.sec != dstsb.mtime.sec
		-> srcsb.mtime.sec < dstsb.mtime.sec
	else
		-> srcsb.mtime.nsec <= dstsb.mtime.nsec
	;;
}