ref: f17b9c158a636daff150e3b3216aaeddc004439e
dir: /mbld/syssel.myr/
use std use bio use "opts" use "types" pkg bld = type syssel(@a) = struct file : byte[:] line : int targ : byte[:] _match : std.htab(byte[:], (int, int))# _best : std.htab(byte[:], @a)# ;; generic mksyssel : (b : build#, f : byte[:], line : int, targ : byte[:] -> syssel(@a)#) generic sysseladd : (b : build#, syssel : syssel(byte[:])#, file : byte[:] -> void) generic sysseladdlist : (b : build#, syssel : syssel(@a)#, base : byte[:], attrs : byte[:][:], val : @a -> void) generic sysselfin : (b : build#, syssel : syssel(@a)# -> @a[:]) const addsysattrs : (sa : build#, tags : byte[:][:] -> void) ;; generic mksyssel = {b, file, line, targ var syssel syssel = std.mk([ .file = file, .line = line, .targ = targ, ._match = std.mkht(), ._best = std.mkht(), ]) -> syssel } generic sysseladd = {b, syssel, f var basename, attrs var attrlist match std.strfind(f, "+") | `std.Some i: basename = f[:i] match std.strrfind(f[i+1:], ".") | `std.Some j: attrs = f[i+1:][:j] | `std.None: std.fatal("unrecognized type for file {}\n", f) ;; | `std.None: match std.strrfind(f, ".") | `std.None: std.fatal("unrecognized type for file {}\n", f) | `std.Some i: basename = f[:i] attrs = "" ;; ;; attrlist = std.strsplit(attrs, "-") sysseladdlist(b, syssel, basename, attrlist, f) std.slfree(attrlist) } generic sysseladdlist = {b, syssel, base, attrs, val var nmatch, vscore, s nmatch = 0 vscore = 0 for a : attrs s = tagscore(b, a) if s < 0 nmatch = -1 break ;; vscore += s nmatch++ ;; match std.htgetv(syssel._match, base, (-1, -1)) | (curbest, curscore): if curbest < nmatch || (nmatch >= 0 && curbest == nmatch && curscore < vscore) std.htput(syssel._match, base, (nmatch, vscore)) std.htput(syssel._best, base, val) ;; ;; } const tagscore = {b, tag var n, v match std.strfind(tag, ":") | `std.Some i: n = tag[:i] v = parseversion(tag[i+1:]) | `std.None: n = tag v = (-1, -1, -1) ;; match std.htget(b.tags, n) | `std.None: -> -1 | `std.Some cv: -> versionscore(cv, v) ;; } const versionscore = {have, want var s s = 0 match (have, want) | ((a0, a1, a2), (v0, v1, v2)): if a0 == -1 && a1 == -1 && a2 == -1 -> s elif v0 == -1 && v1 == -1 && v2 == -1 -> s else s = 1_000_000 * (a0 - v0) if s == 0 s += 1_000 * (a1 - v1) ;; if s == 0 s += (a2 - v2) ;; if s >= 0 s = 100_000_000 - s ;; -> s ;; ;; } generic sysselfin = {b, syssel var keys, nmatch, ret keys = std.htkeys(syssel._match) ret = [][:] for k : keys (nmatch, _) = std.htgetv(syssel._match, k, (-1, -1)) if nmatch == -1 std.fatal("{}:{}: target {}, no applicable file for '{}'\n", \ syssel.file, syssel.line, syssel.targ, k) ;; std.slpush(&ret, std.get(std.htget(syssel._best, k))) ;; std.htfree(syssel._match) std.htfree(syssel._best) -> ret } const addsysattrs = {b, tags if opt_alltags.len > 0 for t : opt_alltags tag(b, t) ;; else std.htput(b.tags, opt_sys, opt_sysvers) match opt_sys | "freebsd": tag(b, "posixy") | "netbsd": tag(b, "posixy") | "openbsd": tag(b, "posixy") | "osx": tag(b, "posixy") | "linux": tag(b, "posixy") | "plan9": | unknown: std.fatal("unknown system \"{}\"\n", unknown) ;; match opt_arch | "x64": tag(b, "x64") if supports(CpuidSSE2) tag(b, "sse2") ;; if supports(CpuidSSE4) tag(b, "sse4") ;; if supports(CpuidFMA) tag(b, "fma") ;; | unknown: std.fatal("unknown architecture {}\n", unknown) ;; for t : tags tag(b, t) ;; loadtagfile(b, "bld.tags") ;; } const supports = {feat match (opt_sys, opt_cpufeatures & feat) /* The version of gas that's shipped with openbsd is too old. */ | ("openbsd", CpuidSSE4): -> false | ("openbsd", CpuidFMA): -> false | ("freebsd", CpuidSSE4): -> false | ("freebsd", CpuidFMA): -> false | ("netbsd", CpuidSSE4): -> false | ("netbsd", CpuidFMA): -> false | (_, f): -> f == feat ;; } const loadtagfile = {b, tagfile var deptags, tagmap, changed var tf, lnum if !std.fexists(tagfile) -> void ;; match bio.open(tagfile, bio.Rd) | `std.Ok f: tf = f | `std.Err e: std.fatal("could not open tagfile: {}\n", e) ;; lnum = 0 tagmap = std.mkht() /* * Parse the list of tags. Each line is in the form of * * tag list ":" tag list * * The left hand side of the tag list describes the tags * that get added if the tags on the right hand side all * are present. */ for ln : bio.byline(tf) lnum++ match std.strfind(ln, ":") | `std.None: for t : std.bytok(ln) tag(b, t) ;; continue | `std.Some idx: if std.strstrip(ln[:idx]).len == 0 std.fatal("{}:{}: missing tags before ':'\n", tagfile, lnum) ;; deptags = [][:] for d : std.bytok(ln[idx+1:]) std.slpush(&deptags, std.sldup(d)) ;; for t : std.bytok(ln[:idx]) match std.htget(tagmap, t) | `std.Some v: std.slpush(&v, deptags) std.htput(tagmap, t, v) | `std.None: std.htput(tagmap, std.sldup(t), std.sldup([deptags][:])) ;; ;; ;; ;; bio.close(tf) /* * Because tags may depend on other tags, we need to iterate * here until the set of tags reach a fixed point. Each tag * that we insert may potentially free other tags to be inserted, * so either we make progress on the finite set of tags, or we * don't make a change and break out of the loop. */ changed = true while changed changed = false for (k, vss) : std.byhtkeyvals(tagmap) if std.hthas(b.tags, k) continue ;; for vs : vss for v : vs if tagscore(b, v) == -1 goto next ;; ;; tag(b, k) changed = true :next ;; ;; ;; for (k, vss) : std.byhtkeyvals(tagmap) std.slfree(k) for vs : vss for v : vs std.slfree(v) ;; std.slfree(vs) ;; std.slfree(vss) ;; } const tag = {b, t var v match std.strfind(t, ":") | `std.None: std.htput(b.tags, std.sldup(t), (-1, -1, -1)) | `std.Some idx: v = parseversion(t[idx+1:]) std.htput(b.tags, std.sldup(t[:idx]), v) ;; }