wcc/ww: ww build --sep separate-compilation driver (M3-tail c3, #46)
build_one_sep (both stages + ww/main.combined.ww regen): discover_deps (transitive directory-package set, dotted-path identity), tri-color reverse-topo (cycle bails; loud reject is commit 4), the transitive producer loop (one w6c -c -I pass per package, dep-first, each both consumer and producer of its .wwi), and a flat w6l of the .o set. combined.ww stays the DEFAULT live path; --sep is additive. Every dep is tagged by its full dotted import path on prepend (//ww:module <path>), so the definer's qualified symbol (#53) == the consumer's qualified reference (#40) and the sep .o set links. The prepend is the TRANSITIVE closure (lead-ratified, superseding rob-c3-spec §1.3 direct-deps): a dep's interface can name a transitive dep's type (os exposes time.instant), so the consuming unit needs the whole closure for resolution — direct-deps-only does not type-check. Consistent with the current flat-unit transitive-namespace model (the visibility tighten is #45, post-M4). Gate 989_sepbuild_run drives ww + ww_ww --sep on the real chain root->os->{rt,time}: build+run (exit 7) + cs==ww per-pkg .s/.wwi/.unit + final binary + transitive-topo discovery + keystone bodies==.wwi (os,root) through the real driver. Cold scratch; -o-redirected.
This commit is contained in:
@@ -3720,6 +3720,545 @@ type lflags = struct {
|
||||
nlibs: i32,
|
||||
};
|
||||
|
||||
// ---- ww build --sep — M3-tail separate-compilation driver ------------
|
||||
//
|
||||
// Port of cmd/ww/main.c build_one_sep (task #46/c3). The `--sep` path
|
||||
// materializes each imported package's `.wwi` interface and compiles
|
||||
// every package on its own (`w6c -c`), then flat-links the `.o` set.
|
||||
// combined.ww stays the DEFAULT live path; --sep is additive.
|
||||
//
|
||||
// Each w6c pass is BOTH consumer (reads dep `.wwi` as import scope) AND
|
||||
// producer (writes this package's `.wwi` via -I). Reverse-topo order
|
||||
// guarantees a package's deps' `.wwi` exist before it compiles.
|
||||
//
|
||||
// The load-bearing rule (#56, rob-resolved): every dep is tagged by its
|
||||
// FULL DOTTED import path on prepend (`//ww:module <path>`), so the
|
||||
// definer's qualified symbol (#53) equals the consumer's qualified
|
||||
// reference (#40) and the sep `.o`s link. The prepend is the TRANSITIVE
|
||||
// closure of a package's deps (lead-ratified): a dep's interface can
|
||||
// name a transitive dep's type, so the consuming unit needs the whole
|
||||
// closure for resolution. The unit composition is byte-identical to the
|
||||
// cstage driver (rule 10) so w6c/w6c_ww emit identical `.s`.
|
||||
|
||||
def SEP_MAXPKG: i32 = 256;
|
||||
|
||||
type seppkg = struct {
|
||||
path: *u8, // dotted import path, NUL-term; root path[0]==0
|
||||
entry: *u8, // resolved package dir (or file, file root), NUL-term
|
||||
isdir: i32,
|
||||
deps: []i32, // direct-dep indices into sepgraph.pkg
|
||||
ndeps: i32,
|
||||
color: i32, // tri-color DFS: 0 white, 1 gray, 2 black
|
||||
};
|
||||
|
||||
type sepgraph = struct {
|
||||
pkg: []seppkg, // alloc'd SEP_MAXPKG
|
||||
n: i32,
|
||||
};
|
||||
|
||||
// Find a package by dotted path, or add it. Returns index, -1 if full.
|
||||
fn sepfindoradd(g: *sepgraph, path: *u8, entry: *u8, isdir: i32) i32 = {
|
||||
let i: i32 = 0;
|
||||
for (i < g.n) {
|
||||
if (cstreq(g.pkg[i].path, path)) { return i; };
|
||||
i += 1;
|
||||
};
|
||||
if (g.n >= SEP_MAXPKG) {
|
||||
cerr("ww --sep: too many packages\n");
|
||||
return -1;
|
||||
};
|
||||
let plen: u64 = cstrlen(path);
|
||||
let elen: u64 = cstrlen(entry);
|
||||
g.pkg[g.n].path = arenadupcstr(path, plen);
|
||||
g.pkg[g.n].entry = arenadupcstr(entry, elen);
|
||||
g.pkg[g.n].isdir = isdir;
|
||||
let dslot: []i32 = alloc([], SEP_MAXPKG: u64)!;
|
||||
dslot.len = SEP_MAXPKG;
|
||||
g.pkg[g.n].deps = dslot;
|
||||
g.pkg[g.n].ndeps = 0;
|
||||
g.pkg[g.n].color = 0;
|
||||
let r: i32 = g.n;
|
||||
g.n += 1;
|
||||
return r;
|
||||
};
|
||||
|
||||
// Build "<scratch>/<base><suffix>" NUL-term; base = path, or "__root"
|
||||
// for the empty root path.
|
||||
fn sepfname(g: *sepgraph, pi: i32, scratch: *u8, suffix: str) *u8 = {
|
||||
let buf: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
buf.len = os.PATH_MAX;
|
||||
let off: u64 = cstrinto(buf.ptr, 0u64, scratch);
|
||||
off = byteinto(buf.ptr, off, 47u8); // '/'
|
||||
if (g.pkg[pi].path[0u64] != 0u8) {
|
||||
off = cstrinto(buf.ptr, off, g.pkg[pi].path);
|
||||
} else {
|
||||
off = strinto(buf.ptr, off, "__root");
|
||||
};
|
||||
off = strinto(buf.ptr, off, suffix);
|
||||
cstrseal(buf.ptr, off);
|
||||
return buf.ptr;
|
||||
};
|
||||
|
||||
// Scan one source file for top-level `import IDENT;`. A DIRECTORY import
|
||||
// is a package boundary: add as a direct dep of pi. A FILE import is an
|
||||
// intra-package split: fold its imports into pi. Mirrors cstage
|
||||
// sep_scan_file (collects PATHS, not bytes).
|
||||
fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
|
||||
fv: *expctx) i32 = {
|
||||
let fview: str;
|
||||
fview.ptr = file;
|
||||
fview.len = cstrlen(file): i32;
|
||||
let fdup: str = strings.dup(fview);
|
||||
if (visitseen(fv, fdup)) { return 0; };
|
||||
visitadd(fv, fdup);
|
||||
let bufp: *u8;
|
||||
let blen: u64;
|
||||
bufp, blen = slurp(file);
|
||||
if (bufp == nil) {
|
||||
cerr("ww --sep: cannot read source\n");
|
||||
return -1;
|
||||
};
|
||||
let i: u64 = 0u64;
|
||||
for (i < blen) {
|
||||
let j: u64 = i;
|
||||
for (j < blen) {
|
||||
if (bufp[j] == 10u8) { break; }; // '\n'
|
||||
j += 1u64;
|
||||
};
|
||||
let idp: *u8;
|
||||
let idn: u64;
|
||||
idp, idn = scanuse(bufp + i, j - i);
|
||||
if (idp != nil) {
|
||||
let isdir: i32 = 0;
|
||||
let ipath: *u8 = locateimport(searchpath, idp, idn, &isdir);
|
||||
if (ipath != nil) {
|
||||
if (isdir != 0) {
|
||||
let nm: []u8 = alloc([], idn + 1u64)!;
|
||||
let k: u64 = 0u64;
|
||||
for (k < idn) { nm[k] = idp[k]; k += 1u64; };
|
||||
nm[idn] = 0u8;
|
||||
let di: i32 = sepfindoradd(g, nm.ptr, ipath, 1);
|
||||
if (di < 0) { return -1; };
|
||||
let seen: bool = false;
|
||||
let m: i32 = 0;
|
||||
for (m < g.pkg[pi].ndeps) {
|
||||
if (g.pkg[pi].deps[m] == di) { seen = true; };
|
||||
m += 1;
|
||||
};
|
||||
if (!seen) {
|
||||
if (g.pkg[pi].ndeps >= SEP_MAXPKG) { return -1; };
|
||||
g.pkg[pi].deps[g.pkg[pi].ndeps] = di;
|
||||
g.pkg[pi].ndeps += 1;
|
||||
};
|
||||
} else {
|
||||
if (sepscanfile(g, pi, ipath, searchpath, fv) < 0) {
|
||||
return -1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
i = j + 1u64;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Discover pi's direct deps + recurse. Enumerate the package's own
|
||||
// files (dir → *.ww less *test.ww; file → the file) and scan each.
|
||||
// `color` doubles as a scanned-marker (2); reset to white before topo.
|
||||
fn sepscanpkg(g: *sepgraph, pi: i32, searchpath: *u8) i32 = {
|
||||
if (g.pkg[pi].color == 2) { return 0; };
|
||||
g.pkg[pi].color = 2;
|
||||
let fv: expctx;
|
||||
fv.out = -1;
|
||||
fv.dirs = searchpath;
|
||||
fv.visit = nil;
|
||||
let rc: i32 = 0;
|
||||
if (g.pkg[pi].isdir != 0) {
|
||||
let names: **u8;
|
||||
let n: i32;
|
||||
names, n = enumeratedir(g.pkg[pi].entry);
|
||||
let dlen: u64 = cstrlen(g.pkg[pi].entry);
|
||||
let i: i32 = 0;
|
||||
for (i < n) {
|
||||
if (rc == 0) {
|
||||
let nlen: u64 = cstrlen(names[i]);
|
||||
let fp: []u8 = alloc([], dlen + 1u64 + nlen + 1u64)!;
|
||||
let k: u64 = 0u64;
|
||||
for (k < dlen) { fp[k] = g.pkg[pi].entry[k]; k += 1u64; };
|
||||
fp[dlen] = 47u8; // '/'
|
||||
k = 0u64;
|
||||
for (k < nlen) { fp[dlen + 1u64 + k] = names[i][k]; k += 1u64; };
|
||||
fp[dlen + 1u64 + nlen] = 0u8;
|
||||
rc = sepscanfile(g, pi, fp.ptr, searchpath, &fv);
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
} else {
|
||||
rc = sepscanfile(g, pi, g.pkg[pi].entry, searchpath, &fv);
|
||||
};
|
||||
if (rc < 0) { return rc; };
|
||||
let k: i32 = 0;
|
||||
for (k < g.pkg[pi].ndeps) {
|
||||
if (sepscanpkg(g, g.pkg[pi].deps[k], searchpath) < 0) { return -1; };
|
||||
k += 1;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// DFS post-order over the dep DAG → reverse-topo (deps before importer).
|
||||
// Tri-color: a back-edge BAILS rather than spinning (loud cycle reject is
|
||||
// commit 4). Cite Hare gather (deps.ha:123).
|
||||
fn septopovisit(g: *sepgraph, pi: i32, order: []i32, no: *i32) i32 = {
|
||||
if (g.pkg[pi].color == 2) { return 0; };
|
||||
if (g.pkg[pi].color == 1) {
|
||||
cerr("ww --sep: import cycle (loud reject lands commit 4)\n");
|
||||
return -1;
|
||||
};
|
||||
g.pkg[pi].color = 1;
|
||||
let k: i32 = 0;
|
||||
for (k < g.pkg[pi].ndeps) {
|
||||
if (septopovisit(g, g.pkg[pi].deps[k], order, no) < 0) { return -1; };
|
||||
k += 1;
|
||||
};
|
||||
g.pkg[pi].color = 2;
|
||||
order[*no] = pi;
|
||||
*no += 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Mark pi's transitive deps (excluding pi) in inset[].
|
||||
fn sepmarkdeps(g: *sepgraph, pi: i32, inset: []u8) void = {
|
||||
let k: i32 = 0;
|
||||
for (k < g.pkg[pi].ndeps) {
|
||||
let di: i32 = g.pkg[pi].deps[k];
|
||||
if (inset[di] == 0u8) {
|
||||
inset[di] = 1u8;
|
||||
sepmarkdeps(g, di, inset);
|
||||
};
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
|
||||
// Emit one of pi's own source files into the sep-unit under the
|
||||
// //ww:module-reset primary boundary (so -c emits its decls, imported
|
||||
// ==0). DIRECTORY imports are skipped (provided as `.wwi` ahead);
|
||||
// FILE imports fold in (intra-package split).
|
||||
fn sepemitbody(fd: i32, path: *u8, visit: *expctx, searchpath: *u8) void = {
|
||||
let pview: str;
|
||||
pview.ptr = path;
|
||||
pview.len = cstrlen(path): i32;
|
||||
let pdup: str = strings.dup(pview);
|
||||
if (visitseen(visit, pdup)) { return; };
|
||||
visitadd(visit, pdup);
|
||||
let bufp: *u8;
|
||||
let blen: u64;
|
||||
bufp, blen = slurp(path);
|
||||
if (bufp == nil) {
|
||||
cerr("ww --sep: cannot read source\n");
|
||||
return;
|
||||
};
|
||||
let i: u64 = 0u64;
|
||||
for (i < blen) {
|
||||
let j: u64 = i;
|
||||
for (j < blen) {
|
||||
if (bufp[j] == 10u8) { break; }; // '\n'
|
||||
j += 1u64;
|
||||
};
|
||||
let idp: *u8;
|
||||
let idn: u64;
|
||||
idp, idn = scanuse(bufp + i, j - i);
|
||||
if (idp != nil) {
|
||||
let isdir: i32 = 0;
|
||||
let ipath: *u8 = locateimport(searchpath, idp, idn, &isdir);
|
||||
if (ipath != nil) {
|
||||
if (isdir == 0) {
|
||||
sepemitbody(fd, ipath, visit, searchpath);
|
||||
};
|
||||
};
|
||||
};
|
||||
i = j + 1u64;
|
||||
};
|
||||
let d: str = "//ww:module-reset\n";
|
||||
os.writeall(fd, d.ptr, d.len: u64);
|
||||
os.writeall(fd, bufp, blen);
|
||||
os.writeall(fd, "\n".ptr, 1u64);
|
||||
};
|
||||
|
||||
fn sepemitdirbody(fd: i32, dir: *u8, visit: *expctx, searchpath: *u8) void = {
|
||||
let names: **u8;
|
||||
let n: i32;
|
||||
names, n = enumeratedir(dir);
|
||||
let dlen: u64 = cstrlen(dir);
|
||||
let i: i32 = 0;
|
||||
for (i < n) {
|
||||
let nlen: u64 = cstrlen(names[i]);
|
||||
let fp: []u8 = alloc([], dlen + 1u64 + nlen + 1u64)!;
|
||||
let k: u64 = 0u64;
|
||||
for (k < dlen) { fp[k] = dir[k]; k += 1u64; };
|
||||
fp[dlen] = 47u8; // '/'
|
||||
k = 0u64;
|
||||
for (k < nlen) { fp[dlen + 1u64 + k] = names[i][k]; k += 1u64; };
|
||||
fp[dlen + 1u64 + nlen] = 0u8;
|
||||
sepemitbody(fd, fp.ptr, visit, searchpath);
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
|
||||
// Compose pi's sep-unit at `unitf`: the transitive-closure `.wwi`s
|
||||
// (reverse-topo order, each tagged by its dotted path), then pi's own
|
||||
// body under //ww:module-reset.
|
||||
fn sepcomposeunit(g: *sepgraph, pi: i32, scratch: *u8, order: []i32,
|
||||
norder: i32, searchpath: *u8, unitf: *u8) i32 = {
|
||||
let u: i32 = os.open(pathstr(unitf), os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
|
||||
if (u < 0) {
|
||||
cerr("ww --sep: cannot open unit\n");
|
||||
return -1;
|
||||
};
|
||||
let inset: []u8 = alloc([], g.n: u64)!;
|
||||
inset.len = g.n;
|
||||
let z: i32 = 0;
|
||||
for (z < g.n) { inset[z] = 0u8; z += 1; };
|
||||
sepmarkdeps(g, pi, inset);
|
||||
let oi: i32 = 0;
|
||||
for (oi < norder) {
|
||||
let dj: i32 = order[oi];
|
||||
if (dj != pi) {
|
||||
if (inset[dj] != 0u8) {
|
||||
let wwi: *u8 = sepfname(g, dj, scratch, ".wwi");
|
||||
let wb: *u8;
|
||||
let wn: u64;
|
||||
wb, wn = slurp(wwi);
|
||||
if (wb == nil) {
|
||||
cerr("ww --sep: missing wwi\n");
|
||||
os.close(u);
|
||||
return -1;
|
||||
};
|
||||
let dm: str = "//ww:module ";
|
||||
os.writeall(u, dm.ptr, dm.len: u64);
|
||||
os.writeall(u, g.pkg[dj].path, cstrlen(g.pkg[dj].path));
|
||||
os.writeall(u, "\n".ptr, 1u64);
|
||||
os.writeall(u, wb, wn);
|
||||
os.writeall(u, "\n".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
oi += 1;
|
||||
};
|
||||
let bv: expctx;
|
||||
bv.out = u;
|
||||
bv.dirs = searchpath;
|
||||
bv.visit = nil;
|
||||
if (g.pkg[pi].isdir != 0) {
|
||||
sepemitdirbody(u, g.pkg[pi].entry, &bv, searchpath);
|
||||
} else {
|
||||
sepemitbody(u, g.pkg[pi].entry, &bv, searchpath);
|
||||
};
|
||||
os.close(u);
|
||||
return 0;
|
||||
};
|
||||
|
||||
// buildonesep — the --sep orchestration: discover deps, reverse-topo,
|
||||
// the transitive producer loop (one `w6c -c -I` per package, dep-first),
|
||||
// then a flat `w6l` of the `.o` set. Side files land in a cold
|
||||
// `<stem>.sepwork` scratch dir. Twin of cstage build_one_sep.
|
||||
fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8,
|
||||
objstem: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
||||
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
|
||||
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
|
||||
|
||||
// Default lib search path: <selfdir>/../../lib
|
||||
let dotdotlib: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
dotdotlib.len = os.PATH_MAX;
|
||||
{
|
||||
let off: u64 = cstrinto(dotdotlib.ptr, 0u64, selfdir);
|
||||
off = strinto(dotdotlib.ptr, off, "/../../lib");
|
||||
cstrseal(dotdotlib.ptr, off);
|
||||
};
|
||||
|
||||
// Source directory (mirrors buildone).
|
||||
let srcd: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
srcd.len = os.PATH_MAX;
|
||||
if (entryisdir != 0) {
|
||||
let slen: u64 = cstrlen(src);
|
||||
let k: u64 = 0u64;
|
||||
for (k < slen) { srcd[k] = src[k]; k += 1u64; };
|
||||
for (slen > 1u64) {
|
||||
if (srcd[slen - 1u64] != 47u8) { break; };
|
||||
slen -= 1u64;
|
||||
};
|
||||
srcd[slen] = 0u8;
|
||||
} else {
|
||||
let slen: u64 = cstrlen(src);
|
||||
let last: u64 = slen;
|
||||
let found: bool = false;
|
||||
let i: u64 = slen;
|
||||
for (i > 0u64) {
|
||||
i -= 1u64;
|
||||
if (src[i] == 47u8) { last = i; found = true; i = 0u64; };
|
||||
};
|
||||
if (found) {
|
||||
let k: u64 = 0u64;
|
||||
for (k < last) { srcd[k] = src[k]; k += 1u64; };
|
||||
srcd[last] = 0u8;
|
||||
} else {
|
||||
srcd[0] = 46u8; // '.'
|
||||
srcd[1] = 0u8;
|
||||
};
|
||||
};
|
||||
|
||||
// searchpath = srcd + ':' + incs + ':' + dotdotlib.
|
||||
let searchpath: []u8 = alloc([], (os.PATH_MAX: u64) * 3u64)!;
|
||||
searchpath.len = ((os.PATH_MAX: u64) * 3u64): i32;
|
||||
{
|
||||
let off: u64 = cstrinto(searchpath.ptr, 0u64, srcd.ptr);
|
||||
off = byteinto(searchpath.ptr, off, 58u8); // ':'
|
||||
if (incs[0u64] != 0u8) {
|
||||
off = cstrinto(searchpath.ptr, off, incs);
|
||||
off = byteinto(searchpath.ptr, off, 58u8);
|
||||
};
|
||||
off = cstrinto(searchpath.ptr, off, dotdotlib.ptr);
|
||||
cstrseal(searchpath.ptr, off);
|
||||
};
|
||||
|
||||
// Stem for the scratch dir (mirrors buildone).
|
||||
let stem: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
stem.len = os.PATH_MAX;
|
||||
if (entryisdir != 0) {
|
||||
let dlen: u64 = cstrlen(srcd.ptr);
|
||||
let bo: u64 = basenameoff(srcd.ptr, dlen);
|
||||
let off: u64 = cstrinto(stem.ptr, 0u64, srcd.ptr);
|
||||
stem[off] = 47u8; off += 1u64; // '/'
|
||||
let i: u64 = bo;
|
||||
for (i < dlen) { stem[off] = srcd[i]; off += 1u64; i += 1u64; };
|
||||
cstrseal(stem.ptr, off);
|
||||
} else {
|
||||
makestem(stem.ptr, src);
|
||||
};
|
||||
let effstem: *u8 = stem.ptr;
|
||||
if (objstem != nil) { effstem = objstem; };
|
||||
let scratch: *u8 = appendlit(effstem, ".sepwork");
|
||||
|
||||
// rm -rf scratch (cold); recreate. Reuse os.removeall if present;
|
||||
// here we mkdir and rely on TRUNC opens to overwrite stale files.
|
||||
os.mkdir(pathstr(scratch), 493i32); // 0o755 (idempotent; stale files TRUNC'd)
|
||||
|
||||
// libwwrt.a path: <selfdir>/../lib/libwwrt.a
|
||||
let libwwrt: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
libwwrt.len = os.PATH_MAX;
|
||||
{
|
||||
let off: u64 = cstrinto(libwwrt.ptr, 0u64, selfdir);
|
||||
off = strinto(libwwrt.ptr, off, "/../lib/libwwrt.a");
|
||||
cstrseal(libwwrt.ptr, off);
|
||||
};
|
||||
|
||||
// Discover.
|
||||
let pkgslot: []seppkg = alloc([], SEP_MAXPKG: u64)!;
|
||||
pkgslot.len = SEP_MAXPKG;
|
||||
let g: *sepgraph = alloc(sepgraph{pkg = pkgslot, n = 0})!;
|
||||
let root: i32 = sepfindoradd(g, "\0".ptr, src, entryisdir);
|
||||
if (root < 0) { return 1; };
|
||||
if (sepscanpkg(g, root, searchpath.ptr) < 0) { return 1; };
|
||||
|
||||
// Reset colors, reverse-topo.
|
||||
let ci: i32 = 0;
|
||||
for (ci < g.n) { g.pkg[ci].color = 0; ci += 1; };
|
||||
let order: []i32 = alloc([], g.n: u64)!;
|
||||
order.len = g.n;
|
||||
let norder: i32 = 0;
|
||||
if (septopovisit(g, root, order, &norder) < 0) { return 1; };
|
||||
|
||||
// Producer loop — dep-first, one `w6c -c -I` per package.
|
||||
let oi: i32 = 0;
|
||||
for (oi < norder) {
|
||||
let pi: i32 = order[oi];
|
||||
let unitf: *u8 = sepfname(g, pi, scratch, ".unit.ww");
|
||||
let wwi: *u8 = sepfname(g, pi, scratch, ".wwi");
|
||||
let asmf: *u8 = sepfname(g, pi, scratch, ".s");
|
||||
let objf: *u8 = sepfname(g, pi, scratch, ".o");
|
||||
if (sepcomposeunit(g, pi, scratch, order, norder, searchpath.ptr, unitf) < 0) {
|
||||
return 1;
|
||||
};
|
||||
{
|
||||
let argv: []*u8 = alloc([], 8u64)!;
|
||||
argv.len = 8;
|
||||
argv[0] = "w6c\0".ptr;
|
||||
argv[1] = "-c\0".ptr;
|
||||
argv[2] = "-I\0".ptr;
|
||||
argv[3] = wwi;
|
||||
argv[4] = "-o\0".ptr;
|
||||
argv[5] = asmf;
|
||||
argv[6] = unitf;
|
||||
argv[7] = nil;
|
||||
if (procrun(c6, argv.ptr) != 0) {
|
||||
cerr("ww --sep: w6c failed\n");
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
{
|
||||
let argv: []*u8 = alloc([], 5u64)!;
|
||||
argv.len = 5;
|
||||
argv[0] = "w6a\0".ptr;
|
||||
argv[1] = "-o\0".ptr;
|
||||
argv[2] = objf;
|
||||
argv[3] = asmf;
|
||||
argv[4] = nil;
|
||||
if (procrun(a6, argv.ptr) != 0) {
|
||||
cerr("ww --sep: w6a failed\n");
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
oi += 1;
|
||||
};
|
||||
|
||||
// Flat link: root.o first (order[norder-1]), deps after; libwwrt.a
|
||||
// selectively pulls runtime symbols. argv: 4 fixed (w6l,-o,out) + 1
|
||||
// per .o + 1 libwwrt + 2*nlibdirs + 2*nlibs + 1 nil.
|
||||
let nldirs: i32 = 0;
|
||||
let nllibs: i32 = 0;
|
||||
let ldirs: **u8 = nil;
|
||||
let llibs: **u8 = nil;
|
||||
if (lf != nil) {
|
||||
nldirs = lf.nlibdirs;
|
||||
nllibs = lf.nlibs;
|
||||
ldirs = lf.libdirs;
|
||||
llibs = lf.libs;
|
||||
};
|
||||
let total: i32 = 3 + norder + 1 + 2 * nldirs + 2 * nllibs + 1;
|
||||
let largv: []*u8 = alloc([], total: u64)!;
|
||||
largv.len = total;
|
||||
largv[0] = "w6l\0".ptr;
|
||||
largv[1] = "-o\0".ptr;
|
||||
largv[2] = out;
|
||||
let pos: i32 = 3;
|
||||
let li: i32 = norder - 1;
|
||||
for (li >= 0) {
|
||||
largv[pos] = sepfname(g, order[li], scratch, ".o");
|
||||
pos += 1;
|
||||
li -= 1;
|
||||
};
|
||||
largv[pos] = libwwrt.ptr; pos += 1;
|
||||
let k: i32 = 0;
|
||||
for (k < nldirs) {
|
||||
largv[pos] = "-L\0".ptr;
|
||||
largv[pos + 1] = ldirs[k];
|
||||
pos += 2;
|
||||
k += 1;
|
||||
};
|
||||
k = 0;
|
||||
for (k < nllibs) {
|
||||
largv[pos] = "-l\0".ptr;
|
||||
largv[pos + 1] = llibs[k];
|
||||
pos += 2;
|
||||
k += 1;
|
||||
};
|
||||
largv[pos] = nil;
|
||||
if (procrun(l6, largv.ptr) != 0) {
|
||||
cerr("ww --sep: w6l failed\n");
|
||||
return 1;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// buildone — compile `src` (file or directory) into the executable
|
||||
// named `out`.
|
||||
// selfdir: NUL-terminated dir containing this driver and the
|
||||
@@ -4088,6 +4627,7 @@ fn defaultoutpath(src: *u8) *u8 = {
|
||||
|
||||
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let src: *u8 = nil;
|
||||
let wantsep: i32 = 0; // --sep: M3-tail separate-compilation path
|
||||
let outflag: *u8 = nil; // -o target (binary + intermediate stem); T3
|
||||
let incs: []u8 = alloc([], (os.PATH_MAX: u64) * 2u64)!;
|
||||
incs.len = ((os.PATH_MAX: u64) * 2u64): i32;
|
||||
@@ -4106,7 +4646,9 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
for (i < argc) {
|
||||
let p: *u8 = argv[i];
|
||||
if (p[0u64] == 45u8) { // '-'
|
||||
if (p[1u64] == 73u8) { // '-I'
|
||||
if (cstreqlit(p, "--sep")) { // M3-tail separate-compile
|
||||
wantsep = 1;
|
||||
} else { if (p[1u64] == 73u8) { // '-I'
|
||||
let dir: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
dir = p + 2u64;
|
||||
@@ -4174,7 +4716,7 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
} else {
|
||||
cerr("ww build: unknown flag\n");
|
||||
return 2;
|
||||
}; }; }; };
|
||||
}; }; }; }; };
|
||||
} else {
|
||||
if (src == nil) { src = p; };
|
||||
};
|
||||
@@ -4221,6 +4763,9 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
lf.nlibdirs = nlibdirs;
|
||||
lf.libs = libs.ptr;
|
||||
lf.nlibs = nlibs;
|
||||
if (wantsep != 0) {
|
||||
return buildonesep(selfdir, resolved, isdir, out, objstem, incs.ptr, &lf);
|
||||
};
|
||||
return buildone(selfdir, resolved, isdir, out, objstem, incs.ptr, &lf, 0i32);
|
||||
};
|
||||
|
||||
@@ -4276,7 +4821,10 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
else {
|
||||
let p: *u8 = argv[i];
|
||||
if (p[0u64] == 45u8) {
|
||||
if (p[1u64] == 73u8) {
|
||||
if (cstreqlit(p, "--sep")) { // build-only; run rejects (rule 10)
|
||||
cerr("ww run: --sep is only valid with build\n");
|
||||
return 2;
|
||||
} else { if (p[1u64] == 73u8) {
|
||||
let dir: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
dir = p + 2u64;
|
||||
@@ -4343,7 +4891,7 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
} else {
|
||||
cerr("ww run: unknown flag\n");
|
||||
return 2;
|
||||
}; }; }; };
|
||||
}; }; }; }; };
|
||||
i += 1;
|
||||
} else {
|
||||
if (src == nil) {
|
||||
|
||||
@@ -846,6 +846,545 @@ type lflags = struct {
|
||||
nlibs: i32,
|
||||
};
|
||||
|
||||
// ---- ww build --sep — M3-tail separate-compilation driver ------------
|
||||
//
|
||||
// Port of cmd/ww/main.c build_one_sep (task #46/c3). The `--sep` path
|
||||
// materializes each imported package's `.wwi` interface and compiles
|
||||
// every package on its own (`w6c -c`), then flat-links the `.o` set.
|
||||
// combined.ww stays the DEFAULT live path; --sep is additive.
|
||||
//
|
||||
// Each w6c pass is BOTH consumer (reads dep `.wwi` as import scope) AND
|
||||
// producer (writes this package's `.wwi` via -I). Reverse-topo order
|
||||
// guarantees a package's deps' `.wwi` exist before it compiles.
|
||||
//
|
||||
// The load-bearing rule (#56, rob-resolved): every dep is tagged by its
|
||||
// FULL DOTTED import path on prepend (`//ww:module <path>`), so the
|
||||
// definer's qualified symbol (#53) equals the consumer's qualified
|
||||
// reference (#40) and the sep `.o`s link. The prepend is the TRANSITIVE
|
||||
// closure of a package's deps (lead-ratified): a dep's interface can
|
||||
// name a transitive dep's type, so the consuming unit needs the whole
|
||||
// closure for resolution. The unit composition is byte-identical to the
|
||||
// cstage driver (rule 10) so w6c/w6c_ww emit identical `.s`.
|
||||
|
||||
def SEP_MAXPKG: i32 = 256;
|
||||
|
||||
type seppkg = struct {
|
||||
path: *u8, // dotted import path, NUL-term; root path[0]==0
|
||||
entry: *u8, // resolved package dir (or file, file root), NUL-term
|
||||
isdir: i32,
|
||||
deps: []i32, // direct-dep indices into sepgraph.pkg
|
||||
ndeps: i32,
|
||||
color: i32, // tri-color DFS: 0 white, 1 gray, 2 black
|
||||
};
|
||||
|
||||
type sepgraph = struct {
|
||||
pkg: []seppkg, // alloc'd SEP_MAXPKG
|
||||
n: i32,
|
||||
};
|
||||
|
||||
// Find a package by dotted path, or add it. Returns index, -1 if full.
|
||||
fn sepfindoradd(g: *sepgraph, path: *u8, entry: *u8, isdir: i32) i32 = {
|
||||
let i: i32 = 0;
|
||||
for (i < g.n) {
|
||||
if (cstreq(g.pkg[i].path, path)) { return i; };
|
||||
i += 1;
|
||||
};
|
||||
if (g.n >= SEP_MAXPKG) {
|
||||
cerr("ww --sep: too many packages\n");
|
||||
return -1;
|
||||
};
|
||||
let plen: u64 = cstrlen(path);
|
||||
let elen: u64 = cstrlen(entry);
|
||||
g.pkg[g.n].path = arenadupcstr(path, plen);
|
||||
g.pkg[g.n].entry = arenadupcstr(entry, elen);
|
||||
g.pkg[g.n].isdir = isdir;
|
||||
let dslot: []i32 = alloc([], SEP_MAXPKG: u64)!;
|
||||
dslot.len = SEP_MAXPKG;
|
||||
g.pkg[g.n].deps = dslot;
|
||||
g.pkg[g.n].ndeps = 0;
|
||||
g.pkg[g.n].color = 0;
|
||||
let r: i32 = g.n;
|
||||
g.n += 1;
|
||||
return r;
|
||||
};
|
||||
|
||||
// Build "<scratch>/<base><suffix>" NUL-term; base = path, or "__root"
|
||||
// for the empty root path.
|
||||
fn sepfname(g: *sepgraph, pi: i32, scratch: *u8, suffix: str) *u8 = {
|
||||
let buf: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
buf.len = os.PATH_MAX;
|
||||
let off: u64 = cstrinto(buf.ptr, 0u64, scratch);
|
||||
off = byteinto(buf.ptr, off, 47u8); // '/'
|
||||
if (g.pkg[pi].path[0u64] != 0u8) {
|
||||
off = cstrinto(buf.ptr, off, g.pkg[pi].path);
|
||||
} else {
|
||||
off = strinto(buf.ptr, off, "__root");
|
||||
};
|
||||
off = strinto(buf.ptr, off, suffix);
|
||||
cstrseal(buf.ptr, off);
|
||||
return buf.ptr;
|
||||
};
|
||||
|
||||
// Scan one source file for top-level `import IDENT;`. A DIRECTORY import
|
||||
// is a package boundary: add as a direct dep of pi. A FILE import is an
|
||||
// intra-package split: fold its imports into pi. Mirrors cstage
|
||||
// sep_scan_file (collects PATHS, not bytes).
|
||||
fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
|
||||
fv: *expctx) i32 = {
|
||||
let fview: str;
|
||||
fview.ptr = file;
|
||||
fview.len = cstrlen(file): i32;
|
||||
let fdup: str = strings.dup(fview);
|
||||
if (visitseen(fv, fdup)) { return 0; };
|
||||
visitadd(fv, fdup);
|
||||
let bufp: *u8;
|
||||
let blen: u64;
|
||||
bufp, blen = slurp(file);
|
||||
if (bufp == nil) {
|
||||
cerr("ww --sep: cannot read source\n");
|
||||
return -1;
|
||||
};
|
||||
let i: u64 = 0u64;
|
||||
for (i < blen) {
|
||||
let j: u64 = i;
|
||||
for (j < blen) {
|
||||
if (bufp[j] == 10u8) { break; }; // '\n'
|
||||
j += 1u64;
|
||||
};
|
||||
let idp: *u8;
|
||||
let idn: u64;
|
||||
idp, idn = scanuse(bufp + i, j - i);
|
||||
if (idp != nil) {
|
||||
let isdir: i32 = 0;
|
||||
let ipath: *u8 = locateimport(searchpath, idp, idn, &isdir);
|
||||
if (ipath != nil) {
|
||||
if (isdir != 0) {
|
||||
let nm: []u8 = alloc([], idn + 1u64)!;
|
||||
let k: u64 = 0u64;
|
||||
for (k < idn) { nm[k] = idp[k]; k += 1u64; };
|
||||
nm[idn] = 0u8;
|
||||
let di: i32 = sepfindoradd(g, nm.ptr, ipath, 1);
|
||||
if (di < 0) { return -1; };
|
||||
let seen: bool = false;
|
||||
let m: i32 = 0;
|
||||
for (m < g.pkg[pi].ndeps) {
|
||||
if (g.pkg[pi].deps[m] == di) { seen = true; };
|
||||
m += 1;
|
||||
};
|
||||
if (!seen) {
|
||||
if (g.pkg[pi].ndeps >= SEP_MAXPKG) { return -1; };
|
||||
g.pkg[pi].deps[g.pkg[pi].ndeps] = di;
|
||||
g.pkg[pi].ndeps += 1;
|
||||
};
|
||||
} else {
|
||||
if (sepscanfile(g, pi, ipath, searchpath, fv) < 0) {
|
||||
return -1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
i = j + 1u64;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Discover pi's direct deps + recurse. Enumerate the package's own
|
||||
// files (dir → *.ww less *test.ww; file → the file) and scan each.
|
||||
// `color` doubles as a scanned-marker (2); reset to white before topo.
|
||||
fn sepscanpkg(g: *sepgraph, pi: i32, searchpath: *u8) i32 = {
|
||||
if (g.pkg[pi].color == 2) { return 0; };
|
||||
g.pkg[pi].color = 2;
|
||||
let fv: expctx;
|
||||
fv.out = -1;
|
||||
fv.dirs = searchpath;
|
||||
fv.visit = nil;
|
||||
let rc: i32 = 0;
|
||||
if (g.pkg[pi].isdir != 0) {
|
||||
let names: **u8;
|
||||
let n: i32;
|
||||
names, n = enumeratedir(g.pkg[pi].entry);
|
||||
let dlen: u64 = cstrlen(g.pkg[pi].entry);
|
||||
let i: i32 = 0;
|
||||
for (i < n) {
|
||||
if (rc == 0) {
|
||||
let nlen: u64 = cstrlen(names[i]);
|
||||
let fp: []u8 = alloc([], dlen + 1u64 + nlen + 1u64)!;
|
||||
let k: u64 = 0u64;
|
||||
for (k < dlen) { fp[k] = g.pkg[pi].entry[k]; k += 1u64; };
|
||||
fp[dlen] = 47u8; // '/'
|
||||
k = 0u64;
|
||||
for (k < nlen) { fp[dlen + 1u64 + k] = names[i][k]; k += 1u64; };
|
||||
fp[dlen + 1u64 + nlen] = 0u8;
|
||||
rc = sepscanfile(g, pi, fp.ptr, searchpath, &fv);
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
} else {
|
||||
rc = sepscanfile(g, pi, g.pkg[pi].entry, searchpath, &fv);
|
||||
};
|
||||
if (rc < 0) { return rc; };
|
||||
let k: i32 = 0;
|
||||
for (k < g.pkg[pi].ndeps) {
|
||||
if (sepscanpkg(g, g.pkg[pi].deps[k], searchpath) < 0) { return -1; };
|
||||
k += 1;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// DFS post-order over the dep DAG → reverse-topo (deps before importer).
|
||||
// Tri-color: a back-edge BAILS rather than spinning (loud cycle reject is
|
||||
// commit 4). Cite Hare gather (deps.ha:123).
|
||||
fn septopovisit(g: *sepgraph, pi: i32, order: []i32, no: *i32) i32 = {
|
||||
if (g.pkg[pi].color == 2) { return 0; };
|
||||
if (g.pkg[pi].color == 1) {
|
||||
cerr("ww --sep: import cycle (loud reject lands commit 4)\n");
|
||||
return -1;
|
||||
};
|
||||
g.pkg[pi].color = 1;
|
||||
let k: i32 = 0;
|
||||
for (k < g.pkg[pi].ndeps) {
|
||||
if (septopovisit(g, g.pkg[pi].deps[k], order, no) < 0) { return -1; };
|
||||
k += 1;
|
||||
};
|
||||
g.pkg[pi].color = 2;
|
||||
order[*no] = pi;
|
||||
*no += 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Mark pi's transitive deps (excluding pi) in inset[].
|
||||
fn sepmarkdeps(g: *sepgraph, pi: i32, inset: []u8) void = {
|
||||
let k: i32 = 0;
|
||||
for (k < g.pkg[pi].ndeps) {
|
||||
let di: i32 = g.pkg[pi].deps[k];
|
||||
if (inset[di] == 0u8) {
|
||||
inset[di] = 1u8;
|
||||
sepmarkdeps(g, di, inset);
|
||||
};
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
|
||||
// Emit one of pi's own source files into the sep-unit under the
|
||||
// //ww:module-reset primary boundary (so -c emits its decls, imported
|
||||
// ==0). DIRECTORY imports are skipped (provided as `.wwi` ahead);
|
||||
// FILE imports fold in (intra-package split).
|
||||
fn sepemitbody(fd: i32, path: *u8, visit: *expctx, searchpath: *u8) void = {
|
||||
let pview: str;
|
||||
pview.ptr = path;
|
||||
pview.len = cstrlen(path): i32;
|
||||
let pdup: str = strings.dup(pview);
|
||||
if (visitseen(visit, pdup)) { return; };
|
||||
visitadd(visit, pdup);
|
||||
let bufp: *u8;
|
||||
let blen: u64;
|
||||
bufp, blen = slurp(path);
|
||||
if (bufp == nil) {
|
||||
cerr("ww --sep: cannot read source\n");
|
||||
return;
|
||||
};
|
||||
let i: u64 = 0u64;
|
||||
for (i < blen) {
|
||||
let j: u64 = i;
|
||||
for (j < blen) {
|
||||
if (bufp[j] == 10u8) { break; }; // '\n'
|
||||
j += 1u64;
|
||||
};
|
||||
let idp: *u8;
|
||||
let idn: u64;
|
||||
idp, idn = scanuse(bufp + i, j - i);
|
||||
if (idp != nil) {
|
||||
let isdir: i32 = 0;
|
||||
let ipath: *u8 = locateimport(searchpath, idp, idn, &isdir);
|
||||
if (ipath != nil) {
|
||||
if (isdir == 0) {
|
||||
sepemitbody(fd, ipath, visit, searchpath);
|
||||
};
|
||||
};
|
||||
};
|
||||
i = j + 1u64;
|
||||
};
|
||||
let d: str = "//ww:module-reset\n";
|
||||
os.writeall(fd, d.ptr, d.len: u64);
|
||||
os.writeall(fd, bufp, blen);
|
||||
os.writeall(fd, "\n".ptr, 1u64);
|
||||
};
|
||||
|
||||
fn sepemitdirbody(fd: i32, dir: *u8, visit: *expctx, searchpath: *u8) void = {
|
||||
let names: **u8;
|
||||
let n: i32;
|
||||
names, n = enumeratedir(dir);
|
||||
let dlen: u64 = cstrlen(dir);
|
||||
let i: i32 = 0;
|
||||
for (i < n) {
|
||||
let nlen: u64 = cstrlen(names[i]);
|
||||
let fp: []u8 = alloc([], dlen + 1u64 + nlen + 1u64)!;
|
||||
let k: u64 = 0u64;
|
||||
for (k < dlen) { fp[k] = dir[k]; k += 1u64; };
|
||||
fp[dlen] = 47u8; // '/'
|
||||
k = 0u64;
|
||||
for (k < nlen) { fp[dlen + 1u64 + k] = names[i][k]; k += 1u64; };
|
||||
fp[dlen + 1u64 + nlen] = 0u8;
|
||||
sepemitbody(fd, fp.ptr, visit, searchpath);
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
|
||||
// Compose pi's sep-unit at `unitf`: the transitive-closure `.wwi`s
|
||||
// (reverse-topo order, each tagged by its dotted path), then pi's own
|
||||
// body under //ww:module-reset.
|
||||
fn sepcomposeunit(g: *sepgraph, pi: i32, scratch: *u8, order: []i32,
|
||||
norder: i32, searchpath: *u8, unitf: *u8) i32 = {
|
||||
let u: i32 = os.open(pathstr(unitf), os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
|
||||
if (u < 0) {
|
||||
cerr("ww --sep: cannot open unit\n");
|
||||
return -1;
|
||||
};
|
||||
let inset: []u8 = alloc([], g.n: u64)!;
|
||||
inset.len = g.n;
|
||||
let z: i32 = 0;
|
||||
for (z < g.n) { inset[z] = 0u8; z += 1; };
|
||||
sepmarkdeps(g, pi, inset);
|
||||
let oi: i32 = 0;
|
||||
for (oi < norder) {
|
||||
let dj: i32 = order[oi];
|
||||
if (dj != pi) {
|
||||
if (inset[dj] != 0u8) {
|
||||
let wwi: *u8 = sepfname(g, dj, scratch, ".wwi");
|
||||
let wb: *u8;
|
||||
let wn: u64;
|
||||
wb, wn = slurp(wwi);
|
||||
if (wb == nil) {
|
||||
cerr("ww --sep: missing wwi\n");
|
||||
os.close(u);
|
||||
return -1;
|
||||
};
|
||||
let dm: str = "//ww:module ";
|
||||
os.writeall(u, dm.ptr, dm.len: u64);
|
||||
os.writeall(u, g.pkg[dj].path, cstrlen(g.pkg[dj].path));
|
||||
os.writeall(u, "\n".ptr, 1u64);
|
||||
os.writeall(u, wb, wn);
|
||||
os.writeall(u, "\n".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
oi += 1;
|
||||
};
|
||||
let bv: expctx;
|
||||
bv.out = u;
|
||||
bv.dirs = searchpath;
|
||||
bv.visit = nil;
|
||||
if (g.pkg[pi].isdir != 0) {
|
||||
sepemitdirbody(u, g.pkg[pi].entry, &bv, searchpath);
|
||||
} else {
|
||||
sepemitbody(u, g.pkg[pi].entry, &bv, searchpath);
|
||||
};
|
||||
os.close(u);
|
||||
return 0;
|
||||
};
|
||||
|
||||
// buildonesep — the --sep orchestration: discover deps, reverse-topo,
|
||||
// the transitive producer loop (one `w6c -c -I` per package, dep-first),
|
||||
// then a flat `w6l` of the `.o` set. Side files land in a cold
|
||||
// `<stem>.sepwork` scratch dir. Twin of cstage build_one_sep.
|
||||
fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8,
|
||||
objstem: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
||||
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
|
||||
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
|
||||
|
||||
// Default lib search path: <selfdir>/../../lib
|
||||
let dotdotlib: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
dotdotlib.len = os.PATH_MAX;
|
||||
{
|
||||
let off: u64 = cstrinto(dotdotlib.ptr, 0u64, selfdir);
|
||||
off = strinto(dotdotlib.ptr, off, "/../../lib");
|
||||
cstrseal(dotdotlib.ptr, off);
|
||||
};
|
||||
|
||||
// Source directory (mirrors buildone).
|
||||
let srcd: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
srcd.len = os.PATH_MAX;
|
||||
if (entryisdir != 0) {
|
||||
let slen: u64 = cstrlen(src);
|
||||
let k: u64 = 0u64;
|
||||
for (k < slen) { srcd[k] = src[k]; k += 1u64; };
|
||||
for (slen > 1u64) {
|
||||
if (srcd[slen - 1u64] != 47u8) { break; };
|
||||
slen -= 1u64;
|
||||
};
|
||||
srcd[slen] = 0u8;
|
||||
} else {
|
||||
let slen: u64 = cstrlen(src);
|
||||
let last: u64 = slen;
|
||||
let found: bool = false;
|
||||
let i: u64 = slen;
|
||||
for (i > 0u64) {
|
||||
i -= 1u64;
|
||||
if (src[i] == 47u8) { last = i; found = true; i = 0u64; };
|
||||
};
|
||||
if (found) {
|
||||
let k: u64 = 0u64;
|
||||
for (k < last) { srcd[k] = src[k]; k += 1u64; };
|
||||
srcd[last] = 0u8;
|
||||
} else {
|
||||
srcd[0] = 46u8; // '.'
|
||||
srcd[1] = 0u8;
|
||||
};
|
||||
};
|
||||
|
||||
// searchpath = srcd + ':' + incs + ':' + dotdotlib.
|
||||
let searchpath: []u8 = alloc([], (os.PATH_MAX: u64) * 3u64)!;
|
||||
searchpath.len = ((os.PATH_MAX: u64) * 3u64): i32;
|
||||
{
|
||||
let off: u64 = cstrinto(searchpath.ptr, 0u64, srcd.ptr);
|
||||
off = byteinto(searchpath.ptr, off, 58u8); // ':'
|
||||
if (incs[0u64] != 0u8) {
|
||||
off = cstrinto(searchpath.ptr, off, incs);
|
||||
off = byteinto(searchpath.ptr, off, 58u8);
|
||||
};
|
||||
off = cstrinto(searchpath.ptr, off, dotdotlib.ptr);
|
||||
cstrseal(searchpath.ptr, off);
|
||||
};
|
||||
|
||||
// Stem for the scratch dir (mirrors buildone).
|
||||
let stem: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
stem.len = os.PATH_MAX;
|
||||
if (entryisdir != 0) {
|
||||
let dlen: u64 = cstrlen(srcd.ptr);
|
||||
let bo: u64 = basenameoff(srcd.ptr, dlen);
|
||||
let off: u64 = cstrinto(stem.ptr, 0u64, srcd.ptr);
|
||||
stem[off] = 47u8; off += 1u64; // '/'
|
||||
let i: u64 = bo;
|
||||
for (i < dlen) { stem[off] = srcd[i]; off += 1u64; i += 1u64; };
|
||||
cstrseal(stem.ptr, off);
|
||||
} else {
|
||||
makestem(stem.ptr, src);
|
||||
};
|
||||
let effstem: *u8 = stem.ptr;
|
||||
if (objstem != nil) { effstem = objstem; };
|
||||
let scratch: *u8 = appendlit(effstem, ".sepwork");
|
||||
|
||||
// rm -rf scratch (cold); recreate. Reuse os.removeall if present;
|
||||
// here we mkdir and rely on TRUNC opens to overwrite stale files.
|
||||
os.mkdir(pathstr(scratch), 493i32); // 0o755 (idempotent; stale files TRUNC'd)
|
||||
|
||||
// libwwrt.a path: <selfdir>/../lib/libwwrt.a
|
||||
let libwwrt: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
libwwrt.len = os.PATH_MAX;
|
||||
{
|
||||
let off: u64 = cstrinto(libwwrt.ptr, 0u64, selfdir);
|
||||
off = strinto(libwwrt.ptr, off, "/../lib/libwwrt.a");
|
||||
cstrseal(libwwrt.ptr, off);
|
||||
};
|
||||
|
||||
// Discover.
|
||||
let pkgslot: []seppkg = alloc([], SEP_MAXPKG: u64)!;
|
||||
pkgslot.len = SEP_MAXPKG;
|
||||
let g: *sepgraph = alloc(sepgraph{pkg = pkgslot, n = 0})!;
|
||||
let root: i32 = sepfindoradd(g, "\0".ptr, src, entryisdir);
|
||||
if (root < 0) { return 1; };
|
||||
if (sepscanpkg(g, root, searchpath.ptr) < 0) { return 1; };
|
||||
|
||||
// Reset colors, reverse-topo.
|
||||
let ci: i32 = 0;
|
||||
for (ci < g.n) { g.pkg[ci].color = 0; ci += 1; };
|
||||
let order: []i32 = alloc([], g.n: u64)!;
|
||||
order.len = g.n;
|
||||
let norder: i32 = 0;
|
||||
if (septopovisit(g, root, order, &norder) < 0) { return 1; };
|
||||
|
||||
// Producer loop — dep-first, one `w6c -c -I` per package.
|
||||
let oi: i32 = 0;
|
||||
for (oi < norder) {
|
||||
let pi: i32 = order[oi];
|
||||
let unitf: *u8 = sepfname(g, pi, scratch, ".unit.ww");
|
||||
let wwi: *u8 = sepfname(g, pi, scratch, ".wwi");
|
||||
let asmf: *u8 = sepfname(g, pi, scratch, ".s");
|
||||
let objf: *u8 = sepfname(g, pi, scratch, ".o");
|
||||
if (sepcomposeunit(g, pi, scratch, order, norder, searchpath.ptr, unitf) < 0) {
|
||||
return 1;
|
||||
};
|
||||
{
|
||||
let argv: []*u8 = alloc([], 8u64)!;
|
||||
argv.len = 8;
|
||||
argv[0] = "w6c\0".ptr;
|
||||
argv[1] = "-c\0".ptr;
|
||||
argv[2] = "-I\0".ptr;
|
||||
argv[3] = wwi;
|
||||
argv[4] = "-o\0".ptr;
|
||||
argv[5] = asmf;
|
||||
argv[6] = unitf;
|
||||
argv[7] = nil;
|
||||
if (procrun(c6, argv.ptr) != 0) {
|
||||
cerr("ww --sep: w6c failed\n");
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
{
|
||||
let argv: []*u8 = alloc([], 5u64)!;
|
||||
argv.len = 5;
|
||||
argv[0] = "w6a\0".ptr;
|
||||
argv[1] = "-o\0".ptr;
|
||||
argv[2] = objf;
|
||||
argv[3] = asmf;
|
||||
argv[4] = nil;
|
||||
if (procrun(a6, argv.ptr) != 0) {
|
||||
cerr("ww --sep: w6a failed\n");
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
oi += 1;
|
||||
};
|
||||
|
||||
// Flat link: root.o first (order[norder-1]), deps after; libwwrt.a
|
||||
// selectively pulls runtime symbols. argv: 4 fixed (w6l,-o,out) + 1
|
||||
// per .o + 1 libwwrt + 2*nlibdirs + 2*nlibs + 1 nil.
|
||||
let nldirs: i32 = 0;
|
||||
let nllibs: i32 = 0;
|
||||
let ldirs: **u8 = nil;
|
||||
let llibs: **u8 = nil;
|
||||
if (lf != nil) {
|
||||
nldirs = lf.nlibdirs;
|
||||
nllibs = lf.nlibs;
|
||||
ldirs = lf.libdirs;
|
||||
llibs = lf.libs;
|
||||
};
|
||||
let total: i32 = 3 + norder + 1 + 2 * nldirs + 2 * nllibs + 1;
|
||||
let largv: []*u8 = alloc([], total: u64)!;
|
||||
largv.len = total;
|
||||
largv[0] = "w6l\0".ptr;
|
||||
largv[1] = "-o\0".ptr;
|
||||
largv[2] = out;
|
||||
let pos: i32 = 3;
|
||||
let li: i32 = norder - 1;
|
||||
for (li >= 0) {
|
||||
largv[pos] = sepfname(g, order[li], scratch, ".o");
|
||||
pos += 1;
|
||||
li -= 1;
|
||||
};
|
||||
largv[pos] = libwwrt.ptr; pos += 1;
|
||||
let k: i32 = 0;
|
||||
for (k < nldirs) {
|
||||
largv[pos] = "-L\0".ptr;
|
||||
largv[pos + 1] = ldirs[k];
|
||||
pos += 2;
|
||||
k += 1;
|
||||
};
|
||||
k = 0;
|
||||
for (k < nllibs) {
|
||||
largv[pos] = "-l\0".ptr;
|
||||
largv[pos + 1] = llibs[k];
|
||||
pos += 2;
|
||||
k += 1;
|
||||
};
|
||||
largv[pos] = nil;
|
||||
if (procrun(l6, largv.ptr) != 0) {
|
||||
cerr("ww --sep: w6l failed\n");
|
||||
return 1;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// buildone — compile `src` (file or directory) into the executable
|
||||
// named `out`.
|
||||
// selfdir: NUL-terminated dir containing this driver and the
|
||||
@@ -1214,6 +1753,7 @@ fn defaultoutpath(src: *u8) *u8 = {
|
||||
|
||||
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let src: *u8 = nil;
|
||||
let wantsep: i32 = 0; // --sep: M3-tail separate-compilation path
|
||||
let outflag: *u8 = nil; // -o target (binary + intermediate stem); T3
|
||||
let incs: []u8 = alloc([], (os.PATH_MAX: u64) * 2u64)!;
|
||||
incs.len = ((os.PATH_MAX: u64) * 2u64): i32;
|
||||
@@ -1232,7 +1772,9 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
for (i < argc) {
|
||||
let p: *u8 = argv[i];
|
||||
if (p[0u64] == 45u8) { // '-'
|
||||
if (p[1u64] == 73u8) { // '-I'
|
||||
if (cstreqlit(p, "--sep")) { // M3-tail separate-compile
|
||||
wantsep = 1;
|
||||
} else { if (p[1u64] == 73u8) { // '-I'
|
||||
let dir: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
dir = p + 2u64;
|
||||
@@ -1300,7 +1842,7 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
} else {
|
||||
cerr("ww build: unknown flag\n");
|
||||
return 2;
|
||||
}; }; }; };
|
||||
}; }; }; }; };
|
||||
} else {
|
||||
if (src == nil) { src = p; };
|
||||
};
|
||||
@@ -1347,6 +1889,9 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
lf.nlibdirs = nlibdirs;
|
||||
lf.libs = libs.ptr;
|
||||
lf.nlibs = nlibs;
|
||||
if (wantsep != 0) {
|
||||
return buildonesep(selfdir, resolved, isdir, out, objstem, incs.ptr, &lf);
|
||||
};
|
||||
return buildone(selfdir, resolved, isdir, out, objstem, incs.ptr, &lf, 0i32);
|
||||
};
|
||||
|
||||
@@ -1402,7 +1947,10 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
else {
|
||||
let p: *u8 = argv[i];
|
||||
if (p[0u64] == 45u8) {
|
||||
if (p[1u64] == 73u8) {
|
||||
if (cstreqlit(p, "--sep")) { // build-only; run rejects (rule 10)
|
||||
cerr("ww run: --sep is only valid with build\n");
|
||||
return 2;
|
||||
} else { if (p[1u64] == 73u8) {
|
||||
let dir: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
dir = p + 2u64;
|
||||
@@ -1469,7 +2017,7 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
} else {
|
||||
cerr("ww run: unknown flag\n");
|
||||
return 2;
|
||||
}; }; }; };
|
||||
}; }; }; }; };
|
||||
i += 1;
|
||||
} else {
|
||||
if (src == nil) {
|
||||
|
||||
Reference in New Issue
Block a user