test: prove local vendor package semantics
This commit is contained in:
@@ -2794,16 +2794,17 @@ package main;
|
|||||||
import lib.math;
|
import lib.math;
|
||||||
```
|
```
|
||||||
|
|
||||||
An import is translated from dots to path separators and resolved only as a
|
A source import is translated from dots to path separators, expanded through
|
||||||
directory package through the entry package's directory, explicit `-I` roots in
|
the nearest eligible local `vendor` directory described in section 11.16, then
|
||||||
command order, and the toolchain source-library root. A same-named `.ww` file is
|
falls back to directory lookup through the entry package's directory, explicit
|
||||||
neither a match nor a shadow for an import, so a later root containing the
|
`-I` roots in command order, and the toolchain source-library root. A same-named
|
||||||
directory wins over an earlier file decoy. There is no network, manifest, or
|
`.ww` file is neither a match nor a shadow for an import, so a later root
|
||||||
imported-file fallback. Explicit single-file CLI roots retain their raw-unit
|
containing the directory wins over an earlier file decoy. There is no network,
|
||||||
compatibility path. The loader uses the compiler frontend's imports-only parser,
|
manifest, or imported-file fallback. Explicit single-file CLI roots retain
|
||||||
unions duplicate imports, byte-sorts direct edges, interns resolved directories
|
their raw-unit compatibility path. The loader uses the compiler frontend's
|
||||||
by filesystem identity, and reports self-imports and stable cycle chains before
|
imports-only parser, unions duplicate imports, byte-sorts direct edges, interns
|
||||||
compilation.
|
canonical directory actions, and reports self-imports and stable cycle chains
|
||||||
|
before compilation.
|
||||||
|
|
||||||
A directory package consists of its immediate regular non-symlink `.ww` files,
|
A directory package consists of its immediate regular non-symlink `.ww` files,
|
||||||
excluding `*_test.ww`, in byte-sorted filename order. Every selected file must
|
excluding `*_test.ww`, in byte-sorted filename order. Every selected file must
|
||||||
@@ -2813,8 +2814,9 @@ instead declares `package main` to validate command kind while retaining its
|
|||||||
complete canonical import identity. A source import of a command package from a
|
complete canonical import identity. A source import of a command package from a
|
||||||
different directory is rejected; the one same-directory exception is an
|
different directory is rejected; the one same-directory exception is an
|
||||||
external `main_test` variant's canonical import of its production action. Two
|
external `main_test` variant's canonical import of its production action. Two
|
||||||
logical identities for one physical directory are rejected rather than compiled
|
ordinary logical identities for one physical directory are rejected rather
|
||||||
twice.
|
than compiled twice; section 11.16 records the deliberate exception for
|
||||||
|
distinct expanded vendor routes that converge through symlinks.
|
||||||
|
|
||||||
Packages compile serially in dependency-first postorder. The compiler emits the
|
Packages compile serially in dependency-first postorder. The compiler emits the
|
||||||
existing deterministic `.wwi` interface for every directory-package action,
|
existing deterministic `.wwi` interface for every directory-package action,
|
||||||
@@ -2828,10 +2830,14 @@ their names. Checked fixed array dimensions are emitted as numeric type facts,
|
|||||||
so a public layout never requires exposing the private constant spelling that
|
so a public layout never requires exposing the private constant spelling that
|
||||||
produced its length.
|
produced its length.
|
||||||
|
|
||||||
A package compilation unit contains only that package's own byte-sorted sources
|
A package compilation unit contains only that package's own byte-sorted sources,
|
||||||
and deterministic `//ww:module-reset` separators. Each **direct** import is a
|
deterministic `//ww:module-reset` separators, and sorted driver-private
|
||||||
separate `--import <canonical-path> <dependency.wwi>` compiler input, sorted by
|
resolution metadata; it never contains a dependency source body. Each
|
||||||
canonical path and deduplicated by the loader; no transitive `.wwi` is passed.
|
**direct** import is a separate
|
||||||
|
`--import <canonical-path> <dependency.wwi>` compiler input, sorted by canonical
|
||||||
|
path and deduplicated by the loader; a source spelling expanded through
|
||||||
|
`vendor` additionally receives the non-dependency `--import-map` described in
|
||||||
|
section 11.16. No transitive `.wwi` is passed.
|
||||||
Origin-tagged facts inside those direct artifacts are compiler data, not source
|
Origin-tagged facts inside those direct artifacts are compiler data, not source
|
||||||
imports: a source qualifier is visible only when its owning package directly
|
imports: a source qualifier is visible only when its owning package directly
|
||||||
imports it, and private members, transitive-only qualifiers, bare values, and
|
imports it, and private members, transitive-only qualifiers, bare values, and
|
||||||
@@ -2909,9 +2915,9 @@ publication spelling, `FILE.wwi.new`, so an incomplete archive/export pair is
|
|||||||
never caused by a late path-overflow failure. Package loading, cycle detection,
|
never caused by a late path-overflow failure. Package loading, cycle detection,
|
||||||
and closure validation retain diagnostic precedence over this publication-only
|
and closure validation retain diagnostic precedence over this publication-only
|
||||||
check, and `-S` does not validate a publication path it never consumes.
|
check, and `-S` does not validate a publication path it never consumes.
|
||||||
Build workdir format 12 invalidates older build-mode unit vouchers before reuse
|
Build workdir format 13 and test workdir format 12 invalidate older unit
|
||||||
because root compiler argv changed; test workdir format 11 is unchanged because
|
vouchers before reuse because source binding and vendor-directory identity now
|
||||||
test variants still link only their explicit generated-main action. Thereafter
|
participate in compiler argv and persistent unit semantics. Thereafter
|
||||||
an equivalent warm library build invokes no tools, a private dependency change
|
an equivalent warm library build invokes no tools, a private dependency change
|
||||||
stops at its unchanged export, and an export change recompiles its direct
|
stops at its unchanged export, and an export change recompiles its direct
|
||||||
importer under the existing propagation rule.
|
importer under the existing propagation rule.
|
||||||
@@ -3082,12 +3088,13 @@ export and emits a root `.wwi/.a`.
|
|||||||
|
|
||||||
The reserved support action and an ordinary source-imported package `test` may
|
The reserved support action and an ordinary source-imported package `test` may
|
||||||
coexist only because the former is explicitly rebound to the compiler-only
|
coexist only because the former is explicitly rebound to the compiler-only
|
||||||
qualifier `__wwtest`. This is the sole directory-alias exception and cannot be
|
qualifier `__wwtest`. This is the sole role-based directory alias and cannot be
|
||||||
created by a source import. All ordinary production actions, including an
|
created by a source import. The separate expanded-vendor-route exception in
|
||||||
external test's colocated production dependency, use canonical directory
|
section 11.16 is canonical source-tree identity, not a role alias. All ordinary
|
||||||
identity and the global bidirectional import-path checks above. There is no
|
production actions, including an external test's colocated production
|
||||||
role-based tolerance for duplicate ordinary import identities and no late
|
dependency, use canonical directory identity and the global bidirectional
|
||||||
product-closure ambiguity to resolve.
|
import-path checks above. There is no role-based tolerance for duplicate
|
||||||
|
ordinary import identities and no late product-closure ambiguity to resolve.
|
||||||
|
|
||||||
The selected internal/external variants and a production action reached by
|
The selected internal/external variants and a production action reached by
|
||||||
their imports or test-runtime closure are the sanctioned graph nodes that may
|
their imports or test-runtime closure are the sanctioned graph nodes that may
|
||||||
@@ -3098,7 +3105,8 @@ 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
|
dependency archives remain in the closure. The external product includes the
|
||||||
production archive. Variant-only archives are never linked into another
|
production archive. Variant-only archives are never linked into another
|
||||||
product. All ordinary logical and physical package-identity collision checks
|
product. All ordinary logical and physical package-identity collision checks
|
||||||
remain unchanged.
|
remain unchanged; only distinct expanded vendor routes receive the section
|
||||||
|
11.16 symlink-convergence exception.
|
||||||
|
|
||||||
Both stage linkers receive the generated-main archive first, followed by the
|
Both stage linkers receive the generated-main archive first, followed by the
|
||||||
complete reverse-topological reachable package-archive closure, runtime, and
|
complete reverse-topological reachable package-archive closure, runtime, and
|
||||||
@@ -3236,9 +3244,9 @@ stamp. A warm invocation byte-compares all applicable live executables before
|
|||||||
considering any committed unit reusable. A missing or changed driver copy
|
considering any committed unit reusable. A missing or changed driver copy
|
||||||
invalidates every `.unit.ww` voucher before compilation; old artifacts may
|
invalidates every `.unit.ww` voucher before compilation; old artifacts may
|
||||||
remain recoverable, but none can be reused without a freshly committed unit.
|
remain recoverable, but none can be reused without a freshly committed unit.
|
||||||
The workdir format revisions are 8 for ordinary builds and 9 for tests. The
|
That slice introduced workdir format revisions 8 for ordinary builds and 9 for
|
||||||
identity-finalization change bumps both formats so no leaf-keyed unit voucher
|
tests. Later package-identity slices supersede those revisions; the current
|
||||||
can be reused as a full-path package action.
|
formats are recorded in section 11.16.
|
||||||
|
|
||||||
This closes a real hidden-input boundary. The driver, rather than `w6c`, owns
|
This closes a real hidden-input boundary. The driver, rather than `w6c`, owns
|
||||||
canonical directory interning, source-derived graph construction, owner-only
|
canonical directory interning, source-derived graph construction, owner-only
|
||||||
@@ -3278,14 +3286,16 @@ or a manifest.
|
|||||||
|
|
||||||
### 11.11 Implemented directory-only source-import slice
|
### 11.11 Implemented directory-only source-import slice
|
||||||
|
|
||||||
Cstage and WWstage now use a directory-only locator for every parsed source
|
Cstage and WWstage now use directory packages for every parsed source import,
|
||||||
import, including imports selected only by a package-test variant and the
|
including imports selected only by a package-test variant and real imports in
|
||||||
compiler-generated test-support edge. The loader makes one ordered pass for
|
the test-support package. Source imports first probe the bounded vendor
|
||||||
`<root>/<import-path>/`; it never probes `<root>/<import-path>.ww`. Unit
|
candidates in section 11.16, then make one ordered fallback pass for
|
||||||
composition consequently writes only the owning package's byte-sorted source
|
`<root>/<import-path>/`; they never probe `<root>/<import-path>.ww`. The
|
||||||
files and never copies a dependency interface or imported source body. Missing
|
compiler-generated edge to test support remains synthetic. Unit composition
|
||||||
imports retain the importing source position and the same stable diagnostic in
|
consequently writes only the owning package's byte-sorted source files and
|
||||||
both stages.
|
never copies a dependency interface or imported source body. Missing imports
|
||||||
|
retain the importing source position and the same stable diagnostic in both
|
||||||
|
stages.
|
||||||
|
|
||||||
Root selection remains a separate compatibility boundary. A literal `.ww` CLI
|
Root selection remains a separate compatibility boundary. A literal `.ww` CLI
|
||||||
target, or a bare CLI target found as `<root>/<name>.ww` after the global
|
target, or a bare CLI target found as `<root>/<name>.ww` after the global
|
||||||
@@ -3331,9 +3341,17 @@ manifest, schema, package database, or network lookup is involved.
|
|||||||
|
|
||||||
Both drivers construct those arguments directly from the package node's sorted,
|
Both drivers construct those arguments directly from the package node's sorted,
|
||||||
deduplicated outgoing edges. They never walk grandchildren for compilation.
|
deduplicated outgoing edges. They never walk grandchildren for compilation.
|
||||||
Every `.unit.ww` contains only the node's byte-sorted source files and reset
|
Every `.unit.ww` contains the node's byte-sorted source files, reset separators,
|
||||||
separators, while executable linking independently walks the full reachable
|
and any sorted driver-private vendor/import-map voucher comments, but no
|
||||||
package closure and passes archives, never interfaces. The same path handles an
|
dependency body. When exact source spelling differs from a selected expanded
|
||||||
|
canonical identity, the drivers also pass sorted, unique
|
||||||
|
`--import-map <source-spelling> <canonical-import-path>` triples. A map must
|
||||||
|
preserve the import leaf and target an ordinary direct `--import`; it adds no
|
||||||
|
export input or graph edge. The compilers rewrite only matching imports in the
|
||||||
|
primary source list before merging interfaces, leaving the source qualifier,
|
||||||
|
position, imported interface metadata, and generated/synthetic imports
|
||||||
|
untouched. Executable linking independently walks the full reachable package
|
||||||
|
closure and passes archives, never interfaces. The same path handles an
|
||||||
ordinary package, the production-plus-internal-test variant, the external test
|
ordinary package, the production-plus-internal-test variant, the external test
|
||||||
package and its reused production package, compiler-generated test main, and
|
package and its reused production package, compiler-generated test main, and
|
||||||
the reserved test-support package. Persistent workdirs compare a newly emitted
|
the reserved test-support package. Persistent workdirs compare a newly emitted
|
||||||
@@ -3540,7 +3558,8 @@ The SHA-256 byte input is exactly:
|
|||||||
WW package paths and host paths cannot contain NUL, so that boundary is
|
WW package paths and host paths cannot contain NUL, so that boundary is
|
||||||
unambiguous. Variant and role are present in both the digest input and the
|
unambiguous. Variant and role are present in both the digest input and the
|
||||||
visible locator tag. The digest is only an action-storage address: units still
|
visible locator tag. The digest is only an action-storage address: units still
|
||||||
begin with `//ww:module-reset <complete-path>`, exports begin with
|
begin with `//ww:module-reset <complete-path>` and may append deterministic
|
||||||
|
driver-private resolution comments, while exports begin with
|
||||||
`//ww:module <complete-path>`, compiler imports carry the complete path, and
|
`//ww:module <complete-path>`, compiler imports carry the complete path, and
|
||||||
qualified declarations use it in generated symbols. A selected executable
|
qualified declarations use it in generated symbols. A selected executable
|
||||||
entry retains its intentional bare linker spelling. User-selected `-o`
|
entry retains its intentional bare linker spelling. User-selected `-o`
|
||||||
@@ -3556,9 +3575,10 @@ existing regular `.unit.ww` voucher against the requested complete semantic
|
|||||||
owner before stale-tool invalidation or reuse; a missing voucher is cold state,
|
owner before stale-tool invalidation or reuse; a missing voucher is cold state,
|
||||||
while a malformed, non-regular, or wrong-owner voucher is a pre-tool error.
|
while a malformed, non-regular, or wrong-owner voucher is a pre-tool error.
|
||||||
Thus ordinary preferred-name collisions are resolved, and a digest collision
|
Thus ordinary preferred-name collisions are resolved, and a digest collision
|
||||||
cannot silently alias two live or warm package actions. The workdir format is
|
cannot silently alias two live or warm package actions. That storage slice
|
||||||
now build version 10 and test version 11 so an older flat-layout voucher is
|
introduced build version 10 and test version 11 so an older flat-layout voucher
|
||||||
never accepted as current state.
|
was never accepted as current state. Section 11.16 records the current
|
||||||
|
superseding formats.
|
||||||
|
|
||||||
The persisted semantic owner is deliberately the complete canonical import
|
The persisted semantic owner is deliberately the complete canonical import
|
||||||
path, not the canonical host directory: host location must not enter compiler
|
path, not the canonical host directory: host location must not enter compiler
|
||||||
@@ -3988,6 +4008,206 @@ normalized tool arguments across independent cold Cstage and WWstage work
|
|||||||
roots. Test variants additionally compare their unit/export/archive bytes and
|
roots. Test variants additionally compare their unit/export/archive bytes and
|
||||||
exercise byte-equivalent runtime output in both stages.
|
exercise byte-equivalent runtime output in both stages.
|
||||||
|
|
||||||
|
### 11.16 Implemented manifest-free local vendor-directory imports
|
||||||
|
|
||||||
|
The local package loader now expands only imports parsed from source. For a
|
||||||
|
source package whose resolved lexical route is `<root>/domain/app`, an import
|
||||||
|
of `lib.math` probes these directory packages in order:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<root>/domain/app/vendor/lib/math
|
||||||
|
<root>/domain/vendor/lib/math
|
||||||
|
<root>/vendor/lib/math
|
||||||
|
<ordinary ordered-root lookup for lib/math>
|
||||||
|
```
|
||||||
|
|
||||||
|
The walk stops at that edge's applicable active source root. It never walks an
|
||||||
|
arbitrary filesystem ancestor and never acquires a boundary from another
|
||||||
|
requested product. Each resolution context carries the current package's
|
||||||
|
lexical route and source-root boundary. An explicitly identified root derives
|
||||||
|
the boundary by removing and round-trip validating exactly its dotted identity
|
||||||
|
components. An unbound literal root chooses the first precedence-valid strict
|
||||||
|
ancestor in its own ordered search roots, or the selected directory itself.
|
||||||
|
An ordinary child records the exact root that selected it; a vendored child
|
||||||
|
inherits the parent's boundary. These values are contextual resolution state,
|
||||||
|
not package/action identity.
|
||||||
|
|
||||||
|
Before source scanning, a literal directory whose lexical route is
|
||||||
|
representable below that boundary binds the complete relative dotted identity,
|
||||||
|
including any `vendor` components. A truly rootless literal keeps its reversible
|
||||||
|
`__wwlocal` identity and cannot be coalesced with a same-physical vendored
|
||||||
|
action. Thus selecting a vendored directory directly remains legal without
|
||||||
|
letting product order donate its action identity to or from a source import.
|
||||||
|
|
||||||
|
A candidate shadows outer and ordinary candidates only when its directory
|
||||||
|
contains an observed non-directory name ending in `.ww`. This deliberately
|
||||||
|
includes `_test.ww` and the bare name `.ww`, matching Go's suffix probe; an
|
||||||
|
actual subdirectory named `x.ww` and an unreadable candidate with no observed
|
||||||
|
source do not shadow. Once a candidate is selected, normal package enumeration
|
||||||
|
reports its real errors, including a test-only or otherwise production-empty
|
||||||
|
directory, rather than falling through.
|
||||||
|
|
||||||
|
Source retains only the effective spelling, such as `lib.math`. Selection
|
||||||
|
assigns the target the complete expanded canonical identity, such as
|
||||||
|
`domain.app.vendor.lib.math`, and separately canonicalizes its physical
|
||||||
|
directory. The action key is that expanded identity and canonical directory,
|
||||||
|
plus the existing variant/role. Different physical vendor copies are distinct;
|
||||||
|
different expanded vendor routes remain distinct even when symlinks converge
|
||||||
|
on one physical directory; repeated resolutions of the same pair reuse one
|
||||||
|
action. Source spelling, importer context, product order, output name, declared
|
||||||
|
leaf, and allocation/discovery order do not enter that key.
|
||||||
|
|
||||||
|
The final exact non-terminal dotted component named `vendor` determines the
|
||||||
|
effective suffix and owner. A nested path uses its final `vendor`; `vendorx` is
|
||||||
|
ordinary; and a path ending exactly in `vendor` names an ordinary package.
|
||||||
|
After resolution and action interning, every source edge first performs vendor
|
||||||
|
visibility and then verifies source spelling. An allowed importer that directly
|
||||||
|
spells an expanded path receives the source-position diagnostic:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<expanded-path> must be imported as <effective-suffix>
|
||||||
|
```
|
||||||
|
|
||||||
|
An outside importer receives, with visibility taking diagnostic precedence:
|
||||||
|
|
||||||
|
```text
|
||||||
|
use of vendored package not allowed
|
||||||
|
```
|
||||||
|
|
||||||
|
Directly selecting a directory below `vendor` remains legal because a command
|
||||||
|
root is not a source import.
|
||||||
|
|
||||||
|
Visibility derives the owner from the current edge's resolved lexical vendor
|
||||||
|
route and only then canonicalizes that owner physically. It never strips
|
||||||
|
components from the already-canonical target, whose symlink shape may have a
|
||||||
|
different depth. The importer is its canonical physical directory. Equality or
|
||||||
|
a real `/`-component descendant is allowed; a raw string prefix is not.
|
||||||
|
Consequently an importer reached through a symlink is judged by its physical
|
||||||
|
containment, while a vendor target symlinked to a differently shaped physical
|
||||||
|
directory keeps the owner established by the lexical vendor route. The check
|
||||||
|
runs after intern/reuse on every source edge, so an action loaded by an allowed
|
||||||
|
importer cannot authorize a later forbidden spelling or importer.
|
||||||
|
|
||||||
|
Cstage represents the resolution with `route` and `source_root` in
|
||||||
|
`sepcontext`, the bounded `sep_resolve_source_import` and vendor helpers, stable
|
||||||
|
integer action references in typed source bindings, and transient
|
||||||
|
`{package,context}` loader children. WWstage uses the isomorphic `sepcontext`,
|
||||||
|
`sepresolvesourceimport`, typed bindings, and transient child vector. The
|
||||||
|
package-global dependency set remains the sorted/deduplicated canonical action
|
||||||
|
set used for compilation and linking; contextual child traversal never creates
|
||||||
|
a second universe or contaminates action identity. All new storage grows with
|
||||||
|
checked allocation, and package/context vectors continue to expose only stable
|
||||||
|
integer references across growth.
|
||||||
|
|
||||||
|
A compile still receives one sorted, deduplicated
|
||||||
|
`--import <expanded-path> <dependency.wwi>` triple for each direct dependency.
|
||||||
|
When source spelling differs, the driver additionally supplies the sorted,
|
||||||
|
unique auxiliary mapping:
|
||||||
|
|
||||||
|
```text
|
||||||
|
--import-map <source-spelling> <expanded-path>
|
||||||
|
```
|
||||||
|
|
||||||
|
Both compilers require the map target to be an existing direct `--import`,
|
||||||
|
require source keys to be sorted and unique, require the final import leaf to
|
||||||
|
remain unchanged, and require a matching import in the primary source input.
|
||||||
|
After parsing only that primary input and before prepending imported interfaces,
|
||||||
|
the compiler replaces its semantic import key while preserving its source leaf,
|
||||||
|
position, and spelling bytes. Imported `.wwi` nodes and synthetic sources are
|
||||||
|
never rewritten. Thus the map adds no dependency or export input, expanded
|
||||||
|
identity flows into self-contained `.wwi` ownership and symbols, and direct
|
||||||
|
exports still have no transitive leakage. Linking remains independent: each
|
||||||
|
executable consumes its root archive and complete reachable archive closure,
|
||||||
|
never `.wwi` or import-map inputs.
|
||||||
|
|
||||||
|
Persistent units record resolution identity in ignored, deterministic comments:
|
||||||
|
|
||||||
|
```text
|
||||||
|
//ww:vendor-dir <hex-canonical-package-directory>
|
||||||
|
//ww:import-map <source-spelling> <expanded-path> <hex-canonical-target-directory>
|
||||||
|
```
|
||||||
|
|
||||||
|
Hex encoding keeps arbitrary legal filesystem bytes inside one comment. The
|
||||||
|
metadata makes ordinary-to-vendor changes and vendor symlink retargeting
|
||||||
|
invalidate the importer even when its source bytes and both already-warm export
|
||||||
|
bytes happen to match. It contains no dependency body and does not alter source
|
||||||
|
positions. The current workdir formats are build 13 and test 12. An equivalent
|
||||||
|
warm request remains a package-production no-op; an export change propagates
|
||||||
|
only through ordinary direct-export comparison.
|
||||||
|
|
||||||
|
Production, same-package internal-test, external `_test`, and real source
|
||||||
|
imports inside the test-support package all use this source-edge resolver.
|
||||||
|
Generated-main-to-variant, generated-main-to-support, and coordinator product
|
||||||
|
edges remain synthetic and receive no map or retroactive source legality.
|
||||||
|
`cmd/wwtest` remains a dispatcher. `internal/wwpackage` still discovers and
|
||||||
|
classifies source groups and submits one command-global union; it performs no
|
||||||
|
vendor resolution. Its only related state change is transactional cleanup: if
|
||||||
|
it created a cold hashed request workdir and the driver rejects before a tool
|
||||||
|
stamp exists, it removes that empty request directory.
|
||||||
|
|
||||||
|
Resolution, identity collision checks, contextual legality, dependency-failure
|
||||||
|
propagation, cycles, command kind, publication paths, and action closures all
|
||||||
|
finish before cold scratch acquisition, product status removal, stale-voucher
|
||||||
|
invalidation, tool identity records, producer execution, publication, or
|
||||||
|
runtime execution. A request whose every product is already invalid returns
|
||||||
|
without acquiring scratch; independent valid sibling products retain the
|
||||||
|
existing isolated-build behavior. A forbidden warm request leaves committed
|
||||||
|
vouchers, status files, tool records, and products byte-unchanged.
|
||||||
|
|
||||||
|
This is only manifest-free local source-tree behavior. It does not implement
|
||||||
|
Go modules, module vendor mode, `go.mod`, `vendor/modules.txt`, importcfg, build
|
||||||
|
IDs, a package database, a CAS, registry access, or network lookup, and it is
|
||||||
|
separate from the future locked vendor store in section 4.6.
|
||||||
|
|
||||||
|
The rule follows the pinned official Go 1.26.5 source at commit
|
||||||
|
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||||
|
|
||||||
|
- source-derived imports are expanded and the expanded vendor path becomes the
|
||||||
|
canonical import path
|
||||||
|
([`pkg.go`, lines 658–668](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L658-L668));
|
||||||
|
- resolution and canonical package-cache reuse occur before the contextual
|
||||||
|
internal/vendor checks, which still run with the importing position on every
|
||||||
|
source edge
|
||||||
|
([`pkg.go`, lines 722–796](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L722-L796));
|
||||||
|
- vendor lookup walks importer ancestors nearest-first to the applicable root,
|
||||||
|
requires a source-bearing directory, and records the expanded identity
|
||||||
|
([`pkg.go`, lines 1213–1263](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1213-L1263));
|
||||||
|
- the source-bearing probe accepts any non-directory `.go` suffix and ignores
|
||||||
|
directory-read failure
|
||||||
|
([`pkg.go`, lines 1418–1429](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1418-L1429));
|
||||||
|
- command roots remain legal, while an expanded source spelling must use its
|
||||||
|
effective suffix
|
||||||
|
([`pkg.go`, lines 1593–1617](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1593-L1617));
|
||||||
|
- vendor ownership uses component-aware physical containment and symlink
|
||||||
|
expansion
|
||||||
|
([`pkg.go`, lines 1620–1667](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1620-L1667));
|
||||||
|
- the final exact non-terminal `vendor` component controls the rule
|
||||||
|
([`pkg.go`, lines 1670–1688](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1670-L1688));
|
||||||
|
- production, internal-test, and external-test imports retain distinct raw
|
||||||
|
source spellings/positions but each resolves canonically, while generated
|
||||||
|
test-main wiring is synthetic
|
||||||
|
([`test.go`, lines 85–173](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L85-L173),
|
||||||
|
[`test.go`, lines 175–266](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L175-L266),
|
||||||
|
[`test.go`, lines 272–373](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L272-L373),
|
||||||
|
[`go/build/build.go`, lines 415–493](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L415-L493)); and
|
||||||
|
- action caching consumes the already-resolved package graph, while output
|
||||||
|
publication remains a later concern
|
||||||
|
([`action.go`, lines 437–455](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L455),
|
||||||
|
[`action.go`, lines 628–659](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L659),
|
||||||
|
[`build.go`, lines 470–558](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L470-L558)).
|
||||||
|
|
||||||
|
The native observer `vendor_directory_import_resolution` generates every source
|
||||||
|
tree temporarily and runs both stages from independent cold work roots. It
|
||||||
|
proves nearest/outer/root/ordinary selection and the active-root boundary;
|
||||||
|
source-bearing versus empty candidates; distinct and reused actions, including
|
||||||
|
same-physical symlink targets; exact spelling/visibility diagnostics and
|
||||||
|
product-order reversal; final-component, terminal-name, component-prefix, and
|
||||||
|
symlink behavior; direct vendored roots; production/internal/external/support
|
||||||
|
source imports and synthetic-main isolation; owner-only sorted units, exact
|
||||||
|
direct exports/import maps, archive-only link closures, runtime output,
|
||||||
|
stage-equal normalized argv/artifacts/binaries, warm no-op production, and cold
|
||||||
|
and warm rejection-state preservation.
|
||||||
|
|
||||||
## 12. Candidate architectures and hard-gate decision
|
## 12. Candidate architectures and hard-gate decision
|
||||||
|
|
||||||
Five candidates were developed as coherent systems, not as feature bins.
|
Five candidates were developed as coherent systems, not as feature bins.
|
||||||
|
|||||||
@@ -435,6 +435,20 @@ fn normalizedtrace(trace: str, work: str, output: str) str = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn hexbytes(value: str) str = {
|
||||||
|
let out: []u8 = alloc([], (value.len * 2 + 1): u64)!;
|
||||||
|
let digits: str = "0123456789abcdef";
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < value.len) {
|
||||||
|
let high: i32 = (value[i] / 16u8): i32;
|
||||||
|
let low: i32 = (value[i] % 16u8): i32;
|
||||||
|
append(out, digits[high]);
|
||||||
|
append(out, digits[low]);
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
return strings.frombytes(out);
|
||||||
|
};
|
||||||
|
|
||||||
@test fn deterministic_routing_and_filters() void = {
|
@test fn deterministic_routing_and_filters() void = {
|
||||||
let root: str = fresh();
|
let root: str = fresh();
|
||||||
let target: str = packagepath("routing");
|
let target: str = packagepath("routing");
|
||||||
@@ -5639,7 +5653,932 @@ fn runtimepath(relative: str) str = {
|
|||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
|
|
||||||
// The package-level -o contract: -c -o names the single package's
|
@test fn vendor_directory_import_resolution() void = {
|
||||||
|
let root: str = fresh();
|
||||||
|
let source: str = strings.concat(root, "/source");
|
||||||
|
let runtime: str = strings.concat(root, "/runtime");
|
||||||
|
let tools: str = strings.concat(root, "/tools");
|
||||||
|
let leaf: str = strings.concat(source, "/lib/leaf");
|
||||||
|
let nearest: str = strings.concat(source,
|
||||||
|
"/domain/app/vendor/lib/math");
|
||||||
|
let outer: str = strings.concat(source, "/domain/vendor/lib/math");
|
||||||
|
let rootvendor: str = strings.concat(source, "/vendor/lib/math");
|
||||||
|
let ordinary: str = strings.concat(source, "/lib/math");
|
||||||
|
let client: str = strings.concat(source, "/domain/app/client");
|
||||||
|
let one: str = strings.concat(source, "/domain/app/one");
|
||||||
|
let two: str = strings.concat(source, "/domain/app/two");
|
||||||
|
let outerclient: str = strings.concat(source, "/domain/other/client");
|
||||||
|
let rootclient: str = strings.concat(source, "/other/client");
|
||||||
|
let emptyclient: str = strings.concat(source, "/domain/empty/client");
|
||||||
|
let testonlyclient: str = strings.concat(source,
|
||||||
|
"/domain/testonly/client");
|
||||||
|
let bareclient: str = strings.concat(source, "/domain/bare/client");
|
||||||
|
let outsider: str = strings.concat(source, "/outsider");
|
||||||
|
let prefixclient: str = strings.concat(source, "/domainx/client");
|
||||||
|
let directclient: str = strings.concat(source, "/domain/app/direct");
|
||||||
|
let directhijack: str = strings.concat(directclient,
|
||||||
|
"/vendor/domain/app/vendor/lib/math");
|
||||||
|
let nested: str = strings.concat(source,
|
||||||
|
"/nested/owner/vendor/outer/vendor/lib/math");
|
||||||
|
let nestedclient: str = strings.concat(source,
|
||||||
|
"/nested/owner/vendor/outer/client");
|
||||||
|
let ordinaryclient: str = strings.concat(source, "/ordinaryclient");
|
||||||
|
let vendorx: str = strings.concat(source, "/vendorx/lib/math");
|
||||||
|
let terminalvendor: str = strings.concat(source, "/terminal/vendor");
|
||||||
|
let physicalclient: str = strings.concat(source,
|
||||||
|
"/domain/app/physicalclient");
|
||||||
|
let aliasclient: str = strings.concat(source, "/aliasclient");
|
||||||
|
let physicaldirect: str = strings.concat(source,
|
||||||
|
"/domain/app/physicaldirect");
|
||||||
|
let aliasdirect: str = strings.concat(source, "/aliasdirect");
|
||||||
|
let escapephysical: str = strings.concat(root, "/escape/client");
|
||||||
|
let escapeclient: str = strings.concat(source, "/domain/app/escape");
|
||||||
|
let shapeclient: str = strings.concat(source, "/shape/owner/client");
|
||||||
|
let shapevendor: str = strings.concat(source,
|
||||||
|
"/shape/owner/vendor/lib/math");
|
||||||
|
let shapevault: str = strings.concat(root, "/vault/deep/different");
|
||||||
|
let testclient: str = strings.concat(source, "/domain/app/testclient");
|
||||||
|
let testbad: str = strings.concat(source, "/testbad");
|
||||||
|
let supportcase: str = strings.concat(source, "/supportcase");
|
||||||
|
let toolsource: str = strings.concat(root, "/toolsource");
|
||||||
|
let toolsupport: str = strings.concat(toolsource, "/test");
|
||||||
|
let toolvendor: str = strings.concat(toolsource,
|
||||||
|
"/test/vendor/fnmatch");
|
||||||
|
let vendortest: str = strings.concat(source,
|
||||||
|
"/literal/owner/vendor/lib/math");
|
||||||
|
let nestedvendortest: str = strings.concat(vendortest,
|
||||||
|
"/vendor/lib/math");
|
||||||
|
let alpha: str = strings.concat(source, "/alpha/client");
|
||||||
|
let beta: str = strings.concat(source, "/beta/client");
|
||||||
|
let alphavendor: str = strings.concat(alpha, "/vendor/lib/math");
|
||||||
|
let betavendor: str = strings.concat(beta, "/vendor/lib/math");
|
||||||
|
let pairvault: str = strings.concat(root, "/pair-vault/math");
|
||||||
|
let boundary: str = strings.concat(root, "/boundary");
|
||||||
|
let active: str = strings.concat(boundary, "/active");
|
||||||
|
let boundaryclient: str = strings.concat(active, "/domain/app");
|
||||||
|
let boundaryordinary: str = strings.concat(active, "/lib/math");
|
||||||
|
let boundaryabove: str = strings.concat(boundary, "/vendor/lib/math");
|
||||||
|
let cleanroot: str = strings.concat(root, "/clean");
|
||||||
|
let cleanclient: str = strings.concat(cleanroot, "/domain/app");
|
||||||
|
let cleanordinary: str = strings.concat(cleanroot, "/lib/math");
|
||||||
|
|
||||||
|
let dirs: []str = [leaf, nearest, outer, rootvendor, ordinary, client,
|
||||||
|
one, two, outerclient, rootclient, emptyclient,
|
||||||
|
strings.concat(emptyclient, "/vendor/lib/math/fake.ww"),
|
||||||
|
testonlyclient, strings.concat(testonlyclient, "/vendor/lib/math"),
|
||||||
|
bareclient, strings.concat(bareclient, "/vendor/lib/math"), outsider,
|
||||||
|
prefixclient, directclient, directhijack, nested, nestedclient,
|
||||||
|
ordinaryclient,
|
||||||
|
vendorx, terminalvendor, physicalclient, physicaldirect, escapephysical,
|
||||||
|
shapeclient, strings.concat(source, "/shape/owner/vendor/lib"),
|
||||||
|
shapevault, testclient, testbad, supportcase, toolsupport,
|
||||||
|
toolvendor, vendortest, nestedvendortest,
|
||||||
|
alpha, beta, strings.concat(alpha, "/vendor/lib"),
|
||||||
|
strings.concat(beta, "/vendor/lib"), pairvault, boundaryclient,
|
||||||
|
boundaryordinary, boundaryabove, cleanclient, cleanordinary,
|
||||||
|
runtime, tools];
|
||||||
|
let di: i32 = 0;
|
||||||
|
for (di < dirs.len) { mkdirall(dirs[di]); di += 1; };
|
||||||
|
writefile(strings.concat(runtime, "/libwwrt.a"),
|
||||||
|
readfile(strings.concat(repo(), "/out/lib/libwwrt.a")));
|
||||||
|
|
||||||
|
writefile(strings.concat(leaf, "/leaf.ww"), strings.concat(
|
||||||
|
"package leaf;\n",
|
||||||
|
"export fn value() i32 = { return 1; };\n"));
|
||||||
|
let neara: str = strings.concat(
|
||||||
|
"package math;\nimport lib.leaf;\n",
|
||||||
|
"export fn value() i32 = { return leaf.value() + 10; };\n");
|
||||||
|
let nearz: str = strings.concat(
|
||||||
|
"package math;\n",
|
||||||
|
"fn private_value() i32 = { return 99; };\n");
|
||||||
|
writefile(strings.concat(nearest, "/a.ww"), neara);
|
||||||
|
writefile(strings.concat(nearest, "/z.ww"), nearz);
|
||||||
|
writefile(strings.concat(outer, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 22; };\n");
|
||||||
|
writefile(strings.concat(rootvendor, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 33; };\n");
|
||||||
|
writefile(strings.concat(ordinary, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 44; };\n");
|
||||||
|
let mainbody: str = strings.concat(
|
||||||
|
"package main;\nimport lib.math;\n",
|
||||||
|
"fn main() i32 = { return math.value(); };\n");
|
||||||
|
writefile(strings.concat(client, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(one, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(two, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(outerclient, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(rootclient, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(emptyclient, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(testonlyclient, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(testonlyclient,
|
||||||
|
"/vendor/lib/math/only_test.ww"),
|
||||||
|
"package math;\n@test fn only() void = { assert(true); };\n");
|
||||||
|
writefile(strings.concat(bareclient, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(bareclient, "/vendor/lib/math/.ww"),
|
||||||
|
"package math;\n");
|
||||||
|
let expanded: str = "domain.app.vendor.lib.math";
|
||||||
|
let directbody: str = strings.concat(
|
||||||
|
"package main;\nimport ", expanded, ";\n",
|
||||||
|
"fn main() i32 = { return math.value(); };\n");
|
||||||
|
writefile(strings.concat(outsider, "/main.ww"), directbody);
|
||||||
|
writefile(strings.concat(prefixclient, "/main.ww"), directbody);
|
||||||
|
writefile(strings.concat(directclient, "/main.ww"), directbody);
|
||||||
|
writefile(strings.concat(directhijack, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 99; };\n");
|
||||||
|
|
||||||
|
writefile(strings.concat(nested, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 77; };\n");
|
||||||
|
writefile(strings.concat(nestedclient, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(vendorx, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 4; };\n");
|
||||||
|
writefile(strings.concat(terminalvendor, "/vendor.ww"),
|
||||||
|
"package vendor;\nexport fn value() i32 = { return 8; };\n");
|
||||||
|
writefile(strings.concat(ordinaryclient, "/main.ww"), strings.concat(
|
||||||
|
"package main;\nimport vendorx.lib.math;\nimport terminal.vendor;\n",
|
||||||
|
"fn main() i32 = { return math.value() + vendor.value(); };\n"));
|
||||||
|
|
||||||
|
writefile(strings.concat(physicalclient, "/main.ww"), mainbody);
|
||||||
|
assert(os.symlink(physicalclient, aliasclient) == 0);
|
||||||
|
writefile(strings.concat(physicaldirect, "/main.ww"), directbody);
|
||||||
|
assert(os.symlink(physicaldirect, aliasdirect) == 0);
|
||||||
|
writefile(strings.concat(escapephysical, "/main.ww"), mainbody);
|
||||||
|
assert(os.symlink(escapephysical, escapeclient) == 0);
|
||||||
|
writefile(strings.concat(shapevault, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 17; };\n");
|
||||||
|
assert(os.symlink(shapevault, shapevendor) == 0);
|
||||||
|
writefile(strings.concat(shapeclient, "/main.ww"), mainbody);
|
||||||
|
|
||||||
|
writefile(strings.concat(testclient, "/testclient.ww"), strings.concat(
|
||||||
|
"package testclient;\nimport lib.math;\n",
|
||||||
|
"export fn value() i32 = { return math.value(); };\n"));
|
||||||
|
writefile(strings.concat(testclient, "/same_test.ww"), strings.concat(
|
||||||
|
"package testclient;\nimport lib.math;\n",
|
||||||
|
"@test fn same_vendor() void = { assert(math.value() == 11); };\n"));
|
||||||
|
writefile(strings.concat(testclient, "/external_test.ww"), strings.concat(
|
||||||
|
"package testclient_test;\nimport lib.math;\n",
|
||||||
|
"import domain.app.testclient;\n",
|
||||||
|
"@test fn external_vendor() void = {\n",
|
||||||
|
" assert(math.value() + testclient.value() == 22);\n};\n"));
|
||||||
|
writefile(strings.concat(testbad, "/testbad.ww"),
|
||||||
|
"package testbad;\nexport fn value() i32 = { return 1; };\n");
|
||||||
|
writefile(strings.concat(testbad, "/same_test.ww"), strings.concat(
|
||||||
|
"package testbad;\nimport ", expanded, ";\n",
|
||||||
|
"@test fn forbidden_vendor() void = { assert(false); };\n"));
|
||||||
|
writefile(strings.concat(supportcase, "/supportcase.ww"),
|
||||||
|
"package supportcase;\nexport fn value() i32 = { return 1; };\n");
|
||||||
|
writefile(strings.concat(supportcase, "/supportcase_test.ww"),
|
||||||
|
"package supportcase;\n@test fn support_runs() void = { assert(value() == 1); };\n");
|
||||||
|
writefile(strings.concat(toolsupport, "/run.ww"),
|
||||||
|
readfile(strings.concat(repo(), "/lib/test/run.ww")));
|
||||||
|
writefile(strings.concat(toolvendor, "/fnmatch.ww"),
|
||||||
|
readfile(strings.concat(repo(), "/lib/fnmatch/fnmatch.ww")));
|
||||||
|
let tooldeps: []str = ["ascii", "bytes", "encoding", "os", "rt",
|
||||||
|
"strings", "time", "types"];
|
||||||
|
di = 0;
|
||||||
|
for (di < tooldeps.len) {
|
||||||
|
assert(os.symlink(strings.concat(repo(), "/lib/", tooldeps[di]),
|
||||||
|
strings.concat(toolsource, "/", tooldeps[di])) == 0);
|
||||||
|
di += 1;
|
||||||
|
};
|
||||||
|
writefile(strings.concat(vendortest, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 7; };\n");
|
||||||
|
writefile(strings.concat(vendortest, "/math_test.ww"), strings.concat(
|
||||||
|
"package math_test;\nimport lib.math;\n",
|
||||||
|
"@test fn nested_external_vendor() void = {\n",
|
||||||
|
" assert(math.value() == 9);\n};\n"));
|
||||||
|
writefile(strings.concat(nestedvendortest, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 9; };\n");
|
||||||
|
|
||||||
|
writefile(strings.concat(pairvault, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 61; };\n");
|
||||||
|
assert(os.symlink(pairvault, alphavendor) == 0);
|
||||||
|
assert(os.symlink(pairvault, betavendor) == 0);
|
||||||
|
writefile(strings.concat(alpha, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(beta, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(boundaryclient, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(boundaryordinary, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 55; };\n");
|
||||||
|
writefile(strings.concat(boundaryabove, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 66; };\n");
|
||||||
|
writefile(strings.concat(cleanclient, "/main.ww"), mainbody);
|
||||||
|
writefile(strings.concat(cleanordinary, "/math.ww"),
|
||||||
|
"package math;\nexport fn value() i32 = { return 44; };\n");
|
||||||
|
|
||||||
|
let compilerwrapper: str = strings.concat(tools, "/w6c.sh");
|
||||||
|
let assemblerwrapper: str = strings.concat(tools, "/w6a.sh");
|
||||||
|
let linkerwrapper: str = strings.concat(tools, "/w6l.sh");
|
||||||
|
writeexecutable(compilerwrapper, strings.concat(
|
||||||
|
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_COMPILER_TRACE\"\n",
|
||||||
|
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||||||
|
"\"$WW_VENDOR_COMPILER_TRACE\"; done\n",
|
||||||
|
"printf '\\n' >> \"$WW_VENDOR_COMPILER_TRACE\"\n",
|
||||||
|
"exec \"$WW_VENDOR_REAL_COMPILER\" \"$@\"\n"));
|
||||||
|
writeexecutable(assemblerwrapper, strings.concat(
|
||||||
|
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_ASSEMBLER_TRACE\"\n",
|
||||||
|
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||||||
|
"\"$WW_VENDOR_ASSEMBLER_TRACE\"; done\n",
|
||||||
|
"printf '\\n' >> \"$WW_VENDOR_ASSEMBLER_TRACE\"\n",
|
||||||
|
"exec \"$WW_VENDOR_REAL_ASSEMBLER\" \"$@\"\n"));
|
||||||
|
writeexecutable(linkerwrapper, strings.concat(
|
||||||
|
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_LINKER_TRACE\"\n",
|
||||||
|
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||||||
|
"\"$WW_VENDOR_LINKER_TRACE\"; done\n",
|
||||||
|
"printf '\\n' >> \"$WW_VENDOR_LINKER_TRACE\"\n",
|
||||||
|
"exec \"$WW_VENDOR_REAL_LINKER\" \"$@\"\n"));
|
||||||
|
|
||||||
|
let stages: []str = ["ww", "ww_ww"];
|
||||||
|
let compilers: []str = ["w6c", "w6c_ww"];
|
||||||
|
let assemblers: []str = ["w6a", "w6a_ww"];
|
||||||
|
let linkers: []str = ["w6l", "w6l_ww"];
|
||||||
|
let works: []str = [strings.concat(root, "/c-work"),
|
||||||
|
strings.concat(root, "/ww-work")];
|
||||||
|
let bins: []str = [strings.concat(root, "/c-bin"),
|
||||||
|
strings.concat(root, "/ww-bin")];
|
||||||
|
let compilertraces: []str = [strings.concat(root, "/c-compiler"),
|
||||||
|
strings.concat(root, "/ww-compiler")];
|
||||||
|
let assemblertraces: []str = [strings.concat(root, "/c-assembler"),
|
||||||
|
strings.concat(root, "/ww-assembler")];
|
||||||
|
let linkertraces: []str = [strings.concat(root, "/c-linker"),
|
||||||
|
strings.concat(root, "/ww-linker")];
|
||||||
|
let coreartifacts: []str = ["lib.leaf", expanded, "domain.app.client"];
|
||||||
|
let coresuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||||||
|
let references: []str = alloc([],
|
||||||
|
(coreartifacts.len * coresuffixes.len): u64)!;
|
||||||
|
let ri: i32 = 0;
|
||||||
|
for (ri < coreartifacts.len * coresuffixes.len) {
|
||||||
|
append(references, ""); ri += 1;
|
||||||
|
};
|
||||||
|
let referencebin: str = "";
|
||||||
|
let referencecompiler: str = "";
|
||||||
|
let referenceassembler: str = "";
|
||||||
|
let referencelinker: str = "";
|
||||||
|
let referencedirectdiag: str = "";
|
||||||
|
let referenceoutsidediag: str = "";
|
||||||
|
let combinedrefs: []str = ["", "", "", "", "", ""];
|
||||||
|
let referencetestout: str = "";
|
||||||
|
let referencetesterr: str = "";
|
||||||
|
let testrefs: []str = ["", "", "", "", "", ""];
|
||||||
|
let referencesupportout: str = "";
|
||||||
|
let supportrefs: []str = ["", "", ""];
|
||||||
|
let referenceorderdiag: str = "";
|
||||||
|
let referenceescapeddiag: str = "";
|
||||||
|
let referencealiasdirectdiag: str = "";
|
||||||
|
let referenceprefixdiag: str = "";
|
||||||
|
let candidate_diag_refs: []str = ["", ""];
|
||||||
|
let supplementalrefs: []str = ["", "", "", "", "", "", "", "",
|
||||||
|
""];
|
||||||
|
let referencevendortestout: str = "";
|
||||||
|
let referencevendortesterr: str = "";
|
||||||
|
let referencetestbaddiag: str = "";
|
||||||
|
let baseenv: []str = os.getenvs();
|
||||||
|
let si: i32 = 0;
|
||||||
|
for (si < stages.len) {
|
||||||
|
assert(os.mkdir(works[si], 448i32) == 0);
|
||||||
|
writefile(compilertraces[si], "");
|
||||||
|
writefile(assemblertraces[si], "");
|
||||||
|
writefile(linkertraces[si], "");
|
||||||
|
let env: []str = alloc([], (baseenv.len + 11): u64)!;
|
||||||
|
let ei: i32 = 0;
|
||||||
|
for (ei < baseenv.len) {
|
||||||
|
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei], "WW_LIB=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei], "WW_SRCLIB=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_VENDOR_COMPILER_TRACE=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_VENDOR_ASSEMBLER_TRACE=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_VENDOR_LINKER_TRACE=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_VENDOR_REAL_COMPILER=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_VENDOR_REAL_ASSEMBLER=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_VENDOR_REAL_LINKER=")) {
|
||||||
|
append(env, baseenv[ei]);
|
||||||
|
};
|
||||||
|
ei += 1;
|
||||||
|
};
|
||||||
|
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||||||
|
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||||||
|
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||||||
|
append(env, strings.concat("WW_LIB=", runtime));
|
||||||
|
append(env, strings.concat("WW_VENDOR_COMPILER_TRACE=",
|
||||||
|
compilertraces[si]));
|
||||||
|
append(env, strings.concat("WW_VENDOR_ASSEMBLER_TRACE=",
|
||||||
|
assemblertraces[si]));
|
||||||
|
append(env, strings.concat("WW_VENDOR_LINKER_TRACE=",
|
||||||
|
linkertraces[si]));
|
||||||
|
append(env, strings.concat("WW_VENDOR_REAL_COMPILER=",
|
||||||
|
driver(compilers[si])));
|
||||||
|
append(env, strings.concat("WW_VENDOR_REAL_ASSEMBLER=",
|
||||||
|
driver(assemblers[si])));
|
||||||
|
append(env, strings.concat("WW_VENDOR_REAL_LINKER=",
|
||||||
|
driver(linkers[si])));
|
||||||
|
|
||||||
|
let av: []str = [driver(stages[si]), "build", "-w", works[si],
|
||||||
|
"-I", source, "-o", bins[si], client];
|
||||||
|
let out: commandout;
|
||||||
|
runcommandenv(root, strings.concat("vendor-cold-", stages[si]), av,
|
||||||
|
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||||
|
let wantnear: str = strings.concat(
|
||||||
|
"//ww:module-reset ", expanded, "\n", neara, "\n",
|
||||||
|
"//ww:module-reset ", expanded, "\n", nearz, "\n",
|
||||||
|
"//ww:vendor-dir ", hexbytes(nearest), "\n");
|
||||||
|
assert(same(readfile(strings.concat(works[si], "/", expanded,
|
||||||
|
".unit.ww")), wantnear));
|
||||||
|
let clientunit: str = readfile(strings.concat(works[si],
|
||||||
|
"/domain.app.client.unit.ww"));
|
||||||
|
assert(has(clientunit, strings.concat("//ww:import-map lib.math ",
|
||||||
|
expanded, " ", hexbytes(nearest), "\n")));
|
||||||
|
let ai: i32 = 0;
|
||||||
|
for (ai < coreartifacts.len) {
|
||||||
|
let xi: i32 = 0;
|
||||||
|
for (xi < coresuffixes.len) {
|
||||||
|
let bytes: str = readfile(strings.concat(works[si], "/",
|
||||||
|
coreartifacts[ai], coresuffixes[xi]));
|
||||||
|
let index: i32 = ai * coresuffixes.len + xi;
|
||||||
|
if (si == 0) { references[index] = strings.dup(bytes); }
|
||||||
|
else { assert(same(references[index], bytes)); };
|
||||||
|
xi += 1;
|
||||||
|
};
|
||||||
|
ai += 1;
|
||||||
|
};
|
||||||
|
let ctrace: str = readfile(compilertraces[si]);
|
||||||
|
let atrace: str = readfile(assemblertraces[si]);
|
||||||
|
let ltrace: str = readfile(linkertraces[si]);
|
||||||
|
assert(occurrences(ctrace, "\n") == 3);
|
||||||
|
assert(occurrences(atrace, "\n") == 3);
|
||||||
|
assert(occurrences(ltrace, "\n") == 1);
|
||||||
|
let nearline: str = linecontaining(ctrace,
|
||||||
|
strings.concat(expanded, ".unit.new"));
|
||||||
|
assert(occurrences(nearline, "<--import><lib.leaf>") == 1);
|
||||||
|
assert(!has(nearline, "domain.app.client.wwi"));
|
||||||
|
let clientline: str = linecontaining(ctrace,
|
||||||
|
"domain.app.client.unit.new");
|
||||||
|
assert(occurrences(clientline, strings.concat("<--import><", expanded,
|
||||||
|
"><", works[si], "/", expanded, ".wwi>")) == 1);
|
||||||
|
assert(occurrences(clientline, strings.concat(
|
||||||
|
"<--import-map><lib.math><", expanded, ">")) == 1);
|
||||||
|
assert(!has(clientline, "lib.leaf.wwi"));
|
||||||
|
assert(occurrences(ltrace, strings.concat("<", works[si],
|
||||||
|
"/domain.app.client.a>")) == 1);
|
||||||
|
assert(occurrences(ltrace, strings.concat("<", works[si], "/",
|
||||||
|
expanded, ".a>")) == 1);
|
||||||
|
assert(occurrences(ltrace, strings.concat("<", works[si],
|
||||||
|
"/lib.leaf.a>")) == 1);
|
||||||
|
assert(!has(ltrace, ".wwi>"));
|
||||||
|
let nc: str = normalizedtrace(ctrace,
|
||||||
|
strings.concat(works[si], "/"), bins[si]);
|
||||||
|
let na: str = normalizedtrace(atrace,
|
||||||
|
strings.concat(works[si], "/"), bins[si]);
|
||||||
|
let nl: str = normalizedtrace(ltrace,
|
||||||
|
strings.concat(works[si], "/"), bins[si]);
|
||||||
|
if (si == 0) {
|
||||||
|
referencecompiler = strings.dup(nc);
|
||||||
|
referenceassembler = strings.dup(na);
|
||||||
|
referencelinker = strings.dup(nl);
|
||||||
|
referencebin = strings.dup(readfile(bins[si]));
|
||||||
|
} else {
|
||||||
|
assert(same(referencecompiler, nc));
|
||||||
|
assert(same(referenceassembler, na));
|
||||||
|
assert(same(referencelinker, nl));
|
||||||
|
assert(same(referencebin, readfile(bins[si])));
|
||||||
|
};
|
||||||
|
let runav: []str = [bins[si]];
|
||||||
|
runcommand(root, strings.concat("vendor-run-", stages[si]), runav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 11);
|
||||||
|
|
||||||
|
let voucher: str = readfile(strings.concat(works[si], "/",
|
||||||
|
expanded, ".unit.ww"));
|
||||||
|
let toolww: str = readfile(strings.concat(works[si], "/.wwtool.ww"));
|
||||||
|
let toolc: str = readfile(strings.concat(works[si], "/.wwtool.w6c"));
|
||||||
|
let toola: str = readfile(strings.concat(works[si], "/.wwtool.w6a"));
|
||||||
|
let stamp: str = readfile(strings.concat(works[si], "/.wwtool.stamp"));
|
||||||
|
rewritefile(compilertraces[si], "");
|
||||||
|
rewritefile(assemblertraces[si], "");
|
||||||
|
rewritefile(linkertraces[si], "");
|
||||||
|
runcommandenv(root, strings.concat("vendor-warm-", stages[si]), av,
|
||||||
|
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(readfile(compilertraces[si]).len == 0);
|
||||||
|
assert(readfile(assemblertraces[si]).len == 0);
|
||||||
|
assert(occurrences(readfile(linkertraces[si]), "\n") == 1);
|
||||||
|
|
||||||
|
rewritefile(compilertraces[si], "");
|
||||||
|
rewritefile(assemblertraces[si], "");
|
||||||
|
rewritefile(linkertraces[si], "");
|
||||||
|
let badout: str = strings.concat(root, "/warm-bad-", stages[si]);
|
||||||
|
let badav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||||||
|
"-I", source, "-o", badout, outsider];
|
||||||
|
runcommandenv(root, strings.concat("vendor-warm-bad-", stages[si]),
|
||||||
|
badav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
let outsidediag: str = strings.concat(outsider,
|
||||||
|
"/main.ww:2:1: error: use of vendored package not allowed\n");
|
||||||
|
assert(same(out.stderr, outsidediag));
|
||||||
|
if (si == 0) { referenceoutsidediag = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(referenceoutsidediag, out.stderr)); };
|
||||||
|
assert(readfile(compilertraces[si]).len == 0);
|
||||||
|
assert(readfile(assemblertraces[si]).len == 0);
|
||||||
|
assert(readfile(linkertraces[si]).len == 0);
|
||||||
|
assert(!os.exists(badout));
|
||||||
|
assert(same(voucher, readfile(strings.concat(works[si], "/",
|
||||||
|
expanded, ".unit.ww"))));
|
||||||
|
assert(same(toolww, readfile(strings.concat(works[si], "/.wwtool.ww"))));
|
||||||
|
assert(same(toolc, readfile(strings.concat(works[si], "/.wwtool.w6c"))));
|
||||||
|
assert(same(toola, readfile(strings.concat(works[si], "/.wwtool.w6a"))));
|
||||||
|
assert(same(stamp, readfile(strings.concat(works[si],
|
||||||
|
"/.wwtool.stamp"))));
|
||||||
|
ai = 0;
|
||||||
|
for (ai < coreartifacts.len) {
|
||||||
|
let xi: i32 = 0;
|
||||||
|
for (xi < coresuffixes.len) {
|
||||||
|
let index: i32 = ai * coresuffixes.len + xi;
|
||||||
|
assert(same(references[index], readfile(strings.concat(
|
||||||
|
works[si], "/", coreartifacts[ai], coresuffixes[xi]))));
|
||||||
|
xi += 1;
|
||||||
|
};
|
||||||
|
ai += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
let coldout: str = strings.concat(root, "/cold-bad-", stages[si]);
|
||||||
|
let coldav: []str = [driver(stages[si]), "build", "-I", source,
|
||||||
|
"-o", coldout, outsider];
|
||||||
|
runcommandenv(root, strings.concat("vendor-cold-bad-", stages[si]),
|
||||||
|
coldav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(same(out.stderr, outsidediag));
|
||||||
|
assert(readfile(compilertraces[si]).len == 0);
|
||||||
|
assert(readfile(assemblertraces[si]).len == 0);
|
||||||
|
assert(readfile(linkertraces[si]).len == 0);
|
||||||
|
assert(!os.exists(coldout));
|
||||||
|
assert(!os.exists(strings.concat(coldout, ".sepwork")));
|
||||||
|
|
||||||
|
let directout: str = strings.concat(root, "/direct-bad-", stages[si]);
|
||||||
|
let directav: []str = [driver(stages[si]), "build", "-I", source,
|
||||||
|
"-o", directout, directclient];
|
||||||
|
runcommandenv(root, strings.concat("vendor-direct-bad-", stages[si]),
|
||||||
|
directav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
let directdiag: str = strings.concat(directclient,
|
||||||
|
"/main.ww:2:1: error: ", expanded,
|
||||||
|
" must be imported as lib.math\n");
|
||||||
|
assert(same(out.stderr, directdiag));
|
||||||
|
if (si == 0) { referencedirectdiag = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(referencedirectdiag, out.stderr)); };
|
||||||
|
assert(readfile(compilertraces[si]).len == 0);
|
||||||
|
assert(readfile(assemblertraces[si]).len == 0);
|
||||||
|
assert(readfile(linkertraces[si]).len == 0);
|
||||||
|
assert(!os.exists(directout));
|
||||||
|
assert(!os.exists(strings.concat(directout, ".sepwork")));
|
||||||
|
|
||||||
|
// The lexical importer is outside domain/app, but the symlink target is
|
||||||
|
// physically inside it. Visibility therefore passes and the later
|
||||||
|
// effective-spelling diagnostic proves physical importer containment.
|
||||||
|
let aliasdirectout: str = strings.concat(root,
|
||||||
|
"/alias-direct-bad-", stages[si]);
|
||||||
|
let aliasdirectav: []str = [driver(stages[si]), "build", "-I", source,
|
||||||
|
"-o", aliasdirectout, aliasdirect];
|
||||||
|
runcommandenv(root, strings.concat("vendor-alias-direct-bad-",
|
||||||
|
stages[si]), aliasdirectav, env,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
let aliasdirectdiag: str = strings.concat(aliasdirect,
|
||||||
|
"/main.ww:2:1: error: ", expanded,
|
||||||
|
" must be imported as lib.math\n");
|
||||||
|
assert(same(out.stderr, aliasdirectdiag));
|
||||||
|
if (si == 0) { referencealiasdirectdiag = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(referencealiasdirectdiag, out.stderr)); };
|
||||||
|
assert(readfile(compilertraces[si]).len == 0);
|
||||||
|
assert(readfile(assemblertraces[si]).len == 0);
|
||||||
|
assert(readfile(linkertraces[si]).len == 0);
|
||||||
|
assert(!os.exists(aliasdirectout));
|
||||||
|
assert(!os.exists(strings.concat(aliasdirectout, ".sepwork")));
|
||||||
|
|
||||||
|
// Remaining resolution rows reuse the stage workdir, but every output
|
||||||
|
// remains independently executable and compared across stages below.
|
||||||
|
let targets: []str = [outerclient, rootclient, emptyclient,
|
||||||
|
boundaryclient, cleanclient, nestedclient, ordinaryclient,
|
||||||
|
aliasclient, shapeclient];
|
||||||
|
let roots: []str = [source, source, source, active, cleanroot, source,
|
||||||
|
source, source, source];
|
||||||
|
let codes: []i32 = [22, 33, 22, 55, 44, 77, 12, 33, 17];
|
||||||
|
let labels: []str = ["outer", "root", "empty", "boundary", "ordinary",
|
||||||
|
"nested", "names", "alias", "shape"];
|
||||||
|
let ti: i32 = 0;
|
||||||
|
for (ti < targets.len) {
|
||||||
|
let output: str = strings.concat(root, "/", labels[ti], "-",
|
||||||
|
stages[si]);
|
||||||
|
let tav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||||||
|
"-I", roots[ti], "-o", output, targets[ti]];
|
||||||
|
runcommandenv(root, strings.concat("vendor-", labels[ti], "-",
|
||||||
|
stages[si]), tav, env,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
let rr: []str = [output];
|
||||||
|
runcommand(root, strings.concat("vendor-run-", labels[ti], "-",
|
||||||
|
stages[si]), rr,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, codes[ti]);
|
||||||
|
if (si == 0) {
|
||||||
|
supplementalrefs[ti] = strings.dup(readfile(output));
|
||||||
|
} else {
|
||||||
|
assert(same(supplementalrefs[ti], readfile(output)));
|
||||||
|
};
|
||||||
|
ti += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
let escapedout: str = strings.concat(root, "/escape-bad-", stages[si]);
|
||||||
|
let escapedav: []str = [driver(stages[si]), "build", "-I", source,
|
||||||
|
"-o", escapedout, escapeclient];
|
||||||
|
runcommandenv(root, strings.concat("vendor-escape-bad-", stages[si]),
|
||||||
|
escapedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(has(out.stderr, "use of vendored package not allowed\n"));
|
||||||
|
if (si == 0) { referenceescapeddiag = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(referenceescapeddiag, out.stderr)); };
|
||||||
|
let prefixout: str = strings.concat(root, "/prefix-bad-", stages[si]);
|
||||||
|
let prefixav: []str = [driver(stages[si]), "build", "-I", source,
|
||||||
|
"-o", prefixout, prefixclient];
|
||||||
|
runcommandenv(root, strings.concat("vendor-prefix-bad-", stages[si]),
|
||||||
|
prefixav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(has(out.stderr, "use of vendored package not allowed\n"));
|
||||||
|
if (si == 0) { referenceprefixdiag = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(referenceprefixdiag, out.stderr)); };
|
||||||
|
|
||||||
|
let candidateclients: []str = [testonlyclient, bareclient];
|
||||||
|
let candidatefiles: []str = ["only_test.ww", ".ww"];
|
||||||
|
let ci: i32 = 0;
|
||||||
|
for (ci < candidateclients.len) {
|
||||||
|
let candidateout: str = strings.concat(root, "/candidate-bad-",
|
||||||
|
stages[si], "-", candidatefiles[ci]);
|
||||||
|
let cav: []str = [driver(stages[si]), "build", "-I", source,
|
||||||
|
"-o", candidateout, candidateclients[ci]];
|
||||||
|
runcommandenv(root, strings.concat("vendor-candidate-bad-",
|
||||||
|
stages[si], "-", candidatefiles[ci]), cav, env,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(has(out.stderr, "/vendor/lib/math: directory contains no WW package sources\n"));
|
||||||
|
assert(!os.exists(strings.concat(candidateout, ".sepwork")));
|
||||||
|
if (si == 0) {
|
||||||
|
candidate_diag_refs[ci] = strings.dup(out.stderr);
|
||||||
|
} else {
|
||||||
|
assert(same(candidate_diag_refs[ci], out.stderr));
|
||||||
|
};
|
||||||
|
ci += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
let selected: str = strings.concat(root, "/selected-", stages[si], ".a");
|
||||||
|
let selectedav: []str = [driver(stages[si]), "build", "-I", source,
|
||||||
|
"-o", selected, nearest];
|
||||||
|
runcommandenv(root, strings.concat("vendor-selected-", stages[si]),
|
||||||
|
selectedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(same(references[1 * coresuffixes.len + 4], readfile(selected)));
|
||||||
|
assert(same(references[1 * coresuffixes.len + 1],
|
||||||
|
readfile(strings.concat(selected, ".wwi"))));
|
||||||
|
|
||||||
|
// One command-global union shares one vendored action between three
|
||||||
|
// importers, keeps an outer physical copy distinct, and also keeps two
|
||||||
|
// expanded identities distinct when their lexical vendor routes converge
|
||||||
|
// through symlinks on one physical directory.
|
||||||
|
rewritefile(compilertraces[si], "");
|
||||||
|
rewritefile(assemblertraces[si], "");
|
||||||
|
rewritefile(linkertraces[si], "");
|
||||||
|
let combinedwork: str = strings.concat(root, "/combined-work-",
|
||||||
|
stages[si]);
|
||||||
|
assert(os.mkdir(combinedwork, 448i32) == 0);
|
||||||
|
let combinedtargets: []str = [client, one, two, outerclient, alpha,
|
||||||
|
beta, pairvault];
|
||||||
|
let combinedpackages: []str = ["main", "main", "main", "main",
|
||||||
|
"main", "main", "math"];
|
||||||
|
let combinedcodes: []i32 = [0, 0, 0, 0, 0, 0, 0];
|
||||||
|
let combinedoutputs: []str = [
|
||||||
|
strings.concat(root, "/combined-client-", stages[si]),
|
||||||
|
strings.concat(root, "/combined-one-", stages[si]),
|
||||||
|
strings.concat(root, "/combined-two-", stages[si]),
|
||||||
|
strings.concat(root, "/combined-outer-", stages[si]),
|
||||||
|
strings.concat(root, "/combined-alpha-", stages[si]),
|
||||||
|
strings.concat(root, "/combined-beta-", stages[si]),
|
||||||
|
strings.concat(root, "/combined-local-", stages[si])];
|
||||||
|
let combinedstatuses: []str = [
|
||||||
|
strings.concat(combinedoutputs[0], ".status"),
|
||||||
|
strings.concat(combinedoutputs[1], ".status"),
|
||||||
|
strings.concat(combinedoutputs[2], ".status"),
|
||||||
|
strings.concat(combinedoutputs[3], ".status"),
|
||||||
|
strings.concat(combinedoutputs[4], ".status"),
|
||||||
|
strings.concat(combinedoutputs[5], ".status"),
|
||||||
|
strings.concat(combinedoutputs[6], ".status")];
|
||||||
|
let combinedav: []str = alloc([], (8 + combinedtargets.len * 6): u64)!;
|
||||||
|
append(combinedav, driver(stages[si]));
|
||||||
|
append(combinedav, "test"); append(combinedav, "-c");
|
||||||
|
append(combinedav, "-w"); append(combinedav, combinedwork);
|
||||||
|
append(combinedav, "-I"); append(combinedav, source);
|
||||||
|
let ui: i32 = 0;
|
||||||
|
for (ui < combinedtargets.len) {
|
||||||
|
append(combinedav, "--ww-package-test");
|
||||||
|
append(combinedav, "production");
|
||||||
|
append(combinedav, combinedpackages[ui]);
|
||||||
|
append(combinedav, combinedtargets[ui]);
|
||||||
|
append(combinedav, combinedoutputs[ui]);
|
||||||
|
append(combinedav, combinedstatuses[ui]);
|
||||||
|
ui += 1;
|
||||||
|
};
|
||||||
|
append(combinedav, client);
|
||||||
|
runcommandenv(root, strings.concat("vendor-combined-", stages[si]),
|
||||||
|
combinedav, env, (180i64 * (time.second: i64)): time.duration,
|
||||||
|
&out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||||
|
let combinedtrace: str = readfile(compilertraces[si]);
|
||||||
|
assert(occurrences(combinedtrace, strings.concat("/", expanded,
|
||||||
|
".unit.new")) == 1);
|
||||||
|
assert(occurrences(combinedtrace,
|
||||||
|
"/domain.vendor.lib.math.unit.new") == 1);
|
||||||
|
assert(occurrences(combinedtrace,
|
||||||
|
"/alpha.client.vendor.lib.math.unit.new") == 1);
|
||||||
|
assert(occurrences(combinedtrace,
|
||||||
|
"/beta.client.vendor.lib.math.unit.new") == 1);
|
||||||
|
assert(has(combinedtrace, localidentity(pairvault, "math")));
|
||||||
|
assert(occurrences(combinedtrace, "/lib.leaf.unit.new") == 1);
|
||||||
|
let combinedidentities: []str = [expanded,
|
||||||
|
"alpha.client.vendor.lib.math", "beta.client.vendor.lib.math"];
|
||||||
|
let combinedsuffixes: []str = [".wwi", ".a"];
|
||||||
|
ui = 0;
|
||||||
|
for (ui < combinedidentities.len) {
|
||||||
|
let xi: i32 = 0;
|
||||||
|
for (xi < combinedsuffixes.len) {
|
||||||
|
let bytes: str = readfile(strings.concat(combinedwork, "/",
|
||||||
|
combinedidentities[ui], combinedsuffixes[xi]));
|
||||||
|
let index: i32 = ui * combinedsuffixes.len + xi;
|
||||||
|
if (si == 0) {
|
||||||
|
combinedrefs[index] = strings.dup(bytes);
|
||||||
|
} else {
|
||||||
|
assert(same(combinedrefs[index], bytes));
|
||||||
|
};
|
||||||
|
xi += 1;
|
||||||
|
};
|
||||||
|
ui += 1;
|
||||||
|
};
|
||||||
|
assert(os.exists(strings.concat(combinedwork,
|
||||||
|
"/domain.vendor.lib.math.wwi")));
|
||||||
|
assert(os.exists(strings.concat(combinedwork,
|
||||||
|
"/domain.vendor.lib.math.a")));
|
||||||
|
ui = 0;
|
||||||
|
for (ui < combinedoutputs.len) {
|
||||||
|
let cav: []str = [combinedoutputs[ui]];
|
||||||
|
runcommand(root, strings.concat("vendor-combined-run-", stages[si],
|
||||||
|
"-", labels[ui]), cav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, combinedcodes[ui]);
|
||||||
|
ui += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A target interned by the allowed product is still checked at the
|
||||||
|
// forbidden source edge. Reversing product descriptors changes neither
|
||||||
|
// the diagnostic nor any status/tool state.
|
||||||
|
let orderwork: str = strings.concat(root, "/vendor-order-work-",
|
||||||
|
stages[si]);
|
||||||
|
assert(os.mkdir(orderwork, 448i32) == 0);
|
||||||
|
let orderallowed: str = strings.concat(root, "/vendor-order-allowed-",
|
||||||
|
stages[si]);
|
||||||
|
let orderforbidden: str = strings.concat(root,
|
||||||
|
"/vendor-order-forbidden-", stages[si]);
|
||||||
|
let orderallowedstatus: str = strings.concat(orderallowed, ".status");
|
||||||
|
let orderforbiddenstatus: str = strings.concat(orderforbidden,
|
||||||
|
".status");
|
||||||
|
writefile(orderallowedstatus, "sentinel-allowed\n");
|
||||||
|
writefile(orderforbiddenstatus, "sentinel-forbidden\n");
|
||||||
|
let orderforward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
|
orderwork, "-I", source,
|
||||||
|
"--ww-package-test", "production", "main", client,
|
||||||
|
orderallowed, orderallowedstatus,
|
||||||
|
"--ww-package-test", "production", "main", outsider,
|
||||||
|
orderforbidden, orderforbiddenstatus, client];
|
||||||
|
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
|
orderwork, "-I", source,
|
||||||
|
"--ww-package-test", "production", "main", outsider,
|
||||||
|
orderforbidden, orderforbiddenstatus,
|
||||||
|
"--ww-package-test", "production", "main", client,
|
||||||
|
orderallowed, orderallowedstatus, client];
|
||||||
|
let orderrequests: [][]str = [orderforward, orderreverse];
|
||||||
|
let orderdiags: []str = ["", ""];
|
||||||
|
let oi: i32 = 0;
|
||||||
|
for (oi < orderrequests.len) {
|
||||||
|
rewritefile(compilertraces[si], "");
|
||||||
|
rewritefile(assemblertraces[si], "");
|
||||||
|
rewritefile(linkertraces[si], "");
|
||||||
|
runcommandenv(root, strings.concat("vendor-order-", stages[si],
|
||||||
|
"-", labels[oi]), orderrequests[oi], env,
|
||||||
|
(180i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(same(out.stderr, outsidediag));
|
||||||
|
orderdiags[oi] = strings.dup(out.stderr);
|
||||||
|
assert(readfile(compilertraces[si]).len == 0);
|
||||||
|
assert(readfile(assemblertraces[si]).len == 0);
|
||||||
|
assert(readfile(linkertraces[si]).len == 0);
|
||||||
|
assert(same(readfile(orderallowedstatus), "sentinel-allowed\n"));
|
||||||
|
assert(same(readfile(orderforbiddenstatus),
|
||||||
|
"sentinel-forbidden\n"));
|
||||||
|
assert(!os.exists(strings.concat(orderwork, "/.wwtool.stamp")));
|
||||||
|
assert(!os.exists(orderallowed) && !os.exists(orderforbidden));
|
||||||
|
oi += 1;
|
||||||
|
};
|
||||||
|
assert(same(orderdiags[0], orderdiags[1]));
|
||||||
|
if (si == 0) { referenceorderdiag = strings.dup(orderdiags[0]); }
|
||||||
|
else { assert(same(referenceorderdiag, orderdiags[0])); };
|
||||||
|
|
||||||
|
// Production, same-package test, and external test sources all retain
|
||||||
|
// their short spelling while consuming the one expanded direct export.
|
||||||
|
let testworkroot: str = strings.concat(root, "/vendor-test-work-",
|
||||||
|
stages[si]);
|
||||||
|
assert(os.mkdir(testworkroot, 448i32) == 0);
|
||||||
|
rewritefile(compilertraces[si], "");
|
||||||
|
rewritefile(assemblertraces[si], "");
|
||||||
|
rewritefile(linkertraces[si], "");
|
||||||
|
let testav: []str = [driver(stages[si]), "test", "-w", testworkroot,
|
||||||
|
"-I", source, testclient];
|
||||||
|
runcommandenv(root, strings.concat("vendor-tests-", stages[si]), testav,
|
||||||
|
env, (180i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(has(out.stdout, "same_vendor ... ok\n"));
|
||||||
|
assert(has(out.stdout, "external_vendor ... ok\n"));
|
||||||
|
if (si == 0) {
|
||||||
|
referencetestout = strings.dup(out.stdout);
|
||||||
|
referencetesterr = strings.dup(out.stderr);
|
||||||
|
} else {
|
||||||
|
assert(same(referencetestout, out.stdout));
|
||||||
|
assert(same(referencetesterr, out.stderr));
|
||||||
|
};
|
||||||
|
let testwork: str = strings.concat(testworkroot, "/",
|
||||||
|
requestworkkey(testclient), "/");
|
||||||
|
let testactions: []str = ["domain.app.testclient-internal-test",
|
||||||
|
"domain.app.testclient_test-external-test"];
|
||||||
|
let testsuffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||||||
|
ui = 0;
|
||||||
|
for (ui < testactions.len) {
|
||||||
|
let xi: i32 = 0;
|
||||||
|
for (xi < testsuffixes.len) {
|
||||||
|
let bytes: str = readfile(strings.concat(testwork,
|
||||||
|
testactions[ui], testsuffixes[xi]));
|
||||||
|
let index: i32 = ui * testsuffixes.len + xi;
|
||||||
|
if (si == 0) { testrefs[index] = strings.dup(bytes); }
|
||||||
|
else { assert(same(testrefs[index], bytes)); };
|
||||||
|
xi += 1;
|
||||||
|
};
|
||||||
|
assert(has(readfile(strings.concat(testwork, testactions[ui],
|
||||||
|
".unit.ww")), strings.concat("//ww:import-map lib.math ",
|
||||||
|
expanded, " ", hexbytes(nearest), "\n")));
|
||||||
|
ui += 1;
|
||||||
|
};
|
||||||
|
let testtrace: str = readfile(compilertraces[si]);
|
||||||
|
assert(occurrences(testtrace, strings.concat("/", expanded,
|
||||||
|
".unit.new")) == 1);
|
||||||
|
let internalline: str = linecontaining(testtrace,
|
||||||
|
"domain.app.testclient-internal-test.unit.new");
|
||||||
|
let externalline: str = linecontaining(testtrace,
|
||||||
|
"domain.app.testclient_test-external-test.unit.new");
|
||||||
|
assert(occurrences(internalline, strings.concat("<--import><", expanded,
|
||||||
|
"><", testwork, expanded, ".wwi>")) == 1);
|
||||||
|
assert(occurrences(externalline, strings.concat("<--import><", expanded,
|
||||||
|
"><", testwork, expanded, ".wwi>")) == 1);
|
||||||
|
assert(occurrences(internalline, strings.concat(
|
||||||
|
"<--import-map><lib.math><", expanded, ">")) == 1);
|
||||||
|
assert(occurrences(externalline, strings.concat(
|
||||||
|
"<--import-map><lib.math><", expanded, ">")) == 1);
|
||||||
|
assert(!has(internalline, "lib.leaf.wwi"));
|
||||||
|
assert(!has(externalline, "lib.leaf.wwi"));
|
||||||
|
let internalmainline: str = linecontaining(testtrace,
|
||||||
|
"domain.app.testclient-internal-test-main.unit.new");
|
||||||
|
let externalmainline: str = linecontaining(testtrace,
|
||||||
|
"domain.app.testclient_test-external-test-main.unit.new");
|
||||||
|
assert(!has(internalmainline, expanded));
|
||||||
|
assert(!has(externalmainline, expanded));
|
||||||
|
assert(!has(readfile(linkertraces[si]), ".wwi>"));
|
||||||
|
|
||||||
|
rewritefile(compilertraces[si], "");
|
||||||
|
rewritefile(assemblertraces[si], "");
|
||||||
|
rewritefile(linkertraces[si], "");
|
||||||
|
runcommandenv(root, strings.concat("vendor-tests-warm-", stages[si]),
|
||||||
|
testav, env, (180i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(same(referencetestout, out.stdout));
|
||||||
|
assert(same(referencetesterr, out.stderr));
|
||||||
|
assert(readfile(compilertraces[si]).len == 0);
|
||||||
|
assert(readfile(assemblertraces[si]).len == 0);
|
||||||
|
|
||||||
|
// A directly selected vendored test root is legal, and its external
|
||||||
|
// source still performs the ordinary nearest-vendor search first.
|
||||||
|
rewritefile(compilertraces[si], "");
|
||||||
|
rewritefile(assemblertraces[si], "");
|
||||||
|
rewritefile(linkertraces[si], "");
|
||||||
|
let vendortestav: []str = [driver(stages[si]), "test", "-I", source,
|
||||||
|
vendortest];
|
||||||
|
runcommandenv(root, strings.concat("vendor-literal-test-", stages[si]),
|
||||||
|
vendortestav, env,
|
||||||
|
(180i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(has(out.stdout, "nested_external_vendor ... ok\n"));
|
||||||
|
if (si == 0) {
|
||||||
|
referencevendortestout = strings.dup(out.stdout);
|
||||||
|
referencevendortesterr = strings.dup(out.stderr);
|
||||||
|
} else {
|
||||||
|
assert(same(referencevendortestout, out.stdout));
|
||||||
|
assert(same(referencevendortesterr, out.stderr));
|
||||||
|
};
|
||||||
|
assert(has(readfile(compilertraces[si]),
|
||||||
|
"literal.owner.vendor.lib.math.vendor.lib.math.unit.ww"));
|
||||||
|
|
||||||
|
// A cold forbidden public test request is rejected before the
|
||||||
|
// coordinator leaves its hashed persistent request directory behind.
|
||||||
|
let testbadworkroot: str = strings.concat(root, "/vendor-test-bad-work-",
|
||||||
|
stages[si]);
|
||||||
|
assert(os.mkdir(testbadworkroot, 448i32) == 0);
|
||||||
|
rewritefile(compilertraces[si], "");
|
||||||
|
rewritefile(assemblertraces[si], "");
|
||||||
|
rewritefile(linkertraces[si], "");
|
||||||
|
let testbadav: []str = [driver(stages[si]), "test", "-w",
|
||||||
|
testbadworkroot, "-I", source, testbad];
|
||||||
|
runcommandenv(root, strings.concat("vendor-test-bad-", stages[si]),
|
||||||
|
testbadav, env, (180i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
let testbaddiag: str = strings.concat(testbad,
|
||||||
|
"/same_test.ww:2:1: error: use of vendored package not allowed\n");
|
||||||
|
assert(has(out.stderr, testbaddiag));
|
||||||
|
if (si == 0) { referencetestbaddiag = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(referencetestbaddiag, out.stderr)); };
|
||||||
|
assert(!os.exists(strings.concat(testbadworkroot, "/",
|
||||||
|
requestworkkey(testbad))));
|
||||||
|
assert(readfile(compilertraces[si]).len == 0);
|
||||||
|
assert(readfile(assemblertraces[si]).len == 0);
|
||||||
|
assert(readfile(linkertraces[si]).len == 0);
|
||||||
|
|
||||||
|
// The coordinator's synthetic support/main edges stay synthetic, while
|
||||||
|
// imports written in the real test-support source use this same resolver.
|
||||||
|
let supportenv: []str = alloc([], (env.len + 1): u64)!;
|
||||||
|
ei = 0;
|
||||||
|
for (ei < env.len) { append(supportenv, env[ei]); ei += 1; };
|
||||||
|
append(supportenv, strings.concat("WW_SRCLIB=", toolsource));
|
||||||
|
let supportworkroot: str = strings.concat(root, "/vendor-support-work-",
|
||||||
|
stages[si]);
|
||||||
|
assert(os.mkdir(supportworkroot, 448i32) == 0);
|
||||||
|
rewritefile(compilertraces[si], "");
|
||||||
|
rewritefile(assemblertraces[si], "");
|
||||||
|
rewritefile(linkertraces[si], "");
|
||||||
|
let supportav: []str = [driver(stages[si]), "test", "-w",
|
||||||
|
supportworkroot, "-I", source, supportcase];
|
||||||
|
runcommandenv(root, strings.concat("vendor-support-", stages[si]),
|
||||||
|
supportav, supportenv,
|
||||||
|
(240i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(has(out.stdout, "support_runs ... ok\n"));
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
if (si == 0) { referencesupportout = strings.dup(out.stdout); }
|
||||||
|
else { assert(same(referencesupportout, out.stdout)); };
|
||||||
|
let supportwork: str = strings.concat(supportworkroot, "/",
|
||||||
|
requestworkkey(supportcase), "/");
|
||||||
|
let supportunit: str = readfile(strings.concat(supportwork,
|
||||||
|
"test.unit.ww"));
|
||||||
|
assert(has(supportunit, strings.concat("//ww:import-map fnmatch ",
|
||||||
|
"test.vendor.fnmatch ", hexbytes(toolvendor), "\n")));
|
||||||
|
assert(has(readfile(strings.concat(supportwork,
|
||||||
|
"test.vendor.fnmatch.unit.ww")), strings.concat(
|
||||||
|
"//ww:vendor-dir ", hexbytes(toolvendor), "\n")));
|
||||||
|
let supporttrace: str = readfile(compilertraces[si]);
|
||||||
|
let supportline: str = linecontaining(supporttrace, "/test.unit.new");
|
||||||
|
assert(occurrences(supportline,
|
||||||
|
"<--import><test.vendor.fnmatch>") == 1);
|
||||||
|
assert(occurrences(supportline,
|
||||||
|
"<--import-map><fnmatch><test.vendor.fnmatch>") == 1);
|
||||||
|
assert(!has(supportline, "/ascii.wwi>"));
|
||||||
|
assert(occurrences(supporttrace,
|
||||||
|
"/test.vendor.fnmatch.unit.new") == 1);
|
||||||
|
let supportmainline: str = linecontaining(supporttrace,
|
||||||
|
"supportcase-internal-test-main.unit.new");
|
||||||
|
assert(!has(supportmainline, "test.vendor.fnmatch"));
|
||||||
|
assert(!has(readfile(linkertraces[si]), ".wwi>"));
|
||||||
|
let supportsuffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||||||
|
ui = 0;
|
||||||
|
for (ui < supportsuffixes.len) {
|
||||||
|
let bytes: str = readfile(strings.concat(supportwork,
|
||||||
|
"test.vendor.fnmatch", supportsuffixes[ui]));
|
||||||
|
if (si == 0) { supportrefs[ui] = strings.dup(bytes); }
|
||||||
|
else { assert(same(supportrefs[ui], bytes)); };
|
||||||
|
ui += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
si += 1;
|
||||||
|
};
|
||||||
|
clean(root);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The package-level -o contract: -c -o names the single package's
|
||||||
// artifact in place of the fixed <package>.test stem; -o without -c
|
// artifact in place of the fixed <package>.test stem; -o without -c
|
||||||
// has nothing to name (runs execute from the temp root); one name
|
// has nothing to name (runs execute from the temp root); one name
|
||||||
// cannot fan out over multiple packages (Go's `go test -o` rule).
|
// cannot fan out over multiple packages (Go's `go test -o` rule).
|
||||||
|
|||||||
Reference in New Issue
Block a user