ww: invalidate partial package commits
This commit is contained in:
@@ -4220,6 +4220,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
return 1;
|
||||
}
|
||||
int any_failed = 0;
|
||||
int commit_open = 0;
|
||||
int commit_integrity_failed = 0;
|
||||
for (int i = 0; i < nproducts; i++)
|
||||
if (g->pkg[products[i].root].failed) any_failed = 1;
|
||||
for (int oi = 0; oi < norder; oi++) {
|
||||
@@ -4255,6 +4257,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
const char *ca = warm ? anew : apath;
|
||||
if (sep_discard_action_staging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew) < 0) {
|
||||
if (warm && unlink(unitf) != 0 && errno != ENOENT)
|
||||
commit_integrity_failed = 1;
|
||||
g->pkg[pi].failed = 1;
|
||||
any_failed = 1;
|
||||
continue;
|
||||
@@ -4262,6 +4266,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
if (sep_compose_unit(g, pi, cu) < 0) {
|
||||
(void)sep_discard_action_staging(warm, unitnew, wwinew,
|
||||
asmnew, objnew, anew);
|
||||
if (warm && unlink(unitf) != 0 && errno != ENOENT)
|
||||
commit_integrity_failed = 1;
|
||||
g->pkg[pi].failed = 1;
|
||||
any_failed = 1;
|
||||
continue;
|
||||
@@ -4271,6 +4277,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
if (g->pkg[g->pkg[pi].deps[k]].export_changed)
|
||||
deps_changed = 1;
|
||||
if (warm && !stale_all && !deps_changed
|
||||
&& !commit_integrity_failed
|
||||
&& file_equal(unitnew, unitf)
|
||||
&& file_is_reg(asmf)
|
||||
&& file_is_reg(wwi)
|
||||
@@ -4284,6 +4291,20 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* Once an action is known not to be reusable, its old unit must no
|
||||
* longer vouch for artifacts if any later producer or commit step
|
||||
* fails. This is especially important when a dependency already
|
||||
* committed a changed export during the same request. */
|
||||
if (warm && unlink(unitf) != 0 && errno != ENOENT) {
|
||||
fprintf(stderr, "ww: cannot invalidate package unit %s\n",
|
||||
unitf);
|
||||
(void)sep_discard_action_staging(warm, unitnew, wwinew,
|
||||
asmnew, objnew, anew);
|
||||
g->pkg[pi].failed = 1;
|
||||
any_failed = 1;
|
||||
commit_integrity_failed = 1;
|
||||
continue;
|
||||
}
|
||||
int nmaps = 0;
|
||||
for (int k = 0; k < g->pkg[pi].bindings.n; k++) {
|
||||
if (!sep_binding_first_map(g, &g->pkg[pi].bindings, k))
|
||||
@@ -4418,8 +4439,22 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
}
|
||||
}
|
||||
/* Commit order: artifacts before the unit that vouches for
|
||||
* them, unit strictly last. */
|
||||
* them, unit strictly last. Remove the workdir identity before the
|
||||
* first artifact rename; failure to do so is a pre-commit rejection
|
||||
* that leaves all committed artifacts untouched. */
|
||||
if (warm) {
|
||||
if (!commit_open) {
|
||||
if (unlink(stampf) != 0 && errno != ENOENT) {
|
||||
fprintf(stderr,
|
||||
"ww: cannot invalidate package workdir\n");
|
||||
g->pkg[pi].failed = 1;
|
||||
any_failed = 1;
|
||||
(void)sep_discard_action_staging(warm, unitnew,
|
||||
wwinew, asmnew, objnew, anew);
|
||||
continue;
|
||||
}
|
||||
commit_open = 1;
|
||||
}
|
||||
if (rename(wwinew, wwi) != 0
|
||||
|| rename(asmnew, asmf) != 0
|
||||
|| (!emit_asm && rename(objnew, obj) != 0)
|
||||
@@ -4429,6 +4464,12 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
g->pkg[pi].path[0] ? g->pkg[pi].path : "(root)");
|
||||
g->pkg[pi].failed = 1;
|
||||
any_failed = 1;
|
||||
/* A failed rename sequence may already have replaced the
|
||||
* interface or another artifact. The stamp is already absent;
|
||||
* also invalidate every unit voucher and force later actions in
|
||||
* this invocation through their producers. */
|
||||
commit_integrity_failed = 1;
|
||||
(void)invalidate_workdir_units(scratch);
|
||||
(void)sep_discard_action_staging(warm, unitnew, wwinew,
|
||||
asmnew, objnew, anew);
|
||||
continue;
|
||||
@@ -4437,9 +4478,9 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
}
|
||||
/* Stale passes removed every old unit voucher before compiling. Current
|
||||
* successful units are therefore safe to vouch for even when a sibling
|
||||
* root failed; a killed pass leaves the old identity and forces another
|
||||
* invalidating pass, never false reuse. */
|
||||
if (warm) {
|
||||
* compiler rejects. A partial artifact commit invalidates all vouchers
|
||||
* and suppresses the workdir identity so the next pass starts stale. */
|
||||
if (warm && !commit_integrity_failed) {
|
||||
if (!file_equal(toolw, self_path)
|
||||
&& copy_file_atomic(self_path, toolw) != 0) {
|
||||
fprintf(stderr, "ww: cannot record %s\n", toolw);
|
||||
@@ -4455,7 +4496,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
fprintf(stderr, "ww: cannot record %s\n", toola);
|
||||
free(order); return 1;
|
||||
}
|
||||
if (!stampok) {
|
||||
if (!stampok || commit_open) {
|
||||
char stampnew[PATH_MAX];
|
||||
int sn = snprintf(stampnew, sizeof stampnew, "%s.new", stampf);
|
||||
if (sn < 0 || (size_t)sn >= sizeof stampnew) {
|
||||
|
||||
@@ -4558,7 +4558,17 @@ loader-owned deterministic error before producers. Compiler-owned scope or use
|
||||
errors may invoke the compiler, but the driver removes that action's staged
|
||||
`.new` unit/export/assembly/object/archive files instead of renaming them. No
|
||||
completion/status marker is written, and no publication occurs after a failed
|
||||
compile.
|
||||
compile. If an artifact rename itself fails after an earlier rename has
|
||||
published part of a new generation, the driver removes every committed unit
|
||||
voucher as well as the remaining staged files. Before the first artifact
|
||||
rename, it must successfully remove the workdir identity stamp; failure is a
|
||||
pre-commit rejection that leaves the prior artifact generation untouched. The
|
||||
stamp remains absent after any partial commit, and later actions in that
|
||||
invocation cannot take the freshness shortcut. Each non-reusable action also
|
||||
loses its old unit voucher before its producers run. The next request must
|
||||
therefore rebuild the incomplete action set and reconsider direct importers; a
|
||||
partly replaced `.wwi` or a failed importer can never make an old importer
|
||||
voucher look fresh.
|
||||
|
||||
Package kind follows the declaration. `package main`, not a path component,
|
||||
marks a command. A path ending in `main` remains importable when it declares a
|
||||
|
||||
@@ -236,7 +236,12 @@ union; command and production/internal/external/generated-main variants retain
|
||||
canonical action ownership; vendor expansion changes identity but not the
|
||||
declared qualifier; compiler argv contains only direct `.wwi` inputs; and its
|
||||
named rejected actions leave neither committed nor staged action artifacts or a
|
||||
published binary. Together with the existing directory, recursive, vendor,
|
||||
published binary. It also forces a staged multi-artifact commit to fail after
|
||||
the interface rename, then proves that both stages invalidate old unit vouchers
|
||||
and reconsider the importer rather than accepting a mixed warm generation. A
|
||||
separate injected stamp-removal failure proves the pre-commit gate leaves all
|
||||
previously committed artifacts byte-identical.
|
||||
Together with the existing directory, recursive, vendor,
|
||||
exact-argv, command, and persistent-workdir observers, the package suite proves
|
||||
archive-only link argv and exact warm/rejection-state behavior without
|
||||
duplicating those broader mechanisms in this observer.
|
||||
|
||||
@@ -5093,6 +5093,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
return 1;
|
||||
};
|
||||
let anyfailed: bool = false;
|
||||
let commitopen: bool = false;
|
||||
let commitintegrityfailed: bool = false;
|
||||
producti = 0;
|
||||
for (producti < nproducts) {
|
||||
if (g.pkg[products[producti].root].failed) { anyfailed = true; };
|
||||
@@ -5139,6 +5141,12 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
};
|
||||
if (sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew) < 0) {
|
||||
if (warm) {
|
||||
let unitrr: i32 = os.remove(pathstr(unitf));
|
||||
if (unitrr != 0 && unitrr != -2) {
|
||||
commitintegrityfailed = true;
|
||||
};
|
||||
};
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
oi += 1;
|
||||
@@ -5147,6 +5155,12 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
if (sepcomposeunit(g, pi, cu) < 0) {
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
if (warm) {
|
||||
let unitrr: i32 = os.remove(pathstr(unitf));
|
||||
if (unitrr != 0 && unitrr != -2) {
|
||||
commitintegrityfailed = true;
|
||||
};
|
||||
};
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
oi += 1;
|
||||
@@ -5162,7 +5176,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
};
|
||||
let fresh: bool = false;
|
||||
if (warm) {
|
||||
if (!staleall && !depschanged) {
|
||||
if (!staleall && !depschanged && !commitintegrityfailed) {
|
||||
fresh = fileequal(unitnew, unitf);
|
||||
if (fresh) { fresh = fileisreg(asmf); };
|
||||
if (fresh) { fresh = fileisreg(wwi); };
|
||||
@@ -5190,6 +5204,22 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
oi += 1;
|
||||
continue;
|
||||
};
|
||||
// Once an action is not reusable, its old unit must not vouch for
|
||||
// artifacts after a later producer or commit failure. This also makes a
|
||||
// failed importer retry after a dependency committed a changed export.
|
||||
if (warm) {
|
||||
let unitrr: i32 = os.remove(pathstr(unitf));
|
||||
if (unitrr != 0 && unitrr != -2) {
|
||||
cerrpath("ww: cannot invalidate package unit ", unitf, "\n");
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
commitintegrityfailed = true;
|
||||
oi += 1;
|
||||
continue;
|
||||
};
|
||||
};
|
||||
{
|
||||
let rawtest: bool = (istest != 0) && g.pkg[pi].root
|
||||
&& g.pkg[pi].isdir == 0;
|
||||
@@ -5378,9 +5408,23 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
continue;
|
||||
};
|
||||
};
|
||||
// Commit order: artifacts before the unit that vouches for
|
||||
// them, unit strictly last.
|
||||
// Commit order: artifacts before the unit that vouches for them, unit
|
||||
// strictly last. Remove the workdir identity before the first artifact
|
||||
// rename; failure is a pre-commit rejection with old artifacts intact.
|
||||
if (warm) {
|
||||
if (!commitopen) {
|
||||
let stamprr: i32 = os.remove(pathstr(stampf));
|
||||
if (stamprr != 0 && stamprr != -2) {
|
||||
cerr("ww: cannot invalidate package workdir\n");
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
oi += 1;
|
||||
continue;
|
||||
};
|
||||
commitopen = true;
|
||||
};
|
||||
let bad: bool = false;
|
||||
if (os.rename(pathstr(wwinew), pathstr(wwi)) != 0) {
|
||||
bad = true;
|
||||
@@ -5418,6 +5462,11 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
};
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
// A failed rename sequence may already have replaced the
|
||||
// interface or another artifact. The stamp is already absent;
|
||||
// invalidate all vouchers and force later actions through tools.
|
||||
commitintegrityfailed = true;
|
||||
invalidateworkdirunits(scratch);
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
oi += 1;
|
||||
@@ -5427,9 +5476,10 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
oi += 1;
|
||||
};
|
||||
// A stale pass removed every old unit voucher before compiling. Current
|
||||
// successful units remain safe to vouch for when a sibling root fails;
|
||||
// a killed pass retains the old identity and invalidates again next time.
|
||||
if (warm) {
|
||||
// successful units remain safe when a sibling compiler rejects. A partial
|
||||
// artifact commit invalidates all vouchers and suppresses the workdir
|
||||
// identity so the next pass starts stale.
|
||||
if (warm && !commitintegrityfailed) {
|
||||
let sametool: bool = fileequal(toolw, selfpath);
|
||||
if (sepfatalallocation) { return 1; };
|
||||
if (!sametool) {
|
||||
@@ -5456,7 +5506,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
};
|
||||
};
|
||||
};
|
||||
if (!stampok) {
|
||||
if (!stampok || commitopen) {
|
||||
if (writestampatomic(stampf, stampwant) != 0) {
|
||||
cerrpath("ww: cannot record ", stampf, "\n");
|
||||
return 1;
|
||||
|
||||
@@ -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], "");
|
||||
|
||||
Reference in New Issue
Block a user