test: prove local vendor package semantics
This commit is contained in:
@@ -2794,16 +2794,17 @@ package main;
|
||||
import lib.math;
|
||||
```
|
||||
|
||||
An import is translated from dots to path separators and resolved only as a
|
||||
directory package through the entry package's directory, explicit `-I` roots in
|
||||
command order, and the toolchain source-library root. A same-named `.ww` file is
|
||||
neither a match nor a shadow for an import, so a later root containing the
|
||||
directory wins over an earlier file decoy. There is no network, manifest, or
|
||||
imported-file fallback. Explicit single-file CLI roots retain their raw-unit
|
||||
compatibility path. The loader uses the compiler frontend's imports-only parser,
|
||||
unions duplicate imports, byte-sorts direct edges, interns resolved directories
|
||||
by filesystem identity, and reports self-imports and stable cycle chains before
|
||||
compilation.
|
||||
A source import is translated from dots to path separators, expanded through
|
||||
the nearest eligible local `vendor` directory described in section 11.16, then
|
||||
falls back to directory lookup through the entry package's directory, explicit
|
||||
`-I` roots in command order, and the toolchain source-library root. A same-named
|
||||
`.ww` file is neither a match nor a shadow for an import, so a later root
|
||||
containing the directory wins over an earlier file decoy. There is no network,
|
||||
manifest, or imported-file fallback. Explicit single-file CLI roots retain
|
||||
their raw-unit compatibility path. The loader uses the compiler frontend's
|
||||
imports-only parser, unions duplicate imports, byte-sorts direct edges, interns
|
||||
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,
|
||||
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
|
||||
different directory is rejected; the one same-directory exception is an
|
||||
external `main_test` variant's canonical import of its production action. Two
|
||||
logical identities for one physical directory are rejected rather than compiled
|
||||
twice.
|
||||
ordinary logical identities for one physical directory are rejected rather
|
||||
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
|
||||
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
|
||||
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
|
||||
separate `--import <canonical-path> <dependency.wwi>` compiler input, sorted by
|
||||
canonical path and deduplicated by the loader; no transitive `.wwi` is passed.
|
||||
A package compilation unit contains only that package's own byte-sorted sources,
|
||||
deterministic `//ww:module-reset` separators, and sorted driver-private
|
||||
resolution metadata; it never contains a dependency source body. Each
|
||||
**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
|
||||
imports: a source qualifier is visible only when its owning package directly
|
||||
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,
|
||||
and closure validation retain diagnostic precedence over this publication-only
|
||||
check, and `-S` does not validate a publication path it never consumes.
|
||||
Build workdir format 12 invalidates older build-mode unit vouchers before reuse
|
||||
because root compiler argv changed; test workdir format 11 is unchanged because
|
||||
test variants still link only their explicit generated-main action. Thereafter
|
||||
Build workdir format 13 and test workdir format 12 invalidate older unit
|
||||
vouchers before reuse because source binding and vendor-directory identity now
|
||||
participate in compiler argv and persistent unit semantics. Thereafter
|
||||
an equivalent warm library build invokes no tools, a private dependency change
|
||||
stops at its unchanged export, and an export change recompiles its direct
|
||||
importer under the existing propagation rule.
|
||||
@@ -3082,12 +3088,13 @@ export and emits a root `.wwi/.a`.
|
||||
|
||||
The reserved support action and an ordinary source-imported package `test` may
|
||||
coexist only because the former is explicitly rebound to the compiler-only
|
||||
qualifier `__wwtest`. This is the sole directory-alias exception and cannot be
|
||||
created by a source import. All ordinary production actions, including an
|
||||
external test's colocated production dependency, use canonical directory
|
||||
identity and the global bidirectional import-path checks above. There is no
|
||||
role-based tolerance for duplicate ordinary import identities and no late
|
||||
product-closure ambiguity to resolve.
|
||||
qualifier `__wwtest`. This is the sole role-based directory alias and cannot be
|
||||
created by a source import. The separate expanded-vendor-route exception in
|
||||
section 11.16 is canonical source-tree identity, not a role alias. All ordinary
|
||||
production actions, including an external test's colocated production
|
||||
dependency, use canonical directory identity and the global bidirectional
|
||||
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
|
||||
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
|
||||
production archive. Variant-only archives are never linked into another
|
||||
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
|
||||
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
|
||||
invalidates every `.unit.ww` voucher before compilation; old artifacts may
|
||||
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
|
||||
identity-finalization change bumps both formats so no leaf-keyed unit voucher
|
||||
can be reused as a full-path package action.
|
||||
That slice introduced workdir format revisions 8 for ordinary builds and 9 for
|
||||
tests. Later package-identity slices supersede those revisions; the current
|
||||
formats are recorded in section 11.16.
|
||||
|
||||
This closes a real hidden-input boundary. The driver, rather than `w6c`, owns
|
||||
canonical directory interning, source-derived graph construction, owner-only
|
||||
@@ -3278,14 +3286,16 @@ or a manifest.
|
||||
|
||||
### 11.11 Implemented directory-only source-import slice
|
||||
|
||||
Cstage and WWstage now use a directory-only locator for every parsed source
|
||||
import, including imports selected only by a package-test variant and the
|
||||
compiler-generated test-support edge. The loader makes one ordered pass for
|
||||
`<root>/<import-path>/`; it never probes `<root>/<import-path>.ww`. Unit
|
||||
composition consequently writes only the owning package's byte-sorted source
|
||||
files and never copies a dependency interface or imported source body. Missing
|
||||
imports retain the importing source position and the same stable diagnostic in
|
||||
both stages.
|
||||
Cstage and WWstage now use directory packages for every parsed source import,
|
||||
including imports selected only by a package-test variant and real imports in
|
||||
the test-support package. Source imports first probe the bounded vendor
|
||||
candidates in section 11.16, then make one ordered fallback pass for
|
||||
`<root>/<import-path>/`; they never probe `<root>/<import-path>.ww`. The
|
||||
compiler-generated edge to test support remains synthetic. Unit composition
|
||||
consequently writes only the owning package's byte-sorted source files and
|
||||
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
|
||||
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,
|
||||
deduplicated outgoing edges. They never walk grandchildren for compilation.
|
||||
Every `.unit.ww` contains only the node's byte-sorted source files and reset
|
||||
separators, while executable linking independently walks the full reachable
|
||||
package closure and passes archives, never interfaces. The same path handles an
|
||||
Every `.unit.ww` contains the node's byte-sorted source files, reset separators,
|
||||
and any sorted driver-private vendor/import-map voucher comments, but no
|
||||
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
|
||||
package and its reused production package, compiler-generated test main, and
|
||||
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
|
||||
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
|
||||
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
|
||||
qualified declarations use it in generated symbols. A selected executable
|
||||
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,
|
||||
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
|
||||
cannot silently alias two live or warm package actions. The workdir format is
|
||||
now build version 10 and test version 11 so an older flat-layout voucher is
|
||||
never accepted as current state.
|
||||
cannot silently alias two live or warm package actions. That storage slice
|
||||
introduced build version 10 and test version 11 so an older flat-layout voucher
|
||||
was never accepted as current state. Section 11.16 records the current
|
||||
superseding formats.
|
||||
|
||||
The persisted semantic owner is deliberately the complete canonical import
|
||||
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
|
||||
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
|
||||
|
||||
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 = {
|
||||
let root: str = fresh();
|
||||
let target: str = packagepath("routing");
|
||||
@@ -5639,7 +5653,932 @@ fn runtimepath(relative: str) str = {
|
||||
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
|
||||
// has nothing to name (runs execute from the temp root); one name
|
||||
// cannot fan out over multiple packages (Go's `go test -o` rule).
|
||||
|
||||
Reference in New Issue
Block a user