build: omit named test source operands

This commit is contained in:
2026-08-22 21:53:10 +09:00
parent 3922f1c34e
commit 2e5451a601
13 changed files with 1666 additions and 21 deletions

View File

@@ -10080,6 +10080,170 @@ No serialized format changes. Build workdir format remains `18`, test workdir
format remains `19`, semantic storage format remains `3`, and there is no test
result cache.
### 11.51 Implemented explicit named test-source build omission
A single existing raw `ww build` operand whose requested final basename ends
exactly `_test.ww` is now a test-only named source. WW syntax-observes only the
package clause and its initial contiguous import section, preserves any read/header
diagnostic, then omits the valid test-only root before logical resolution or
action construction. This is deliberately distinct from the leading-dot and
underscore rule in 11.50: `_test.ww` reaches that earlier exclusion, whereas a
visible `x_test.ww` reaches this test-only omission.
#### Pinned authority, tests, and applicability
- **behavior directly implemented or asserted by pinned Go** — official Go
1.26.5 commit `c19862e5f8415b4f24b189d065ed739517c548ba` recognizes an
existing non-directory named `.go` operand in `PackagesAndErrors`
([`cmd/go/internal/load/pkg.go`, lines 29032918](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L2903-L2918)),
builds its synthetic command-line package with `UseAllFiles`
([lines 32443315](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L3244-L3315)),
and classifies `_test.go` separately from `GoFiles`
([`go/build/build.go`, lines 9301036](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L930-L1036)).
`UseAllFiles` bypasses ordinary target and build-expression rejection, but
not that test-file classification
([lines 14381509](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1438-L1509)).
- **behavior directly implemented or asserted by pinned Go** — on a
successfully scanned header, `readGoInfo` reads the initial package/import
header and one stop byte, then removes that byte before parsing; on header
syntax recovery it deliberately drains the remaining source
([`go/build/read.go`, lines 265315](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/read.go#L265-L315)).
Every raw NUL reached by its reader is a read error
([lines 7189](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/read.go#L71-L89));
`parser.ImportsOnly` stops before ordinary declarations
([`go/parser/parser.go`, lines 28872923](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/parser/parser.go#L2887-L2923)).
Thus a package or initial-import header error precedes omission, while a
later declaration/body error does not become an ordinary-build error.
- **behavior directly implemented or asserted by pinned Go** — ordinary
initial loading does not recursively resolve test imports
([`cmd/go/internal/load/pkg.go`, lines 350358](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L350-L358));
`go/build` records their metadata at `go/build/build.go:10371040`.
Build checks loader errors first and then omits a test-only root before
output/action construction
([`cmd/go/internal/work/build.go`, lines 459559 and 731745](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L459-L559)).
- **behavior directly implemented or asserted by pinned Go** — related
official anchors are `go/build/build_test.go:382421` (`TestMatchFile`),
`go/build/build_test.go:812831` (`TestDirectives` and `XTestDirectives`),
`go/build/read_test.go:1773,120159,165249` (header boundary, NUL, and
syntax recovery),
`cmd/go/testdata/script/test_relative_cmdline.txt:149`, and
`cmd/go/testdata/script/build_test_only.txt:118`. The pinned official
repository has no script that directly invokes `go build NAME_test.go`; the
exact named-build result below is derived from its pinned loader and action
ordering, not presented as an unanchored direct script assertion.
- **behavior derived from the pinned implementation** — one valid named test
source produces no build action. No effective `-o` and exact `/dev/null`
succeed silently; a non-directory output reports `ww: no packages to
build\n`; an existing or trailing-slash output directory reports
`ww: no main packages to build\n`. Header loading occurs before those
empty-selection branches.
- **behavior derived from the pinned implementation** — the pinned body
boundary is byte-sensitive: an ordinary non-`i` stop byte, a first malformed
UTF-8 byte, or a later BOM byte is excluded, while a reached NUL and an
unterminated comment remain load errors; a following byte `i` is attempted as
another import. WW applies the corresponding rule at its lexical `import`
boundary: it never lexes the first ordinary body token, but preserves NUL and
comment diagnostics encountered while skipping header trivia.
The rule honestly applies to WW's local, manifest-free literal `.ww` model:
`*_test.ww` already means test source for directory selection and `ww test`.
It introduces no module, manifest, registry, lock, network resolver, cache,
generalized import grammar, source build expression, or test-result cache.
#### Source and identity ownership
- **behavior directly implemented or asserted by pinned Go** — named-source
package construction presents `Stat`-derived file information through its
synthetic directory. The operand spelling remains the file name presented
to selection, even though `Stat` follows a symlink.
- **behavior derived from the pinned implementation** — the twin true owners
are `cmd/ww/main.c::do_build` and `selfhost/cmd/ww/main.ww::dobuild`, after
the existing hidden-prefix check and before `resolve_module` / `resolvemodule`.
A requested visible non-directory spelling ending `_test.ww` is classified
from that spelling; a symlink to a directory remains a directory request.
The finite-stream header reader is not a regular-file restriction: a supplied
finite FIFO is read as a source, while an unreadable entry reports its header
read failure. Physical parent directories and symlink targets remain loader
metadata, never canonical package or import identity.
- **behavior derived from the pinned implementation** — platform-looking
visible names such as `x_windows_test.ww` remain test-only named sources.
Multiple named sources, logical operands without `.ww`, directories,
recursive requests, `ww run`, and all visible non-test raw operands retain
their prior routes. In particular, this does not implement Go's multiple
named-source package collection.
- **behavior derived from the pinned implementation** — the header observation
is discarded after classification. It creates no canonical package,
command-line package representative, dotted import identity, qualifier,
symbol namespace, `.wwi`, action, archive, publication, or persistence key.
Initial import syntax is checked solely for load-error precedence; omitted
test-source imports are not resolved and create no graph edge or action.
#### Build, test, package, and import effects
- **behavior directly measured WW behavior** — before this change, both stages
compiled, linked, published, persisted, and ran a valid visible
`only_test.ww`; `ww build -w WORK -o OUT only_test.ww` exited 0 with empty
streams, a byte-identical mode-0755 executable, populated work state, and
runtime status 19. A missing test-only import likewise reached ordinary
import resolution.
- **behavior derived from the pinned implementation** — post-contract build
behavior is an empty production selection after a valid header: no compiler,
assembler, archiver, linker, generated main, test harness, test child, or
program process starts. `-S` follows the same no-action rule. No runtime
result can occur.
- **behavior derived from the pinned implementation** — package behavior is
confined to the transient header check; no production/test variant or
package action remains. Import behavior is likewise confined to syntax;
missing test-only dotted imports, late imports, body syntax/type errors, and
runtime faults cannot enter the production graph.
- **behavior derived from the pinned implementation** — test behavior is an
explicit non-effect. Raw `ww test`, `ww test -c`, and `ww test -S` continue
to select named test files. Directory and recursive `ww build` already
exclude selected test sources and remain unchanged.
#### Diagnostics, artifacts, and lifecycle
- **behavior derived from the pinned implementation** — CLI-shape and
multiple-operand delegation retain their prior precedence; 11.50 prefix
exclusion precedes this header reader. Header read/package/initial-import
diagnostics precede output policy. A first ordinary malformed UTF-8/BOM body
byte is excluded, while a reached NUL or unterminated header-trivia comment
is a header diagnostic. Once the header is valid, no unresolved import,
graph, producer, linker, or runtime diagnostic may surface. Both
stages use the same header boundary and must produce byte-identical status,
stdout, and stderr.
- **behavior derived from the pinned implementation** — cold no-action success
creates no default/explicit output, output directory, `.wwi`, unit, assembly,
object, archive, init product, `.sepwork`, workdir, stamp, transaction,
capture, result, or private build temporary. Output-policy diagnostics also
create none of those products.
- **behavior derived from the pinned implementation** — a warm request starts
no transaction, mutation, invalidation, reuse check, timestamp refresh, or
producer. Existing output, sidecar, workdir and artifacts remain
byte-identical on success and failure; no `.new`, `.install`, `.wwtxn.*`,
backup, or recovery residue remains. Restoring a visible non-test source
reuses prior valid warm state by the unchanged ordinary route.
- **behavior derived from the pinned implementation** — no action means no
publication, rollback work, producer/runtime failure path, shared lock, or
child process. Concurrent Cstage/WWstage no-action and header-diagnostic
requests are isolated. Interruption during header reading leaves no owned
persistent state; after a valid header there is no child or publication window
to clean up. Every reader closes its descriptor and releases request-local
storage before return.
- **behavior derived from the pinned implementation** — focused proof covers
valid, malformed-header, missing-test-import, wrong-platform, symlink and
finite-stream operands; default/file/directory/null/assembly output modes;
cold/warm preservation; trace-proven tool and runtime absence; rollback,
interruption, concurrency, cleanup, complete per-stage work preservation, and
Cstage/WWstage semantic-artifact parity. The complete per-stage snapshot
includes `.wwtool.ww`; cross-stage comparison excludes only that intentionally
different producer binary snapshot. Unaffected visible production controls
retain byte-identical outputs.
No serialized format changes accompany this omission. Build workdir format
remains `18`, test workdir format remains `19`, semantic storage format remains
`3`, and the slice adds no cache or persistent record.
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.