test: prove captured action stdin isolation

This commit is contained in:
2026-08-20 17:39:10 +09:00
parent a8b88afcba
commit b996099270
4 changed files with 321 additions and 54 deletions

View File

@@ -5928,6 +5928,117 @@ Checked command-global allocation-failure parity remains owned by
crosses the former fixed environment-size boundary and verifies that no
partial execution environment or staged `.new` state is published.
### 11.25 Implemented null standard input for captured actions
Every process launched through WW's captured asynchronous executor now receives
an explicit fd 0. An empty `exec.command.stdinpath`, which is the production
default, opens the null device read-only; a nonempty value opens that exact path.
Consequently every coordinator-executed directory test product observes
immediate EOF instead of inheriting and consuming the invoking terminal, pipe,
or file. Captured directory build plans and the compiler, assembler, and linker
processes that inherit their stdio receive the same noninteractive boundary.
#### Pinned Go evidence and pre-fix WW behavior
The authority is official Go 1.26.5 at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- `runTestActor.Act` constructs an `exec.Cmd`, assigns its package directory,
environment, stdout, stderr, cancellation, and wait delay, and invokes
`Run` without assigning `Stdin`
([`cmd/go/internal/test/test.go`, lines 16611697](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1661-L1697)).
- `Cmd.Stdin` specifies that a nil value reads from `os.DevNull`;
`childStdin` opens that device and retains the file for the child; and
`Start` installs it as the first child file before process creation
([`os/exec/exec.go`, lines 193206, 531538, and 710738](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/exec/exec.go#L193-L206)).
- The ordinary build-command path has the same default. `Shell.runOut` creates
an `exec.Cmd`, assigns output, directory, and environment, and runs it without
assigning `Stdin`
([`cmd/go/internal/work/shell.go`, lines 600663](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/shell.go#L600-L663)).
- Official `os/exec` tests define a `cat` helper that copies stdin to EOF and
require that helper to terminate successfully when run with no `Stdin`
assignment
([`os/exec/exec_test.go`, lines 201204 and 416459](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/exec/exec_test.go#L416-L459)).
The command testdata separately exercises deliberately supplied stdin-pipe
lifetime and closure for orphaned test descendants
([`cmd/go/testdata/script/test_timeout_stdin.txt`, lines 121 and 3988](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_timeout_stdin.txt#L1-L21));
that script is adjacent stream-lifetime evidence, while the default null-fd
conclusion comes directly from the implementation chain above.
Before this slice, `lib/os/exec.start` redirected only stdout and stderr. A
directory driver invoked with a nonempty stdin file passed the same open file
description through the top-level inherited-stdio handoff, the package
coordinator, its captured builder, and the generated product. Serial products
could consume caller data; parallel products raced on the shared file offset;
a test that waited for input could wait on an interactive caller. Direct
measurement with a one-byte pipe made the same directory `@test` fail under
both Cstage and WWstage because its first read returned that byte. The raw
single-file route also read the byte and failed, but that route intentionally
remains inherited-stdio compatibility behavior.
#### Descriptor ownership, action boundaries, and concurrency
`exec.start` validates `stdinpath`, selects `/dev/null` for the empty value, and
opens the input before creating either output capture. `safefd` moves all three
standard streams above fd 2 when a caller had closed a standard descriptor.
After fork, the child maps the owned input to fd 0 before mapping the captures
to fd 1 and fd 2; setup failures travel through the existing close-on-exec
marker. The parent closes its input copy immediately after fork. Every
pre-fork error path closes every successfully acquired descriptor.
The package coordinator does not read or mutate its own fd 0. Each captured
build or run child opens an independent null descriptor, so `-j N` products
share neither readable caller data nor an input offset. Production, internal,
external, recompiled-for-test, support, and generated-main actions still form
the same graph and the one directory product still owns one process. Package
and test-only dependency initialization observes EOF inside that process.
Filters, list mode, no-match execution, failure, and timeout use the same
boundary.
Standard input is request-time process metadata only. It does not enter
canonical dotted identity, declared-name binding, actions, units, exports,
symbols, archives, generated main, executable bytes, product names, storage
keys, or diagnostics. The source path accepted by `stdinpath` is an executor
resource, not a package or filesystem-identity input.
#### Inherited-stdio routes, failure, persistence, and proof
`exec.runstdio` remains unchanged. The top-level driver therefore preserves
inherited stdin for raw single-file tests and runs, and a published test binary
invoked directly receives its invoker's fd 0. Directory `ww test -c`, including
`-c -o`, starts no product; the compiled binary acquires no embedded stdin
policy. No-selected-test packages likewise start no product. Directory build
and compile-only plans are captured actions and therefore noninteractive, but
their output, cwd, environment, graph, and publication rules are unchanged.
Failure to open an explicit input path or the default null device is a
pre-fork `termination.ERROR` with positive errno. Because input opens first,
neither output capture exists. A child-side `dup2` or close failure is reported
through the setup marker, distinguished from exit 127, and follows the existing
process-group cleanup path. Test failures, timeouts, post-build directory
removal, sibling isolation, transaction rollback, and scratch removal retain
their prior contracts.
No test-result cache exists. Caller stdin bytes never affect source actions or
persistent artifacts, and changing only the explicit proof input causes no
compile or assemble work beyond the established warm final relink. Build
workdir format remains `18`, test workdir format remains `19`, and semantic
storage remains `3` because no persisted byte schema changed.
The focused native owner remains
`directory_test_execution_working_directory` in
`test/package/package_test.ww`. It now drives every relevant command with a
known nonempty input file and requires EOF across all directory action/test
variants, production and test-only dependency initialization, serial and
parallel products, filters/list/no-match, recursive and equivalent roots,
failure, timeout, persistent cold/warm/data-only runs, and post-build child
setup failure. Tool wrappers require EOF without changing cwd, argv, locale, or
`TMPDIR`. Direct published and raw single-file binaries must instead read the
supplied data. The observer also proves input-open failure creates no captures,
source-class rejection creates no persistent state, Cstage/WWstage diagnostics
and output match, compile-only binaries are equal, persisted artifact bytes do
not change, and no `.new` residue survives.
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.

View File

@@ -657,14 +657,23 @@ remaining separate actions. Reachable dependency initialization consequently
observes the tested product's directory; a dependency tested as its own product
observes its own directory.
The same coordinator-executed product reads standard input from the null
device. Its first read observes EOF regardless of the terminal, pipe, or file
connected to the invoking command. Each parallel product owns a separate null
descriptor, and production or test-only dependency initialization, filtering,
listing, no-match execution, failure, and timeout all retain that boundary.
Standard input is runtime process metadata and contributes no package, action,
artifact, or persistence identity.
The coordinator does not change its own cwd or environment. Parallel products
receive independent child environments and each uses its own source directory.
Relative ordinary files, `testdata`, and writes resolve there for every
executing filter or list path. `ww build`, directory `ww test -c` (including
`-c -o`), and a no-selected-test directory execute no test child and receive no
execution-directory effect. A published test binary invoked directly, and the
raw single-file compatibility route, inherit the user's invocation cwd and
environment; no package directory is embedded or forced by the binary.
raw single-file compatibility route, inherit the user's invocation cwd,
environment, and standard input; no package directory or input policy is
embedded or forced by the binary.
---

View File

@@ -242,14 +242,22 @@ creates a separate process using the dependency directory. Equivalent direct,
recursive, redundant, absolute, and root-symlink spellings converge before
this runtime field is assigned.
Every such product also receives an independently opened null device as fd 0.
Caller terminal, pipe, and file bytes remain with the coordinator; serial and
parallel products observe immediate EOF rather than consuming a shared input
offset. Production and test-only dependency initialization, filters, list mode,
no-match execution, failure, and timeout use that same process boundary.
The physical directory remains distinct from exact dotted package identity and
from production, internal, external, recompiled, support, and generated-main
action identity. Only product execution uses it. Compiler, assembler, archiver,
linker, support, and generated-main commands retain their build-plan cwd and
environment. `ww build`, `ww test -c`, `-c -o`, and no-selected-test products
start no test child. A published test binary run directly and the raw
single-file compatibility path inherit the user's cwd/environment and contain
no forced package-directory behavior.
environment. Captured directory build plans begin with null stdin, which their
inherited-stdio tool descendants retain. `ww build`, `ww test -c`, `-c -o`, and
no-selected-test products start no test child. A published test binary run
directly and the raw single-file compatibility path inherit the user's
cwd/environment/stdin and contain no forced package-directory or input
behavior.
Before package grouping, both directory drivers and the shared recursive
coordinator apply Go 1.26.5's filename OS/architecture rule for WW's fixed
@@ -319,15 +327,18 @@ independent temporary directories from an unrelated caller cwd and compares
Cstage/WWstage output for production/internal/external/combined and every
test-only shape; production and test-only dependency initialization;
recompiled external self-import; duplicate inherited `PWD`; ordinary data,
`testdata`, and relative writes; direct/recursive/redundant/absolute/symlink
roots; reversed request and creation order; serial and parallel products;
filters, list, and no-match execution; failure and timeout; build/no-test and
compile-only paths; direct published binaries and raw single-file execution;
build-tool cwd/argv/environment; persistent data-only reuse and artifact bytes;
and deterministic post-build child-`chdir` failure isolated from a successful
sibling. It pads the inherited environment beyond former fixed observer sizes,
requires one appended product `PWD`, and sweeps the persistent workdir for
staged residue. Command-global bounded-memory failure remains independently
`testdata`, relative writes, and deliberately nonempty caller stdin;
direct/recursive/redundant/absolute/symlink roots; reversed request and creation
order; serial and parallel products; filters, list, and no-match execution;
failure and timeout; build/no-test and compile-only paths; direct published
binaries and raw single-file execution; build-tool cwd/argv/environment/stdin;
persistent data-only reuse and artifact bytes; input-open failure before capture
creation; source-class rejection with empty workdirs; and deterministic
post-build child-`chdir` failure isolated from a successful sibling. It pads the
inherited environment beyond former fixed observer sizes, requires one appended
product `PWD`, requires EOF for captured actions and caller data for inherited-
stdio routes, and sweeps the persistent workdir for staged residue.
Command-global bounded-memory failure remains independently
owned by `allocation_failure_is_command_global`.
The same package owner contains the focused
@@ -499,12 +510,14 @@ target once within one invocation, but two independent `make bootstrap`
invocations are not safe to run concurrently and remain mutually exclusive.
`lib/os/exec` is the sole reusable WW subprocess mechanism. Fixture and package
coordinators use its captured asynchronous path. The WW driver directly uses
`os.exec.runstdio` for inherited-stdio, inherited-environment, leader-only
compiler, assembler, linker, cleanup, run, and single-file-test calls. The
local WW `procrun` implementation is deleted. The C bootstrap retains its C
process implementation because it cannot consume a WW standard-library
module.
coordinators use its captured asynchronous path. An empty captured-command
`stdinpath` opens the null device; an explicit path supplies controlled input,
and either descriptor is installed before exec with checked setup reporting.
The WW driver directly uses `os.exec.runstdio` for inherited-stdio,
inherited-environment, leader-only compiler, assembler, linker, cleanup, run,
and single-file-test calls. The local WW `procrun` implementation is deleted.
The C bootstrap retains its C process implementation because it cannot consume
a WW standard-library module.
WW has tokens and an opaque type for future CSP/channel work, but no mature
production channel operations, task runtime, or scheduler. No channel,

View File

@@ -174,6 +174,29 @@ fn runcommandenvdir(root: str, name: str, argv: []str, env: []str,
out.stderr = readfile(c.stderrpath);
};
// A direct descriptor mapping keeps the caller's duplicate environment and cwd
// intact while giving the nested driver known nonempty input.
fn runcommandinputenvdir(root: str, name: str, argv: []str, env: []str,
dir: str, input: str, lifetime: time.duration, out: *commandout) void = {
let c: exec.command;
c.path = argv[0];
c.argv = argv;
c.env = env;
c.dir = dir;
c.stdinpath = input;
c.stdoutpath = strings.concat(root, "/", name, ".stdout");
c.stderrpath = strings.concat(root, "/", name, ".stderr");
c.deadline = time.add(time.now(time.clock.monotonic), lifetime);
c.grace = (100i64 * (time.millisecond: i64)): time.duration;
let r: exec.result;
exec.run(&c, &r);
assert(r.errno == 0 && r.cleanuperrno == 0);
out.termination = r.termination;
out.code = r.code;
out.stdout = readfile(c.stdoutpath);
out.stderr = readfile(c.stderrpath);
};
fn clean(root: str) void = {
let av: []str = ["/bin/rm", "-rf", "--", root];
let c: exec.command;
@@ -592,19 +615,22 @@ fn cwdtestenv(stage: str) []str = {
};
fn cwdassertrecord(text: str, label: str, cwd: str, pwd: str,
pwdcount: str, pwdlast: str, data: str, testdata: str) void = {
pwdcount: str, pwdlast: str, data: str, testdata: str,
input: str) void = {
assert(has(text, strings.concat(label, " cwd=", cwd, "\n")));
assert(has(text, strings.concat(label, " PWD=", pwd, "\n")));
assert(has(text, strings.concat(label, " PWD-count=", pwdcount, "\n")));
assert(has(text, strings.concat(label, " PWD-last=", pwdlast, "\n")));
assert(has(text, strings.concat(label, " data=", data, "\n")));
assert(has(text, strings.concat(label, " testdata=", testdata, "\n")));
assert(has(text, strings.concat(label, " stdin=", input, "\n")));
assert(has(text, strings.concat(label, " create=ok\n")));
};
fn cwdassertpackage(text: str, label: str, dir: str, data: str,
testdata: str) void = {
cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata);
cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata,
"eof");
};
fn cwdwritedata(dir: str, label: str) void = {
@@ -689,6 +715,35 @@ fn cwdwritedata(dir: str, label: str) void = {
mkdirall(failtree);
cwdwritedata(caller, "caller");
writefile(strings.concat(caller, "/sentinel"), "parent-sentinel\n");
let stdinpath: str = strings.concat(root, "/stdin.txt");
let stdinbytes: []u8 = alloc([], 4096u64)!;
let inputi: i32 = 0;
for (inputi < 2048) {
append(stdinbytes, 'X');
append(stdinbytes, '\n');
inputi += 1;
};
writefile(stdinpath, strings.frombytes(stdinbytes));
let missingstdinout: str = strings.concat(root, "/missing-stdin.stdout");
let missingstdinerr: str = strings.concat(root, "/missing-stdin.stderr");
let missingstdinav: []str = ["/bin/true"];
let missingstdincmd: exec.command;
missingstdincmd.path = missingstdinav[0];
missingstdincmd.argv = missingstdinav;
missingstdincmd.env = os.getenvs();
missingstdincmd.dir = caller;
missingstdincmd.stdinpath = strings.concat(root, "/missing-stdin");
missingstdincmd.stdoutpath = missingstdinout;
missingstdincmd.stderrpath = missingstdinerr;
missingstdincmd.deadline.sec = 0i64;
missingstdincmd.deadline.nsec = 0i64;
missingstdincmd.grace = 0i64: time.duration;
let missingstdinresult: exec.result;
exec.run(&missingstdincmd, &missingstdinresult);
assert(missingstdinresult.termination == exec.termination.ERROR);
assert(missingstdinresult.errno == 2
&& missingstdinresult.cleanuperrno == 0);
assert(!os.exists(missingstdinout) && !os.exists(missingstdinerr));
// Create in reverse lexical order; discovery and product emission may not
// inherit filesystem or request order.
@@ -763,6 +818,7 @@ fn cwdwritedata(dir: str, label: str) void = {
" if (last == env.len - 1) { put(\"yes\\n\"); } else { put(\"no\\n\"); };\n",
" emitfile(label, \"data\", \"data.txt\");\n",
" emitfile(label, \"testdata\", \"testdata/input.txt\");\n",
" let input: [1]u8;\n let inputn: i64 = os.read(os.STDIN_FILENO, &input[0], 1u64);\n put(label); put(\" stdin=\");\n if (inputn == 0i64) { put(\"eof\\n\"); } else {\n if (inputn == 1i64) { put(\"data\\n\"); } else { put(\"<error>\\n\"); };\n };\n",
" let fd: i32 = os.open(\"created.txt\", os.flag.WRONLY |\n",
" os.flag.CREATE | os.flag.TRUNC, 384i32);\n",
" put(label); put(\" create=\");\n",
@@ -824,7 +880,7 @@ fn cwdwritedata(dir: str, label: str) void = {
"fn init() void = { probe.run(\"cmdno-init\"); };\n",
"fn main() void = { probe.run(\"cmdno-main\"); };\n"));
let failnames: []str = ["p8", "p7"];
let failnames: []str = ["reject", "p8", "p7"];
i = 0;
for (i < failnames.len) {
let dir: str = strings.concat(failtree, "/", failnames[i]);
@@ -834,6 +890,7 @@ fn cwdwritedata(dir: str, label: str) void = {
};
let p7: str = strings.concat(failtree, "/p7");
let p8: str = strings.concat(failtree, "/p8");
let reject: str = strings.concat(failtree, "/reject");
writefile(strings.concat(p7, "/p7_test.ww"), strings.concat(
"package p7;\nimport probe;\n@test fn fails() void = {\n",
" probe.run(\"p7-fail\"); assert(false);\n};\n"));
@@ -842,6 +899,10 @@ fn cwdwritedata(dir: str, label: str) void = {
" probe.run(\"p8-timeout\");\n",
" time.sleep((2i64 * (time.second: i64)): time.duration, ",
"time.clock.monotonic);\n};\n"));
writefile(strings.concat(reject, "/reject.ww"),
"package reject;\nexport fn value() i32 = { return 1; };\n");
writefile(strings.concat(reject, "/bad_test.ww"),
"package unrelated;\n@test fn unreachable() void = { assert(false); };\n");
let out: commandout;
let lnav: []str = ["/bin/ln", "-s", tree, treelink];
@@ -868,9 +929,11 @@ fn cwdwritedata(dir: str, label: str) void = {
p6, p5, p4, p3, p2, p1, testdep, dep];
let outc: commandout;
let outw: commandout;
runcommandenvdir(root, "cwd-multi-c", forward, cenv, caller,
runcommandinputenvdir(root, "cwd-multi-c", forward, cenv, caller,
stdinpath,
(120i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-multi-ww", reverse, wenv, caller,
runcommandinputenvdir(root, "cwd-multi-ww", reverse, wenv, caller,
stdinpath,
(120i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
@@ -901,7 +964,8 @@ fn cwdwritedata(dir: str, label: str) void = {
// selected test body runs.
let filterav: []str = [driver("ww"), "test", "-run", "cwd_external",
"-I", tree, p3];
runcommandenvdir(root, "cwd-filter", filterav, cenv, caller,
runcommandinputenvdir(root, "cwd-filter", filterav, cenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
@@ -912,7 +976,8 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(!has(out.stdout, "p3-internal cwd="));
let listav: []str = [driver("ww_ww"), "test", "-list", "-I", tree, p3];
runcommandenvdir(root, "cwd-list", listav, wenv, caller,
runcommandinputenvdir(root, "cwd-list", listav, wenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
@@ -925,7 +990,8 @@ fn cwdwritedata(dir: str, label: str) void = {
let nomatchav: []str = [driver("ww"), "test", "-run", "no-such-test",
"-I", tree, p3];
runcommandenvdir(root, "cwd-no-match", nomatchav, cenv, caller,
runcommandinputenvdir(root, "cwd-no-match", nomatchav, cenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
@@ -939,9 +1005,11 @@ fn cwdwritedata(dir: str, label: str) void = {
p7];
let failw: []str = [driver("ww_ww"), "test", "-I", tree, "-I",
failtree, p7];
runcommandenvdir(root, "cwd-fail-c", failc, cenv, caller,
runcommandinputenvdir(root, "cwd-fail-c", failc, cenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-fail-ww", failw, wenv, caller,
runcommandinputenvdir(root, "cwd-fail-ww", failw, wenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 1);
expectexit(&outw, 1);
@@ -955,9 +1023,11 @@ fn cwdwritedata(dir: str, label: str) void = {
tree, "-I", failtree, p8];
let timeoutw: []str = [driver("ww_ww"), "test", "-timeout-ms=50", "-I",
tree, "-I", failtree, p8];
runcommandenvdir(root, "cwd-timeout-c", timeoutc, cenv, caller,
runcommandinputenvdir(root, "cwd-timeout-c", timeoutc, cenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-timeout-ww", timeoutw, wenv, caller,
runcommandinputenvdir(root, "cwd-timeout-ww", timeoutw, wenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 1);
expectexit(&outw, 1);
@@ -969,11 +1039,35 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(same(outc.stderr, strings.concat("FAIL ", p8,
" [p8] (test exit 1)\n")));
// Source-class rejection precedes every captured build or test child, so a
// nonempty caller input cannot create a partial persistent generation.
let rejectcwork: str = strings.concat(root, "/stdin-reject-c");
let rejectwwork: str = strings.concat(root, "/stdin-reject-ww");
assert(os.mkdir(rejectcwork, 448i32) == 0);
assert(os.mkdir(rejectwwork, 448i32) == 0);
let rejectcav: []str = [driver("ww"), "test", "-w", rejectcwork,
"-I", failtree, reject];
let rejectwav: []str = [driver("ww_ww"), "test", "-w", rejectwwork,
"-I", failtree, reject];
runcommandinputenvdir(root, "stdin-reject-c", rejectcav, cenv, caller,
stdinpath, (60i64 * (time.second: i64)): time.duration, &outc);
runcommandinputenvdir(root, "stdin-reject-ww", rejectwav, wenv, caller,
stdinpath, (60i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 1);
expectexit(&outw, 1);
assert(outc.stdout.len == 0 && outw.stdout.len == 0);
assert(same(outc.stderr, outw.stderr));
assert(has(outc.stderr,
"test package must match production package or <package>_test\n"));
assert(directoryisempty(rejectcwork));
assert(directoryisempty(rejectwwork));
// Packages without selected test sources and `ww build` never start a test
// process, even if production initialization would make that visible.
let notestav: []str = [driver("ww_ww"), "test", "-I", tree,
plain, cmdno];
runcommandenvdir(root, "cwd-no-tests", notestav, wenv, caller,
runcommandinputenvdir(root, "cwd-no-tests", notestav, wenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(!has(out.stdout, "plain-init"));
@@ -984,7 +1078,8 @@ fn cwdwritedata(dir: str, label: str) void = {
let commandbin: str = strings.concat(root, "/cmdno.bin");
let buildav: []str = [driver("ww"), "build", "-I", tree, "-o",
commandbin, cmdno];
runcommandenvdir(root, "cwd-build", buildav, cenv, caller,
runcommandinputenvdir(root, "cwd-build", buildav, cenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(os.exists(commandbin));
@@ -995,7 +1090,8 @@ fn cwdwritedata(dir: str, label: str) void = {
// wrapper: direct invocation keeps the caller's cwd and duplicate PWDs.
assert(os.remove(strings.concat(p2, "/created.txt")) == 0);
let defaultcompile: []str = [driver("ww"), "test", "-c", "-I", tree, p2];
runcommandenvdir(root, "cwd-compile-default", defaultcompile, cenv, caller,
runcommandinputenvdir(root, "cwd-compile-default", defaultcompile, cenv,
caller, stdinpath,
(90i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let defaultbin: str = strings.concat(p2, "/p2.test");
@@ -1012,9 +1108,11 @@ fn cwdwritedata(dir: str, label: str) void = {
"-I", tree, p1];
let compilew: []str = [driver("ww_ww"), "test", "-c", "-o", p1wbin,
"-I", tree, p1];
runcommandenvdir(root, "cwd-compile-c", compilec, cenv, caller,
runcommandinputenvdir(root, "cwd-compile-c", compilec, cenv, caller,
stdinpath,
(90i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-compile-ww", compilew, wenv, caller,
runcommandinputenvdir(root, "cwd-compile-ww", compilew, wenv, caller,
stdinpath,
(90i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
@@ -1023,15 +1121,16 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(!has(outc.stdout, "dep-init") && !has(outw.stdout, "dep-init"));
let directbin: []str = [p1cbin, "-package=p1"];
runcommandenvdir(root, "cwd-published-direct", directbin, cenv, caller,
runcommandinputenvdir(root, "cwd-published-direct", directbin, cenv,
caller, stdinpath,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
cwdassertrecord(out.stdout, "dep-init", caller,
"/ww-cwd-inherited-first", "2", "no", "caller-data",
"caller-testdata");
"caller-testdata", "data");
cwdassertrecord(out.stdout, "p1-internal", caller,
"/ww-cwd-inherited-first", "2", "no", "caller-data",
"caller-testdata");
"caller-testdata", "data");
assert(os.exists(strings.concat(caller, "/created.txt")));
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
@@ -1041,10 +1140,12 @@ fn cwdwritedata(dir: str, label: str) void = {
strings.concat(p4, "/internal_test.ww")];
let raww: []str = [driver("ww_ww"), "test", "-I", tree,
strings.concat(p4, "/internal_test.ww")];
runcommandenvdir(root, "cwd-raw-c", rawc, cbase, caller,
runcommandinputenvdir(root, "cwd-raw-c", rawc, cbase, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &outc);
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
runcommandenvdir(root, "cwd-raw-ww", raww, wenv, caller,
runcommandinputenvdir(root, "cwd-raw-ww", raww, wenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
@@ -1052,7 +1153,7 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(same(outc.stderr, outw.stderr));
cwdassertrecord(outc.stdout, "p4-internal-only", caller,
"/ww-cwd-inherited-first", "2", "yes", "caller-data",
"caller-testdata");
"caller-testdata", "data");
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
cwdassertpackage(multiout, "dep-init", p2, "p2-data", "p2-testdata");
cwdassertpackage(multiout, "p2-external", p2,
@@ -1076,9 +1177,11 @@ fn cwdwritedata(dir: str, label: str) void = {
strings.concat(tree, "/...")];
let linkrec: []str = [driver("ww_ww"), "test", "-j", "4", "-I",
"../tree-link", "../tree-link/..."];
runcommandenvdir(root, "cwd-recursive-real", realrec, cenv, caller,
runcommandinputenvdir(root, "cwd-recursive-real", realrec, cenv, caller,
stdinpath,
(120i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-recursive-link", linkrec, wenv, caller,
runcommandinputenvdir(root, "cwd-recursive-link", linkrec, wenv, caller,
stdinpath,
(120i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
@@ -1096,9 +1199,11 @@ fn cwdwritedata(dir: str, label: str) void = {
let equivalent: []str = [driver("ww_ww"), "test", "-j", "3", "-I",
"../tree-link", "../tree/p1", p1, "../tree/./p1/../p1",
"../tree-link/p1"];
runcommandenvdir(root, "cwd-direct", direct, cenv, caller,
runcommandinputenvdir(root, "cwd-direct", direct, cenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-equivalent", equivalent, wenv, caller,
runcommandinputenvdir(root, "cwd-equivalent", equivalent, wenv, caller,
stdinpath,
(60i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
@@ -1124,6 +1229,9 @@ fn cwdwritedata(dir: str, label: str) void = {
"/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n",
"printf 'compiler env=LC_ALL:%s TMPDIR:%s\\n' \"$LC_ALL\" ",
"\"$TMPDIR\" >> \"$WW_CWD_TOOL_TRACE\"\n",
"if IFS= read -r WW_CWD_STDIN; then\n",
" printf 'compiler stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
"else printf 'compiler stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n",
"printf 'compiler argv' >> \"$WW_CWD_TOOL_TRACE\"\n",
"for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ",
"\"$WW_CWD_TOOL_TRACE\"; done\n",
@@ -1135,6 +1243,9 @@ fn cwdwritedata(dir: str, label: str) void = {
"/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n",
"printf 'assembler env=LC_ALL:%s TMPDIR:%s\\n' \"$LC_ALL\" ",
"\"$TMPDIR\" >> \"$WW_CWD_TOOL_TRACE\"\n",
"if IFS= read -r WW_CWD_STDIN; then\n",
" printf 'assembler stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
"else printf 'assembler stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n",
"printf 'assembler argv' >> \"$WW_CWD_TOOL_TRACE\"\n",
"for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ",
"\"$WW_CWD_TOOL_TRACE\"; done\n",
@@ -1146,6 +1257,9 @@ fn cwdwritedata(dir: str, label: str) void = {
"/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n",
"printf 'linker env=LC_ALL:%s TMPDIR:%s\\n' \"$LC_ALL\" ",
"\"$TMPDIR\" >> \"$WW_CWD_TOOL_TRACE\"\n",
"if IFS= read -r WW_CWD_STDIN; then\n",
" printf 'linker stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
"else printf 'linker stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n",
"printf 'linker argv' >> \"$WW_CWD_TOOL_TRACE\"\n",
"for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ",
"\"$WW_CWD_TOOL_TRACE\"; done\n",
@@ -1174,7 +1288,8 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(os.mkdir(work, 448i32) == 0);
let persistav: []str = [driver("ww"), "test", "-w", work, "-I", tree,
p1];
runcommandenvdir(root, "cwd-persist-cold", persistav, traceenv, caller,
runcommandinputenvdir(root, "cwd-persist-cold", persistav, traceenv,
caller, stdinpath,
(120i64 * (time.second: i64)): time.duration, &outc);
expectexit(&outc, 0);
cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata");
@@ -1188,6 +1303,10 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(has(coldtrace, "compiler env=LC_ALL:C TMPDIR:/tmp/"));
assert(has(coldtrace, "assembler env=LC_ALL:C TMPDIR:/tmp/"));
assert(has(coldtrace, "linker env=LC_ALL:C TMPDIR:/tmp/"));
assert(has(coldtrace, "compiler stdin=eof\n"));
assert(has(coldtrace, "assembler stdin=eof\n"));
assert(has(coldtrace, "linker stdin=eof\n"));
assert(!has(coldtrace, "stdin=data\n"));
assert(has(coldtrace, "compiler argv <"));
assert(has(coldtrace, ".unit.new>"));
assert(has(coldtrace, "assembler argv <"));
@@ -1221,7 +1340,8 @@ fn cwdwritedata(dir: str, label: str) void = {
};
rewritefile(tooltrace, "");
runcommandenvdir(root, "cwd-persist-warm", persistav, traceenv, caller,
runcommandinputenvdir(root, "cwd-persist-warm", persistav, traceenv,
caller, stdinpath,
(120i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outw, 0);
assert(same(outc.stdout, outw.stdout));
@@ -1230,6 +1350,8 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(!has(warmtrace, "assembler cwd="));
assert(occurrences(warmtrace, "linker cwd=") == 1);
assert(has(warmtrace, strings.concat("linker cwd=", caller, "\n")));
assert(has(warmtrace, "linker stdin=eof\n"));
assert(!has(warmtrace, "stdin=data\n"));
// Non-source data does not enter graph/action/storage bytes. The established
// warm final relink remains one invocation, while the next uncached runtime
@@ -1237,8 +1359,17 @@ fn cwdwritedata(dir: str, label: str) void = {
rewritefile(strings.concat(p1, "/data.txt"), "p1-data-v2\n");
rewritefile(strings.concat(p1, "/testdata/input.txt"),
"p1-testdata-v2\n");
let changedstdin: []u8 = alloc([], 4096u64)!;
inputi = 0;
for (inputi < 2048) {
append(changedstdin, 'Y');
append(changedstdin, '\n');
inputi += 1;
};
rewritefile(stdinpath, strings.frombytes(changedstdin));
rewritefile(tooltrace, "");
runcommandenvdir(root, "cwd-persist-data", persistav, traceenv, caller,
runcommandinputenvdir(root, "cwd-persist-data", persistav, traceenv,
caller, stdinpath,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
cwdassertpackage(out.stdout, "dep-init", p1,
@@ -1250,6 +1381,8 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(!has(datatrace, "assembler cwd="));
assert(occurrences(datatrace, "linker cwd=") == 1);
assert(has(datatrace, strings.concat("linker cwd=", caller, "\n")));
assert(has(datatrace, "linker stdin=eof\n"));
assert(!has(datatrace, "stdin=data\n"));
i = 0;
for (i < artifactpaths.len) {
assert(same(artifacts[i], readfile(artifactpaths[i])));
@@ -1263,7 +1396,8 @@ fn cwdwritedata(dir: str, label: str) void = {
let p1after: str = strings.concat(root, "/p1-after-data.test");
let afterav: []str = [driver("ww_ww"), "test", "-c", "-o", p1after,
"-I", tree, p1];
runcommandenvdir(root, "cwd-after-data-binary", afterav, wenv, caller,
runcommandinputenvdir(root, "cwd-after-data-binary", afterav, wenv,
caller, stdinpath,
(90i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(same(readfile(p1cbin), readfile(p1after)));
@@ -1295,8 +1429,8 @@ fn cwdwritedata(dir: str, label: str) void = {
append(hideenv, strings.concat("WW_CWD_HIDE_DIR=", p1));
let hideav: []str = [driver("wwtest"), "package", "--ww-driver",
hidewrapper, "-j", "2", "-I", tree, p1, p2];
runcommandenvdir(root, strings.concat("cwd-hide-", hidestages[i]),
hideav, hideenv, caller,
runcommandinputenvdir(root, strings.concat("cwd-hide-", hidestages[i]),
hideav, hideenv, caller, stdinpath,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, strings.concat("wrapper-hidden=", p1, "\n")));