cmd: compile packages from direct exports

This commit is contained in:
2026-08-12 01:12:50 +09:00
parent 9b970eb16a
commit 0ac0fd68ec
4 changed files with 265 additions and 108 deletions

View File

@@ -624,16 +624,12 @@ type lflags = struct {
// 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. Qualifier lookup remains scoped to the direct
// imports owned by each source package, so those type facts do not expose
// a transitive package. The unit composition is byte-identical to the cstage
// driver (rule 10) so w6c/w6c_ww emit identical `.s`.
// Every direct dep is tagged by its FULL DOTTED import path on prepend
// (`//ww:module <path>`), so the definer's qualified symbol equals the
// consumer's qualified reference and the sep `.o`s link. A dependency's
// compiler-owned `.wwi` carries its reachable public foreign type facts;
// separately prepending transitive interfaces is neither necessary nor
// allowed. The unit composition is byte-identical to the cstage driver.
def SEP_MAXPKG: i32 = 256;
@@ -670,7 +666,8 @@ fn sepfindoradd(g: *sepgraph, path: *u8, entry: *u8, isdir: i32) i32 = {
return i;
};
if (os.samefile(pathstr(g.pkg[i].entry), pathstr(entry))) {
cerr("ww: one package directory has identities ");
cerr("ww: package directory "); cerr(pathstr(entry));
cerr(" has identities ");
if (g.pkg[i].path[0u64] == 0u8) { cerr("(root)"); }
else { cerr(pathstr(g.pkg[i].path)); };
cerr(" and ");
@@ -1047,18 +1044,6 @@ fn septopovisit(g: *sepgraph, pi: i32, order: []i32, no: *i32,
return 0;
};
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);
@@ -1165,50 +1150,41 @@ fn sepemitbody(fd: i32, path: *u8, visit: *expctx, searchpath: *u8,
return 0;
};
// 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. Directory membership comes only from the
// package node loaded before planning.
fn sepcomposeunit(g: *sepgraph, pi: i32, scratch: *u8, order: []i32,
norder: i32, searchpath: *u8, unitf: *u8) i32 = {
// Compose pi's sep-unit at `unitf`: its byte-sorted DIRECT dependency
// `.wwi`s, each tagged by its dotted path, then pi's own body under
// //ww:module-reset. Compiler exports are self-contained for public type
// facts; the linker separately retains the reachable archive closure.
fn sepcomposeunit(g: *sepgraph, pi: i32, scratch: *u8,
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: 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: missing wwi\n");
os.close(u);
return -1;
};
let dm: str = "//ww:module ";
if (!sepwriteall(u, dm.ptr, dm.len: u64)
|| !sepwriteall(u, g.pkg[dj].path,
cstrlen(g.pkg[dj].path))
|| !sepwriteall(u, "\n".ptr, 1u64)
|| !sepwriteall(u, wb, wn)
|| !sepwriteall(u, "\n".ptr, 1u64)) {
cerr("ww: cannot compose package unit\n");
os.close(u);
return -1;
};
};
let k: i32 = 0;
for (k < g.pkg[pi].ndeps) {
let dj: i32 = g.pkg[pi].deps[k];
let wwi: *u8 = sepfname(g, dj, scratch, ".wwi");
let wb: *u8;
let wn: u64;
wb, wn = slurp(wwi);
if (wb == nil) {
cerr("ww: missing wwi\n");
os.close(u);
return -1;
};
oi += 1;
let dm: str = "//ww:module ";
if (!sepwriteall(u, dm.ptr, dm.len: u64)
|| !sepwriteall(u, g.pkg[dj].path,
cstrlen(g.pkg[dj].path))
|| !sepwriteall(u, "\n".ptr, 1u64)
|| !sepwriteall(u, wb, wn)
|| !sepwriteall(u, "\n".ptr, 1u64)) {
cerr("ww: cannot compose package unit\n");
os.close(u);
return -1;
};
k += 1;
};
let bv: expctx;
bv.out = u;
@@ -1429,14 +1405,14 @@ fn copyfileatomic(src: *u8, dst: *u8) i32 = {
fn workdirstamptext(istest: i32, emitasm: i32) str = {
if (istest != 0) {
if (emitasm != 0) {
return "ww workdir fmt 1 mode test asm 1\n";
return "ww workdir fmt 2 mode test asm 1\n";
};
return "ww workdir fmt 1 mode test asm 0\n";
return "ww workdir fmt 2 mode test asm 0\n";
};
if (emitasm != 0) {
return "ww workdir fmt 1 mode build asm 1\n";
return "ww workdir fmt 2 mode build asm 1\n";
};
return "ww workdir fmt 1 mode build asm 0\n";
return "ww workdir fmt 2 mode build asm 0\n";
};
fn stampmatches(path: *u8, want: str) bool = {
@@ -1699,7 +1675,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
};
let needsexport: bool = (pi != root) || rootpackage;
let needsarchive: bool = (pi != root) || rootpackage;
if (sepcomposeunit(g, pi, scratch, order, norder, searchpath.ptr, cu) < 0) {
if (sepcomposeunit(g, pi, scratch, searchpath.ptr, cu) < 0) {
return 1;
};
let fresh: bool = false;