ref: 0893bfe6bde2b9498a32354894b003adc13a9ea9
dir: /mbld/build.myr/
use std
use "config"
use "deps"
use "opts"
use "parse"
use "types"
use "util"
pkg bld =
const buildall : (b : build# -> bool)
const genall : (b : build# -> bool)
const buildtarg : (b : build#, target : byte[:] -> bool)
const buildbin : (b : build#, bt : myrtarg#, addsrc : bool -> bool)
const buildlib : (b : build#, lt : myrtarg# -> bool)
;;
const buildall = {b
for tn in b.all
if std.hthas(b.built, tn)
continue
;;
std.htput(b.built, tn, true)
match gettarg(b.targs, tn)
| `Bin bt:
if !bt.istest
buildbin(b, bt, false)
;;
| `Lib lt: buildlib(b, lt)
| `Gen gt: genfiles(b, gt)
| `Data _: /* nothing needed */
| `Man _: /* nothing needed */
| `Cmd _: /* these are for manual commands or tests */
;;
;;
setdir(b, "")
-> true
}
const buildtarg = {b, targ
var depset
depset = std.mkht(std.strhash, std.streq)
addeps(b, targ, depset)
for tn in b.all
if std.hthas(b.built, tn) || !std.hthas(depset, tn)
continue
;;
if std.sleq(tn, targ)
break
;;
std.htput(b.built, tn, true)
match gettarg(b.targs, tn)
| `Bin bt:
if !bt.istest
buildbin(b, bt, false)
;;
| `Lib lt: buildlib(b, lt)
| `Gen gt: genfiles(b, gt)
| `Data _: /* nothing needed */
| `Man _: /* nothing needed */
| `Cmd _: /* these are for manual commands or tests */
;;
;;
build(b, targ)
std.htfree(depset)
-> true
}
const addeps = {b, targ, depset
if std.hthas(depset, targ)
-> void
;;
std.htput(depset, targ, true)
match gettarg(b.targs, targ)
| `Bin bt:
for (dir, lib, targname) in bt.libdeps
addeps(b, targname, depset)
;;
| `Lib lt:
for (dir, lib, targname) in lt.libdeps
addeps(b, targname, depset)
;;
| _:
;;
}
const genall = {b
for tn in b.all
match gettarg(b.targs, tn)
| `Gen gt: runin(b, gt.cmd, gt.dir)
| _: /* 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 (`Gen gt): runin(b, gt.cmd, gt.dir)
| `std.Some (`Cmd ct): runin(b, ct.cmd, ct.dir)
| `std.Some (`Data _): /* nothing needed */
| `std.Some (`Man _): /* nothing needed */
| `std.None: std.fatal("invalid target {}\n", targ)
;;
-> true
}
const runin = {b, cmd, dir
setdir(b, dir)
run(cmd)
}
const buildbin = {b, targ, addsrc
var dg, src, path
var libpath, incs
setdir(b, targ.dir)
addincludes(b, targ)
path = std.pathcat(b.curdir, targ.name)
mbldput("{}...\n", path)
std.slfree(path)
dg = myrdeps(b, targ, false, addsrc)
if !std.hthas(dg.deps, targ.name)
std.fatal("no input files for {}\n", targ.name)
;;
if builddep(b, dg, targ.name, targ.incpath) || !freshlibs(targ, targ.name, dg.libs)
src = std.htkeys(dg.sources)
incs = std.sldup(targ.incpath)
if opt_instbase.len > 0 && !std.sleq(opt_instbase, "none")
libpath = std.pathcat(bld.opt_instbase, config.Libpath)
std.slpush(&incs, libpath)
;;
linkbin(dg, targ.name, src, targ.ldscript, targ.runtime, incs, targ.libdeps)
std.slfree(incs)
std.slfree(src)
;;
-> true
}
const buildlib = {b, targ
var archive, usefile
var u, l
var dg
var lib, src
setdir(b, targ.dir)
addincludes(b, targ)
lib = targ.name
mbldput("{}/lib{}.a...\n", b.curdir, lib)
archive = std.fmt("lib{}.a", lib)
usefile = std.fmt("lib{}.use", lib)
dg = myrdeps(b, targ, false, false)
if !std.hthas(dg.deps, lib)
std.fatal("no target declared for {}\n", lib)
;;
u = builddep(b, dg, usefile, targ.incpath)
l = builddep(b, dg, archive, targ.incpath)
if u || l || !freshlibs(targ, usefile, 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)
-> true
}
const genfiles = {b, gt
setdir(b, gt.dir)
for out in gt.out
if !std.fexists(out) || !allfresh(gt.deps, out)
run(gt.cmd)
break
;;
;;
}
const addincludes = {b, targ
for (inc, lib, subtarg) in targ.libdeps
if !hasinc(targ.incpath, inc)
std.slput(&targ.incpath, 0, inc)
;;
;;
}
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
;;
match std.htget(b.gensrc, d)
| `std.Some gt:
if !std.fexists(d) || !allfresh(gt.deps, d)
run(gt.cmd)
;;
| `std.None:
if !std.fexists(d)
std.fatal("no input file {}\n", d)
;;
;;
if !isfresh(d, out)
stale = true
;;
;;
| `std.None:
;;
match std.htget(dg.input, out)
| `std.Some src:
if stale
compile(dg, src, incs)
;;
std.htput(dg.updated, out, true)
| `std.None:
;;
-> stale
}
const compile = {dg, src, incs
var o
var cmd
cmd = [][:]
if std.hassuffix(src, ".myr")
std.slpush(&cmd, opt_mc)
for inc in incs
std.slpush(&cmd, "-I")
std.slpush(&cmd, inc)
;;
if opt_genasm
std.slpush(&cmd, "-S")
;;
std.slpush(&cmd, src)
run(cmd)
std.slfree(cmd)
elif std.hassuffix(src, ".s")
o = srcswapsuffix(src, config.Objsuffix)
for c in config.Ascmd
std.slpush(&cmd, c)
;;
std.slpush(&cmd,"-o")
std.slpush(&cmd, o)
std.slpush(&cmd, src)
run(cmd)
std.slfree(o)
elif std.hassuffix(src, ".glue.c")
o = srcswapsuffix(src, config.Objsuffix)
std.slpush(&cmd, "cc")
std.slpush(&cmd,"-c")
std.slpush(&cmd,"-o")
std.slpush(&cmd, o)
std.slpush(&cmd, src)
for flg in std.htgetv(dg.cflags, src, [][:])
std.slpush(&cmd, flg)
;;
run(cmd)
else
std.fatal("Unknown file type for {}\n", src)
;;
}
const linkbin = {dg, bin, srcfiles, ldscript, rt, incs, extlibs
var cmd
cmd = [][:]
/* ld -o bin */
for c in config.Linkcmd
std.slpush(&cmd, std.sldup(c))
;;
std.slpush(&cmd, std.sldup(bin))
/* [-T script] */
if ldscript.len > 0
std.slpush(&cmd, std.sldup("-T"))
std.slpush(&cmd, std.sldup(ldscript))
;;
if rt.len != 0
if !std.sleq(rt, "none")
std.slpush(&cmd, std.sldup(rt))
;;
else
std.slpush(&cmd, std.sldup(opt_runtime))
;;
/* input.o list.o... */
for f in srcfiles
std.slpush(&cmd, srcswapsuffix(f, config.Objsuffix))
;;
/* -L path -l lib... */
cmd = addlibs(cmd, dg.libs, incs)
/* add extra libs */
for l in dg.extlibs
std.slpush(&cmd, std.fmt("-l{}", l))
;;
/* special for OSX: it warns if we don't add this */
if std.sleq(opt_sys, "osx")
std.slpush(&cmd, std.sldup("-macosx_version_min"))
std.slpush(&cmd, std.sldup("10.6"))
elif std.sleq(opt_sys, "linux") && dg.dynamic
std.slpush(&cmd, std.sldup("-dynamic-linker"))
std.slpush(&cmd, std.sldup("/lib64/ld-linux-x86-64.so.2"))
;;
run(cmd)
strlistfree(cmd)
}
const archivelib = {dg, lib, files, incs
var cmd
var obj
cmd = [][:]
for c in config.Arcmd
std.slpush(&cmd, std.sldup(c))
;;
std.slpush(&cmd, std.fmt("lib{}.a", lib))
for f in files
obj = srcswapsuffix(f, config.Objsuffix)
std.slpush(&cmd, obj)
;;
run(cmd)
strlistfree(cmd)
}
const mergeuse = {dg, lib, files, incs
var cmd
cmd = [][:]
std.slpush(&cmd, std.sldup(opt_muse))
std.slpush(&cmd, std.sldup("-o"))
std.slpush(&cmd, std.fmt("lib{}.use", lib))
std.slpush(&cmd, std.sldup("-p"))
std.slpush(&cmd, std.sldup(lib))
for f in files
if std.hassuffix(f, ".myr")
std.slpush(&cmd, srcswapsuffix(f, ".use"))
elif !std.hassuffix(f, ".s") && !std.hassuffix(f, ".glue.c")
std.fatal("unknown file type for {}\n", f)
;;
;;
for l in dg.extlibs
std.slpush(&cmd, std.fmt("-l{}", l))
;;
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
std.slpush(&cmd, std.fmt("-L{}", inc))
;;
;;
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("cycle in library graph involving \"{}\"\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
var sep
if !config.Directlib
-> std.slput(&cmd, head, std.fmt("-l{}", lib))
else
match findlib(lib, incs)
| `std.None:
mbldput("in path: ")
sep = ""
for inc in incs
mbldput("\t{}{}\n", sep, inc)
sep = ", "
;;
std.fatal("could not find library lib{}.a.\n", 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{}.a", lib)
for i in incs
p = std.pathcat(i, sl)
if std.fexists(p)
-> `std.Some p
;;
std.slfree(p)
;;
p = std.pathjoin([opt_instbase, config.Libpath, sl][:])
if std.fexists(p)
-> `std.Some p
;;
std.slfree(p)
-> `std.None
}
const freshlibs = {targ, output, libgraph
var libs
libs = std.htkeys(libgraph)
for l in libs
match findlib(l, targ.incpath)
| `std.Some lib:
if !isfresh(lib, output)
std.slfree(lib)
-> false
;;
std.slfree(lib)
| `std.None:
std.fput(1, "{}: could not find library lib{}.a\n", targ.name, l)
std.fput(1, "searched:\n")
for inc in targ.incpath
std.fput(1, "\t{}\n", inc)
;;
std.fput(1, "\t{}/{}\n", opt_instbase, config.Libpath)
std.exit(1)
;;
;;
std.slfree(libs)
-> true
}
const allfresh = {deps, out
for d in deps
if !isfresh(d, out)
-> false
;;
;;
-> 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.Ok mt: srcmt = mt
| `std.Err e: std.fatal("could not stat {}: {}\n", src, e)
;;
match std.fmtime(dst)
| `std.Ok mt: dstmt = mt
| `std.Err e: -> false
;;
-> srcmt <= dstmt
}