build: route .ww directories as packages

This commit is contained in:
2026-08-22 23:39:06 +09:00
parent 2e5451a601
commit ca0e533c13
5 changed files with 683 additions and 30 deletions

View File

@@ -1466,14 +1466,20 @@ fn cwdwritedata(dir: str, label: str) void = {
};
// A visible raw test-source build observes only its initial package/import
// header. It must not turn that observation into a package, import, action,
// artifact, or work-state identity.
// header and then omits it. A .ww-spelled directory, including a symlink with
// a _test.ww spelling, instead enters the ordinary directory-package route.
// Neither classification may redefine package or dotted-import identity.
@test fn named_test_sources_are_header_loaded_then_omitted() void = {
let root: str = fresh();
let source: str = strings.concat(root, "/source");
let dirsource: str = strings.concat(source, "/directory");
let include: str = strings.concat(root, "/include");
let dependency: str = strings.concat(include, "/domain/value");
let dirsource: str = strings.concat(source, "/directory.ww");
let dirtarget: str = strings.concat(source, "/directory-target");
mkdirall(source); mkdirall(dirsource); mkdirall(dirtarget);
let emptydir: str = strings.concat(source, "/empty.ww");
let baddirtarget: str = strings.concat(source, "/broken-target");
mkdirall(source); mkdirall(dependency); mkdirall(dirsource); mkdirall(dirtarget);
mkdirall(emptydir); mkdirall(baddirtarget);
let ignored: str = strings.concat(source, "/raw_windows_test.ww");
let platform: str = strings.concat(source, "/platform_windows_test.ww");
@@ -1486,11 +1492,18 @@ fn cwdwritedata(dir: str, label: str) void = {
let linktarget: str = strings.concat(source, "/target.ww");
let link: str = strings.concat(source, "/requested_test.ww");
let dirlink: str = strings.concat(source, "/directory_test.ww");
let baddirlink: str = strings.concat(source, "/broken_test.ww");
let rawmain: str = strings.concat(source, "/raw.ww");
let runsource: str = strings.concat(source, "/run_test.ww");
let testroute: str = strings.concat(source, "/route_test.ww");
let warmpath: str = strings.concat(source, "/warm.ww");
let warmtest: str = strings.concat(source, "/warm_test.ww");
let dirtargettest: str = strings.concat(dirtarget, "/ignored_test.ww");
let dirtargettestexcluded: str =
"package main;\nimport missing.test.only;\n";
let dirtargettestselected: str = strings.concat(
"package main;\nimport domain.value;\n",
"@test fn directory_selected() void = { assert(value.number() == 5); };\n");
let header: str = strings.concat(
"package raw_test;\nimport absent.pkg;\n",
"this body is deliberately malformed and never reaches a producer {\n",
@@ -1513,24 +1526,40 @@ fn cwdwritedata(dir: str, label: str) void = {
"package route;\n",
"@test fn still_selected() void = { assert(true); };\n"));
writefile(warmpath, "package main;\nfn main() i32 = { return 6; };\n");
writefile(strings.concat(dependency, "/value.ww"), strings.concat(
"package value;\n",
"export fn number() i32 = { return 5; };\n"));
writefile(strings.concat(dirsource, "/main.ww"),
"package main;\nfn main() i32 = { return 3; };\n");
writefile(strings.concat(dirtarget, "/main.ww"),
"package main;\nfn main() i32 = { return 5; };\n");
writefile(strings.concat(dirsource, "/ignored_test.ww"),
"package main;\nimport missing.test.only;\n");
writefile(strings.concat(dirtarget, "/main.ww"), strings.concat(
"package main;\nimport domain.value;\n",
"fn main() i32 = { return value.number(); };\n"));
writefile(dirtargettest, dirtargettestexcluded);
writefile(strings.concat(baddirtarget, "/main.ww"),
"package main;\nimport ;\n");
assert(os.symlink(linktarget, link) == 0);
assert(os.symlink(dirtarget, dirlink) == 0);
assert(os.symlink(baddirtarget, baddirlink) == 0);
let compilerwrapper: str = strings.concat(root, "/testonly-w6c.sh");
let assemblerwrapper: str = strings.concat(root, "/testonly-w6a.sh");
let linkerwrapper: str = strings.concat(root, "/testonly-w6l.sh");
writeexecutable(compilerwrapper, strings.concat(
"#!/bin/sh\nprintf 'compile\\n' >> \"$WW_TESTONLY_CTRACE\"\n",
"if test \"$WW_TESTONLY_FAIL_C\" = 1; then\n",
" printf 'injected directory compiler failure\\n' >&2\n",
" exit 97\nfi\n",
"exec \"$WW_TESTONLY_REAL_C\" \"$@\"\n"));
writeexecutable(assemblerwrapper, strings.concat(
"#!/bin/sh\nprintf 'assemble\\n' >> \"$WW_TESTONLY_ATRACE\"\n",
"exec \"$WW_TESTONLY_REAL_A\" \"$@\"\n"));
writeexecutable(linkerwrapper, strings.concat(
"#!/bin/sh\nprintf 'link\\n' >> \"$WW_TESTONLY_LTRACE\"\n",
"if test \"$WW_TESTONLY_LINK_MODE\" = signal; then\n",
" kill -TERM \"$$\"\n",
"fi\n",
"exec \"$WW_TESTONLY_REAL_L\" \"$@\"\n"));
let stages: []str = ["ww", "ww_ww"];
@@ -1552,15 +1581,32 @@ fn cwdwritedata(dir: str, label: str) void = {
let ccompiled: str = "";
let ctestasm: str = "";
let crawbin: str = "";
let cdirectdirbin: str = "";
let cdirectdirartifacts: str = "";
let clinkdirbin: str = "";
let clinkdirartifacts: str = "";
let clinkdirasm: str = "";
let clinkdirplainasm: str = "";
let clinkdirchanged: str = "";
let clinkdirchangedartifacts: str = "";
let compilerfaildiag: str = "";
let linkersignaldiag: str = "";
let directorytestrunout: str = "";
let directorytestrunerr: str = "";
let directorytestrunartifacts: str = "";
let directorytestbin: str = "";
let directorytestcompileartifacts: str = "";
let directorytestasm: str = "";
let baseenv: []str = os.getenvs();
let si: i32 = 0;
for (si < stages.len) {
rewritefile(dirtargettest, dirtargettestexcluded);
let ctrace: str = strings.concat(root, "/testonly-c-", tags[si]);
let atrace: str = strings.concat(root, "/testonly-a-", tags[si]);
let ltrace: str = strings.concat(root, "/testonly-l-", tags[si]);
writefile(ctrace, ""); writefile(atrace, ""); writefile(ltrace, "");
append(tracecs, ctrace); append(traceas, atrace); append(tracels, ltrace);
let env: []str = alloc([], (baseenv.len + 10): u64)!;
let env: []str = alloc([], (baseenv.len + 12): u64)!;
let ei: i32 = 0;
for (ei < baseenv.len) {
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
@@ -1580,6 +1626,10 @@ fn cwdwritedata(dir: str, label: str) void = {
append(env, strings.concat("WW_TESTONLY_REAL_C=", driver(compilers[si])));
append(env, strings.concat("WW_TESTONLY_REAL_A=", driver(assemblers[si])));
append(env, strings.concat("WW_TESTONLY_REAL_L=", driver(linkers[si])));
let failcindex: i32 = env.len;
append(env, "WW_TESTONLY_FAIL_C=");
let linkmodeindex: i32 = env.len;
append(env, "WW_TESTONLY_LINK_MODE=");
append(environments, env);
let out: commandout;
@@ -1745,29 +1795,289 @@ fn cwdwritedata(dir: str, label: str) void = {
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0);
// Directory loading wins before raw-source suffix classification. Empty
// and malformed directories retain directory diagnostics, including the
// requested symlink spelling of a selected source.
let emptywork: str = strings.concat(root, "/empty-dir-work-", tags[si]);
let emptyout: str = strings.concat(root, "/empty-dir-output-", tags[si]);
mkdirall(emptywork);
let emptyav: []str = [driver(stages[si]), "build", "-w", emptywork,
"-I", include, "-o", emptyout, emptydir];
runcommandenv(root, strings.concat("testonly-empty-directory-", tags[si]),
emptyav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let emptywant: str = strings.concat("ww: ", emptydir,
": directory contains no WW package sources\n");
assert(out.stdout.len == 0 && same(out.stderr, emptywant)
&& !has(out.stderr, "cannot read source") && directoryisempty(emptywork)
&& !os.exists(emptyout) && readfile(ctrace).len == 0
&& readfile(atrace).len == 0 && readfile(ltrace).len == 0);
let baddirwork: str = strings.concat(root, "/bad-dir-work-", tags[si]);
let baddirout: str = strings.concat(root, "/bad-dir-output-", tags[si]);
mkdirall(baddirwork);
let baddirav: []str = [driver(stages[si]), "build", "-w", baddirwork,
"-I", include, "-o", baddirout, baddirlink];
runcommandenv(root, strings.concat("testonly-bad-directory-", tags[si]),
baddirav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let baddirwant: str = strings.concat(baddirlink,
"/main.ww:2:8: error: expected identifier, got ;\n");
assert(out.stdout.len == 0 && same(out.stderr, baddirwant)
&& !has(out.stderr, "cannot read source") && directoryisempty(baddirwork)
&& !os.exists(baddirout) && readfile(ctrace).len == 0
&& readfile(atrace).len == 0 && readfile(ltrace).len == 0);
// A real directory whose requested basename ends in .ww remains a
// directory request. The suffix does not make the directory a raw source.
let directdirwork: str = strings.concat(root, "/direct-dir-work-",
tags[si]);
let directdirout: str = strings.concat(root, "/direct-dir-output-",
tags[si]);
mkdirall(directdirwork);
let directdirav: []str = [driver(stages[si]), "build", "-w",
directdirwork, "-I", include, "-o", directdirout, dirsource];
runcommandenv(root, strings.concat("testonly-direct-directory-",
tags[si]), directdirav, env,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& os.exists(directdirout) && readfile(ctrace).len != 0
&& readfile(atrace).len != 0 && readfile(ltrace).len != 0);
let directdirbytes: str = strings.dup(readfile(directdirout));
let directdirartifacts: str = strings.dup(
artifacttreesnapshot(directdirwork));
if (si == 0) {
cdirectdirbin = strings.dup(directdirbytes);
cdirectdirartifacts = strings.dup(directdirartifacts);
} else {
assert(same(cdirectdirbin, directdirbytes)
&& same(cdirectdirartifacts, directdirartifacts));
};
let directrun: []str = [directdirout];
runcommand(root, strings.concat("testonly-direct-directory-run-",
tags[si]), directrun, time.second, &out);
expectexit(&out, 3);
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
// A directory target remains a directory request despite a test-looking
// requested spelling; this is deliberately the one producer-positive row.
// requested symlink spelling. Both stages reach the ordinary directory
// graph and produce byte-identical commands and semantic artifacts.
let linkdirwork: str = strings.concat(root, "/link-dir-work-", tags[si]);
let linkdirout: str = strings.concat(root, "/link-dir-output-", tags[si]);
mkdirall(linkdirwork);
let linkdirav: []str = [driver(stages[si]), "build", "-w", linkdirwork,
"-o", linkdirout, dirlink];
"-I", include, "-o", linkdirout, dirlink];
runcommandenv(root, strings.concat("testonly-directory-link-", tags[si]),
linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& os.exists(linkdirout) && readfile(ctrace).len != 0
&& readfile(atrace).len != 0 && readfile(ltrace).len != 0);
let linkdirbytes: str = strings.dup(readfile(linkdirout));
let linkdirartifacts: str = strings.dup(
artifacttreesnapshot(linkdirwork));
if (si == 0) {
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& os.exists(linkdirout) && readfile(ctrace).len != 0
&& readfile(atrace).len != 0 && readfile(ltrace).len != 0);
clinkdirbin = strings.dup(linkdirbytes);
clinkdirartifacts = strings.dup(linkdirartifacts);
} else {
// The selected non-directory classifier must not absorb this
// pre-existing WWstage resolution difference into the slice.
expectexit(&out, 1);
assert(out.stdout.len == 0
&& same(out.stderr, "ww: cannot read source\n")
&& !os.exists(linkdirout) && readfile(ctrace).len == 0
&& readfile(atrace).len == 0 && readfile(ltrace).len == 0);
assert(same(clinkdirbin, linkdirbytes)
&& same(clinkdirartifacts, linkdirartifacts));
};
let linkdirrun: []str = [linkdirout];
runcommand(root, strings.concat("testonly-directory-link-run-", tags[si]),
linkdirrun, time.second, &out);
expectexit(&out, 5);
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
runcommandenv(root, strings.concat("testonly-directory-link-warm-",
tags[si]), linkdirav, env,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& same(linkdirbytes, readfile(linkdirout))
&& same(clinkdirartifacts, artifacttreesnapshot(linkdirwork))
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len != 0);
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
// Output-directory presentation comes from the canonical selected target,
// while exact-null and assembly-only disposition do not change selection.
let linkoutdir: str = strings.concat(root, "/link-output-directory-",
tags[si]);
let linkoutwork: str = strings.concat(root, "/link-output-work-",
tags[si]);
mkdirall(linkoutdir); mkdirall(linkoutwork);
writefile(strings.concat(linkoutdir, "/sentinel"), "caller-owned\n");
let linkoutav: []str = [driver(stages[si]), "build", "-w", linkoutwork,
"-I", include, "-o", linkoutdir, dirlink];
runcommandenv(root, strings.concat("testonly-link-output-directory-",
tags[si]), linkoutav, env,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let linkoutchild: str = strings.concat(linkoutdir, "/directory-target");
assert(out.stdout.len == 0 && out.stderr.len == 0
&& same(readfile(strings.concat(linkoutdir, "/sentinel")),
"caller-owned\n")
&& same(linkdirbytes, readfile(linkoutchild))
&& readfile(ctrace).len != 0 && readfile(atrace).len != 0
&& readfile(ltrace).len != 0);
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
let linknullwork: str = strings.concat(root, "/link-null-work-", tags[si]);
mkdirall(linknullwork);
let linknullav: []str = [driver(stages[si]), "build", "-w", linknullwork,
"-I", include, "-o", "/dev/null", dirlink];
runcommandenv(root, strings.concat("testonly-link-null-", tags[si]),
linknullav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& readfile(ctrace).len != 0 && readfile(atrace).len != 0
&& readfile(ltrace).len != 0
&& artifacttreesnapshot(linknullwork).len != 0);
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
let asmdirwork: str = strings.concat(root, "/link-asm-work-", tags[si]);
let asmdirstem: str = strings.concat(root, "/link-asm-output-", tags[si]);
mkdirall(asmdirwork);
let asmdirav: []str = [driver(stages[si]), "build", "-S", "-w",
asmdirwork, "-I", include, "-o", asmdirstem, dirlink];
runcommandenv(root, strings.concat("testonly-link-assembly-", tags[si]),
asmdirav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& !os.exists(asmdirstem) && readfile(ctrace).len != 0
&& readfile(atrace).len == 0 && readfile(ltrace).len == 0);
let asmdirsnapshot: str = strings.dup(
artifacttreesnapshot(asmdirwork));
assert(asmdirsnapshot.len != 0);
if (si == 0) { clinkdirasm = strings.dup(asmdirsnapshot); }
else { assert(same(clinkdirasm, asmdirsnapshot)); };
// Without -o, the requested directory spelling owns presentation only.
// Plain -S keeps that selection but stops before public output, assembler,
// and link actions just like the explicit assembly-only form.
let defaultcaller: str = strings.concat(root, "/link-default-caller-",
tags[si]);
let defaultwork: str = strings.concat(root, "/link-default-work-",
tags[si]);
mkdirall(defaultcaller); mkdirall(defaultwork);
let defaultdirav: []str = [driver(stages[si]), "build", "-w",
defaultwork, "-I", include, dirlink];
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
runcommandenvdir(root, strings.concat("testonly-link-default-", tags[si]),
defaultdirav, env, defaultcaller,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let defaultdirout: str = strings.concat(defaultcaller,
"/directory_test.ww");
assert(out.stdout.len == 0 && out.stderr.len == 0
&& same(linkdirbytes, readfile(defaultdirout))
&& !os.exists(strings.concat(defaultcaller, "/directory-target"))
&& same(clinkdirartifacts, artifacttreesnapshot(defaultwork))
&& readfile(ctrace).len != 0 && readfile(atrace).len != 0
&& readfile(ltrace).len != 0);
let defaultrun: []str = [defaultdirout];
runcommand(root, strings.concat("testonly-link-default-run-", tags[si]),
defaultrun, time.second, &out);
expectexit(&out, 5);
let plainasmcaller: str = strings.concat(root, "/link-plain-asm-caller-",
tags[si]);
let plainasmwork: str = strings.concat(root, "/link-plain-asm-work-",
tags[si]);
mkdirall(plainasmcaller); mkdirall(plainasmwork);
let plainasmav: []str = [driver(stages[si]), "build", "-S", "-w",
plainasmwork, "-I", include, dirlink];
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
runcommandenvdir(root, strings.concat("testonly-link-plain-assembly-",
tags[si]), plainasmav, env, plainasmcaller,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& !os.exists(strings.concat(plainasmcaller, "/directory_test.ww"))
&& readfile(ctrace).len != 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0);
let plainasmsnapshot: str = strings.dup(
artifacttreesnapshot(plainasmwork));
assert(same(asmdirsnapshot, plainasmsnapshot));
if (si == 0) { clinkdirplainasm = strings.dup(plainasmsnapshot); }
else { assert(same(clinkdirplainasm, plainasmsnapshot)); };
// A failed invalidation leaves the prior command and committed work
// byte-for-byte intact. A later successful invalidation and restoration
// reuse the same directory identity rather than a raw-source identity.
let dirtargetmain: str = strings.concat(dirtarget, "/main.ww");
let dirtargetoriginal: str = strings.concat(
"package main;\nimport domain.value;\n",
"fn main() i32 = { return value.number(); };\n");
let dirtargetchanged: str = strings.concat(
"package main;\nimport domain.value;\n",
"fn main() i32 = { return value.number() + 2; };\n");
let linkdirworkbefore: str = strings.dup(treesnapshot(linkdirwork));
rewritefile(dirtargetmain, dirtargetchanged);
env[failcindex] = "WW_TESTONLY_FAIL_C=1";
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
runcommandenv(root, strings.concat("testonly-link-rollback-", tags[si]),
linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0
&& has(out.stderr, "injected directory compiler failure\n")
&& same(linkdirbytes, readfile(linkdirout))
&& same(linkdirworkbefore, treesnapshot(linkdirwork))
&& readfile(ctrace).len != 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0
&& !os.exists(strings.concat(linkdirout, ".new"))
&& !directoryhasnew(linkdirwork)
&& !directoryhasfragment(linkdirwork, ".wwtxn."));
if (si == 0) { compilerfaildiag = strings.dup(out.stderr); }
else { assert(same(compilerfaildiag, out.stderr)); };
env[failcindex] = "WW_TESTONLY_FAIL_C=";
env[linkmodeindex] = "WW_TESTONLY_LINK_MODE=signal";
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
runcommandenv(root, strings.concat("testonly-link-interrupted-", tags[si]),
linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0 && out.stderr.len != 0
&& has(out.stderr, "ww: w6l failed\n")
&& same(linkdirbytes, readfile(linkdirout))
&& same(linkdirworkbefore, treesnapshot(linkdirwork))
&& readfile(ctrace).len != 0 && readfile(atrace).len != 0
&& readfile(ltrace).len != 0
&& !os.exists(strings.concat(linkdirout, ".new"))
&& !directoryhasnew(linkdirwork)
&& !directoryhasfragment(linkdirwork, ".wwtxn."));
if (si == 0) { linkersignaldiag = strings.dup(out.stderr); }
else { assert(same(linkersignaldiag, out.stderr)); };
env[linkmodeindex] = "WW_TESTONLY_LINK_MODE=";
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
runcommandenv(root, strings.concat("testonly-link-invalidated-", tags[si]),
linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let changedbytes: str = strings.dup(readfile(linkdirout));
let changedartifacts: str = strings.dup(
artifacttreesnapshot(linkdirwork));
assert(out.stdout.len == 0 && out.stderr.len == 0
&& !same(linkdirbytes, changedbytes)
&& readfile(ctrace).len != 0 && readfile(atrace).len != 0
&& readfile(ltrace).len != 0);
if (si == 0) {
clinkdirchanged = strings.dup(changedbytes);
clinkdirchangedartifacts = strings.dup(changedartifacts);
} else {
assert(same(clinkdirchanged, changedbytes)
&& same(clinkdirchangedartifacts, changedartifacts));
};
runcommand(root, strings.concat("testonly-link-invalidated-run-", tags[si]),
linkdirrun, time.second, &out);
expectexit(&out, 7);
rewritefile(dirtargetmain, dirtargetoriginal);
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
runcommandenv(root, strings.concat("testonly-link-restored-", tags[si]),
linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& same(linkdirbytes, readfile(linkdirout))
&& same(clinkdirartifacts, artifacttreesnapshot(linkdirwork))
&& readfile(ctrace).len != 0 && readfile(atrace).len != 0
&& readfile(ltrace).len != 0
&& !directoryhasnew(linkdirwork)
&& !directoryhasfragment(linkdirwork, ".wwtxn."));
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
// A valid retained raw source seeds state. Renaming only its requested
@@ -1819,8 +2129,99 @@ fn cwdwritedata(dir: str, label: str) void = {
&& same(cwarmartifacts, artifacttreesnapshot(warmwork)));
};
// Test and run keep their existing raw-source semantics, while a visible
// non-test raw build and a multi-operand error do not enter this slice.
// Test, test -c, and test -S already route this symlink as a directory.
// Temporarily make its otherwise build-excluded test source valid and pin
// exact stage parity without changing the negative production-build rows.
rewritefile(dirtargettest, dirtargettestselected);
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
let directorytestwork: str = strings.concat(root, "/directory-test-work-",
tags[si]);
mkdirall(directorytestwork);
let directorytestav: []str = [driver(stages[si]), "test", "-w",
directorytestwork, "-I", include, dirlink];
runcommandenv(root, strings.concat("testonly-directory-test-", tags[si]),
directorytestav, env,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stderr.len == 0
&& has(out.stdout, "directory_selected ... ok\n")
&& readfile(ctrace).len != 0 && readfile(atrace).len != 0
&& readfile(ltrace).len != 0);
let directorytestrunstate: str = strings.dup(
artifacttreesnapshot(directorytestwork));
if (si == 0) {
directorytestrunout = strings.dup(out.stdout);
directorytestrunerr = strings.dup(out.stderr);
directorytestrunartifacts = strings.dup(directorytestrunstate);
} else {
assert(same(directorytestrunout, out.stdout)
&& same(directorytestrunerr, out.stderr)
&& same(directorytestrunartifacts, directorytestrunstate));
};
let directorycompiled: str = strings.concat(root,
"/directory-test-compiled-", tags[si]);
let directorycompilework: str = strings.concat(root,
"/directory-test-compile-work-", tags[si]);
mkdirall(directorycompilework);
let directorycompileav: []str = [driver(stages[si]), "test", "-c", "-w",
directorycompilework, "-I", include, "-o", directorycompiled, dirlink];
runcommandenv(root, strings.concat("testonly-directory-test-compile-",
tags[si]), directorycompileav, env,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& os.exists(directorycompiled));
let directorycompiledbytes: str = strings.dup(readfile(directorycompiled));
let directorycompiledstate: str = strings.dup(
artifacttreesnapshot(directorycompilework));
if (si == 0) {
directorytestbin = strings.dup(directorycompiledbytes);
directorytestcompileartifacts = strings.dup(directorycompiledstate);
} else {
assert(same(directorytestbin, directorycompiledbytes)
&& same(directorytestcompileartifacts, directorycompiledstate));
};
let directorytestasmwork: str = strings.concat(root,
"/directory-test-assembly-work-", tags[si]);
let directorytestasmout: str = strings.concat(root,
"/directory-test-assembly-output-", tags[si]);
mkdirall(directorytestasmwork);
let directorytestasmav: []str = [driver(stages[si]), "test", "-S", "-w",
directorytestasmwork, "-I", include, "-o", directorytestasmout, dirlink];
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
runcommandenv(root, strings.concat("testonly-directory-test-assembly-",
tags[si]), directorytestasmav, env,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 2);
assert(out.stdout.len == 0
&& same(out.stderr, "ww test: -S needs a single test file\n")
&& !os.exists(directorytestasmout)
&& directoryisempty(directorytestasmwork)
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0);
if (si == 0) { directorytestasm = strings.dup(out.stderr); }
else { assert(same(directorytestasm, out.stderr)); };
// Run was not changed by the build-only dispatcher fix. Cstage follows
// the selected directory and executes it; WWstage retains its exact
// pre-existing raw-source failure for the same requested symlink.
let directoryrunav: []str = [driver(stages[si]), "run", "-I", include,
dirlink];
runcommandenv(root, strings.concat("testonly-directory-run-", tags[si]),
directoryrunav, env,
(60i64 * (time.second: i64)): time.duration, &out);
if (si == 0) {
expectexit(&out, 5);
assert(out.stdout.len == 0 && out.stderr.len == 0);
} else {
expectexit(&out, 1);
assert(out.stdout.len == 0
&& same(out.stderr, "ww: cannot read source\n"));
};
rewritefile(dirtargettest, dirtargettestexcluded);
// Raw-source test and run retain their separate existing semantics, while
// a visible non-test raw build and multi-operand error stay out of this slice.
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
let testwork: str = strings.concat(root, "/route-work-", tags[si]);
mkdirall(testwork);
@@ -1891,9 +2292,76 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(same(commentbad[0], commentbad[1]));
assert(same(multibad[0], multibad[1]));
// Simultaneous selected-directory requests use isolated work and output
// transactions while retaining exact cross-stage product and artifact bytes.
let ti: i32 = 0;
for (ti < stages.len) {
rewritefile(tracecs[ti], "");
rewritefile(traceas[ti], "");
rewritefile(tracels[ti], "");
ti += 1;
};
let pdcwork: str = strings.concat(root, "/parallel-directory-c-work");
let pdwwork: str = strings.concat(root, "/parallel-directory-ww-work");
let pdcout: str = strings.concat(root, "/parallel-directory-c-output");
let pdwout: str = strings.concat(root, "/parallel-directory-ww-output");
mkdirall(pdcwork); mkdirall(pdwwork);
let pdcav: []str = [driver("ww"), "build", "-w", pdcwork, "-I", include,
"-o", pdcout, dirlink];
let pdwav: []str = [driver("ww_ww"), "build", "-w", pdwwork, "-I",
include, "-o", pdwout, dirlink];
let pdc: exec.command;
pdc.path = pdcav[0]; pdc.argv = pdcav; pdc.env = environments[0];
pdc.dir = repo();
pdc.stdoutpath = strings.concat(root, "/parallel-directory-c.stdout");
pdc.stderrpath = strings.concat(root, "/parallel-directory-c.stderr");
pdc.deadline = time.add(time.now(time.clock.monotonic),
(60i64 * (time.second: i64)): time.duration);
pdc.grace = (100i64 * (time.millisecond: i64)): time.duration;
let pdw: exec.command;
pdw.path = pdwav[0]; pdw.argv = pdwav; pdw.env = environments[1];
pdw.dir = repo();
pdw.stdoutpath = strings.concat(root, "/parallel-directory-ww.stdout");
pdw.stderrpath = strings.concat(root, "/parallel-directory-ww.stderr");
pdw.deadline = time.add(time.now(time.clock.monotonic),
(60i64 * (time.second: i64)): time.duration);
pdw.grace = (100i64 * (time.millisecond: i64)): time.duration;
let pdcp: exec.process;
let pdwp: exec.process;
exec.start(&pdcp, &pdc); exec.start(&pdwp, &pdw);
let pdcdone: bool = false;
let pdwdone: bool = false;
for (!pdcdone || !pdwdone) {
if (!pdcdone) { pdcdone = exec.poll(&pdcp); };
if (!pdwdone) { pdwdone = exec.poll(&pdwp); };
if (!pdcdone || !pdwdone) {
time.sleep(time.millisecond, time.clock.monotonic);
};
};
assert(pdcp.result.errno == 0 && pdcp.result.cleanuperrno == 0
&& pdcp.result.termination == exec.termination.EXIT
&& pdcp.result.code == 0 && pdwp.result.errno == 0
&& pdwp.result.cleanuperrno == 0
&& pdwp.result.termination == exec.termination.EXIT
&& pdwp.result.code == 0);
assert(readfile(pdc.stdoutpath).len == 0
&& readfile(pdc.stderrpath).len == 0
&& readfile(pdw.stdoutpath).len == 0
&& readfile(pdw.stderrpath).len == 0
&& same(clinkdirbin, readfile(pdcout))
&& same(clinkdirbin, readfile(pdwout))
&& same(clinkdirartifacts, artifacttreesnapshot(pdcwork))
&& same(clinkdirartifacts, artifacttreesnapshot(pdwwork))
&& readfile(tracecs[0]).len != 0 && readfile(traceas[0]).len != 0
&& readfile(tracels[0]).len != 0 && readfile(tracecs[1]).len != 0
&& readfile(traceas[1]).len != 0 && readfile(tracels[1]).len != 0
&& !directoryhasnew(pdcwork) && !directoryhasnew(pdwwork)
&& !directoryhasfragment(pdcwork, ".wwtxn.")
&& !directoryhasfragment(pdwwork, ".wwtxn."));
// Two simultaneous omitted requests have no work/output collision and no
// producer invocation in either stage.
let ti: i32 = 0;
ti = 0;
for (ti < stages.len) {
rewritefile(tracecs[ti], "");
rewritefile(traceas[ti], "");