build: pass direct exports to package compilers

This commit is contained in:
2026-08-12 18:42:22 +09:00
parent b373c445bd
commit edd3f4ee83
13 changed files with 678 additions and 611 deletions

View File

@@ -286,12 +286,11 @@ fn rejectstable(dir: str, label: str, target: str, needle: str) void = {
|| testenv.has(apiiface, "hidden")) {
fail("self-contained", "api export leaked unrelated or private declarations");
};
let expectedunit: str = strings.concat("//ww:module api\n", apiiface,
"\n//ww:module-reset\n", mainsrc, "\n");
let expectedunit: str = strings.concat("//ww:module-reset\n", mainsrc, "\n");
if (!testenv.same(expectedunit, testenv.readfile(strings.concat(cwork,
"__root.unit.ww"))) || !testenv.same(expectedunit,
testenv.readfile(strings.concat(wwork, "__root.unit.ww")))) {
fail("self-contained", "consumer compiler input was not direct-api-only");
fail("self-contained", "consumer unit was not exactly its owned source");
};
if (!testenv.exists(strings.concat(cwork, "implementation.a"))
|| !testenv.exists(strings.concat(cwork, "dimensions.a"))) {
@@ -405,10 +404,12 @@ fn writediamond(td: str, reverse: bool) str = {
};
let unit: str = testenv.readfile(strings.concat(scratch,
"__root.unit.ww"));
let lp: i32 = testenv.pos(unit, "//ww:module left\n");
let rp: i32 = testenv.pos(unit, "//ww:module right\n");
if (lp < 0 || rp < 0 || lp >= rp) {
fail("diamond", "dependency traversal did not byte-sort imports");
let rootbody: str = testenv.readfile(strings.concat(main, "/main.ww"));
let wantroot: str = strings.concat("//ww:module-reset\n", rootbody,
"\n");
if (!testenv.same(unit, wantroot) || testenv.has(unit,
"//ww:module ")) {
fail("diamond", "root unit contains non-owned export text");
};
let sharedunit: str = testenv.readfile(strings.concat(scratch,
"shared.unit.ww"));
@@ -426,8 +427,7 @@ fn writediamond(td: str, reverse: bool) str = {
};
if (testenv.occurrences(leftiface, "export type token") != 1
|| testenv.occurrences(rightiface, "export type token") != 1
|| testenv.occurrences(unit, "export type token") != 2
|| testenv.occurrences(unit, "//ww:module shared\n") != 2) {
|| testenv.has(unit, "export type token")) {
fail("diamond", "origin fact closure did not merge deterministically");
};
i += 1;
@@ -452,43 +452,6 @@ fn writediamond(td: str, reverse: bool) str = {
fail("diamond", "shuffled enumeration changed package units");
};
// Count actual compiler actions, rather than inferring interning from
// artifact names. The wrapper is selected through the production
// driver's existing tool override and records one row per w6c process.
let trace: str = strings.concat(td, "/compiler.trace");
let wrapper: str = strings.concat(td, "/trace-w6c.sh");
testenv.writefile(trace, "");
testenv.writeexecutable(wrapper, strings.concat(
"#!/bin/sh\n",
"printf '%s\\n' \"$*\" >> \"$WW_LOCALBUILD_TRACE\"\n",
"exec \"$WW_LOCALBUILD_W6C\" \"$@\"\n"));
let baseenv: []str = os.getenvs();
let env: []str = alloc([], (baseenv.len + 3): u64)!;
let ei: i32 = 0;
for (ei < baseenv.len) {
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
&& !strings.hasprefix(baseenv[ei], "WW_LOCALBUILD_TRACE=")
&& !strings.hasprefix(baseenv[ei], "WW_LOCALBUILD_W6C=")) {
append(env, baseenv[ei]);
};
ei += 1;
};
append(env, strings.concat("WW_W6C=", wrapper));
append(env, strings.concat("WW_LOCALBUILD_TRACE=", trace));
append(env, strings.concat("WW_LOCALBUILD_W6C=",
testenv.driver("w6c")));
let countedout: str = strings.concat(td, "/diamond-counted");
let countedav: []str = [testenv.driver("ww"), "build", "-I", treea,
"-o", countedout, main];
let counted: testenv.commandout;
testenv.runcommandenv(td, td, "diamond_compile_count", countedav,
env, tmo(), &counted);
if (counted.termination != exec.termination.EXIT || counted.code != 0) {
fail("diamond", "instrumented build failed");
};
if (testenv.occurrences(testenv.readfile(trace), "shared.unit.ww") != 1) {
fail("diamond", "shared package was not compiled exactly once");
};
testenv.clean(td);
};
@@ -604,10 +567,11 @@ fn writediamond(td: str, reverse: bool) str = {
"__root.unit.ww"));
let depunit: str = testenv.readfile(strings.concat(work,
"dep.unit.ww"));
if (!testenv.has(rootunit, "//ww:module dep\n")
|| testenv.has(rootunit, "//ww:module leaf\n")
|| !testenv.has(depunit, "//ww:module leaf\n")) {
fail("library-roots", "compiler inputs crossed the direct-export boundary");
if (!testenv.has(rootunit, "//ww:module-reset\npackage main;")
|| testenv.has(rootunit, "//ww:module ")
|| !testenv.has(depunit, "//ww:module-reset dep\npackage dep;")
|| testenv.has(depunit, "//ww:module ")) {
fail("library-roots", "package units contain non-owned export text");
};
if (!testenv.exists(strings.concat(work, "leaf.wwi"))
|| !testenv.exists(strings.concat(work, "leaf.a"))
@@ -636,7 +600,8 @@ fn writediamond(td: str, reverse: bool) str = {
fail("library-roots", "compiler actions were not deterministic postorder");
};
if (!testenv.has(ctrace, strings.concat(
"BEGIN<-c><-I><", work, "dep.wwi><-o><", work,
"BEGIN<-c><--import><leaf><", work, "leaf.wwi><-I><", work,
"dep.wwi><-o><", work,
"dep.s><", work, "dep.unit.ww>"))
|| testenv.occurrences(atrace, "\n") != 3
|| !testenv.has(atrace, strings.concat("BEGIN<-o><", work,
@@ -758,7 +723,8 @@ fn writediamond(td: str, reverse: bool) str = {
runav) != 42) {
fail("library-roots", "empty override did not use default roots");
};
if (!testenv.has(rootunit, "//ww:module types\n")
if (!testenv.has(rootunit, "//ww:module-reset\npackage main;")
|| testenv.has(rootunit, "//ww:module ")
|| !testenv.exists(strings.concat(emptywork, "types.wwi"))
|| !testenv.exists(strings.concat(emptywork, "types.a"))
|| testenv.occurrences(ctrace, strings.concat("<", emptywork,
@@ -904,10 +870,11 @@ fn writediamond(td: str, reverse: bool) str = {
let depunitpath: str = strings.concat(work, "/dep.unit.ww");
let rootunit: str = testenv.readfile(rootunitpath);
let depunit: str = testenv.readfile(depunitpath);
if (!testenv.has(rootunit, "//ww:module dep\n")
|| testenv.has(rootunit, "//ww:module leaf\n")
|| !testenv.has(depunit, "//ww:module leaf\n")) {
fail("driver-identity", "warm units crossed the direct-export boundary");
if (!testenv.has(rootunit, "//ww:module-reset\npackage main;")
|| testenv.has(rootunit, "//ww:module ")
|| !testenv.has(depunit, "//ww:module-reset dep\npackage dep;")
|| testenv.has(depunit, "//ww:module ")) {
fail("driver-identity", "warm units contain non-owned export text");
};
if (!testenv.exists(strings.concat(work, "/leaf.wwi"))
|| !testenv.exists(strings.concat(work, "/leaf.a"))
@@ -923,7 +890,7 @@ fn writediamond(td: str, reverse: bool) str = {
"/.wwtool.w6a")), testenv.readfile(assembler))
|| !testenv.same(testenv.readfile(strings.concat(work,
"/.wwtool.stamp")),
"ww workdir fmt 5 mode build asm 0\n")) {
"ww workdir fmt 6 mode build asm 0\n")) {
fail("driver-identity", "persistent artifacts or identities are incomplete");
};
let coldwwi: str = testenv.readfile(strings.concat(work, "/dep.wwi"));
@@ -934,10 +901,12 @@ fn writediamond(td: str, reverse: bool) str = {
let coldlinker: str = testenv.readfile(linkertrace);
if (testenv.occurrences(coldcompiler, "\n") != 3
|| !testenv.has(coldcompiler, strings.concat(
"BEGIN<-c><-I><", work, "/dep.wwi.new><-o><", work,
"BEGIN<-c><--import><leaf><", work, "/leaf.wwi><-I><", work,
"/dep.wwi.new><-o><", work,
"/dep.s.new><", work, "/dep.unit.new>"))
|| !testenv.has(coldcompiler, strings.concat(
"BEGIN<-c><-o><", work, "/__root.s.new><", work,
"BEGIN<-c><--import><dep><", work, "/dep.wwi><-o><", work,
"/__root.s.new><", work,
"/__root.unit.new>"))
|| testenv.occurrences(coldassembler, "\n") != 3
|| !testenv.has(coldassembler, strings.concat(
@@ -964,21 +933,62 @@ fn writediamond(td: str, reverse: bool) str = {
fail("driver-identity", "cold binary returned the wrong value");
};
testenv.writefile(strings.concat(leaf, "/extra.ww"), strings.concat(
"package leaf;\n",
"export fn unchanged_api() i32 = { return 1; };\n"));
testenv.runcommandenv(cwd, td, strings.concat("driver-export-change-",
tags[si]), av, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0) {
fail("driver-identity", "dependency export-change build failed");
};
let changedexportcompiler: str = testenv.readfile(compilertrace);
let changedexportassembler: str = testenv.readfile(assemblertrace);
if (testenv.occurrences(changedexportcompiler, strings.concat("<", work,
"/leaf.unit.new>")) != 2
|| testenv.occurrences(changedexportcompiler, strings.concat("<", work,
"/dep.unit.new>")) != 2
|| testenv.occurrences(changedexportcompiler, strings.concat("<", work,
"/__root.unit.new>")) != 1
|| testenv.occurrences(changedexportcompiler, "\n") != 5
|| testenv.occurrences(changedexportassembler, "\n") != 5) {
fail("driver-identity", "changed export did not stop at an unchanged importer export");
};
if (code(cwd, strings.concat("driver-export-change-run-", tags[si]),
runav) != 42) {
fail("driver-identity", "export-change binary returned the wrong value");
};
let changedexportbin: str = testenv.readfile(bin);
let beforewarmcompiler: str = strings.dup(changedexportcompiler);
let beforewarmassembler: str = strings.dup(changedexportassembler);
testenv.runcommandenv(cwd, td, strings.concat("driver-warm-",
tags[si]), av, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0
|| !testenv.same(coldcompiler, testenv.readfile(compilertrace))
|| !testenv.same(coldassembler, testenv.readfile(assemblertrace))
|| testenv.occurrences(testenv.readfile(linkertrace), "\n") != 2
|| !testenv.same(beforewarmcompiler, testenv.readfile(compilertrace))
|| !testenv.same(beforewarmassembler, testenv.readfile(assemblertrace))
|| testenv.occurrences(testenv.readfile(linkertrace), "\n") != 3
|| !testenv.same(rootunit, testenv.readfile(rootunitpath))
|| !testenv.same(coldwwi, testenv.readfile(strings.concat(work,
"/dep.wwi")))
|| !testenv.same(coldarchive, testenv.readfile(strings.concat(work,
"/dep.a")))
|| !testenv.same(coldbin, testenv.readfile(bin))) {
|| !testenv.same(changedexportbin, testenv.readfile(bin))) {
fail("driver-identity", "unchanged warm build did not reuse packages");
};
assert(os.remove(strings.concat(leaf, "/extra.ww")) == 0);
testenv.runcommandenv(cwd, td, strings.concat("driver-export-restore-",
tags[si]), av, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0
|| testenv.occurrences(testenv.readfile(compilertrace), strings.concat(
"<", work, "/leaf.unit.new>")) != 3
|| testenv.occurrences(testenv.readfile(compilertrace), strings.concat(
"<", work, "/dep.unit.new>")) != 3
|| testenv.occurrences(testenv.readfile(compilertrace), strings.concat(
"<", work, "/__root.unit.new>")) != 1
|| testenv.occurrences(testenv.readfile(compilertrace), "\n") != 7
|| testenv.occurrences(testenv.readfile(assemblertrace), "\n") != 7
|| !testenv.same(coldbin, testenv.readfile(bin))) {
fail("driver-identity", "restored export did not rebuild only its direct importer");
};
assert(os.remove(copied[si]) == 0);
testenv.writeexecutable(copied[si], strings.concat(driverbytes,
@@ -994,14 +1004,14 @@ fn writediamond(td: str, reverse: bool) str = {
let changedassembler: str = testenv.readfile(assemblertrace);
let changedlinker: str = testenv.readfile(linkertrace);
if (testenv.occurrences(changedcompiler, strings.concat("<", work,
"/leaf.unit.new>")) != 2
"/leaf.unit.new>")) != 4
|| testenv.occurrences(changedcompiler, strings.concat("<", work,
"/dep.unit.new>")) != 2
"/dep.unit.new>")) != 4
|| testenv.occurrences(changedcompiler, strings.concat("<", work,
"/__root.unit.new>")) != 2
|| testenv.occurrences(changedcompiler, "\n") != 6
|| testenv.occurrences(changedassembler, "\n") != 6
|| testenv.occurrences(changedlinker, "\n") != 3) {
|| testenv.occurrences(changedcompiler, "\n") != 10
|| testenv.occurrences(changedassembler, "\n") != 10
|| testenv.occurrences(changedlinker, "\n") != 5) {
fail("driver-identity", "driver change reused stale package actions");
};
if (!testenv.same(testenv.readfile(strings.concat(work,