ref: e9b59bee4efb648e323dfb6269209b59c575806f
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) if mt.install go.instdir = config.Libpath gu.instdir = config.Libpath ;; go.instmode = 0o644 gu.instmode = 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) if mt.install go.instdir = config.Binpath go.instmode = 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:]) if config.Stripman n.instname = std.sldup(pg[:i]) ;; ;; n.instdir = r n.instmode = 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) n.instdir = dt.path if dt.path.len == 0 n.instdir = config.Sharepath ;; n.instmode = 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) 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 std.slpush(&n.cmd, "-T") 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 }