ref: dda657081b032dc653f669ca85f2c09984f0cb35
dir: /mbld/deps.myr/
use std
use regex
use bio
use "config"
use "opts"
use "types"
use "util"
use "libs"
pkg bld =
const deps : (b : build# -> void)
const testdeps : (b : build# -> void)
const resolve : (b : build# -> void)
;;
type dep = union
`Xdep byte[:]
`Ldep byte[:]
;;
var usepat : regex.regex#
var cflagpat : regex.regex#
var clibpat : regex.regex#
const __init__ = {
usepat = std.try(regex.compile("^\\s*use\\s+((\\<\\S+\\>)|\"(\\S+)\").*"))
cflagpat = std.try(regex.compile("/\\*\\s*CFLAGS:\\s*(.*)\\s*\\*/"))
clibpat = std.try(regex.compile("/\\*\\s*LIBS:\\s*(.*)\\s*\\*/"))
}
const deps = {b
for (name, `Gen gt) : std.byhtkeyvals(b.targs)
cmddeps(b, name, gt)
;;
for name : b.all
match gettarg(b.targs, name)
| `Bin bt: myrdeps(b, name, bt)
| `Lib lt: myrdeps(b, name, lt)
| `Cmd ct: cmddeps(b, name, ct)
| `Man mt: mandeps(b, name, mt)
| `Data dt: datdeps(b, name, dt)
| `Gen gt: /* gen was dealt with earlier */
;;
;;
}
const testdeps = {b
for name : b.all
match gettarg(b.targs, name)
| `Bin bt: addtests(b, name, bt)
| `Lib lt: addtests(b, name, lt)
| _: /* skip */
;;
;;
}
const myrdeps = {b, name, mt
var t, p, o, u, n, to, tu
var libs, dynlibs
var cflags, ll
var g, a, deps
var gu, go
g = b.deps
if mt.islib
u = std.fmt("lib{}.use", mt.name)
o = std.fmt("lib{}.a", mt.name)
tu = std.pathjoin([opt_objdir, mt.dir, u][:])
to = std.pathjoin([opt_objdir, mt.dir, o][:])
gu = node(g, tu)
go = node(g, to)
go.instdir = config.Libpath
gu.instdir = config.Libpath
go.instmod = 0o644
gu.instmod = 0o644
generates(g, gu, tu)
generates(g, go, to)
a = std.htgetv(g.targs, "all", [][:])
std.slpush(&a, gu)
std.slpush(&a, go)
std.htput(g.targs, "all", a)
std.htput(g.targs, name, std.sldup([gu, go][:]))
std.slfree(o)
std.slfree(u)
else
u = std.fmt("{}.use", mt.name)
tu = std.pathjoin([opt_objdir, mt.dir, u][:])
to = std.pathjoin([opt_objdir, mt.dir, mt.name][:])
go = node(g, to)
gu = node(g, tu)
generates(g, go, to)
std.htput(g.targs, name, std.sldup([go][:]))
if mt.istest
n = node(g, mt.name)
depends(g, n, to)
addnode(g, "test", n)
n.wdir = std.sldup(mt.dir)
std.slpush(&n.cmd, std.pathjoin([b.basedir, opt_objdir, mt.dir, mt.name][:]))
elif mt.isbench
n = node(g, mt.name)
depends(g, n, to)
addnode(g, "bench", n)
n.wdir = std.sldup(mt.dir)
std.slpush(&n.cmd, std.pathjoin([b.basedir, opt_objdir, mt.dir, mt.name][:]))
else
addnode(g, "all", go)
go.instdir = config.Binpath
go.instmod = 0o755
;;
std.slfree(u)
;;
libs = [][:]
dynlibs = [][:]
for f : mt.inputs
p = std.pathcat(mt.dir, f)
leaf(g, p)
if std.hassuffix(f, ".myr")
t = changesuffix(p, config.Objsuffix)
o = std.pathcat(opt_objdir, t)
std.slfree(t)
t = changesuffix(p, ".use")
u = std.pathcat(opt_objdir, t)
std.slfree(t)
depends(g, go, o)
depends(g, gu, u)
n = node(g, o)
generates(g, n, o)
generates(g, n, u)
depends(g, n, p)
deps = scrapedeps(b, mt, p)
for `Ldep d : deps
depends(g, n, d)
;;
for `Xdep d : deps
scrapelib(b, mt.name, d, mt.incpath)
xdepends(b, g, n, d)
std.slpush(&libs, d)
;;
myrcmd(b, n, mt, p, false)
std.slfree(deps)
elif std.hassuffix(f, ".s")
t = changesuffix(p, config.Objsuffix)
o = std.pathcat(opt_objdir, t)
std.slfree(t)
depends(g, go, o)
n = node(g, o)
generates(g, n, o)
depends(g, n, p)
ascmd(b, n, mt, o, p)
elif std.hassuffix(f, ".glue.c")
t = changesuffix(p, config.Objsuffix)
o = std.pathcat(opt_objdir, t)
std.slfree(t)
(cflags, ll) = scrapecflags(b, mt, p)
depends(g, go, o)
n = node(g, o)
generates(g, n, o)
depends(g, n, p)
ccmd(b, n, mt, o, p, cflags)
for l : ll
std.slpush(&dynlibs, l)
;;
mt.isdyn = true
elif std.hassuffix(f, config.Objsuffix)
depends(g, go, p)
else
std.fatal("don't know how to build {}/{}\n", mt.dir, f)
;;
;;
if mt.islib
arcmd(b, go, mt, to)
musecmd(b, gu, mt, tu, dynlibs)
builtlib(b, mt, libs, dynlibs)
else
linkcmd(b, go, mt, to, libs, dynlibs, false)
std.slfree(libs)
;;
}
const cmddeps = {b, name, ct
var n, a, p, gen, pid
n = node(b.deps, std.strjoin(ct.cmd, " "))
n.wdir = std.sldup(ct.dir)
n.durable = ct.durable
for c : ct.cmd
std.slpush(&n.cmd, std.sldup(c))
;;
if ct.deps.len == 0
std.slpush(&b.deps.leaves, n)
else
for d : ct.deps
leaf(b.deps, d)
depends(b.deps, n, d)
;;
;;
gen = false
for g : ct.gen
p = std.pathcat(ct.dir, g)
gen = gen || !std.fexists(p)
generates(b.deps, n, p)
;;
if ct.istest
a = std.htgetv(b.deps.targs, "test", [][:])
std.slpush(&a, n)
std.htput(b.deps.targs, "test", a)
elif ct.isbench
a = std.htgetv(b.deps.targs, "bench", [][:])
std.slpush(&a, n)
std.htput(b.deps.targs, "bench", a)
elif gen
pid = run(ct.cmd, ct.dir)
match std.wait(pid)
| `std.Wfailure: std.fatal("FAIL: {j= }\n", ct.cmd)
| `std.Wsignalled: std.fatal("CRASH: {j= }\n", ct.cmd)
| `std.Waiterror: std.fatal("WAT: {j= }\n", ct.cmd)
| `std.Wsuccess: /* ok */
;;
;;
}
const mandeps = {b, name, mt
var p, r, n
for pg : mt.pages
p = std.pathcat(mt.dir, pg)
n = leaf(b.deps, p)
match std.strrfind(pg, ".")
| `std.None: std.fatal("manpage {} missing section\n", pg)
| `std.Some i: r = std.strcat(config.Manpath, pg[i + 1:])
;;
n.instdir = r
n.instmod = 0o644
addnode(b.deps, "all", n)
;;
}
const datdeps = {b, name, dt
var p, n
for db : dt.blobs
p = std.pathcat(dt.dir, db)
n = leaf(b.deps, p)
if dt.path.len == 0
n.instdir = config.Sharepath
else
n.instdir = dt.path
;;
n.instmod = 0o644
addnode(b.deps, "all", n)
;;
}
const addtests = {b, name, mt
for f : mt.inputs
addtest(b, mt, f)
addbench(b, mt, f)
;;
}
const addtest = {b, mt, f
addalt(b, mt, "test", f)
}
const addbench = {b, mt, f
addalt(b, mt, "bench", f)
}
const addalt = {b, mt, kind, f
var libs, deps
var sp, tp, op
var s, t, o
var g, n
var testinc
/*
change of suffix is to support testing assembly,
C glue, and foo+sys.myr forms.
*/
g = b.deps
s = changesuffix(f, ".myr")
sp = std.pathjoin([mt.dir, kind, s][:])
std.slfree(s)
if !std.fexists(sp)
std.slfree(sp)
-> void
;;
libs = [][:]
leaf(g, sp)
t = changesuffix(f, "")
tp = std.pathjoin([opt_objdir, mt.dir, kind, t][:])
std.slfree(t)
o = changesuffix(f, config.Objsuffix)
op = std.pathjoin([opt_objdir, mt.dir, kind, o][:])
std.slfree(o)
n = node(g, sp)
generates(g, n, op)
depends(g, n, sp)
testinc = [][:]
std.slpush(&testinc, mt.dir)
std.sljoin(&testinc, mt.incpath)
deps = scrapedeps(b, mt, sp)
for `Ldep d : deps
depends(g, n, d)
;;
for `Xdep d : deps
scrapelib(b, mt.name, d, mt.incpath)
xdepends(b, g, n, d)
std.slpush(&libs, d)
;;
myrcmd(b, n, mt, sp, true)
std.slfree(mt.incpath)
n = node(g, tp)
generates(g, n, tp)
depends(g, n, op)
linkcmd(b, n, mt, tp, libs, [][:], true)
std.slfree(libs)
n = node(g, tp)
depends(g, n, tp)
n.wdir = std.sldup(std.dirname(std.dirname(sp)))
std.slpush(&n.cmd, std.pathjoin([b.basedir, opt_objdir, mt.dir, kind, std.basename(tp)][:]))
addnode(g, kind, n)
}
const resolve = {b
var visited, looped, stk
var g
g = b.deps
for n : g.nodes
for e : n.dep
edge(g, n, e)
;;
;;
stk = [][:]
visited = std.mkht()
looped = std.mkht()
for n : g.nodes
checkloop(g, n, visited, looped, &stk)
;;
std.htfree(visited)
std.htfree(looped)
}
const edge = {g, n, e
match std.htget(g.gen, e)
| `std.None:
std.fatal("{}: missing build rule for {}\n", n.lbl, e)
| `std.Some d:
std.slpush(&n.ndep, d)
std.slpush(&d.ngen, n)
n.nblock++
;;
}
const checkloop = {g, n, visited, looped, stk
if std.hthas(looped, n)
std.slpush(stk, n.lbl)
std.fatal("dependency loop: {j= -> }\n", stk#)
;;
if std.hthas(visited, n)
-> void
;;
std.slpush(stk, n.lbl)
std.htput(visited, n, void)
std.htput(looped, n, void)
for d : n.ndep
checkloop(g, d, visited, looped, stk)
;;
std.slpop(stk)
std.htdel(looped, n)
}
const musecmd = {b, n, mt, mu, dynlibs
std.slpush(&n.cmd, std.sldup(opt_muse))
for o : opt_museflags
std.slpush(&n.cmd, o)
;;
for l : dynlibs
std.slpush(&n.cmd, std.fmt("-l{}", l))
;;
std.slpush(&n.cmd, std.sldup("-o"))
std.slpush(&n.cmd, std.sldup(mu))
std.slpush(&n.cmd, std.sldup("-p"))
std.slpush(&n.cmd, std.sldup(mt.name))
for u : n.dep
std.slpush(&n.cmd, std.sldup(u))
;;
}
const arcmd = {b, n, mt, ar
for c : config.Arcmd
std.slpush(&n.cmd, std.sldup(c))
;;
std.slpush(&n.cmd, std.sldup(ar))
for obj : n.dep
std.slpush(&n.cmd, std.sldup(obj))
;;
}
const linkcmd = {b, n, mt, bin, libs, dynlibs, istest
var dynlink
for c : config.Linkcmd
std.slpush(&n.cmd, std.sldup(c))
;;
for o : opt_ldflags
std.slpush(&n.cmd, o)
;;
std.slpush(&n.cmd, "-o")
std.slpush(&n.cmd, std.sldup(bin))
if mt.ldscript.len > 0
std.slpush(&n.cmd, std.sldup("-T"))
std.slpush(&n.cmd, std.sldup(mt.ldscript))
;;
if mt.runtime.len == 0 || std.eq(mt.runtime, "none")
std.slpush(&n.cmd, std.sldup(opt_runtime))
else
std.slpush(&n.cmd, std.sldup(mt.runtime))
;;
for o : n.dep
std.slpush(&n.cmd, std.sldup(o))
;;
dynlink = addlibs(b, &n.cmd, libs, mt.incpath) || mt.isdyn
for l : dynlibs
std.slpush(&n.cmd, std.fmt("-l{}", l))
;;
if dynlink
for f : config.Dlflags
std.slpush(&n.cmd, std.sldup(f))
;;
;;
/* OSX warns if we don't add a version */
if std.eq(opt_sys, "osx")
std.slpush(&n.cmd, std.sldup("-macosx_version_min"))
std.slpush(&n.cmd, std.sldup("10.6"))
;;
}
const myrcmd = {b, n, mt, src, istest
std.slpush(&n.cmd, std.sldup(opt_mc))
for o : opt_mcflags
std.slpush(&n.cmd, o)
;;
if opt_objdir.len > 0
pushopt(&n.cmd, "-O", std.sldup(opt_objdir))
;;
for inc : mt.incpath[:mt.incpath.len - 1]
pushopt(&n.cmd, "-I", inc)
;;
if istest
for (dir, _, _) : mt.tstdeps
pushopt(&n.cmd, "-I", std.pathcat(opt_objdir, dir))
;;
pushopt(&n.cmd, "-I", std.pathcat(opt_objdir, mt.dir))
;;
for (dir, _, _) : mt.libdeps
pushopt(&n.cmd, "-I", std.pathcat(opt_objdir, dir))
;;
if opt_genasm
std.slpush(&n.cmd, "-S")
;;
std.slpush(&n.cmd, src)
}
const ascmd = {b, n, mt, out, src
for c : config.Ascmd
std.slpush(&n.cmd, c)
;;
std.slpush(&n.cmd,"-o")
std.slpush(&n.cmd, out)
std.slpush(&n.cmd, src)
}
const ccmd = {b, n, mt, out, src, cflags
std.slpush(&n.cmd, "cc")
std.slpush(&n.cmd,"-c")
std.slpush(&n.cmd,"-o")
std.slpush(&n.cmd, out)
std.slpush(&n.cmd, src)
for flg : cflags
std.slpush(&n.cmd, flg)
;;
}
const scrapedeps = {b : build#, mt, path
var p, f, l
match bio.open(path, bio.Rd)
| `std.Ok fd: f = fd
| `std.Err e: std.fatal("error opening {}: {}\n", path, e)
;;
l = [][:]
for ln : bio.byline(f)
match std.strfind(ln, "use")
| `std.None: continue
| `std.Some _: /* ok */
;;
match regex.exec(usepat, ln)
| `std.None:
| `std.Some uses:
if uses[2].len > 0
/* external library */
p = std.sldup(uses[2])
std.slpush(&l, `Xdep p)
else
/* internal library */
p = std.pathjoin([opt_objdir, mt.dir, uses[3]][:])
std.sljoin(&p, ".use")
std.slpush(&l, `Ldep p)
;;
regex.matchfree(uses)
;;
;;
bio.close(f)
-> l
}
const scrapecflags = {b, mt, path
var f, fl, cflags, libs
match bio.open(path, bio.Rd)
| `std.Ok fd: f = fd
| `std.Err e: std.fatal("error opening {}: {}\n", path, e)
;;
cflags = [][:]
libs = [][:]
for ln : bio.byline(f)
match regex.exec(cflagpat, ln)
| `std.None: /* skip */
| `std.Some m:
fl = std.strtok(m[1])
for s : fl
std.slpush(&cflags, std.sldup(s))
;;
std.slfree(fl)
;;
match regex.exec(clibpat, ln)
| `std.None: /* skip */
| `std.Some m:
fl = std.strtok(m[1])
for s : fl
std.slpush(&libs, std.sldup(s))
;;
std.slfree(fl)
;;
;;
-> (cflags, libs)
}
const generates = {g, n, dep
std.mkpath(std.dirname(dep))
std.slpush(&n.gen, dep)
std.htput(g.gen, dep, n)
}
const depends = {g, n, dep
std.slpush(&n.dep, dep)
}
const xdepends = {b, g, n, dep
match std.htget(b.libs, dep)
| `std.Some ldep:
if ldep.genuse.len != 0
depends(g, n, ldep.genuse)
depends(g, n, ldep.genar)
;;
n.deptime = std.max(n.deptime, ldep.mtime)
| `std.None:
std.fatal("unknown xdep {} (known: {})\n", dep, std.htkeys(b.libs))
;;
}
const leaf = {g, f
var nod
match std.htget(g.gen, f)
| `std.Some n:
-> n
| `std.None:
nod = node(g, f)
nod.durable = true
generates(g, nod, f)
std.slpush(&g.leaves, nod)
-> nod
;;
}
const addnode = {g, targ, n
var nl
nl = std.htgetv(g.targs, targ, [][:])
std.slpush(&nl, n)
std.htput(g.targs, targ, nl)
}
const pushopt = {lst, pfx, dir
std.slpush(lst, std.sldup(pfx))
std.slpush(lst, std.pathnorm(dir))
}
const node = {g, lbl
var nod
nod = std.mk([
.lbl=lbl,
.cmd=[][:],
.gen=[][:],
.dep=[][:],
.nblock=0,
.mtime=0,
])
std.slpush(&g.nodes, nod)
-> nod
}