ref: d811a3c7a8fab7269e0798ee884d4d1363d913df
dir: /mbld/deps.myr/
use std use regex use bio use "config" use "opts" use "types" use "util" pkg bld = const myrdeps : (b : build#, mt : myrtarg#, doclean : bool, addsrc : bool -> depgraph#) ;; const Abiversion = 11 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*\\*/")) } 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 cflags, libs 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), .cflags = std.mkht(std.strhash, std.streq), .extlibs = [][:], .dynamic = false, ]) /* 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]) elif std.hassuffix(srcs[i], ".glue.c") (cflags, libs) = scrapecflags(b, dg, srcs[i]) std.htput(dg.cflags, srcs[i], cflags) std.sljoin(&dg.extlibs, libs) dg.dynamic = true ;; ;; for i = 0; i < srcs.len; i++ pushdep(dg, objs[i], out) if !std.hassuffix(srcs[i], ".myr") continue ;; if mt.islib pushdep(dg, uses[i], useout) ;; srcdeps(b, &ds, dg, srcs[i], objs[i], uses[i]) ;; dumpgraph(dg) -> dg } const swapall = {srcs, suff var sl sl = [][:] for s in srcs std.slpush(&sl, srcswapsuffix(s, suff)) ;; -> sl } const dumpgraph = {dg var keys if !opt_debug -> void ;; keys = std.htkeys(dg.deps) mbldput("digraph dg {{\n") for k in keys for v in std.htgetv(dg.deps, k, ["WTFUNKNOWN!"][:]) mbldput("\t\"{}\" -> \"{}\";\n", k, v) ;; ;; mbldput("}\n") } const srcdeps = {b, ds, g, path, obj, usefile var deps if std.hthas(g.done, path) -> void ;; 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.sljoin(&l, ".use") ;; if obj.len != 0 pushdep(g, l, obj) ;; if usefile.len != 0 pushdep(g, l, usefile) ;; addusedep(b, ds, g, path, l, lnum) ;; ;; 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 mbldput("already loaded deps for {}\n", usefile) ;; -> void ;; 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 scrapecflags = {b, ds, path var cflags, libs, lnum var f lnum = 0 cflags = [][:] libs = [][:] f = opensrc(b, path) while true lnum++ match bio.readln(f) | `bio.Err e: std.fatal("unable to read {}: {}\n", path, e) | `bio.Eof: break | `bio.Ok ln: (cflags, libs) = getcflags(ln, cflags, libs) std.slfree(ln) ;; ;; bio.close(f) -> (cflags, libs) } const getcflags = {ln, cflags, libs var flags match regex.exec(cflagpat, ln) | `std.None: | `std.Some m: flags = std.strtok(m[1]) for fl in flags std.slpush(&cflags, std.sldup(fl)) ;; std.slfree(flags) regex.matchfree(m) ;; match regex.exec(clibpat, ln) | `std.None: | `std.Some m: flags = std.strtok(m[1]) for fl in flags std.slpush(&libs, std.sldup(fl)) ;; std.slfree(flags) regex.matchfree(m) ;; -> (cflags, libs) } const getdeps = {b, ds, path var deps, lnum var f lnum = 0 deps = [][:] f = opensrc(b, path) while true lnum++ match bio.readln(f) | `bio.Err e: std.fatal("unable to read {}: {}\n", path, e) | `bio.Eof: break | `bio.Ok ln: deps = depname(deps, ln, lnum) std.slfree(ln) ;; ;; bio.close(f) -> deps } const opensrc = {b, path 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.Err m: std.fatal("could not open {}: {}\n", path, m) | `std.Ok f: -> f ;; } 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 std.slpush(&deps, `Lib (std.sldup(uses[2]), lnum)) else std.slpush(&deps, `Local (std.sldup(uses[3]), lnum)) ;; regex.matchfree(uses) | `std.None: /* nothing to do */ ;; -> deps } const scrapelibs = {dg, lib, incs var deps, d var f var done if std.hthas(dg.libs, lib) -> void ;; f = openlib(lib, incs) match bio.getc(f) | `bio.Ok 'U': /* nothing */ | `bio.Ok _: std.fatal("library {}: corrupt or invalid usefile\n", lib) | `bio.Err e: std.fatal("library {}: could not read usefile: {}\n", lib, e) | `bio.Eof: std.fatal("library {}: could not read usefile\n", lib) ;; match bio.getbe32(f) | `bio.Ok Abiversion: /* nothing: version matches. */ | `bio.Ok v: if v < Abiversion std.fput(1, "library {}: warning: old abi version {}\n", lib, v) else std.fput(1, "library {}: usefile version {} unknown\n", lib, v) ;; | `bio.Err e: std.fatal("library {}: error reading usefile: {}\n", lib, e) | `bio.Eof: std.fatal("library {}: corrupt or truncated usefile\n", lib) ;; std.slfree(rdstr(f)) done = false deps = [][:] while !done match bio.getc(f) | `bio.Ok 'L': d = rdstr(f) std.slpush(&deps, d) | `bio.Ok 'X': d = rdstr(f) std.slpush(&dg.extlibs, d) dg.dynamic = true | `bio.Ok _: done = true | `bio.Eof: done = true | `bio.Err e: std.fatal("io error reading {}: {}", lib, e) ;; ;; bio.close(f) std.htput(dg.libs, lib, deps) for dep in deps scrapelibs(dg, dep, incs) ;; } const openlib = {lib, incs var path, libname for p in incs libname = std.fmt("lib{}.use", lib) path = std.pathjoin([p, libname][:]) std.slfree(libname) if std.fisreg(path) goto found ;; std.slfree(path) path = std.pathjoin([p, lib][:]) if std.fisreg(path) goto found ;; std.slfree(path) ;; libname = std.fmt("lib{}.use", lib) path = std.pathjoin([opt_instbase, config.Libpath, libname][:]) if !std.fisreg(path) std.slfree(path) path = std.pathjoin([opt_instbase, config.Libpath, lib][:]) ;; std.slfree(libname) :found match bio.open(path, bio.Rd) | `std.Ok file: std.slfree(path) -> file | `std.Err m: ;; std.fput(std.Err, "could not find library {}\n", lib) std.fput(std.Err, "search path is:\n") for p in incs std.fput(std.Err, "\t{}\n", p) ;; std.fput(std.Err, "\t{}\n", config.Libpath) std.exit(1) } /* pushes a dep into the dependency list */ const pushdep = {dg, src, dst var sl if opt_debug mbldput("{} <= {}\n", dst, src) ;; sl = std.htgetv(dg.deps, dst, [][:]) std.slpush(&sl, src) std.htput(dg.deps, dst, sl) } const rdstr = {f var len var sl match bio.getbe32(f) | `bio.Ok l: len = l sl = std.slalloc(len) | `bio.Eof: std.fatal("end of file while reading string") | `bio.Err e: std.fatal("error while reading string: {}", e) ;; bio.read(f, sl) -> sl }