ref: a1df687064dca2079aff7f1630d050de1e304b2f
dir: /mbld/deps.myr/
use std use regex use bio use "config.use" use "opts.use" use "types.use" use "util.use" pkg bld = const myrdeps : (b : build#, mt : myrtarg#, doclean : bool, addsrc : bool -> depgraph#) /* a bit ugly: initialized from main() */ var usepat : regex.regex# ;; var usepat : regex.regex# type dep = union `Local (byte[:], int) `Lib (byte[:], int) ;; type depscan = struct doclean : bool addsrc : bool tagsel : std.htab(byte[:], byte[:])# targ : myrtarg# incs : byte[:][:] depstk : byte[:][:] ;; const myrdeps = {b, mt, doclean, addsrc var objs, uses, srcs var out, useout var dg : depgraph# var ds : depscan var i dg = std.mk([ .deps = std.mkht(std.strhash, std.streq), .libs = std.mkht(std.strhash, std.streq), .input = std.mkht(std.strhash, std.streq), .sources = std.mkht(std.strhash, std.streq), .updated = std.mkht(std.strhash, std.streq), .seen = std.mkht(std.strhash, std.streq), .done = std.mkht(std.strhash, std.streq), ]) /* direct dependencies of binary */ if mt.islib out = std.fmt("lib{}.a", mt.name) useout = std.sldup(mt.name) else out = std.sldup(mt.name) useout = "" ;; ds = [ .doclean = doclean, .addsrc = addsrc, .incs = mt.incpath, .targ = mt, .depstk = [][:], ] srcs = mt.inputs objs = swapall(srcs, config.Objsuffix) uses = swapall(srcs, ".use") for i = 0; i < srcs.len; i++ std.htput(dg.input, objs[i], srcs[i]) std.htput(dg.sources, srcs[i], true) pushdep(dg, srcs[i], objs[i]) if std.hassuffix(srcs[i], ".myr") std.htput(dg.input, uses[i], srcs[i]) pushdep(dg, srcs[i], uses[i]) ;; ;; for i = 0; i < srcs.len; i++ pushdep(dg, objs[i], out) if mt.islib && std.hassuffix(srcs[i], ".myr") pushdep(dg, uses[i], useout) ;; ;; for i = 0; i < srcs.len; i++ srcdeps(b, &ds, dg, srcs[i], objs[i], uses[i]) ;; dumpgraph(dg) -> dg } 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\"{}\" -> \"{}\";\n", k, v) ;; ;; std.put("}\n") } const srcdeps = {b, ds, g, path, obj, usefile var deps if std.hthas(g.done, path) -> ;; ds.depstk = std.slpush(ds.depstk, path) if std.htgetv(g.seen, path, false) std.fput(1, "dependency loop involving {}:\n", path) for d in ds.depstk std.fput(1, "\t{}\n", d) ;; std.exit(1) ;; deps = getdeps(b, ds, path) std.htput(g.seen, path, true) for d in deps match d | `Lib (lib, lnum): /* If we're cleaning, we don't care about libraries; at best, this does nothing. At worst, this will cause failure if the library is a local library that gets cleand. */ if !ds.doclean scrapelibs(g, lib, ds.incs) ;; | `Local (l, lnum): if !std.hassuffix(l, ".use") std.fatal("{}:{}: local dependency \"{}\" should end with .use\n", path, lnum, l) ;; if obj.len != 0 pushdep(g, l, obj) ;; if usefile.len != 0 pushdep(g, l, usefile) ;; addusedep(b, ds, g, path, l, lnum) ;; ;; ds.depstk = std.slgrow(ds.depstk, ds.depstk.len - 1) std.htput(g.seen, path, false) std.htput(g.done, path, true) } const addusedep = {b, ds, g, f, usefile, line var src if std.hthas(g.done, usefile) if opt_debug std.put("already loaded deps for {}\n", usefile) ;; -> ;; match std.htget(g.input, usefile) | `std.Some path: src = std.sldup(path) | `std.None: src = swapsuffix(usefile, ".use", ".myr") if ds.addsrc std.htput(g.sources, src, true) elif !std.hthas(g.input, usefile) std.fatal("{}:{}: source file {} not listed in bldfile\n", f, line, src) ;; ;; pushdep(g, src, usefile) std.htput(g.input, usefile, src) srcdeps(b, ds, g, src, "", usefile) std.htput(g.done, usefile, true) } const getdeps = {b, ds, path var deps, lnum var f deps = [][:] if !std.fexists(path) match std.htget(b.gensrc, path) | `std.Some gt: run(gt.cmd) | `std.None: std.fatal("no input file {}\n", path) ;; ;; match bio.open(path, bio.Rd) | `std.Ok fd: f = fd | `std.Fail m: std.fatal("could not open {}: {}\n", path, m) ;; lnum = 0 while true lnum++ match bio.readln(f) | `std.Some ln: deps = depname(deps, ln, lnum) std.slfree(ln) | `std.None: bio.close(f) -> deps ;; ;; std.die("unreachable") } const scrapelibs = {dg, lib, incs var deps, d var f var done if std.hthas(dg.libs, lib) -> ;; deps = [][:] f = openlib(lib, incs) match bio.getc(f) | `std.Some 'U': /* nothing */ | `std.Some _: std.fatal("library {}: corrupt or invalid usefile\n", lib) | `std.None: std.fatal("library {}: could not read usefile\n", lib) ;; match bio.getbe32(f) | `std.Some 4: /* nothing: version matches. */ | `std.Some 3: std.fput(1, "library {}: warning: old usefile version\n", lib) | `std.Some 2: std.fput(1, "library {}: warning: old usefile version\n", lib) | `std.Some 1: std.fput(1, "library {}: warning: old usefile version\n", lib) | `std.Some 0: std.fput(1, "library {}: warning: old usefile version\n", lib) | `std.Some _: std.fatal("library {}: usefile version unknown\n", lib) | `std.None: std.fatal("library {}: corrutpt or invalid 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, incs) ;; } const openlib = {lib, incs var path for p in incs path = std.pathjoin([p, lib][:]) match bio.open(path, bio.Rd) | `std.Ok file: -> file | `std.Fail m: /* nothing */ ;; ;; path = std.pathjoin([opt_instroot, config.Libpath, lib][:]) match bio.open(path, bio.Rd) | `std.Ok file: -> file | `std.Fail m: /* nothing */ ;; std.fatal("could not find library {}.\n", lib) } const depname = {deps, ln, lnum /* 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, `Lib (std.sldup(uses[2]), lnum)) else deps = std.slpush(deps, `Local (std.sldup(uses[3]), lnum)) ;; | `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("{} <= {}\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 var sl match bio.getbe32(f) | `std.Some l: len = l sl = std.slalloc(len) | `std.None: std.die("string length not readable") ;; bio.read(f, sl) -> sl }