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

@@ -10244,6 +10244,138 @@ 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.
### 11.52 Implemented `.ww`-spelled directory build routing
An existing local literal passed to `ww build` whose requested final basename
ends `.ww` is a directory-package request when ordinary `stat` reports a
directory. This includes a direct directory named `.ww` and a visible
`_test.ww` symlink to a directory. The suffix does not turn the directory into
a raw source and does not trigger the non-directory test-source omission in
11.51.
#### Pinned authority and applicability
- **behavior directly implemented or asserted by pinned Go** — official Go
1.26.5 commit `c19862e5f8415b4f24b189d065ed739517c548ba` implements
symlink-following `Stat` in `cmd/go/internal/fsys/fsys.go:633638`.
`PackagesAndErrors` enters named-file mode only when an operand ends `.go`,
`Stat` succeeds, and the result is not a directory
(`cmd/go/internal/load/pkg.go:29032932`, especially 29112918). Local
literals that do not enter that route are matched and directory-loaded by
`cmd/go/internal/modload/load.go:251307,526597` and
`cmd/go/internal/search/search.go:276293`. `runBuild` uses that loader at
`cmd/go/internal/work/build.go:459477`.
- **behavior directly implemented or asserted by pinned Go** — official
`cmd/go/testdata/script/list_ambiguous_path.txt:115,2936` directly proves
that directory `./foo.go` is a package while regular file `./a.go` is a
`command-line-arguments` package. Official
`cmd/go/testdata/script/mod_symlink_dotgo.txt:19,1117` directly asserts
that `dir.go -> dir` is not a source entry; `go/build/build.go:886900`
implements that directory-enumeration rule.
- **behavior derived from the pinned implementation** — because the initial
`Stat` follows the terminal symlink, a requested `.go` path whose target is a
directory takes the directory package route under build. That file-kind rule
applies directly to WW's existing local raw-source/directory distinction and
needs no module, manifest, registry, cache, network resolution, generalized
import syntax, or build expression.
#### Ownership and command boundary
- **directly measured WW behavior** — before this change, Cstage already
stat-routed a valid `visible_test.ww -> pkgdir` build, produced a normal
directory graph and executable, and returned status 0 with empty streams.
WWstage instead returned status 1, empty stdout, exact
`ww: cannot read source\n` stderr, and no artifact. The same split occurred
for `visible.ww -> pkgdir` and a real directory named `literal.ww`; a
`regular_test.ww` symlink to a regular source retained the aligned 11.51
omission.
- **directly measured WW behavior** — the true owner is the
requested-kind branch in `selfhost/cmd/ww/main.ww::dobuild`, after its
existing `Stat`, prefix exclusion, and non-directory `_test.ww` check and
before logical resolution. Cstage and both test fronts already have the
required classification and do not change.
- **behavior directly implemented or asserted by pinned Go** — `go run` has a
deliberately different front door: `cmd/go/internal/run/run.go:95112`
consumes leading `.go` suffix operands as named files without build/test's
directory guard, after which `GoFilesPackage` rejects a directory at
`cmd/go/internal/load/pkg.go:32743281`.
- **behavior derived from the pinned implementation** — therefore this slice
does not change the shared WW resolver or `ww run`. Multiple operands,
recursive patterns, logical names, ordinary directories, regular and
non-regular sources, dangling symlinks, raw `ww test`, `test -c`, and
`test -S` retain their existing front doors.
#### Build, test, package, and import effects
- **directly measured WW behavior** — build is the primary
axis. The selected request enters the same directory enumeration, graph,
action, output, and transaction route as its direct target and the Cstage
spelling. A requested `_test.ww` basename is only request metadata; ordinary
directory production selection still excludes actual `*_test.ww` entries.
- **directly measured WW behavior** — package selection is
the mechanism. The requested symlink spelling and physical target remain
loader/presentation metadata; downstream canonical local package, graph,
action, symbol, artifact, `.wwi`, publication, and persistence identities are
unchanged. No fake raw root or spelling-specific generation is created.
- **directly measured WW behavior** — imports are exactly
the selected directory package's existing dotted imports. The change adds no
qualifier, syntax, search order, edge kind, cycle rule, or identity. It makes
the established graph reachable instead of failing before directory load.
- **directly measured WW behavior** — raw `ww test` was already aligned because
both stages independently stat literal test requests. Running tests, `-c`
retained binaries, `-S` directory diagnostics, stdout/stderr, and binary
bytes matched through the `.ww` symlink before the build fix. Test-process
topology and `-run` matching are explicit non-effects.
#### Diagnostics, artifacts, and lifecycle
- **directly measured WW behavior** — CLI parsing,
multi-root/tree delegation, leading-prefix selection, and non-directory
`_test.ww` header diagnostics keep their precedence. After directory
selection, no-source, package/import/source, output, producer, publication,
and collision diagnostics use the existing directory order and requested
path presentation. The false raw-source read diagnostic disappears only for
this build route.
- **directly measured WW behavior** — file output,
output-directory publication, exact `/dev/null`, assembly-only `-S`, default
output, and persistent `-w` reuse use the existing directory actions and
transaction. Cold success creates the same units, `.wwi`, assembly, objects,
archives, initializer products, and optional executable as Cstage. Cold
source or producer failure commits no partial generation; warm injected
compiler failure and signaled-linker failure preserve every prior public and
semantic byte and leave no `.new`, install backup, or `.wwtxn.*`.
- **directly measured WW behavior** — source/import diagnostics, compiler
failure, and a signaled linker preserve the equivalent directory request's
prior output and work bytes. The focused concurrent row proves isolated
Cstage/WWstage selected requests. The classifier itself is request-local and
read-only; unchanged directory machinery continues to own assembler,
archiver, ordinary linker and publication failures, shared action locking,
driver interruption, process reaping, rollback, and filesystem cleanup.
- **directly measured WW behavior** — direct `SIGTERM` of either driver while a
selected warm build is blocked in compilation is an inherited lifecycle
non-effect, not part of the classifier change. Both stages terminate with
shell status 143, reap the complete owned process group, and preserve the
prior public output and committed semantic bytes, but the existing directory
machinery leaves three request-private `.new` files. That independently
verified cleanup gap remains open and this slice does not describe it as
fixed.
- **directly measured WW behavior** — Cstage and WWstage agree byte-for-byte
on status, stdout, stderr, diagnostics, public output, and every semantic
artifact for selected success and failure rows. Complete
within-stage preservation includes the invoking driver's `.wwtool.ww`
provenance snapshot; cross-stage semantic comparison excludes only those
intentionally different producer bytes.
The focused WW-native observer directly measures direct and symlinked `.ww`
directories, the visible `_test.ww` spelling, regular-file omission, directory
no-source and source/import precedence, a reachable dotted dependency,
default/file/directory/null/assembly outputs, cold and warm
reuse/invalidation/rollback, compiler failure and signaled-linker interruption,
runtime reachability, selected-route concurrency, cleanup, and the exact
test/run command boundaries. No serialized representation changes: build
workdir format remains `18`, test workdir format remains `19`, semantic storage
format remains `3`, and no cache or result record is added.
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.

View File

@@ -358,6 +358,22 @@ ImportPath = ident { "." ident } .
symbol, `.wwi`, publication, or persistence identity. Build workdir format
remains 18, test workdir format remains 19, and semantic storage format
remains 3.
- A local literal passed to `ww build` whose requested final basename ends
`.ww` remains a directory request when ordinary `stat` reports a directory,
including when the spelling is a symlink or ends `_test.ww`. The directory
target therefore bypasses raw named-source prefix and test-source
classification and enters ordinary directory enumeration. Actual eligible
production entries, package declarations, imports, graph actions, producers,
output policy, publication, persistence, invalidation, rollback, and cleanup
are exactly those of the existing directory package route. The requested
spelling and physical target are loader/presentation metadata only and create
no new package, dotted-import, graph, action, symbol, artifact, `.wwi`,
publication, or persistence identity. Raw `ww test`, `ww test -c`, and
`ww test -S` already use the same directory classification and are unchanged.
`ww run` retains its separate named-operand front door. Multiple operands,
recursive and logical requests, regular and non-regular source targets, and
dangling symlinks are not changed. Build workdir format remains 18, test
workdir format remains 19, and semantic storage format remains 3.
- `import acme.codec;` loads the canonical package `acme.codec`. If that
package declares `package wire;`, the importing file sees its exported names
as `wire.Name`; `codec.Name` is not an additional binding. An explicit alias
@@ -465,14 +481,24 @@ ImportPath = ident { "." ident } .
the action list; non-main command dependencies retain ordinary actions, and
a selection containing no command rejects before tools or output creation.
A raw command-line `.ww` source uses its source basename without `.ww`, and
a raw non-main source is the same no-command rejection. Missing
trailing-slash hierarchies are created transactionally from `0777`, filtered
by the caller's umask.
a raw non-main source is the same no-command rejection. An existing
directory whose requested basename ends `.ww` is not such a raw source: it
retains directory-package output naming. With no `-o`, its requested leaf
is used verbatim (for example, `directory_test.ww`); with an existing
output-directory `-o`, publication instead uses the canonical selected
target leaf (for example, `directory-target`).
Missing trailing-slash hierarchies are created transactionally from `0777`,
filtered by the caller's umask.
Loading and package/import diagnostics precede no-main, derived-path,
duplicate-destination, implicit-default, and other output preflight; all of
those checks precede creation. Producer failure or interruption preserves
existing directory contents and removes only request-created prefixes and
stages. A non-directory output retains the single-product file/archive rule.
those checks precede creation. Producer failure and coordinator-owned
interruption preserve existing directory contents and remove only
request-created prefixes and stages. Direct external `SIGTERM` of a build
driver is a verified-open exception: both stages preserve public and
committed work bytes and reap their process group, but may leave `.new`
staging files that make a later persistent-work request reject until those
files are removed. A non-directory output retains the single-product
file/archive rule.
If a lone
command's synthesized default basename already names a directory, loading
and graph validation complete and the build rejects before tools without

View File

@@ -265,6 +265,26 @@ directory/recursive selection, package/import identities, graph/action
identities, persistence formats (build 18, test 19, semantic 3), and test
process topology are explicit non-effects.
An existing local directory whose requested build basename ends `.ww`
(including a visible `_test.ww` symlink to a directory) remains a directory
package, not a raw named test source. WWstage `ww build` now uses the same
symlink-following requested-kind decision as Cstage and enters the established
directory coordinator. The directory's actual `*_test.ww` entries remain
excluded from production and available to `ww test`; the requested suffix does
not itself create a test variant. Raw `ww test`, `test -c`, and `test -S` were
already stat-routed and do not change. The focused package observer directly
measures Cstage/WWstage build diagnostics and semantic/public artifact-byte
parity, direct and symlinked `.ww` directories, a dotted dependency, output
modes, cold/warm transaction behavior, compiler and signaled-linker failure
preservation, selected-route concurrency, cleanup, and the exact test-command
behavior. Direct driver interruption remains owned by the unchanged directory
machinery and is not claimed fixed here. The shared resolver and `ww run` are
deliberately unchanged—and the observer
retains their pre-existing stage difference—because pinned Go's run command
has a distinct named-file front door.
No test product, result, package/import identity, or serialized format changes;
build remains 18, test remains 19, and semantic storage remains 3.
List mode uses that same product process and initialization boundary but starts
no per-test child. The shared language harness emits only selected qualified
test names, one per line in descriptor order. A valid filter selecting no tests