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;
|
return 1;
|
||||||
}
|
}
|
||||||
int any_failed = 0;
|
int any_failed = 0;
|
||||||
|
int commit_open = 0;
|
||||||
|
int commit_integrity_failed = 0;
|
||||||
for (int i = 0; i < nproducts; i++)
|
for (int i = 0; i < nproducts; i++)
|
||||||
if (g->pkg[products[i].root].failed) any_failed = 1;
|
if (g->pkg[products[i].root].failed) any_failed = 1;
|
||||||
for (int oi = 0; oi < norder; oi++) {
|
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;
|
const char *ca = warm ? anew : apath;
|
||||||
if (sep_discard_action_staging(warm, unitnew, wwinew, asmnew,
|
if (sep_discard_action_staging(warm, unitnew, wwinew, asmnew,
|
||||||
objnew, anew) < 0) {
|
objnew, anew) < 0) {
|
||||||
|
if (warm && unlink(unitf) != 0 && errno != ENOENT)
|
||||||
|
commit_integrity_failed = 1;
|
||||||
g->pkg[pi].failed = 1;
|
g->pkg[pi].failed = 1;
|
||||||
any_failed = 1;
|
any_failed = 1;
|
||||||
continue;
|
continue;
|
||||||
@@ -4262,6 +4266,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
|||||||
if (sep_compose_unit(g, pi, cu) < 0) {
|
if (sep_compose_unit(g, pi, cu) < 0) {
|
||||||
(void)sep_discard_action_staging(warm, unitnew, wwinew,
|
(void)sep_discard_action_staging(warm, unitnew, wwinew,
|
||||||
asmnew, objnew, anew);
|
asmnew, objnew, anew);
|
||||||
|
if (warm && unlink(unitf) != 0 && errno != ENOENT)
|
||||||
|
commit_integrity_failed = 1;
|
||||||
g->pkg[pi].failed = 1;
|
g->pkg[pi].failed = 1;
|
||||||
any_failed = 1;
|
any_failed = 1;
|
||||||
continue;
|
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)
|
if (g->pkg[g->pkg[pi].deps[k]].export_changed)
|
||||||
deps_changed = 1;
|
deps_changed = 1;
|
||||||
if (warm && !stale_all && !deps_changed
|
if (warm && !stale_all && !deps_changed
|
||||||
|
&& !commit_integrity_failed
|
||||||
&& file_equal(unitnew, unitf)
|
&& file_equal(unitnew, unitf)
|
||||||
&& file_is_reg(asmf)
|
&& file_is_reg(asmf)
|
||||||
&& file_is_reg(wwi)
|
&& file_is_reg(wwi)
|
||||||
@@ -4284,6 +4291,20 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
|||||||
}
|
}
|
||||||
continue;
|
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;
|
int nmaps = 0;
|
||||||
for (int k = 0; k < g->pkg[pi].bindings.n; k++) {
|
for (int k = 0; k < g->pkg[pi].bindings.n; k++) {
|
||||||
if (!sep_binding_first_map(g, &g->pkg[pi].bindings, 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
|
/* 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 (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
|
if (rename(wwinew, wwi) != 0
|
||||||
|| rename(asmnew, asmf) != 0
|
|| rename(asmnew, asmf) != 0
|
||||||
|| (!emit_asm && rename(objnew, obj) != 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].path[0] ? g->pkg[pi].path : "(root)");
|
||||||
g->pkg[pi].failed = 1;
|
g->pkg[pi].failed = 1;
|
||||||
any_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,
|
(void)sep_discard_action_staging(warm, unitnew, wwinew,
|
||||||
asmnew, objnew, anew);
|
asmnew, objnew, anew);
|
||||||
continue;
|
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
|
/* Stale passes removed every old unit voucher before compiling. Current
|
||||||
* successful units are therefore safe to vouch for even when a sibling
|
* successful units are therefore safe to vouch for even when a sibling
|
||||||
* root failed; a killed pass leaves the old identity and forces another
|
* compiler rejects. A partial artifact commit invalidates all vouchers
|
||||||
* invalidating pass, never false reuse. */
|
* and suppresses the workdir identity so the next pass starts stale. */
|
||||||
if (warm) {
|
if (warm && !commit_integrity_failed) {
|
||||||
if (!file_equal(toolw, self_path)
|
if (!file_equal(toolw, self_path)
|
||||||
&& copy_file_atomic(self_path, toolw) != 0) {
|
&& copy_file_atomic(self_path, toolw) != 0) {
|
||||||
fprintf(stderr, "ww: cannot record %s\n", toolw);
|
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);
|
fprintf(stderr, "ww: cannot record %s\n", toola);
|
||||||
free(order); return 1;
|
free(order); return 1;
|
||||||
}
|
}
|
||||||
if (!stampok) {
|
if (!stampok || commit_open) {
|
||||||
char stampnew[PATH_MAX];
|
char stampnew[PATH_MAX];
|
||||||
int sn = snprintf(stampnew, sizeof stampnew, "%s.new", stampf);
|
int sn = snprintf(stampnew, sizeof stampnew, "%s.new", stampf);
|
||||||
if (sn < 0 || (size_t)sn >= sizeof stampnew) {
|
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
|
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
|
`.new` unit/export/assembly/object/archive files instead of renaming them. No
|
||||||
completion/status marker is written, and no publication occurs after a failed
|
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,
|
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
|
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
|
canonical action ownership; vendor expansion changes identity but not the
|
||||||
declared qualifier; compiler argv contains only direct `.wwi` inputs; and its
|
declared qualifier; compiler argv contains only direct `.wwi` inputs; and its
|
||||||
named rejected actions leave neither committed nor staged action artifacts or a
|
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
|
exact-argv, command, and persistent-workdir observers, the package suite proves
|
||||||
archive-only link argv and exact warm/rejection-state behavior without
|
archive-only link argv and exact warm/rejection-state behavior without
|
||||||
duplicating those broader mechanisms in this observer.
|
duplicating those broader mechanisms in this observer.
|
||||||
|
|||||||
@@ -5093,6 +5093,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
let anyfailed: bool = false;
|
let anyfailed: bool = false;
|
||||||
|
let commitopen: bool = false;
|
||||||
|
let commitintegrityfailed: bool = false;
|
||||||
producti = 0;
|
producti = 0;
|
||||||
for (producti < nproducts) {
|
for (producti < nproducts) {
|
||||||
if (g.pkg[products[producti].root].failed) { anyfailed = true; };
|
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,
|
if (sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||||
objnew, anew) < 0) {
|
objnew, anew) < 0) {
|
||||||
|
if (warm) {
|
||||||
|
let unitrr: i32 = os.remove(pathstr(unitf));
|
||||||
|
if (unitrr != 0 && unitrr != -2) {
|
||||||
|
commitintegrityfailed = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
g.pkg[pi].failed = true;
|
g.pkg[pi].failed = true;
|
||||||
anyfailed = true;
|
anyfailed = true;
|
||||||
oi += 1;
|
oi += 1;
|
||||||
@@ -5147,6 +5155,12 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
if (sepcomposeunit(g, pi, cu) < 0) {
|
if (sepcomposeunit(g, pi, cu) < 0) {
|
||||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||||
objnew, anew);
|
objnew, anew);
|
||||||
|
if (warm) {
|
||||||
|
let unitrr: i32 = os.remove(pathstr(unitf));
|
||||||
|
if (unitrr != 0 && unitrr != -2) {
|
||||||
|
commitintegrityfailed = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
g.pkg[pi].failed = true;
|
g.pkg[pi].failed = true;
|
||||||
anyfailed = true;
|
anyfailed = true;
|
||||||
oi += 1;
|
oi += 1;
|
||||||
@@ -5162,7 +5176,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
};
|
};
|
||||||
let fresh: bool = false;
|
let fresh: bool = false;
|
||||||
if (warm) {
|
if (warm) {
|
||||||
if (!staleall && !depschanged) {
|
if (!staleall && !depschanged && !commitintegrityfailed) {
|
||||||
fresh = fileequal(unitnew, unitf);
|
fresh = fileequal(unitnew, unitf);
|
||||||
if (fresh) { fresh = fileisreg(asmf); };
|
if (fresh) { fresh = fileisreg(asmf); };
|
||||||
if (fresh) { fresh = fileisreg(wwi); };
|
if (fresh) { fresh = fileisreg(wwi); };
|
||||||
@@ -5190,6 +5204,22 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
oi += 1;
|
oi += 1;
|
||||||
continue;
|
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
|
let rawtest: bool = (istest != 0) && g.pkg[pi].root
|
||||||
&& g.pkg[pi].isdir == 0;
|
&& g.pkg[pi].isdir == 0;
|
||||||
@@ -5378,9 +5408,23 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// Commit order: artifacts before the unit that vouches for
|
// Commit order: artifacts before the unit that vouches for them, unit
|
||||||
// them, unit strictly last.
|
// strictly last. Remove the workdir identity before the first artifact
|
||||||
|
// rename; failure is a pre-commit rejection with old artifacts intact.
|
||||||
if (warm) {
|
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;
|
let bad: bool = false;
|
||||||
if (os.rename(pathstr(wwinew), pathstr(wwi)) != 0) {
|
if (os.rename(pathstr(wwinew), pathstr(wwi)) != 0) {
|
||||||
bad = true;
|
bad = true;
|
||||||
@@ -5418,6 +5462,11 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
};
|
};
|
||||||
g.pkg[pi].failed = true;
|
g.pkg[pi].failed = true;
|
||||||
anyfailed = 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,
|
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||||
objnew, anew);
|
objnew, anew);
|
||||||
oi += 1;
|
oi += 1;
|
||||||
@@ -5427,9 +5476,10 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
oi += 1;
|
oi += 1;
|
||||||
};
|
};
|
||||||
// A stale pass removed every old unit voucher before compiling. Current
|
// A stale pass removed every old unit voucher before compiling. Current
|
||||||
// successful units remain safe to vouch for when a sibling root fails;
|
// successful units remain safe when a sibling compiler rejects. A partial
|
||||||
// a killed pass retains the old identity and invalidates again next time.
|
// artifact commit invalidates all vouchers and suppresses the workdir
|
||||||
if (warm) {
|
// identity so the next pass starts stale.
|
||||||
|
if (warm && !commitintegrityfailed) {
|
||||||
let sametool: bool = fileequal(toolw, selfpath);
|
let sametool: bool = fileequal(toolw, selfpath);
|
||||||
if (sepfatalallocation) { return 1; };
|
if (sepfatalallocation) { return 1; };
|
||||||
if (!sametool) {
|
if (!sametool) {
|
||||||
@@ -5456,7 +5506,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (!stampok) {
|
if (!stampok || commitopen) {
|
||||||
if (writestampatomic(stampf, stampwant) != 0) {
|
if (writestampatomic(stampf, stampwant) != 0) {
|
||||||
cerrpath("ww: cannot record ", stampf, "\n");
|
cerrpath("ww: cannot record ", stampf, "\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -7879,6 +7879,8 @@ fn runtimepath(relative: str) str = {
|
|||||||
let scopetwo: str = strings.concat(source, "/scope/two");
|
let scopetwo: str = strings.concat(source, "/scope/two");
|
||||||
let scoped: str = strings.concat(source, "/cmd/scoped");
|
let scoped: str = strings.concat(source, "/cmd/scoped");
|
||||||
let repeated: str = strings.concat(source, "/cmd/repeated");
|
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 leafbad: str = strings.concat(source, "/cmd/leafbad");
|
||||||
let leak: str = strings.concat(source, "/cmd/leak");
|
let leak: str = strings.concat(source, "/cmd/leak");
|
||||||
let duplicate: str = strings.concat(source, "/cmd/duplicate");
|
let duplicate: str = strings.concat(source, "/cmd/duplicate");
|
||||||
@@ -7895,7 +7897,8 @@ fn runtimepath(relative: str) str = {
|
|||||||
"/vend/vendor/short/codec");
|
"/vend/vendor/short/codec");
|
||||||
let vendorclient: str = strings.concat(source, "/vend/client");
|
let vendorclient: str = strings.concat(source, "/vend/client");
|
||||||
let dirs: []str = [source, tools, codec, bridge, direct, app, scopeone,
|
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,
|
crosscollision, conflict, leafmain, program, leafmainuser, programuser, testcodec,
|
||||||
testhelper, vendored, vendorclient];
|
testhelper, vendored, vendorclient];
|
||||||
let di: i32 = 0;
|
let di: i32 = 0;
|
||||||
@@ -7909,6 +7912,12 @@ fn runtimepath(relative: str) str = {
|
|||||||
"export fn value() i32 = { return 42; };\n");
|
"export fn value() i32 = { return 42; };\n");
|
||||||
let codecfile: str = strings.concat(codec, "/codec.ww");
|
let codecfile: str = strings.concat(codec, "/codec.ww");
|
||||||
writefile(codecfile, wirebody);
|
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(
|
writefile(strings.concat(bridge, "/bridge.ww"), strings.concat(
|
||||||
"package bridge;\nimport acme.codec;\n",
|
"package bridge;\nimport acme.codec;\n",
|
||||||
// WW retains its direct-import bare-declaration convenience. It is
|
// 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\" >> ",
|
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||||||
"\"$WW_NAME_COMPILER_TRACE\"; done\n",
|
"\"$WW_NAME_COMPILER_TRACE\"; done\n",
|
||||||
"printf '\\n' >> \"$WW_NAME_COMPILER_TRACE\"\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 stages: []str = ["ww", "ww_ww"];
|
||||||
let compilers: []str = ["w6c", "w6c_ww"];
|
let compilers: []str = ["w6c", "w6c_ww"];
|
||||||
@@ -8032,6 +8049,16 @@ fn runtimepath(relative: str) str = {
|
|||||||
strings.concat(root, "/ww-scoped")];
|
strings.concat(root, "/ww-scoped")];
|
||||||
let repeatedbins: []str = [strings.concat(root, "/c-repeated"),
|
let repeatedbins: []str = [strings.concat(root, "/c-repeated"),
|
||||||
strings.concat(root, "/ww-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"),
|
let leafmainbins: []str = [strings.concat(root, "/c-leafmain"),
|
||||||
strings.concat(root, "/ww-leafmain")];
|
strings.concat(root, "/ww-leafmain")];
|
||||||
let vendorbins: []str = [strings.concat(root, "/c-vendor"),
|
let vendorbins: []str = [strings.concat(root, "/c-vendor"),
|
||||||
@@ -8053,13 +8080,22 @@ fn runtimepath(relative: str) str = {
|
|||||||
let scopedtraceref: str = "";
|
let scopedtraceref: str = "";
|
||||||
let repeatedtraceref: str = "";
|
let repeatedtraceref: str = "";
|
||||||
let changedtraceref: str = "";
|
let changedtraceref: str = "";
|
||||||
|
let commitfailref: str = "";
|
||||||
|
let commitpreflightref: str = "";
|
||||||
|
let commitbindingref: str = "";
|
||||||
|
let commitbinref: str = "";
|
||||||
let initialwwiref: str = "";
|
let initialwwiref: str = "";
|
||||||
let testoutref: str = "";
|
let testoutref: str = "";
|
||||||
let baseenv: []str = os.getenvs();
|
let baseenv: []str = os.getenvs();
|
||||||
let si: i32 = 0;
|
let si: i32 = 0;
|
||||||
for (si < stages.len) {
|
for (si < stages.len) {
|
||||||
rewritefile(codecfile, wirebody);
|
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(works[si], 448i32) == 0);
|
||||||
|
assert(os.mkdir(commitworks[si], 448i32) == 0);
|
||||||
writefile(traces[si], "");
|
writefile(traces[si], "");
|
||||||
let env: []str = alloc([], (baseenv.len + 7): u64)!;
|
let env: []str = alloc([], (baseenv.len + 7): u64)!;
|
||||||
let ei: i32 = 0;
|
let ei: i32 = 0;
|
||||||
@@ -8071,7 +8107,9 @@ fn runtimepath(relative: str) str = {
|
|||||||
&& !strings.hasprefix(baseenv[ei],
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
"WW_NAME_COMPILER_TRACE=")
|
"WW_NAME_COMPILER_TRACE=")
|
||||||
&& !strings.hasprefix(baseenv[ei],
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
"WW_NAME_REAL_COMPILER=")) {
|
"WW_NAME_REAL_COMPILER=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_COMMIT_BLOCK_STAMP=")) {
|
||||||
append(env, baseenv[ei]);
|
append(env, baseenv[ei]);
|
||||||
};
|
};
|
||||||
ei += 1;
|
ei += 1;
|
||||||
@@ -8122,6 +8160,127 @@ fn runtimepath(relative: str) str = {
|
|||||||
(30i64 * (time.second: i64)): time.duration, &out);
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 42);
|
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,
|
// The same declared qualifier belongs to each importing source file,
|
||||||
// not to the package-wide namespace.
|
// not to the package-wide namespace.
|
||||||
rewritefile(traces[si], "");
|
rewritefile(traces[si], "");
|
||||||
|
|||||||
Reference in New Issue
Block a user