ww: select build action from package declaration

This commit is contained in:
2026-08-13 13:36:10 +09:00
parent 61a2ee5221
commit dab3cb4998
61 changed files with 872 additions and 157 deletions

View File

@@ -2843,34 +2843,78 @@ source-like `.wwi` syntax remains a transitional export encoding pending the
binary `.wwe` format described above, but the separate direct-input ownership
boundary is live in production Cstage and WWstage compilers and drivers.
An ordinary executable directory root is one normal package action. Its
finalized canonical import identity tags its owner-only unit; it receives only
direct exports, emits `.wwi`, `.o`, and a deterministic `.a`, and is compiled
exactly once. The declared package name validates the last component of that
identity for ordinary importable packages. A selected command root may declare
`package main`; `main` validates its executable role and never replaces or
truncates an identity such as `cmd.tool`. The narrow compiler `--entry` flag
both classifies that primary unit as a command and controls bare `main` codegen;
the package driver uses the parser-only `--command-package` marker for command
test variants that must remain ordinary archive code. Neither flag changes
export identity, and imported interfaces remain under strict leaf validation.
The linker receives that root archive first, then the complete reachable
package-archive closure and runtime archive; it never receives `.wwi`. The
linkers seed `main` before archive selection, so the existing WWAR member
protocol needs no special root object or format change.
A selected production root is one normal package action. Its finalized
canonical import identity tags its owner-only unit; it receives only direct
exports, emits `.wwi`, `.o`, and a deterministic `.a`, and is compiled exactly
once. The declared package name validates the last component of that identity
for ordinary importable packages. After loading and identity finalization, the
declaration also selects the terminal build action: `package main` is a command
and every other valid declaration is a compile-only library. A command root
receives the narrow compiler `--entry` flag, which controls bare `main` codegen,
then the linker receives that root archive first, the complete reachable
package-archive closure, and the runtime archive; it never receives `.wwi`. A
non-main root receives no `--entry` and never enters the linker. `main` validates
command kind but never replaces or truncates an identity such as `cmd.tool`.
The explicit package-less single-file compatibility path has no directory
package declaration to classify and remains a raw command unit; it does not
participate in canonical directory-package interning.
The package driver still uses the parser-only `--command-package` marker for
command test variants that must remain ordinary archive code. Neither marker
changes export identity, and imported interfaces remain under strict leaf
validation. The linkers seed `main` before archive selection, so the existing
WWAR member protocol needs no special root object or format change.
`ww build -p -o lib.a DIR` explicitly requests a non-main package product: it
emits a deterministic archive at `lib.a` and its compiler interface at
`lib.a.wwi`, without invoking the linker. A logical target retains its full
identity (`ww build -p -I ROOT -o bar.a foo.bar` emits `foo.bar.*` symbols),
while a literal directory is reverse-resolved through the active source roots
or receives the deterministic local identity described below. Its declared
leaf can never invent or truncate that identity. Package output requires a
directory and `-p` cannot be combined with assembly-only `-S`. Two cold builds
with identical inputs are required to produce byte-identical requested
products. Compiler intrinsics keep their package-mode runtime ABI independent
of transitive source interfaces (for example, `alloc` lowers to the runtime
allocator without requiring an `rt.wwi` compiler input).
Publication is separate from that semantic action choice. `ww build -o lib.a
DIR` or `ww build -I ROOT -o bar.a foo.bar` automatically publishes a non-main
root's deterministic archive and self-contained compiler export at
`FILE.wwi`, without invoking the linker; the latter retains `foo.bar.*` symbols.
Without `-o`, a non-main root and its dependencies are compiled in the selected
scratch or persistent work directory and no cwd product is invented. For a
command, `-o` continues to name the executable publication path. Output names,
request order, and whether publication was requested never enter package or
action identity. The historical action-selecting `-p` exception is removed and
rejected as an unknown build flag. Assembly-only `-S` still stops before object,
archive, publication, or link production. A literal directory is
reverse-resolved through the active source roots or receives the deterministic
local identity described below; its declared leaf can never invent or truncate
that identity. Two cold builds with identical inputs are required to produce
byte-identical requested products. Compiler intrinsics keep their package-mode
runtime ABI independent of transitive source interfaces (for example, `alloc`
lowers to the runtime allocator without requiring an `rt.wwi` compiler input).
This rule follows the pinned official Go 1.26.5 source at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`. `go/build.Package` stores source
directory, declared name, and import path independently, and defines a command
solely as a package named `main`
([`go/build/build.go`, lines 436449](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L436-L449),
[`go/build/build.go`, lines 514519](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L514-L519)).
The Go builder's `AutoAction` links only `main` and returns the archive compile
action for every other package
([`cmd/go/internal/work/action.go`, lines 450456](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L450-L456)).
The build command invents a default output only for one `main`, applies an
explicit `-o` to either AutoAction result, and otherwise builds each requested
package without conflating publication and semantic kind
([`cmd/go/internal/work/build.go`, lines 473478](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L473-L478),
[`cmd/go/internal/work/build.go`, lines 508548](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L508-L548),
[`cmd/go/internal/work/build.go`, lines 551558](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L551-L558)).
Both WW stages represent the rule with the existing loaded declaration and
stable root action index; no package, dependency, ownership, locator, traversal,
or closure allocation is added. `ww run` adds only a command-kind requirement:
a successfully loaded, cycle-free non-main root produces the deterministic
`package PATH is not a main package` diagnostic before any compiler, assembler,
or linker invocation; package and graph failures retain precedence. Before a
non-main `-o` build invokes a producer, both stages validate the longest atomic
publication spelling, `FILE.wwi.new`, so an incomplete archive/export pair is
never caused by a late path-overflow failure. Package loading, cycle detection,
and closure validation retain diagnostic precedence over this publication-only
check, and `-S` does not validate a publication path it never consumes.
Build workdir format 12 invalidates older build-mode unit vouchers before reuse
because root compiler argv changed; test workdir format 11 is unchanged because
test variants still link only their explicit generated-main action. Thereafter
an equivalent warm library build invokes no tools, a private dependency change
stops at its unchanged export, and an export change recompiles its direct
importer under the existing propagation rule.
### 11.7 Implemented directory package-test slice
@@ -2939,7 +2983,7 @@ invocation, each still-unbound directory is finalized by this exact algorithm:
invalidates a name inferred from a later or nested root.
3. Bind the first precedence-valid candidate from each reaching context through
the command-global bidirectional interner. An identity supplied by successful
logical package lookup, such as `-p encoding.utf8`, is already bound and is
logical package lookup, such as `encoding.utf8`, is already bound and is
preserved exactly. That forward-selected identity is authoritative: reverse
derivation applies only to still-unbound literal roots, so a nested active
root cannot rename an explicitly resolved package.