ww: invalidate partial package commits

This commit is contained in:
2026-08-14 05:12:45 +09:00
parent 402f50ac8d
commit 351f4a25bc
5 changed files with 282 additions and 17 deletions

View File

@@ -7879,6 +7879,8 @@ fn runtimepath(relative: str) str = {
let scopetwo: str = strings.concat(source, "/scope/two");
let scoped: str = strings.concat(source, "/cmd/scoped");
let repeated: str = strings.concat(source, "/cmd/repeated");
let commitcodec: str = strings.concat(source, "/commit/codec");
let commitapp: str = strings.concat(source, "/cmd/commit");
let leafbad: str = strings.concat(source, "/cmd/leafbad");
let leak: str = strings.concat(source, "/cmd/leak");
let duplicate: str = strings.concat(source, "/cmd/duplicate");
@@ -7895,7 +7897,8 @@ fn runtimepath(relative: str) str = {
"/vend/vendor/short/codec");
let vendorclient: str = strings.concat(source, "/vend/client");
let dirs: []str = [source, tools, codec, bridge, direct, app, scopeone,
scopetwo, scoped, repeated, leafbad, leak, duplicate, collision,
scopetwo, scoped, repeated, commitcodec, commitapp, leafbad, leak,
duplicate, collision,
crosscollision, conflict, leafmain, program, leafmainuser, programuser, testcodec,
testhelper, vendored, vendorclient];
let di: i32 = 0;
@@ -7909,6 +7912,12 @@ fn runtimepath(relative: str) str = {
"export fn value() i32 = { return 42; };\n");
let codecfile: str = strings.concat(codec, "/codec.ww");
writefile(codecfile, wirebody);
let commitcodecfile: str = strings.concat(commitcodec, "/codec.ww");
let commitappfile: str = strings.concat(commitapp, "/main.ww");
writefile(commitcodecfile, wirebody);
writefile(commitappfile, strings.concat(
"package main;\nimport commit.codec;\n",
"fn main() i32 = { return wire.value(); };\n"));
writefile(strings.concat(bridge, "/bridge.ww"), strings.concat(
"package bridge;\nimport acme.codec;\n",
// WW retains its direct-import bare-declaration convenience. It is
@@ -8014,7 +8023,15 @@ fn runtimepath(relative: str) str = {
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_NAME_COMPILER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_NAME_COMPILER_TRACE\"\n",
"exec \"$WW_NAME_REAL_COMPILER\" \"$@\"\n"));
"\"$WW_NAME_REAL_COMPILER\" \"$@\"\n",
"status=$?\n",
"if [ \"$status\" -eq 0 ] && ",
"[ -n \"$WW_COMMIT_BLOCK_STAMP\" ]; then\n",
" mv -- \"$WW_COMMIT_BLOCK_STAMP\" ",
"\"$WW_COMMIT_BLOCK_STAMP.saved\" || exit 97\n",
" mkdir -- \"$WW_COMMIT_BLOCK_STAMP\" || exit 98\n",
"fi\n",
"exit \"$status\"\n"));
let stages: []str = ["ww", "ww_ww"];
let compilers: []str = ["w6c", "w6c_ww"];
@@ -8032,6 +8049,16 @@ fn runtimepath(relative: str) str = {
strings.concat(root, "/ww-scoped")];
let repeatedbins: []str = [strings.concat(root, "/c-repeated"),
strings.concat(root, "/ww-repeated")];
let commitworks: []str = [strings.concat(root, "/c-commit-work"),
strings.concat(root, "/ww-commit-work")];
let commitcoldbins: []str = [strings.concat(root, "/c-commit-cold"),
strings.concat(root, "/ww-commit-cold")];
let commitblockbins: []str = [strings.concat(root, "/c-commit-block"),
strings.concat(root, "/ww-commit-block")];
let commitrejectbins: []str = [strings.concat(root, "/c-commit-reject"),
strings.concat(root, "/ww-commit-reject")];
let commitfixedbins: []str = [strings.concat(root, "/c-commit-fixed"),
strings.concat(root, "/ww-commit-fixed")];
let leafmainbins: []str = [strings.concat(root, "/c-leafmain"),
strings.concat(root, "/ww-leafmain")];
let vendorbins: []str = [strings.concat(root, "/c-vendor"),
@@ -8053,13 +8080,22 @@ fn runtimepath(relative: str) str = {
let scopedtraceref: str = "";
let repeatedtraceref: str = "";
let changedtraceref: str = "";
let commitfailref: str = "";
let commitpreflightref: str = "";
let commitbindingref: str = "";
let commitbinref: str = "";
let initialwwiref: str = "";
let testoutref: str = "";
let baseenv: []str = os.getenvs();
let si: i32 = 0;
for (si < stages.len) {
rewritefile(codecfile, wirebody);
rewritefile(commitcodecfile, wirebody);
rewritefile(commitappfile, strings.concat(
"package main;\nimport commit.codec;\n",
"fn main() i32 = { return wire.value(); };\n"));
assert(os.mkdir(works[si], 448i32) == 0);
assert(os.mkdir(commitworks[si], 448i32) == 0);
writefile(traces[si], "");
let env: []str = alloc([], (baseenv.len + 7): u64)!;
let ei: i32 = 0;
@@ -8071,7 +8107,9 @@ fn runtimepath(relative: str) str = {
&& !strings.hasprefix(baseenv[ei],
"WW_NAME_COMPILER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_NAME_REAL_COMPILER=")) {
"WW_NAME_REAL_COMPILER=")
&& !strings.hasprefix(baseenv[ei],
"WW_COMMIT_BLOCK_STAMP=")) {
append(env, baseenv[ei]);
};
ei += 1;
@@ -8122,6 +8160,127 @@ fn runtimepath(relative: str) str = {
(30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
// A failed multi-file artifact rename may have published a new
// interface before a later rename fails. It invalidates every old unit
// voucher and the workdir identity, so the next request must reconsider
// an importer even when the regenerated dependency export now compares
// equal to that partially published interface.
let commitcoldav: []str = [driver(stages[si]), "build", "-w",
commitworks[si], "-I", source, "-o", commitcoldbins[si], commitapp];
runcommandenv(root, strings.concat("name-commit-cold-", stages[si]),
commitcoldav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let commitrun: []str = [commitcoldbins[si]];
runcommand(root, strings.concat("name-commit-cold-run-", stages[si]),
commitrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
rewritefile(commitcodecfile, cablebody);
let commitstamp: str = strings.concat(commitworks[si],
"/.wwtool.stamp");
let savedstamp: str = strings.concat(commitstamp, ".saved");
let oldcommitwwi: str = readfile(strings.concat(commitworks[si],
"/commit.codec.wwi"));
let oldcommits: str = readfile(strings.concat(commitworks[si],
"/commit.codec.s"));
let oldcommito: str = readfile(strings.concat(commitworks[si],
"/commit.codec.o"));
let oldcommita: str = readfile(strings.concat(commitworks[si],
"/commit.codec.a"));
let blockenv: []str = alloc([], (env.len + 1): u64)!;
let bei: i32 = 0;
for (bei < env.len) { append(blockenv, env[bei]); bei += 1; };
append(blockenv, strings.concat("WW_COMMIT_BLOCK_STAMP=", commitstamp));
let commitblockav: []str = [driver(stages[si]), "build", "-w",
commitworks[si], "-I", source, "-o", commitblockbins[si],
commitapp];
runcommandenv(root, strings.concat("name-commit-preflight-", stages[si]),
commitblockav, blockenv,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "ww: cannot invalidate package workdir\n"));
if (si == 0) { commitpreflightref = strings.dup(out.stderr); }
else { assert(same(commitpreflightref, out.stderr)); };
assert(same(oldcommitwwi, readfile(strings.concat(commitworks[si],
"/commit.codec.wwi"))));
assert(same(oldcommits, readfile(strings.concat(commitworks[si],
"/commit.codec.s"))));
assert(same(oldcommito, readfile(strings.concat(commitworks[si],
"/commit.codec.o"))));
assert(same(oldcommita, readfile(strings.concat(commitworks[si],
"/commit.codec.a"))));
assert(!os.exists(strings.concat(commitworks[si],
"/commit.codec.unit.ww")));
assert(!os.exists(commitblockbins[si]));
assert(os.rmdir(commitstamp) == 0);
assert(os.rename(savedstamp, commitstamp) == 0);
// With the stamp preflight restored, force a later artifact rename to
// fail after the changed interface has already been published.
let blockedasm: str = strings.concat(commitworks[si],
"/commit.codec.s");
assert(os.remove(blockedasm) == 0);
assert(os.mkdir(blockedasm, 448i32) == 0);
let commitrejectav: []str = [driver(stages[si]), "build", "-w",
commitworks[si], "-I", source, "-o", commitrejectbins[si],
commitapp];
runcommandenv(root, strings.concat("name-commit-partial-", stages[si]),
commitrejectav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "ww: cannot commit commit.codec\n"));
if (si == 0) { commitfailref = strings.dup(out.stderr); }
else { assert(same(commitfailref, out.stderr)); };
assert(has(readfile(strings.concat(commitworks[si],
"/commit.codec.wwi")),
"//ww:module commit.codec\npackage cable;\n"));
assert(!os.exists(strings.concat(commitworks[si],
"/commit.codec.unit.ww")));
assert(!os.exists(strings.concat(commitworks[si],
"/cmd.commit.unit.ww")));
assert(!os.exists(strings.concat(commitworks[si], "/.wwtool.stamp")));
let partialsuffixes: []str = [".unit.new", ".wwi.new", ".s.new",
".o.new", ".a.new"];
let psi: i32 = 0;
for (psi < partialsuffixes.len) {
assert(!os.exists(strings.concat(commitworks[si],
"/commit.codec", partialsuffixes[psi])));
psi += 1;
};
assert(!os.exists(commitrejectbins[si]));
assert(os.rmdir(blockedasm) == 0);
rewritefile(traces[si], "");
runcommandenv(root, strings.concat("name-commit-reconsider-", stages[si]),
commitrejectav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "undefined: wire"));
let ncommitbinding: str = normalizedtrace(out.stderr,
strings.concat(commitworks[si], "/"), commitrejectbins[si]);
if (si == 0) { commitbindingref = strings.dup(ncommitbinding); }
else { assert(same(commitbindingref, ncommitbinding)); };
assert(has(readfile(traces[si]), "/cmd.commit.unit.new"));
assert(os.exists(strings.concat(commitworks[si],
"/commit.codec.unit.ww")));
assert(!os.exists(strings.concat(commitworks[si],
"/cmd.commit.unit.ww")));
assert(!os.exists(commitrejectbins[si]));
rewritefile(commitappfile, strings.concat(
"package main;\nimport commit.codec;\n",
"fn main() i32 = { return cable.value(); };\n"));
let commitfixedav: []str = [driver(stages[si]), "build", "-w",
commitworks[si], "-I", source, "-o", commitfixedbins[si], commitapp];
runcommandenv(root, strings.concat("name-commit-fixed-", stages[si]),
commitfixedav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let commitfixedrun: []str = [commitfixedbins[si]];
runcommand(root, strings.concat("name-commit-fixed-run-", stages[si]),
commitfixedrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
let commitbinbytes: str = readfile(commitfixedbins[si]);
if (si == 0) { commitbinref = strings.dup(commitbinbytes); }
else { assert(same(commitbinref, commitbinbytes)); };
// The same declared qualifier belongs to each importing source file,
// not to the package-wide namespace.
rewritefile(traces[si], "");