build: make executable and test roots package actions

This commit is contained in:
2026-08-12 22:00:02 +09:00
parent fc4bde703e
commit 1724ea086f
11 changed files with 722 additions and 364 deletions

View File

@@ -2810,15 +2810,16 @@ the final component of its import path; two logical identities for one physical
directory are rejected rather than compiled twice.
Packages compile serially in dependency-first postorder. The compiler emits the
existing deterministic `.wwi` interface for every importable package. Its
primary section contains that package's byte-sorted direct imports and exported
declarations. The compiler then appends byte-sorted, origin-tagged sections for
only the exported foreign type and constant facts recursively reachable from
the primary public signatures. This makes each direct dependency interface
self-contained for the public type information its consumers need while
retaining the deeper declarations' original package identity. Checked fixed
array dimensions are emitted as numeric type facts, so a public layout never
requires exposing the private constant spelling that produced its length.
existing deterministic `.wwi` interface for every directory-package action,
including an executable root. Its primary section contains that package's
byte-sorted direct imports and exported declarations. The compiler then appends
byte-sorted, origin-tagged sections for only the foreign type and constant facts
recursively reachable from the primary public signatures. Reachable owner-local
private nominal types are carried without `export`: they make the export
self-contained for type checking, but qualified source lookup still rejects
their names. Checked fixed array dimensions are emitted as numeric type facts,
so a public layout never requires exposing the private constant spelling that
produced its length.
A package compilation unit contains only that package's own byte-sorted sources
and deterministic `//ww:module-reset` separators. Each **direct** import is a
@@ -2835,19 +2836,25 @@ 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 root is linked with the full reachable object closure into the
requested executable (legacy WW programs may use a package name other than
`main`). `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
An ordinary executable directory root is one normal package action. Its
declared package identity tags its owner-only unit; it receives only direct
exports, emits `.wwi`, `.o`, and a deterministic `.a`, and is compiled exactly
once. The narrow compiler `--entry` flag controls only bare `main` codegen and is
independent of export production. 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.
`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 uses its declared leaf package. 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), while the
linker still receives every reachable package archive plus the runtime archive.
runtime allocator without requiring an `rt.wwi` compiler input).
### 11.7 Implemented directory package-test slice
@@ -2868,11 +2875,14 @@ semantic selections are only the directory and selected variant/package
identities; it also carries output destinations, coordinator-private completion
paths, import search roots, and the optional command-scoped work-directory
policy. The command owns source selection, package loading, compiler inputs,
archive construction, and linking for every non-importable root:
archive construction, generated-main construction, and linking for every
package variant:
- `same-test` selects the byte-sorted production files followed by the
byte-sorted matching `package p` test files. They form one compiler unit, so
tests can use private production declarations.
- The ordinary production action selects the directory's byte-sorted
non-test files and is reused wherever that canonical package is imported.
- `same-test` is a distinct internal production-plus-test action. It selects
the byte-sorted production files followed by the byte-sorted matching
`package p` test files, so tests can use private production declarations.
- `external-test` selects only matching `package p_test` files. Its `import p`
is a direct edge to the canonical production action for that directory.
That action compiles with module qualifier `p`, selects every production
@@ -2882,17 +2892,23 @@ archive construction, and linking for every non-importable root:
owning root, such as `__ww-test-001-external-production`; a normal import of
the same `p` and canonical directory reuses that action rather than creating
a second compilation.
- Each selected internal or external variant gets a distinct generated-main
package action. Its generated owner-only unit declares `package main` and
imports exactly the selected variant and test-support package. It consumes
exactly those direct `.wwi` artifacts, emits its own `.wwi/.o/.a`, and alone
receives compiler `-T --entry`.
The command loads all roots into one command-scoped package universe. Each root
retains an injective artifact key derived from its deterministic request ordinal
and variant, such as `__ww-test-000-same` or
`__ww-test-003-external`. Hyphens make that namespace illegal as a WW import
identity. Imports of the same canonical production directory intern to one
production node across every selected test directory. A deterministic
dependency-first traversal of the complete union therefore invokes the
compiler and archiver once for every reachable canonical production package,
even when many directory products need it. Each root is still compiled once
with its own selected sources and linked separately. The shared plan is
The command loads all variants into one command-scoped package universe.
Directory variants retain injective artifact keys such as
`__ww-test-000-same` or `__ww-test-003-external`; their generated mains use
matching `__ww-test-NNN-main` keys and distinct internal package identities.
Hyphens keep artifact keys illegal as WW import identities. Repeated requests
for the same canonical directory and variant reuse one compile action, and
imports of the same canonical production directory intern to one production
action across every selected test product. A deterministic dependency-first
traversal of the complete union therefore invokes the compiler and archiver
once per package variant, even when many products share it. Generated-main
actions remain product-specific and are linked separately. The shared plan is
deliberately package-test-specific: it is not a generalized scheduler, action
schema, cache, or protocol.
@@ -2914,26 +2930,30 @@ missing product as a build failure. The single union build and completed test
products share the coordinator's existing `-j` process bound; captured output
is still emitted only in byte-sorted directory/package order.
Every non-root dependency is always a production variant, so dependency
Every source-imported dependency is a production variant, so dependency
`*_test.ww` files never enter the graph. Imports that occur only in selected
test files add edges only to that test root. Each compiler unit contains only
the variant's owned source set, while its invocation receives only the
byte-sorted direct dependency `.wwi` artifacts as separate inputs. The final
test link still receives the root object and the complete reverse-topological
archive closure. The compiler-generated `-T` dispatcher owns the implicit
direct test-runtime support edge and remains embedded in each independently
compiled test root; it is the narrow test-main variant, not a
coordinator-generated graph package. The command-scoped plan compiles the
common runtime production package once for the complete test request. The
command resolves that edge from the selected toolchain source tree, not the
user search path; the support package's own imports are also loaded in that
toolchain context. Normally its graph
test files add edges only to that internal or external variant. Each compiler
unit contains only its action's owned source set, while its invocation receives
only the byte-sorted direct dependency `.wwi` artifacts as separate inputs.
Variant compiles receive `--test-package`, which validates and retains private
`@test` declarations as compiler-only export metadata without synthesizing an
entry point. The distinct generated-main action consumes that metadata from its
direct variant export and synthesizes the dispatcher with `-T`.
The generated-main action, rather than the tested variant, owns the implicit
direct test-runtime support edge. The command-scoped plan compiles the common
support production package once for the complete test request. The command
resolves that edge from the selected toolchain source tree, not the user search
path; the support package's own imports are also loaded in that toolchain
context. Normally its graph
qualifier is `test`, so an explicit source `import test` coalesces with the same
canonical package. When a real user package occupies that identity, the command
presents the runtime edge to the compiler under the reserved `__wwtest`
qualifier. This keeps a production package named `test` available to external
tests while preserving raw `w6c -T` compatibility, whose default unresolved
qualifier remains `test`.
tests. Explicit raw single-file `ww test FILE` fixtures retain the narrow fused
`-T` compatibility path because an anonymous multi-package raw unit is not a
canonical directory package; that path still consumes support as a direct
export and emits a root `.wwi/.a`.
The reserved support action and an ordinary source-imported toolchain `test`
action may coexist in the command universe because their compiler qualifiers
@@ -2947,24 +2967,25 @@ only narrow action-role exceptions: each product closure is checked to contain
at most one importable action for a compiler module qualifier, so unrelated
roles can never introduce duplicate linked package symbols.
The selected test roots and a production variant reached by their imports or
test-runtime closure are the only sanctioned graph nodes that may share a
physical directory. This also lets a toolchain package's own tests coexist with
the production variant required by the test runtime. A same-package root
already defines those production symbols, so the production archive is omitted
from that product's final link while the production node's dependency archives
remain in its closure. A reserved compiler-support action at that same physical
directory is still retained. The external product includes the production
archive.
Variant-only archives are never linked into the other product. All ordinary
logical and physical package-identity collision checks remain unchanged.
The Cstage linker, like the WWstage linker, passes the root object, every
reachable archive, the runtime, and explicit `-L`/`-l` values through a
structured argument vector; no fixed flattened command buffer can truncate a
large closure. WWstage emits joined `-Ldir` and `-lname` arguments accepted by
its native linker, while Cstage preserves the equivalent split forms. Generated
artifact paths are bounds-checked before any unit is opened, so distinct root
keys cannot alias by truncation.
The selected internal/external variants and a production action reached by
their imports or test-runtime closure are the sanctioned graph nodes that may
share a physical directory. This also lets a toolchain package's own tests
coexist with the production action required by the test runtime. An internal
variant already defines those production symbols, so the colocated production
archive is omitted from that product's final link while the production action's
dependency archives remain in the closure. The external product includes the
production archive. Variant-only archives are never linked into another
product. All ordinary logical and physical package-identity collision checks
remain unchanged.
Both stage linkers receive the generated-main archive first, followed by the
complete reverse-topological reachable package-archive closure, runtime, and
explicit `-L`/`-l` values through a structured argument vector. No `.wwi` or
special root `.o` appears in linker argv, and no fixed flattened command buffer
can truncate a large closure. WWstage emits joined `-Ldir` and `-lname`
arguments accepted by its native linker, while Cstage preserves the equivalent
split forms. Generated artifact paths are bounds-checked before any unit is
opened, so distinct root keys cannot alias by truncation.
Recursive discovery groups by physical directory before sorting filenames, and
one stable escaped request key names the persistent command work directory.
Every `*_test.ww` package variant is built even when a file only
@@ -3021,7 +3042,7 @@ package, compiler actions receive direct dependency exports as individual
arguments beside an owner-only `.unit.ww`, and links receive the complete
per-root `.a` closure. Repository-native coverage wraps all three real stage
tools at executable paths containing spaces, records every argument boundary,
inspects `.unit.ww`, `.wwi`, `.a`, and root object placement, runs the published
inspects `.unit.ww`, `.wwi`, `.a`, and root archive placement, runs the published
binary, compares repeated Cstage/WWstage artifacts and traces, and removes one
direct export at compiler entry to compare package-attributed diagnostics.
@@ -3198,22 +3219,26 @@ schema or identity record.
The exact-argv regression uses the real diamond
`base -> {left,right} -> root`. It proves one compile per node; no input for
`base`; only `base.wwi` for each middle node; only sorted `left.wwi` and
`right.wwi` for `root`; exact owner-only unit bytes; the complete three-archive
link closure; no link-time `.wwi`; exit status 42; and byte-identical units,
exports, archives, executables, and tool argument vectors across two clean
`right.wwi` for `root`; exact owner-only unit bytes; the complete four-package
link closure including the root archive; no link-time `.wwi`; exit status 42;
and byte-identical units, exports, archives, executables, and tool argument
vectors across two clean
Cstage builds and two clean WWstage builds. The existing directory-package
variant regression checks exact direct inputs for internal and external
generated roots and the external-production action; both stages also compile
and run ordinary production and support actions with owner-only units and
byte-identical artifacts.
variant regression checks separate production, internal, external, and
generated-main actions, exact generated-main direct variant/support exports,
the external-production action, and archive-only link closures. Both stages
compile and run those actions with owner-only units and byte-identical
artifacts.
The pinned Go 1.26.5 implementation supplies the design boundary:
The pinned official Go 1.26.5 tag (commit
`c19862e5f8415b4f24b189d065ed739517c548ba`) supplies the design boundary:
- Directory identity starts with `ImportDir`'s named directory
([`go/build/build.go`, lines 521524](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/go/build/build.go#521)); directory reads are name-sorted
([lines 108111](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/go/build/build.go#108)), lookup selects directory candidates
([lines 725809](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/go/build/build.go#725)), and the selected directory is enumerated
([lines 859900](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/go/build/build.go#859)).
([lines 859900](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/go/build/build.go#859)). The sorted iteration filters files and appends each source to exactly one ordinary/internal/external-test bucket
([lines 8951036](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/go/build/build.go#895)).
- The loader records direct imports
([`load/pkg.go`, lines 220225](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/pkg.go#220)), guarantees repeated cache loads return the same package pointer
([lines 633636](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/pkg.go#633)), keys reuse by canonical import path
@@ -3221,22 +3246,26 @@ The pinned Go 1.26.5 implementation supplies the design boundary:
([lines 27762795](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/pkg.go#2776)).
- Build actions are interned by mode and package identity
([`work/action.go`, lines 437447](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go#437)); compilation depends only on direct imports
([lines 628659](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go#628)), whereas link actions expand transitive dependencies
([lines 628659](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go#628)), and even package `main` receives that normal interned archive-producing compile action
([lines 641647](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go#641)). A link action places that main compile action at its first dependency and expands transitive dependencies
([lines 918957](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go#918),
[lines 10341068](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go#1034)). The executor derives compiler import inputs from those direct actions
([`work/exec.go`, lines 864884](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/exec.go#864)), passes them separately from source files
([lines 928930](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/exec.go#928)), and separately emits the expanded linker closure
([lines 15921647](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/exec.go#1592)).
([lines 928930](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/exec.go#928)), packs remaining objects into the package archive and records it as the built result
([lines 10171033](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/exec.go#1017)), then supplies the root archive and all expanded package inputs to linking
([lines 15921624](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/exec.go#1592),
[lines 16351647](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/exec.go#1635)).
- Tests preserve four package roles
([`load/test.go`, lines 85102](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/test.go#85)): internal production-plus-test
([lines 175225](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/test.go#175)), external test
([lines 228265](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/test.go#228)), and generated main
([lines 272293](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/test.go#272)), with their distinct imports attached at
([lines 272293](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/test.go#272)). Its support imports are loaded for that generated package
([lines 307332](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/test.go#307)), with the selected internal/external variants attached as distinct direct imports at
[lines 351373](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/test.go#351).
- Unified export writing re-links, re-exports, and prunes facts
([`noder/unified.go`, lines 152165](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/compile/internal/noder/unified.go#152)), finalizes self-contained data
([lines 463470](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/compile/internal/noder/unified.go#463)), and sorts declarations and bodies before serialization
([lines 514570](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/compile/internal/noder/unified.go#514)). Compiler import lookup opens the separately mapped artifact
([lines 463470](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/compile/internal/noder/unified.go#463)), selects export roots, and sorts declaration and body indices before serialization
([lines 495570](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/compile/internal/noder/unified.go#495)). Compiler import lookup opens the separately mapped artifact
([`noder/import.go`, lines 61101](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/compile/internal/noder/import.go#61)) and decodes its exports independently of source parsing
([lines 170225](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/compile/internal/noder/import.go#170)); `ReadPackage` consumes that package decoder
([`importer/ureader.go`, lines 2862](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/compile/internal/importer/ureader.go#28)).