ww: put selected toolchain first in test PATH

This commit is contained in:
2026-08-21 05:30:10 +09:00
parent 1b71250ad5
commit 9b6b1100f4
7 changed files with 586 additions and 96 deletions

View File

@@ -5849,22 +5849,24 @@ reinterpret them. No runtime coordinator-global `chdir` was added; its cwd and
`PWD` remain unchanged.
Each started product also receives a newly allocated run environment. It
retains inherited entries in order except exact `TMPDIR=`, `LC_ALL=`, and
uppercase `PWD=` entries, then appends `LC_ALL=C`, the product-local absolute
`TMPDIR`, and `PWD=<pkggroup.dir>`. Removing prior `PWD` entries reproduces
Go's observable last-wins result because WW's executor intentionally preserves
retains inherited entries in order except exact `TMPDIR=`, `LC_ALL=`, uppercase
`PATH=`, and uppercase `PWD=` entries, then appends `LC_ALL=C`, the
product-local absolute `TMPDIR`, the selected toolchain `PATH`, and
`PWD=<pkggroup.dir>`. Removing prior `PWD` and `PATH` entries reproduces Go's
observable last-wins result because WW's executor intentionally preserves
duplicates and WW `os.getenv` returns the first one. Case-distinct and malformed
entries remain untouched. The product observes exactly one uppercase `PWD`, at
the appended position.
the appended position, and one effective uppercase `PATH`; section 11.32 owns
the latter rule.
The vector and both generated strings are product-local, dynamically sized,
The vector and its generated strings are product-local, dynamically sized,
and published only after every checked allocation succeeds. Partial failure
frees only initialized owned storage and never frees borrowed inherited
strings. `exec.start` synchronously deep-copies the command before returning,
after which the coordinator frees its run vector, generated `TMPDIR`, generated
`PWD`, generated `-package` argument, and argv vector. Concurrent children
therefore hold independent fork snapshots; no shared environment vector or
process-global state is mutated.
`PATH`, generated `PWD`, generated `-package` argument, and argv vector.
Concurrent children therefore hold independent fork snapshots; no shared
environment vector or process-global state is mutated.
All dependency initialization occurs inside that product process. A production
or test-only dependency reached by package `p` sees `p`'s directory. If the
@@ -5880,15 +5882,19 @@ emission remains byte-sorted and identical to `-j 1`.
`-c -o`, builds or publishes but never enters `pkgstartrun`; no execution cwd
or run environment is allocated. A published binary subsequently invoked by
the user bypasses the coordinator and inherits the user's cwd and environment.
The raw single-file test compatibility route is likewise unchanged. A
directory with no selected test source still creates no support, generated
main, link, run, result, or execution-context state.
The raw single-file test compatibility route retains its caller cwd, `PWD`,
stdin, and stream behavior, but section 11.32 applies the test-process `PATH`
rule at its driver-owned launch. A directory with no selected test source still
creates no support, generated main, link, run, result, or execution-context
state.
Build-plan commands retain `dir=""` and their existing tool environment.
Compiler, assembler, in-driver archiver, linker, support generation, generated
main construction, the Cstage driver, and the WWstage driver therefore retain
their exact prior cwd, argv, and environment. The runtime rule required no
`lib/os/exec`, compiler, checker, writer, assembler, linker, or driver change.
Compiler, assembler, in-driver archiver, linker, support generation, and
generated-main construction therefore retain their exact prior cwd, argv, and
environment. The directory cwd rule required no `lib/os/exec`, compiler,
checker, writer, assembler, linker, or driver change; the later test-only PATH
rule is isolated at launch and changes neither build-plan environment nor those
tools.
Independent Cstage/WWstage compile-only products remain byte-identical, and
changing only `data.txt` or `testdata` changes no unit, `.wwi`, assembly,
object, archive, generated-main, or binary bytes.
@@ -5916,13 +5922,15 @@ The focused native owner is
`directory_test_execution_working_directory` in
`test/package/package_test.ww`. It creates only disposable source trees and
compares Cstage and WWstage across duplicate and large environments; exact
`getcwd`, effective/count/position of `PWD`; ordinary data, `testdata`, and
`getcwd`, effective/count/position of `PWD`; absent, empty, nonempty, and
duplicate inherited `PATH`; ordinary data, `testdata`, and
relative writes; all production/internal/external/test-only shapes;
production and test-only dependency initialization; recompiled external
self-import; direct/recursive/redundant/absolute/symlink roots; reversed roots
and creation order; `-j 1`/parallel execution; filters/list/no-match; failure
and timeout; no-test and build paths; `-c`, `-c -o`, direct binaries, and raw
single files; exact tool cwd/argv/locale/TMPDIR; persistent data-only reuse and
and timeout; no-test and build paths; `-c`, `-c -o`, running retained tests,
direct binaries, and raw single files; exact tool cwd/argv/locale/TMPDIR/PATH;
persistent data-only reuse and
artifact/binary identity; and a deterministic post-build directory removal
where the affected product reports `ENOENT` while its sibling succeeds.
Checked command-global allocation-failure parity remains owned by
@@ -6904,6 +6912,141 @@ and retained-binary contracts. Existing request-transaction, timeout,
interruption, and concurrent-driver owners continue to cover those unchanged
dimensions.
### 11.32 Implemented selected toolchain first in test `PATH`
Every test binary actually started by `ww test` now receives one effective
uppercase `PATH` beginning with the canonical absolute directory of the
selected WW driver. An absent or empty inherited value produces only that
directory. A nonempty first effective inherited value follows it after `:`;
ordinary duplicate `PATH=` entries collapse to the one child value. This is
test-process metadata only: build tools and every request that starts no test
process retain their prior environment.
#### Pinned Go evidence and classification
The sole authority is official Go 1.26.5 at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- The `go test` command documentation states that `$GOROOT/bin` is placed at
the beginning of the test process's `PATH`, so an executed test resolves the
`go` command from the invoking toolchain
([`cmd/go/internal/test/test.go`, lines 8794](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L87-L94)).
- `(*runTestActor).Act` begins with `cfg.OrigEnv`, applies
`base.AppendPATH`, then `base.AppendPWD`, and assigns that environment to
each test command
([`cmd/go/internal/test/test.go`, lines 16611668](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1661-L1668)).
`AppendPATH` appends `PATH=$GOROOT/bin` for an empty effective inherited
value and `PATH=$GOROOT/bin:<old>` otherwise
([`cmd/go/internal/base/env.go`, lines 2945](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/base/env.go#L29-L45)).
- `Cmd.environ` removes duplicate environment keys while preferring the later
entry, making the appended value effective
([`os/exec/exec.go`, lines 12311265](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/exec/exec.go#L1231-L1265)).
On the supported Unix model, initial environment lookup uses the first
inherited occurrence
([`syscall/env_unix.go`, lines 2044 and 6684](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/syscall/env_unix.go#L20-L84)).
- Official `test_goroot_PATH.txt` tests both an empty `PATH` and a nonempty
directory containing no executable; the test must find the `go` executable
in the current toolchain's `$GOROOT/bin`
([lines 141](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_goroot_PATH.txt#L1-L41)).
Those source rules and official assertions are behavior directly implemented
or asserted by pinned Go. Using the selected WW driver's sibling directory as
the local toolchain-bin analogue is derived from that implementation: WW has
no `GOROOT`, and that directory already supplies the driver's default compiler,
assembler, linker, and package-test coordinator. Applying the rule to WW's raw
single-file compatibility route is also derived; it is a local input extension,
but it executes the same observable test and initialization code. Canonical
absolute spelling is runtime metadata only and prevents a relative driver path
from leaking the coordinator's later package cwd into `PATH`.
#### Fresh four-axis audit and direct pre-fix measurements
The bounded audit considered all four permanent axes and selected only this
test-runtime gap:
- **Build:** pinned `runBuild` loads all roots and checks package errors before
output/action construction
([`cmd/go/internal/work/build.go`, lines 459478](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L459-L478)); official
`build_json.txt` distinguishes load errors from compiler failures
([lines 1526 and 3945](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/build_json.txt#L15-L45)).
A direct missing-import probe made both WW stages exit `1` with the identical
source diagnostic and no producer or public output. This candidate was
aligned.
- **Test:** direct Cstage and WWstage directory probes, each invoked with
`PATH=/usr/bin:/bin`, both exited `0` while the test printed exactly that
unchanged value. `/home/kimchi/src/ww/out/bin` was absent. These are directly
measured pre-fix WW facts and establish the selected external difference.
- **Package:** pinned `types2.(*Checker).initFiles` rejects package name `_`
([`cmd/compile/internal/types2/check.go`, lines 311355](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/check.go#L311-L355)),
with official anchors in
[`internal/types/testdata/check/blank.go`](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/blank.go#L1-L5)
and [`test/blank1.go`](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/blank1.go#L1-L10).
Direct production, imported, and test-only probes were rejected identically
by both WW stages. This candidate was aligned.
- **Import:** pinned `unusedImports` requires every nonblank import binding to
be used
([`cmd/compile/internal/types2/resolver.go`, lines 706740](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L706-L740)); official
`importdecl0` covers default, alias, dot, and blank forms
([lines 531](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/importdecl0/importdecl0a.go#L5-L31)).
A direct unused default-import probe produced identical Cstage/WWstage
diagnostics and no committed work. This candidate was aligned.
The build, package, and import observations above are directly measured WW
behavior; the linked rules are behavior directly implemented or asserted by
pinned Go. The conclusion that this slice crosses those axes only when code in
a successfully loaded test variant or initialized dependency observes `PATH`
is derived from the pinned launch placement.
#### Ownership, launch behavior, and preserved boundaries
The environment is synthesized at the three true test-process launch owners:
`internal/wwpackage.pkgstartrun` for directory products, Cstage
`run_test_bin`, and WWstage `runsingletest` for raw single-file tests. Each
uses the selected driver directory's canonical absolute spelling. The first
effective uppercase inherited value is the suffix; absent and empty values
have no suffix; ordinary uppercase duplicates are removed. Unrelated entries,
including case-distinct and malformed names, retain their previous order and
bytes. Directory products retain their established appended locale, temporary
directory, and final `PWD`; raw tests retain caller cwd, `PWD`, stdin, and split
streams.
The rule covers internal, external, and combined directory products;
dependency initialization; filters and list mode; running retained tests; and
raw single-file tests. It is independently materialized for each concurrent
product. `ww build`, `ww run`, compiler/assembler/linker and generated-main
commands, compile-only and assembly-only tests, no-test products, rejected
requests, and later direct execution of a retained binary receive no test
environment transformation.
Loading, graph construction, compilation, assembly, linking, action keys, and
artifact production are unchanged. A load, compile, or link failure starts no
test process, so runtime `PATH` is inapplicable and the existing diagnostic
precedence remains. A started test observes the new environment before normal
success, assertion failure, signal, timeout, or interruption. Existing process
groups, cancellation, output capture, and cleanup own those outcomes; the
environment adds no global mutable state. A running retained request remains
private build, private run, then guarded install, so any unsuccessful run
publishes nothing and preserves prior bytes. Parallel products receive separate
environment arrays and retain existing result isolation.
Canonical physical driver directories do not become package, import, graph,
action, artifact, symbol, `.wwi`, publication, or persistence identity.
Compiled units, interfaces, archives, executables, modes, diagnostics, and
public-output disposition are unchanged. No stored key or byte changed, so
build workdir format remains `18`, test workdir format remains `19`, and
semantic storage format remains `3`.
The extended WW-native `directory_test_execution_working_directory` observer
proves Cstage/WWstage equality for absent, empty, nonempty, and duplicate
caller `PATH`; all directory test shapes and dependency initialization;
filters, listing, raw single-file, and running retained tests; relative selected
driver canonicalization; build/no-test/compile-only/rejection exclusions;
unchanged compiler, assembler, and linker environments; concurrent products;
runtime failure with prior retained-byte rollback; artifact-byte identity; and
transaction, stage, workdir, and generated-file cleanup. Existing signal,
timeout, interruption, concurrent-driver, and public-output transaction owners
cover the unchanged mechanisms at those boundaries.
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.