test: prove local vendor package semantics

This commit is contained in:
2026-08-13 19:18:05 +09:00
parent b84fb3ff25
commit 5d50788e46
2 changed files with 1204 additions and 45 deletions

View File

@@ -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 658668](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 722796](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 12131263](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 14181429](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 15931617](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 16201667](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 16701688](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 85173](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L85-L173),
[`test.go`, lines 175266](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L175-L266),
[`test.go`, lines 272373](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L272-L373),
[`go/build/build.go`, lines 415493](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 437455](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L455),
[`action.go`, lines 628659](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L659),
[`build.go`, lines 470558](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.

View File

@@ -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).