test: prove case-fold collision preflight
This commit is contained in:
@@ -311,11 +311,21 @@ and matches its final single `linux` token, exactly as Go does.
|
||||
Selection is additive, not replacement-based: every matching file belongs to
|
||||
the package. The production variant then excludes `*_test.ww`; internal and
|
||||
external test classification uses only the already platform-selected test
|
||||
files. An excluded file creates no source occurrence, import, dependency edge,
|
||||
package/action/variant identity, compiler input, export, archive member, link
|
||||
input, artifact, status, or persistence dependency. Adding or editing one is a
|
||||
producer no-op. Adding, removing, or editing a selected file changes the owning
|
||||
unit normally.
|
||||
files. After those decisions, distinct selected basenames in one canonical
|
||||
directory MUST NOT be equal under Go 1.26.5's Unicode simple-fold comparison.
|
||||
The check spans the production, internal-test, and external-test selections of
|
||||
one `ww test` product without merging those source units. An ordinary
|
||||
`ww build` sees production names only. Exact basename reuse by another action
|
||||
view of that directory is not a collision.
|
||||
|
||||
An excluded file creates no source occurrence, collision, import, dependency
|
||||
edge, package/action/variant identity, compiler input, export, archive member,
|
||||
link input, artifact, status, or persistence dependency. Adding or editing one
|
||||
is a producer no-op. Adding, removing, or editing a selected noncolliding file
|
||||
changes the owning unit normally. This applicability is deliberately narrower
|
||||
than Go's `Package.AllFiles`: WW omits wrong-platform and `*_test.ww` names
|
||||
from an ordinary build because those files are not loaded in WW's
|
||||
fixed-target, manifest-free source model.
|
||||
|
||||
WW implements no source-level build expressions, user tags, target descriptor,
|
||||
`UseAllFiles` escape, `+tag` replacement scheme, or manifest-defined selector.
|
||||
@@ -5588,6 +5598,154 @@ source-level build expressions, quoted/grouped/dot imports, and targets other
|
||||
than the separately specified fixed `linux/amd64` filename selection. None is
|
||||
used to define canonical package or directory-product identity.
|
||||
|
||||
### 11.23 Implemented case-fold collision preflight
|
||||
|
||||
This slice pins the collision semantics to Go 1.26.5, tag commit
|
||||
`c19862e5f8415b4f24b189d065ed739517c548ba`. The official command loader uses
|
||||
an exact import cache at `src/cmd/go/internal/load/pkg.go:633-636,757-775`,
|
||||
performs contextual and vendor resolution before cache lookup at
|
||||
`:840-911,974-1005`, owns one command-global folded import table at `:1725`,
|
||||
and rejects a second fold-equivalent import spelling at `:1950-1959`. Its
|
||||
selected-name collision is over `Package.AllFiles` (`:149-194`) and is applied
|
||||
at `:1991-2000`. `src/cmd/go/internal/str/str.go:32-89` defines the pinned
|
||||
`ToFold` and `FoldDup` algorithms; the direct, transitive, and filename
|
||||
expectations are in
|
||||
`src/cmd/go/testdata/script/list_case_collision.txt:1-41`. Filename discovery
|
||||
and package/test classification are ordered by
|
||||
`src/go/build/build.go:859-914,1005-1036,1076-1082,1450-1469`.
|
||||
|
||||
Before this slice, a Linux case-sensitive filesystem let distinct directories
|
||||
such as `domain.Foo` and `domain.foo`, expanded vendor identities, and selected
|
||||
files such as `File.ww` and `file.ww` build as unrelated packages or sources.
|
||||
Both Cstage and WWstage did so byte-identically. A symlink making the two import
|
||||
spellings reach one directory happened to trip the older exact
|
||||
directory/identity check, but that host-physical consequence was neither the Go
|
||||
diagnostic nor the required package-graph rule.
|
||||
|
||||
#### Exact identity and request-only folded keys
|
||||
|
||||
Canonical package identity remains the exact, case-sensitive effective dotted
|
||||
identity. It is still the key used by action interning, source import maps,
|
||||
`.wwi` ownership, mangled symbols, storage digests, archives, diagnostics, and
|
||||
link closure. Default bindings, explicit aliases, blank imports, declared
|
||||
package names, path leaves, artifacts, and physical directories do not replace
|
||||
it. Exact repeated occurrences therefore continue to form one canonical edge
|
||||
and one package action.
|
||||
|
||||
The loader graph additionally owns a checked, dynamically grown, request-only
|
||||
table from a simple-fold key to the first exact package representative. Every
|
||||
ordinary effective identity registers after contextual local/vendor expansion
|
||||
and before exact action reuse or physical-directory conflict checks. Exact
|
||||
reuse succeeds. A distinct spelling with the same key rejects as
|
||||
`ww: case-insensitive import collision: "A" and "a"`; WW byte-sorts the two
|
||||
exact spellings so root, request, import, and discovery order cannot select a
|
||||
different diagnostic. It never stores, interns, looks up, or publishes the
|
||||
folded spelling as package identity.
|
||||
|
||||
WW dotted import components are ASCII by construction: the C lexer/parser
|
||||
accept them through `cmd/wcc/lex.c:53-63` and
|
||||
`cmd/wcc/parse.c:1318-1353`, and the self-hosted syntax path mirrors that in
|
||||
`lib/ww/syntax/lex.ww` and `lib/ww/syntax/decl.ww`. Context-derived identities
|
||||
are revalidated, and arbitrary local filesystem bytes are escaped into ASCII.
|
||||
The general Unicode fold routine is nevertheless shared with filename
|
||||
preflight; for package identities its observable domain reduces exactly to
|
||||
ASCII case folding.
|
||||
|
||||
Vendor imports register the fully expanded canonical identity, not the short
|
||||
source spelling. Thus `lib.Foo` and `lib.foo` reached below one vendor owner
|
||||
collide as, for example, `domain.app.vendor.lib.Foo` and
|
||||
`domain.app.vendor.lib.foo`. Reaching one physical directory through two such
|
||||
identities changes neither ownership nor the diagnostic. The physical
|
||||
directory remains a resolution fact, never a substitute identity.
|
||||
|
||||
Production, same-package test (`ptest`), external test (`pxtest`), and
|
||||
product-scoped recompiled actions share their one exact ordinary package
|
||||
representative and do not re-register as different packages. External
|
||||
compiler identity may still carry `_test`; that action path is not the folded
|
||||
package key. Copy-on-write clones copy exact identity and do not register.
|
||||
Generated main has no ordinary import identity and does not register. Ordinary
|
||||
toolchain package `test` registers like any real package; only the reserved
|
||||
synthetic `__wwtest` role stays outside the ordinary representative table.
|
||||
|
||||
#### Selected source basenames
|
||||
|
||||
Directory enumeration first excludes leading-dot, leading-underscore,
|
||||
wrong-platform, and variant-ineligible files, validates the selected regular
|
||||
sources, and byte-sorts their names. Before delegation, the coordinator performs
|
||||
its required package-clause classification and parses selected production files
|
||||
to reject `@test` declarations outside `*_test.ww`. The delegated driver then
|
||||
registers each selected basename in a second request-only table scoped by
|
||||
canonical physical directory. Repeated views of the exact same selected
|
||||
basename across `p`, `ptest`, or `pxtest` are accepted. Distinct fold-equivalent
|
||||
basenames reject as
|
||||
`ww: case-insensitive file name collision: "File.ww" and "file.ww"` before the
|
||||
driver's graph-import scan or any producer. The preflight is not an earlier
|
||||
replacement for the coordinator-owned source validation parse.
|
||||
|
||||
One `ww test` request shares that directory scope across production,
|
||||
same-package test, external test, same-only, external-only, and mixed test-only
|
||||
actions while preserving their separate compilation units. This catches a
|
||||
collision crossing classifications, such as production `X_TEST.ww` and test
|
||||
`x_test.ww`. An ordinary `ww build` excludes `*_test.ww` before registration.
|
||||
Hidden, underscore-prefixed, and wrong-platform files never register and
|
||||
therefore create no collision or invalidation.
|
||||
|
||||
This last ordering is WW's explicit applicability boundary rather than a claim
|
||||
that every upstream `AllFiles` member is selected here. Go includes test files
|
||||
and some ignored Go files in `AllFiles`, so its ordinary build can diagnose a
|
||||
broader set. WW intentionally follows its existing fixed-target source
|
||||
eligibility and build/test isolation: files it does not load have no graph or
|
||||
persistence effect.
|
||||
|
||||
Filesystem basenames are arbitrary non-NUL bytes, so their fold keys reproduce
|
||||
the pinned Unicode 15.0 `unicode.SimpleFold` minimum-cycle behavior without
|
||||
locale or normalization. Each malformed UTF-8 byte contributes one U+FFFD to
|
||||
the temporary key, as Go string ranging does; diagnostics preserve the exact
|
||||
original byte and quote it as `\xNN`. Printable Unicode remains UTF-8, other
|
||||
nonprinting runes use Go-style `\u` or `\U` escapes, and composed/decomposed
|
||||
Unicode spellings are not normalized.
|
||||
|
||||
#### Tool, artifact, transaction, and persistence ownership
|
||||
|
||||
The coordinator owns initial eligibility and production/test classification;
|
||||
the delegated driver loader owns both fold checks. The language parser still
|
||||
owns the exact import occurrence and qualifier. The driver owns per-site self,
|
||||
internal, vendor, and imported-main structural legality, while the compiler
|
||||
checker owns file-local binding, use, and visibility. The export writer/reader
|
||||
owns exact canonical `.wwi` data. Compiler, assembler, archiver, and linker
|
||||
protocols did not change. Successful neighboring units, `.wwi`, assembly,
|
||||
objects, archives, generated mains, binaries, and exact tool argv therefore
|
||||
remain byte-identical in Cstage and WWstage.
|
||||
|
||||
All root and reachable dependency loading, fold registration, and final exact
|
||||
identity binding finish before scratch acquisition, support or generated-main
|
||||
producer work, compilation, assembly, archive construction, or linking. A
|
||||
collision invokes none of those tools and creates no unit, `.wwi`, assembly,
|
||||
object, archive, main, binary, result, status, voucher, stamp, or `.new` stage.
|
||||
Request-wide publication remains transactional: committed sibling and
|
||||
dependency bytes survive a newly introduced collision, and removing the
|
||||
colliding source restores precise warm reuse.
|
||||
|
||||
The fold tables live only for one command and are freed at graph teardown.
|
||||
Entries become live only after every owned string and vector allocation
|
||||
succeeds, so allocation failure cannot publish a partial table or make cleanup
|
||||
traverse uninitialized entries. Successful action/unit/storage content did not
|
||||
change; build workdir format stays `18`, test workdir format stays `19`, and
|
||||
semantic storage stays `3`. A format bump would only discard valid exact-key
|
||||
artifacts and is therefore not used.
|
||||
|
||||
Native proof extends `package_graph_diagnostics_are_stable`,
|
||||
`platform_filename_source_selection`, and
|
||||
`vendor_directory_import_resolution`, with command-global allocation failure
|
||||
retained in `allocation_failure_is_command_global`. The matrix covers direct,
|
||||
transitive, reversed, recursive, same-directory, and vendor-expanded imports;
|
||||
exact-repeat acceptance; reversed vendor import and product order;
|
||||
production/internal/external/test-only filenames; reversed creation and
|
||||
direct/recursive collision-diagnostic parity; cross-classification, Unicode,
|
||||
invalid UTF-8, ignored files, and absence of normalization; zero-tool
|
||||
rejection; multi-product publication isolation; cold/warm add-remove reuse;
|
||||
exact artifact preservation; and Cstage/WWstage diagnostic and byte identity.
|
||||
|
||||
## 12. Candidate architectures and hard-gate decision
|
||||
|
||||
Five candidates were developed as coherent systems, not as feature bins.
|
||||
|
||||
23
docs/spec.md
23
docs/spec.md
@@ -269,8 +269,14 @@ ImportPath = ident { "." ident } .
|
||||
are ignored. Eligible names are byte-sorted before source validation.
|
||||
Production excludes selected `*_test.ww`; test variants classify only those
|
||||
selected test files. An excluded file contributes no declarations, imports,
|
||||
package edge, action, export, artifact, initialization, test, or persistent
|
||||
invalidation.
|
||||
filename collision, package edge, action, export, artifact, initialization,
|
||||
test, or persistent invalidation. After eligibility, two distinct selected
|
||||
basenames in one canonical directory that are equal under Go 1.26.5 Unicode
|
||||
simple folding are rejected after the coordinator's required package-clause
|
||||
classification and production `@test` validation parses, but before the
|
||||
delegated graph-import scan or tools. An ordinary build compares production
|
||||
names only; one test product compares its production, same-package test, and
|
||||
external-test selections without merging their units.
|
||||
- `import acme.codec;` loads the canonical package `acme.codec`. If that
|
||||
package declares `package wire;`, the importing file sees its exported names
|
||||
as `wire.Name`; `codec.Name` is not an additional binding. An explicit alias
|
||||
@@ -294,6 +300,12 @@ ImportPath = ident { "." ident } .
|
||||
edge. Test-only occurrences never enter ordinary production. Self-import is
|
||||
rejected, except that toolchain-owned external-test self wiring is rebound to
|
||||
the effective augmented package action after ordinary per-site validation.
|
||||
- Canonical import identity is exact and case-sensitive. After contextual local
|
||||
or vendor expansion, two distinct effective identities that are equal under
|
||||
Go 1.26.5 simple folding are a request-wide structural error. Folding is only
|
||||
a temporary collision key: it never changes lookup, action identity, `.wwi`
|
||||
ownership, symbols, artifacts, storage, or diagnostics. Repeated occurrences
|
||||
of the same exact identity remain valid and deduplicate normally.
|
||||
- An executable package is one declared `package main` and containing a
|
||||
`fn main`; path and directory spelling do not classify commands. An ordinary
|
||||
import of a package declared `main` is rejected, except for the toolchain's
|
||||
@@ -618,6 +630,13 @@ under test binds to the augmented white-box action when it exists; affected
|
||||
transitive importers are copied and rewired so ordinary and augmented package
|
||||
state do not coexist in the linked closure.
|
||||
|
||||
Those variants are action distinctions over exact package representatives, not
|
||||
new ordinary package identities for case-fold comparison. Production,
|
||||
same-package test, external test, and recompiled copies of one exact canonical
|
||||
package therefore do not collide with each other. Their selected source units
|
||||
remain separate, while the directory-owned filename preflight spans the
|
||||
production/internal/external selections applicable to that test product.
|
||||
|
||||
Before a test function runs, the one generated product initializes its exact
|
||||
effective graph dependency-first and once per canonical action. Imports,
|
||||
runtime lets, and init declarations found only in `*_test.ww` never enter an
|
||||
|
||||
@@ -242,6 +242,22 @@ directory entry order. A recursive pattern omits a directory with no eligible
|
||||
source, while an explicit wrong-target-only build rejects it as having no WW
|
||||
package source. There is no source-level build-expression or user-tag mode.
|
||||
|
||||
After that eligibility boundary and the coordinator's required package-clause
|
||||
classification and production `@test` validation parses, the delegated loader
|
||||
performs selected-basename Go 1.26.5 simple-fold preflight before its graph
|
||||
import scan. During graph loading, effective canonical imports register only
|
||||
after contextual local/vendor expansion; exact identity reuse is accepted, but
|
||||
two distinct fold-equivalent identities reject the entire shared plan.
|
||||
Selected basenames register in one canonical-directory scope. A production
|
||||
build registers production names only. A test plan shares the filename scope
|
||||
across production, internal-test, and external-test actions, including
|
||||
same-only, external-only, and mixed directories, without combining their source
|
||||
units. Hidden, underscore-prefixed, wrong-platform, and otherwise ineligible
|
||||
files never register. Package variants, product-scoped recompiled copies,
|
||||
support, and generated main remain action wiring over exact identities and do
|
||||
not manufacture package collisions. The fold preflight does not replace or
|
||||
precede those coordinator-owned source-validation parses.
|
||||
|
||||
Separate compilation is the only driver build path; no compatibility mode
|
||||
switch remains.
|
||||
|
||||
@@ -294,6 +310,24 @@ have the same `ww: out of memory` diagnostic, invoke no compiler, assembler, or
|
||||
linker, and leave an empty caller work directory with no output, status, or
|
||||
staging path.
|
||||
|
||||
The same observer now owns selected-basename folding: ASCII production,
|
||||
internal, external, same-only, external-only, mixed, and cross-classification
|
||||
collisions; printable Unicode Kelvin folding; malformed UTF-8 replacement-rune
|
||||
keys with exact `\xNN` diagnostics; accepted composed/decomposed neighbors;
|
||||
ordinary-build isolation from test collisions; ignored folded pairs; reversed
|
||||
creation order; and direct/recursive collision-diagnostic parity. It proves zero
|
||||
compiler/assembler/linker calls and an empty workdir, including no
|
||||
test-support/main artifact, on cold rejection. A committed warm package then
|
||||
gains and loses a colliding file; the failed middle request preserves every
|
||||
unit, `.wwi`, assembly, object, archive, binary, tool record, and stamp, and
|
||||
removal restores exact reuse.
|
||||
`package_graph_diagnostics_are_stable` provides the corresponding direct,
|
||||
transitive, reversed-root, recursive, same-directory, exact-repeat, and warm
|
||||
import proof. `vendor_directory_import_resolution` proves folding uses the
|
||||
expanded vendor identity under reversed imports and product descriptors, even
|
||||
when both spellings reach one physical directory, and that multi-product
|
||||
rejection publishes no output, status, tool state, or staging residue.
|
||||
|
||||
`test/sep/sepinit_test.ww` is the single focused package-initialization owner.
|
||||
It generates all source trees temporarily and runs independent cold/persistent
|
||||
Cstage and WWstage legs. Its matrix covers blank-only reachability; dependency,
|
||||
@@ -353,6 +387,13 @@ real directory test products always run, while no-selected-test directories do
|
||||
not create a process. The byte-identity and bootstrap gates keep building on
|
||||
fresh scratch. `make clean` reclaims every workdir under `out/`.
|
||||
|
||||
Fold keys and first-spelling tables are request-only loader state and are never
|
||||
stored in a workdir. They do not alter successful unit bytes, action/storage
|
||||
keys, or tool records, so build format 18, test format 19, and semantic storage
|
||||
format 3 remain unchanged. Structural fold rejection occurs before staging and
|
||||
cannot mutate a committed generation; a later request with the collision
|
||||
removed evaluates the original exact-key reuse contract.
|
||||
|
||||
On a package or tree target, `ww build/test -w DIR` forwards that exact caller
|
||||
path as the one command-global semantic-action store; neither pattern spelling
|
||||
nor package-group name derives a persistent subdirectory. The delegated driver
|
||||
|
||||
@@ -481,6 +481,36 @@ fn directoryhasnew(path: str) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
fn directoryisempty(path: str) bool = {
|
||||
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
|
||||
assert(fd >= 0);
|
||||
let buf: []u8 = alloc([], 16384u64)!;
|
||||
buf.len = 16384;
|
||||
let n: i64 = os.getdents64(fd, buf.ptr, buf.len: u64);
|
||||
for (n > 0i64) {
|
||||
let off: i32 = 0;
|
||||
for (off < n: i32) {
|
||||
let reclen: i32 = (buf[off + 16]: i32)
|
||||
+ ((buf[off + 17]: i32) * 256);
|
||||
assert(reclen >= 20 && off + reclen <= n: i32);
|
||||
let len: i32 = 0;
|
||||
for (buf[off + 19 + len] != 0u8) { len += 1; };
|
||||
let dot: bool = len == 1 && buf[off + 19] == '.';
|
||||
let dotdot: bool = len == 2 && buf[off + 19] == '.'
|
||||
&& buf[off + 20] == '.';
|
||||
if (!dot && !dotdot) {
|
||||
assert(os.close(fd) == 0);
|
||||
return false;
|
||||
};
|
||||
off += reclen;
|
||||
};
|
||||
n = os.getdents64(fd, buf.ptr, buf.len: u64);
|
||||
};
|
||||
assert(n == 0i64);
|
||||
assert(os.close(fd) == 0);
|
||||
return true;
|
||||
};
|
||||
|
||||
fn byteshasat(s: str, off: i32, want: str) bool = {
|
||||
if (off < 0 || off + want.len > s.len) { return false; };
|
||||
let i: i32 = 0;
|
||||
@@ -5749,6 +5779,23 @@ fn hexbytes(value: str) str = {
|
||||
let identityactual: str = strings.concat(root, "/identityactual");
|
||||
let identityalias: str = strings.concat(root, "/identityalias");
|
||||
let reserved: str = strings.concat(root, "/reservedprobe");
|
||||
let foldsource: str = strings.concat(root, "/foldsource");
|
||||
let foldupper: str = strings.concat(foldsource, "/domain/Foo");
|
||||
let foldlower: str = strings.concat(foldsource, "/domain/foo");
|
||||
let folddirect: str = strings.concat(foldsource, "/cmd/direct");
|
||||
let foldbackward: str = strings.concat(foldsource, "/cmd/backward");
|
||||
let foldindirect: str = strings.concat(foldsource, "/cmd/indirect");
|
||||
let foldleft: str = strings.concat(foldsource, "/parent/left");
|
||||
let foldright: str = strings.concat(foldsource, "/parent/right");
|
||||
let foldrepeat: str = strings.concat(foldsource, "/cmd/repeat");
|
||||
let foldaliasroot: str = strings.concat(foldsource, "/aliasroot");
|
||||
let foldaliasactual: str = strings.concat(foldsource, "/alias/Foo");
|
||||
let foldalias: str = strings.concat(foldsource, "/alias/foo");
|
||||
let foldrootleft: str = strings.concat(foldsource, "/roots/left");
|
||||
let foldrootright: str = strings.concat(foldsource, "/roots/right");
|
||||
let foldrecursive: str = strings.concat(root, "/foldrecursive");
|
||||
let foldrecursiveupper: str = strings.concat(foldrecursive, "/domain/Foo");
|
||||
let foldrecursivelower: str = strings.concat(foldrecursive, "/domain/foo");
|
||||
assert(os.mkdir(missing, 448i32) == 0);
|
||||
assert(os.mkdir(cycle, 448i32) == 0);
|
||||
assert(os.mkdir(left, 448i32) == 0);
|
||||
@@ -5758,6 +5805,11 @@ fn hexbytes(value: str) str = {
|
||||
assert(os.mkdir(identityroot, 448i32) == 0);
|
||||
assert(os.mkdir(identityactual, 448i32) == 0);
|
||||
assert(os.mkdir(reserved, 448i32) == 0);
|
||||
mkdirall(foldupper); mkdirall(foldlower); mkdirall(folddirect);
|
||||
mkdirall(foldbackward); mkdirall(foldindirect); mkdirall(foldleft);
|
||||
mkdirall(foldright); mkdirall(foldrepeat); mkdirall(foldaliasroot);
|
||||
mkdirall(foldaliasactual); mkdirall(foldrootleft); mkdirall(foldrootright);
|
||||
mkdirall(foldrecursiveupper); mkdirall(foldrecursivelower);
|
||||
writefile(strings.concat(missing, "/missing.ww"),
|
||||
"package missing;\nfn local() void = { };\n");
|
||||
writefile(strings.concat(missing, "/missing_test.ww"), strings.concat(
|
||||
@@ -5798,6 +5850,49 @@ fn hexbytes(value: str) str = {
|
||||
writefile(strings.concat(reserved, "/reservedprobe.ww"), strings.concat(
|
||||
"package reservedprobe;\nimport __wwlocal.fake;\n",
|
||||
"export fn value() i32 = { return 1; };\n"));
|
||||
writefile(strings.concat(foldupper, "/pkg.ww"),
|
||||
"package upperdecl;\nexport fn value() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(foldlower, "/pkg.ww"),
|
||||
"package lowerdecl;\nexport fn value() i32 = { return 2; };\n");
|
||||
// File creation and import order deliberately disagree with byte order.
|
||||
writefile(strings.concat(folddirect, "/z.ww"), strings.concat(
|
||||
"package main;\nimport Lower domain.foo;\n",
|
||||
"fn main() i32 = { return 0; };\n"));
|
||||
writefile(strings.concat(folddirect, "/a.ww"),
|
||||
"package main;\nimport Upper domain.Foo;\n");
|
||||
writefile(strings.concat(foldbackward, "/a.ww"),
|
||||
"package main;\nimport Lower domain.foo;\n");
|
||||
writefile(strings.concat(foldbackward, "/z.ww"), strings.concat(
|
||||
"package main;\nimport Upper domain.Foo;\n",
|
||||
"fn main() i32 = { return 0; };\n"));
|
||||
writefile(strings.concat(foldleft, "/pkg.ww"), strings.concat(
|
||||
"package left;\nimport _ domain.Foo;\n",
|
||||
"export fn value() i32 = { return 1; };\n"));
|
||||
writefile(strings.concat(foldright, "/pkg.ww"), strings.concat(
|
||||
"package right;\nimport _ domain.foo;\n",
|
||||
"export fn value() i32 = { return 2; };\n"));
|
||||
writefile(strings.concat(foldindirect, "/main.ww"), strings.concat(
|
||||
"package main;\nimport _ parent.left;\nimport _ parent.right;\n",
|
||||
"fn main() i32 = { return 0; };\n"));
|
||||
writefile(strings.concat(foldrepeat, "/a.ww"),
|
||||
"package main;\nimport _ domain.Foo;\n");
|
||||
writefile(strings.concat(foldrepeat, "/z.ww"), strings.concat(
|
||||
"package main;\nimport _ domain.Foo;\n",
|
||||
"fn main() i32 = { return 0; };\n"));
|
||||
writefile(strings.concat(foldaliasactual, "/pkg.ww"),
|
||||
"package aliasdecl;\nexport fn value() i32 = { return 1; };\n");
|
||||
assert(os.symlink(foldaliasactual, foldalias) == 0);
|
||||
writefile(strings.concat(foldaliasroot, "/main.ww"), strings.concat(
|
||||
"package main;\nimport _ alias.Foo;\nimport _ alias.foo;\n",
|
||||
"fn main() i32 = { return 0; };\n"));
|
||||
writefile(strings.concat(foldrootleft, "/pkg.ww"),
|
||||
"package leftroot;\nimport _ domain.Foo;\n");
|
||||
writefile(strings.concat(foldrootright, "/pkg.ww"),
|
||||
"package rightroot;\nimport _ domain.foo;\n");
|
||||
writefile(strings.concat(foldrecursiveupper, "/pkg.ww"),
|
||||
"package upper;\nexport fn value() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(foldrecursivelower, "/pkg.ww"),
|
||||
"package lower;\nexport fn value() i32 = { return 2; };\n");
|
||||
rejectpackagestable(root, "missing", missing,
|
||||
"cannot find package nowhere");
|
||||
rejectpackagestable(root, "cycle", cycle, "ww: dependency cycle:");
|
||||
@@ -5808,32 +5903,46 @@ fn hexbytes(value: str) str = {
|
||||
let stages: []str = ["ww", "ww_ww"];
|
||||
let tooltrace: str = strings.concat(root, "/identity-tool.trace");
|
||||
let compilerwrapper: str = strings.concat(root, "/identity-w6c.sh");
|
||||
let assemblerwrapper: str = strings.concat(root, "/identity-w6a.sh");
|
||||
let linkerwrapper: str = strings.concat(root, "/identity-w6l.sh");
|
||||
writefile(tooltrace, "");
|
||||
writeexecutable(compilerwrapper, strings.concat(
|
||||
"#!/bin/sh\nprintf 'compile\\n' >> \"$WW_IDENTITY_TRACE\"\n",
|
||||
"#!/bin/sh\nprintf 'compile' >> \"$WW_IDENTITY_TRACE\"\n",
|
||||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||||
"\"$WW_IDENTITY_TRACE\"; done\nprintf '\\n' >> ",
|
||||
"\"$WW_IDENTITY_TRACE\"\n",
|
||||
"exec \"$WW_IDENTITY_W6C\" \"$@\"\n"));
|
||||
writeexecutable(assemblerwrapper, strings.concat(
|
||||
"#!/bin/sh\nprintf 'assemble' >> \"$WW_IDENTITY_TRACE\"\n",
|
||||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||||
"\"$WW_IDENTITY_TRACE\"; done\nprintf '\\n' >> ",
|
||||
"\"$WW_IDENTITY_TRACE\"\n",
|
||||
"exec \"$WW_IDENTITY_W6A\" \"$@\"\n"));
|
||||
writeexecutable(linkerwrapper, strings.concat(
|
||||
"#!/bin/sh\nprintf 'link\\n' >> \"$WW_IDENTITY_TRACE\"\n",
|
||||
"#!/bin/sh\nprintf 'link' >> \"$WW_IDENTITY_TRACE\"\n",
|
||||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||||
"\"$WW_IDENTITY_TRACE\"; done\nprintf '\\n' >> ",
|
||||
"\"$WW_IDENTITY_TRACE\"\n",
|
||||
"exec \"$WW_IDENTITY_W6L\" \"$@\"\n"));
|
||||
let baseenv: []str = os.getenvs();
|
||||
let traceenv: []str = alloc([], (baseenv.len + 5): u64)!;
|
||||
let traceenv: []str = alloc([], (baseenv.len + 4): 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_IDENTITY_TRACE=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6C=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6A=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6L=")) {
|
||||
append(traceenv, baseenv[ei]);
|
||||
};
|
||||
ei += 1;
|
||||
};
|
||||
append(traceenv, strings.concat("WW_W6C=", compilerwrapper));
|
||||
append(traceenv, strings.concat("WW_W6A=", assemblerwrapper));
|
||||
append(traceenv, strings.concat("WW_W6L=", linkerwrapper));
|
||||
append(traceenv, strings.concat("WW_IDENTITY_TRACE=", tooltrace));
|
||||
append(traceenv, strings.concat("WW_IDENTITY_W6C=", driver("w6c")));
|
||||
append(traceenv, strings.concat("WW_IDENTITY_W6L=", driver("w6l")));
|
||||
let out: commandout;
|
||||
let i: i32 = 0;
|
||||
let identitydiagnostics: []str = ["", ""];
|
||||
@@ -5857,6 +5966,187 @@ fn hexbytes(value: str) str = {
|
||||
// compiler or linker invocation. The converse is covered by the shared
|
||||
// multi-context resolution test above.
|
||||
assert(readfile(tooltrace).len == 0);
|
||||
|
||||
let foldexpected: str =
|
||||
"ww: case-insensitive import collision: \"domain.Foo\" and \"domain.foo\"\n";
|
||||
let aliasexpected: str =
|
||||
"ww: case-insensitive import collision: \"alias.Foo\" and \"alias.foo\"\n";
|
||||
let foldtargets: []str = [folddirect, foldbackward, foldindirect,
|
||||
foldaliasroot];
|
||||
let foldlabels: []str = ["direct", "backward", "indirect", "one-dir"];
|
||||
let foldwant: []str = [foldexpected, foldexpected, foldexpected,
|
||||
aliasexpected];
|
||||
let compilers: []str = ["w6c", "w6c_ww"];
|
||||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||||
let linkers: []str = ["w6l", "w6l_ww"];
|
||||
let foldrefs: []str = ["", "", "", "", "", "", ""];
|
||||
let repeatref: str = "";
|
||||
i = 0;
|
||||
for (i < stages.len) {
|
||||
let foldenv: []str = alloc([], (traceenv.len + 3): u64)!;
|
||||
let tei: i32 = 0;
|
||||
for (tei < traceenv.len) {
|
||||
append(foldenv, traceenv[tei]); tei += 1;
|
||||
};
|
||||
append(foldenv, strings.concat("WW_IDENTITY_W6C=",
|
||||
driver(compilers[i])));
|
||||
append(foldenv, strings.concat("WW_IDENTITY_W6A=",
|
||||
driver(assemblers[i])));
|
||||
append(foldenv, strings.concat("WW_IDENTITY_W6L=",
|
||||
driver(linkers[i])));
|
||||
let fi: i32 = 0;
|
||||
for (fi < foldtargets.len) {
|
||||
rewritefile(tooltrace, "");
|
||||
let work: str = strings.concat(root, "/fold-", foldlabels[fi],
|
||||
"-", stages[i], "-work");
|
||||
assert(os.mkdir(work, 448i32) == 0);
|
||||
let output: str = strings.concat(root, "/fold-", foldlabels[fi],
|
||||
"-", stages[i]);
|
||||
let av: []str = [driver(stages[i]), "build", "-w", work,
|
||||
"-I", foldsource, "-o", output, foldtargets[fi]];
|
||||
runcommandenv(root, strings.concat("case-fold-", foldlabels[fi],
|
||||
"-", stages[i]), av, foldenv,
|
||||
(60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(out.stdout.len == 0 && same(out.stderr, foldwant[fi]));
|
||||
assert(readfile(tooltrace).len == 0);
|
||||
assert(!os.exists(output));
|
||||
assert(!os.exists(strings.concat(work, "/.wwtool.stamp")));
|
||||
assert(directoryisempty(work));
|
||||
if (i == 0) { foldrefs[fi] = strings.dup(out.stderr); }
|
||||
else { assert(same(foldrefs[fi], out.stderr)); };
|
||||
fi += 1;
|
||||
};
|
||||
|
||||
// The coordinator canonicalizes product order, so reversing roots cannot
|
||||
// choose a different representative or diagnostic.
|
||||
let rootfirst: []str = [foldrootleft, foldrootright];
|
||||
let rootsecond: []str = [foldrootright, foldrootleft];
|
||||
let oi: i32 = 0;
|
||||
for (oi < rootfirst.len) {
|
||||
rewritefile(tooltrace, "");
|
||||
let work: str = strings.concat(root, "/fold-roots-", stages[i],
|
||||
"-", foldlabels[oi], "-work");
|
||||
assert(os.mkdir(work, 448i32) == 0);
|
||||
let av: []str = [driver(stages[i]), "build", "-w", work,
|
||||
"-I", foldsource, rootfirst[oi], rootsecond[oi]];
|
||||
runcommandenv(root, strings.concat("case-fold-roots-", stages[i],
|
||||
"-", foldlabels[oi]), av, foldenv,
|
||||
(60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(out.stdout.len == 0 && has(out.stderr, foldexpected));
|
||||
assert(readfile(tooltrace).len == 0);
|
||||
assert(!os.exists(strings.concat(work, "/.wwtool.stamp")));
|
||||
assert(directoryisempty(work));
|
||||
let refindex: i32 = foldtargets.len + oi;
|
||||
if (i == 0) { foldrefs[refindex] = strings.dup(out.stderr); }
|
||||
else { assert(same(foldrefs[refindex], out.stderr)); };
|
||||
oi += 1;
|
||||
};
|
||||
assert(same(foldrefs[4], foldrefs[5]));
|
||||
|
||||
rewritefile(tooltrace, "");
|
||||
let recursivework: str = strings.concat(root, "/fold-recursive-",
|
||||
stages[i], "-work");
|
||||
assert(os.mkdir(recursivework, 448i32) == 0);
|
||||
let recursiveav: []str = [driver(stages[i]), "build", "-w",
|
||||
recursivework, "-I", foldrecursive,
|
||||
strings.concat(foldrecursive, "/...")];
|
||||
runcommandenv(root, strings.concat("case-fold-recursive-", stages[i]),
|
||||
recursiveav, foldenv,
|
||||
(60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(out.stdout.len == 0 && has(out.stderr, foldexpected));
|
||||
assert(readfile(tooltrace).len == 0);
|
||||
assert(!os.exists(strings.concat(recursivework, "/.wwtool.stamp")));
|
||||
assert(directoryisempty(recursivework));
|
||||
if (i == 0) { foldrefs[6] = strings.dup(out.stderr); }
|
||||
else { assert(same(foldrefs[6], out.stderr)); };
|
||||
|
||||
// Exact repeated occurrences retain one canonical action and accepted
|
||||
// compiler input, rather than being merged by the folded key.
|
||||
rewritefile(tooltrace, "");
|
||||
let repeatwork: str = strings.concat(root, "/fold-repeat-", stages[i],
|
||||
"-work");
|
||||
let repeatout: str = strings.concat(root, "/fold-repeat-", stages[i]);
|
||||
assert(os.mkdir(repeatwork, 448i32) == 0);
|
||||
let repeatav: []str = [driver(stages[i]), "build", "-w", repeatwork,
|
||||
"-I", foldsource, "-o", repeatout, foldrepeat];
|
||||
runcommandenv(root, strings.concat("case-fold-repeat-", stages[i]),
|
||||
repeatav, foldenv,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||
let repeattrace: str = readfile(tooltrace);
|
||||
assert(occurrences(repeattrace,
|
||||
"/domain.Foo.unit.new>") == 1);
|
||||
let repeatline: str = linecontaining(repeattrace,
|
||||
"/cmd.repeat.unit.new>");
|
||||
assert(occurrences(repeatline, "<--import><domain.Foo>") == 1);
|
||||
assert(occurrences(repeatline, "/domain.Foo.wwi.new>") == 1);
|
||||
assert(os.exists(strings.concat(repeatwork, "/.wwtool.stamp")));
|
||||
let repeatpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c",
|
||||
"/.wwtool.w6a", "/.wwtool.stamp", "/cmd.repeat.unit.ww",
|
||||
"/cmd.repeat.wwi", "/cmd.repeat.s", "/cmd.repeat.o",
|
||||
"/cmd.repeat.a", "/cmd.repeat.init.unit.ww",
|
||||
"/cmd.repeat.init.s", "/cmd.repeat.init.o",
|
||||
"/domain.Foo.unit.ww", "/domain.Foo.wwi", "/domain.Foo.s",
|
||||
"/domain.Foo.o", "/domain.Foo.a"];
|
||||
let repeatbytes: []str = alloc([], repeatpaths.len: u64)!;
|
||||
let rsi: i32 = 0;
|
||||
for (rsi < repeatpaths.len) {
|
||||
append(repeatbytes, strings.dup(readfile(strings.concat(repeatwork,
|
||||
repeatpaths[rsi]))));
|
||||
rsi += 1;
|
||||
};
|
||||
let repeatoutput: str = strings.dup(readfile(repeatout));
|
||||
if (i == 0) { repeatref = strings.dup(repeatoutput); }
|
||||
else { assert(same(repeatref, repeatoutput)); };
|
||||
let repeatcollision: str = strings.concat(foldrepeat, "/m.ww");
|
||||
writefile(repeatcollision,
|
||||
"package main;\nimport _ domain.foo;\n");
|
||||
rewritefile(tooltrace, "");
|
||||
runcommandenv(root, strings.concat("case-fold-repeat-added-", stages[i]),
|
||||
repeatav, foldenv,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(same(out.stderr, foldexpected));
|
||||
assert(readfile(tooltrace).len == 0);
|
||||
rsi = 0;
|
||||
for (rsi < repeatpaths.len) {
|
||||
assert(same(repeatbytes[rsi], readfile(strings.concat(repeatwork,
|
||||
repeatpaths[rsi]))));
|
||||
rsi += 1;
|
||||
};
|
||||
assert(!directoryhasnew(repeatwork));
|
||||
assert(same(repeatoutput, readfile(repeatout)));
|
||||
assert(!os.exists(strings.concat(repeatout, ".new")));
|
||||
assert(!os.exists(strings.concat(repeatout, ".sepwork")));
|
||||
assert(!os.exists(strings.concat(repeatwork, "/domain.foo.unit.ww")));
|
||||
assert(!os.exists(strings.concat(repeatwork, "/domain.foo.wwi")));
|
||||
assert(!os.exists(strings.concat(repeatwork, "/domain.foo.a")));
|
||||
assert(os.remove(repeatcollision) == 0);
|
||||
rewritefile(tooltrace, "");
|
||||
runcommandenv(root, strings.concat("case-fold-repeat-removed-", stages[i]),
|
||||
repeatav, foldenv,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(occurrences(readfile(tooltrace), "compile") == 0);
|
||||
assert(occurrences(readfile(tooltrace), "assemble") == 0);
|
||||
assert(occurrences(readfile(tooltrace), "link") == 1);
|
||||
rsi = 0;
|
||||
for (rsi < repeatpaths.len) {
|
||||
assert(same(repeatbytes[rsi], readfile(strings.concat(repeatwork,
|
||||
repeatpaths[rsi]))));
|
||||
rsi += 1;
|
||||
};
|
||||
assert(!directoryhasnew(repeatwork));
|
||||
assert(same(repeatoutput, readfile(repeatout)));
|
||||
assert(!os.exists(strings.concat(repeatout, ".new")));
|
||||
assert(!os.exists(strings.concat(repeatout, ".sepwork")));
|
||||
i += 1;
|
||||
};
|
||||
rewritefile(tooltrace, "");
|
||||
let reserveddiagnostics: []str = ["", ""];
|
||||
i = 0;
|
||||
for (i < stages.len) {
|
||||
@@ -5889,9 +6179,25 @@ fn hexbytes(value: str) str = {
|
||||
let vendored: str = strings.concat(app, "/vendor/vendored");
|
||||
let order: str = strings.concat(root, "/order");
|
||||
let tools: str = strings.concat(root, "/tools");
|
||||
let foldfiles: str = strings.concat(root, "/foldfiles");
|
||||
let foldunicode: str = strings.concat(foldfiles, "/unicode");
|
||||
let foldinvalid: str = strings.concat(foldfiles, "/invalid");
|
||||
let foldnonormal: str = strings.concat(foldfiles, "/nonormal");
|
||||
let foldinternal: str = strings.concat(foldfiles, "/internal");
|
||||
let foldexternal: str = strings.concat(foldfiles, "/external");
|
||||
let foldsameonly: str = strings.concat(foldfiles, "/sameonly");
|
||||
let foldexternalonly: str = strings.concat(foldfiles, "/externalonly");
|
||||
let foldmixed: str = strings.concat(foldfiles, "/mixed");
|
||||
let foldcross: str = strings.concat(foldfiles, "/cross");
|
||||
let foldcreationroot: str = strings.concat(root, "/fold-creation-order");
|
||||
let foldcreation: str = strings.concat(foldcreationroot, "/prod");
|
||||
mkdirall(app); mkdirall(good); mkdirall(pkg); mkdirall(testgood);
|
||||
mkdirall(wrongonly); mkdirall(notests); mkdirall(internal);
|
||||
mkdirall(command); mkdirall(vendored); mkdirall(order); mkdirall(tools);
|
||||
mkdirall(foldunicode); mkdirall(foldinvalid); mkdirall(foldnonormal);
|
||||
mkdirall(foldinternal); mkdirall(foldexternal); mkdirall(foldsameonly);
|
||||
mkdirall(foldexternalonly); mkdirall(foldmixed); mkdirall(foldcross);
|
||||
mkdirall(foldcreation);
|
||||
|
||||
let good5: str = strings.concat("package good;\n",
|
||||
"export fn value() i32 = { return 5; };\n");
|
||||
@@ -5949,6 +6255,12 @@ fn hexbytes(value: str) str = {
|
||||
ignoredinternal);
|
||||
writefile(strings.concat(app, "/badmain_linux_arm64.ww"), ignoredmain);
|
||||
writefile(strings.concat(app, "/badvendor_windows.ww"), ignoredvendor);
|
||||
writefile(strings.concat(app, "/.Fold.ww"), ignoredparse);
|
||||
writefile(strings.concat(app, "/.fold.ww"), ignoredparseedit);
|
||||
writefile(strings.concat(app, "/_Fold.ww"), ignoredparse);
|
||||
writefile(strings.concat(app, "/_fold.ww"), ignoredparseedit);
|
||||
writefile(strings.concat(app, "/Fold_windows.ww"), ignoredparse);
|
||||
writefile(strings.concat(app, "/fold_windows.ww"), ignoredparseedit);
|
||||
writefile(strings.concat(internal, "/secret.ww"), strings.concat(
|
||||
"package secret;\n",
|
||||
"export fn value() i32 = { return 19; };\n"));
|
||||
@@ -6004,6 +6316,76 @@ fn hexbytes(value: str) str = {
|
||||
writefile(strings.concat(notests, "/base.ww"),
|
||||
"package notests;\nexport fn value() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(notests, "/only_windows_test.ww"), ignoredparse);
|
||||
writefile(strings.concat(pkg, "/.Fold_test.ww"), ignoredparse);
|
||||
writefile(strings.concat(pkg, "/.fold_test.ww"), ignoredparseedit);
|
||||
writefile(strings.concat(pkg, "/_Fold_test.ww"), ignoredparse);
|
||||
writefile(strings.concat(pkg, "/_fold_test.ww"), ignoredparseedit);
|
||||
writefile(strings.concat(pkg, "/Fold_windows_test.ww"), ignoredparse);
|
||||
writefile(strings.concat(pkg, "/fold_windows_test.ww"), ignoredparseedit);
|
||||
|
||||
writefile(strings.concat(foldunicode, "/K.ww"),
|
||||
"package main;\nfn latin() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(foldunicode, "/K.ww"),
|
||||
"package main;\nfn kelvin() i32 = { return 2; };\n");
|
||||
writefile(strings.concat(foldunicode, "/main.ww"),
|
||||
"package main;\nfn main() i32 = { return 0; };\n");
|
||||
let invalidupperbytes: []u8 = alloc([], 6u64)!;
|
||||
append(invalidupperbytes, 'A'); append(invalidupperbytes, '-');
|
||||
append(invalidupperbytes, 255u8); append(invalidupperbytes, '.');
|
||||
append(invalidupperbytes, 'w'); append(invalidupperbytes, 'w');
|
||||
let invalidlowerbytes: []u8 = alloc([], 6u64)!;
|
||||
append(invalidlowerbytes, 'a'); append(invalidlowerbytes, '-');
|
||||
append(invalidlowerbytes, 254u8); append(invalidlowerbytes, '.');
|
||||
append(invalidlowerbytes, 'w'); append(invalidlowerbytes, 'w');
|
||||
writefile(strings.concat(foldinvalid, "/",
|
||||
strings.frombytes(invalidupperbytes)),
|
||||
"package main;\nfn invalidupper() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(foldinvalid, "/",
|
||||
strings.frombytes(invalidlowerbytes)),
|
||||
"package main;\nfn invalidlower() i32 = { return 2; };\n");
|
||||
writefile(strings.concat(foldinvalid, "/main.ww"),
|
||||
"package main;\nfn main() i32 = { return 0; };\n");
|
||||
writefile(strings.concat(foldnonormal, "/É.ww"),
|
||||
"package main;\nfn composed() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(foldnonormal, "/É.ww"),
|
||||
"package main;\nfn decomposed() i32 = { return 2; };\n");
|
||||
writefile(strings.concat(foldnonormal, "/main.ww"),
|
||||
"package main;\nfn main() i32 = { return 0; };\n");
|
||||
writefile(strings.concat(foldinternal, "/base.ww"),
|
||||
"package sample;\nexport fn value() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(foldinternal, "/File_test.ww"),
|
||||
"package sample;\n@test fn upper() void = { assert(true); };\n");
|
||||
writefile(strings.concat(foldinternal, "/file_test.ww"),
|
||||
"package sample;\n@test fn lower() void = { assert(true); };\n");
|
||||
writefile(strings.concat(foldexternal, "/base.ww"),
|
||||
"package sample;\nexport fn value() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(foldexternal, "/File_test.ww"),
|
||||
"package sample_test;\n@test fn upper() void = { assert(true); };\n");
|
||||
writefile(strings.concat(foldexternal, "/file_test.ww"),
|
||||
"package sample_test;\n@test fn lower() void = { assert(true); };\n");
|
||||
writefile(strings.concat(foldsameonly, "/File_test.ww"),
|
||||
"package sample;\n@test fn upper() void = { assert(true); };\n");
|
||||
writefile(strings.concat(foldsameonly, "/file_test.ww"),
|
||||
"package sample;\n@test fn lower() void = { assert(true); };\n");
|
||||
writefile(strings.concat(foldexternalonly, "/File_test.ww"),
|
||||
"package sample_test;\n@test fn upper() void = { assert(true); };\n");
|
||||
writefile(strings.concat(foldexternalonly, "/file_test.ww"),
|
||||
"package sample_test;\n@test fn lower() void = { assert(true); };\n");
|
||||
writefile(strings.concat(foldmixed, "/File_test.ww"),
|
||||
"package sample;\n@test fn upper() void = { assert(true); };\n");
|
||||
writefile(strings.concat(foldmixed, "/file_test.ww"),
|
||||
"package sample_test;\n@test fn lower() void = { assert(true); };\n");
|
||||
writefile(strings.concat(foldcross, "/X_TEST.ww"),
|
||||
"package cross;\nexport fn value() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(foldcross, "/x_test.ww"),
|
||||
"package cross;\n@test fn lower() void = { assert(value() == 1); };\n");
|
||||
// The lower spelling is created first to prove getdents creation order is
|
||||
// irrelevant to both direct and recursive collision diagnostics.
|
||||
writefile(strings.concat(foldcreation, "/file.ww"),
|
||||
"package main;\nfn lower() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(foldcreation, "/File.ww"), strings.concat(
|
||||
"package main;\nfn upper() i32 = { return 2; };\n",
|
||||
"fn main() i32 = { return 0; };\n"));
|
||||
// Creation order is deliberately the inverse of byte order.
|
||||
writefile(strings.concat(order, "/z_linux.ww"),
|
||||
"package order;\n@test fn z() void = { };\n");
|
||||
@@ -6061,6 +6443,12 @@ fn hexbytes(value: str) str = {
|
||||
let faildiagref: str = "";
|
||||
let recursiveoutref: str = "";
|
||||
let recursiveerrref: str = "";
|
||||
let foldfilerefs: []str = ["", "", "", "", "", "", "", ""];
|
||||
let crossref: str = "";
|
||||
let nonormalref: str = "";
|
||||
let nonormalunitref: str = "";
|
||||
let foldcreationref: str = "";
|
||||
let foldcreationrecursiveref: str = "";
|
||||
let baseenv: []str = os.getenvs();
|
||||
let si: i32 = 0;
|
||||
for (si < stages.len) {
|
||||
@@ -6513,6 +6901,267 @@ fn hexbytes(value: str) str = {
|
||||
assert(same(recursiveoutref, out.stdout));
|
||||
assert(same(recursiveerrref, out.stderr));
|
||||
};
|
||||
|
||||
// An upper-case TEST suffix is production source, while its lower-case
|
||||
// twin is test source. Ordinary build stays isolated; test selects both
|
||||
// categories into one directory-owned filename-collision scope.
|
||||
let crosswork: str = strings.concat(root, "/", labels[si],
|
||||
"-fold-cross-build-work");
|
||||
let crossout: str = strings.concat(root, "/", labels[si],
|
||||
"-fold-cross.a");
|
||||
assert(os.mkdir(crosswork, 448i32) == 0);
|
||||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||||
rewritefile(linkertrace, "");
|
||||
let crossbuildav: []str = [driver(stages[si]), "build", "-w",
|
||||
crosswork, "-I", foldfiles, "-o", crossout, foldcross];
|
||||
runcommandenv(root, strings.concat("fold-cross-build-", labels[si]),
|
||||
crossbuildav, env,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||
assert(!has(readfile(strings.concat(crosswork, "/cross.unit.ww")),
|
||||
"@test fn lower"));
|
||||
if (si == 0) { crossref = strings.dup(readfile(crossout)); }
|
||||
else { assert(same(crossref, readfile(crossout))); };
|
||||
|
||||
let foldtargets: []str = [foldunicode, foldinvalid, foldinternal,
|
||||
foldexternal, foldsameonly, foldexternalonly, foldmixed, foldcross];
|
||||
let foldmodes: []str = ["build", "build", "test", "test", "test",
|
||||
"test", "test", "test"];
|
||||
let foldpackages: []str = ["", "", "sample", "sample", "sample",
|
||||
"sample", "sample", "cross"];
|
||||
let foldlabels: []str = ["unicode", "invalid", "internal", "external",
|
||||
"same-only", "external-only", "mixed", "cross"];
|
||||
let foldwants: []str = [
|
||||
"ww: case-insensitive file name collision: \"K.ww\" and \"K.ww\"\n",
|
||||
"ww: case-insensitive file name collision: \"A-\\xff.ww\" and \"a-\\xfe.ww\"\n",
|
||||
"ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n",
|
||||
"ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n",
|
||||
"ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n",
|
||||
"ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n",
|
||||
"ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n",
|
||||
"ww: case-insensitive file name collision: \"X_TEST.ww\" and \"x_test.ww\"\n",
|
||||
];
|
||||
let fi: i32 = 0;
|
||||
for (fi < foldtargets.len) {
|
||||
let foldwork: str = strings.concat(root, "/", labels[si],
|
||||
"-fold-", foldlabels[fi], "-work");
|
||||
let foldoutput: str = strings.concat(root, "/", labels[si],
|
||||
"-fold-", foldlabels[fi]);
|
||||
assert(os.mkdir(foldwork, 448i32) == 0);
|
||||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||||
rewritefile(linkertrace, "");
|
||||
let foldbuildav: []str = [driver(stages[si]), "build", "-w",
|
||||
foldwork, "-I", foldfiles, "-o", foldoutput, foldtargets[fi]];
|
||||
let foldtestav: []str = [driver(stages[si]), "test", "-w",
|
||||
foldwork, "-I", foldfiles, foldtargets[fi]];
|
||||
let foldav: []str = foldtestav;
|
||||
if (same(foldmodes[fi], "build")) { foldav = foldbuildav; };
|
||||
runcommandenv(root, strings.concat("filename-fold-", labels[si],
|
||||
"-", foldlabels[fi]), foldav, env,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
let foldfullwant: str = foldwants[fi];
|
||||
if (same(foldmodes[fi], "test")) {
|
||||
foldfullwant = strings.concat(foldwants[fi], "FAIL ",
|
||||
foldtargets[fi], " [", foldpackages[fi],
|
||||
"] (build exit 1)\n");
|
||||
};
|
||||
assert(out.stdout.len == 0 && same(out.stderr, foldfullwant));
|
||||
assert(readfile(compilertrace).len == 0);
|
||||
assert(readfile(assemblertrace).len == 0);
|
||||
assert(readfile(linkertrace).len == 0);
|
||||
assert(!os.exists(foldoutput));
|
||||
assert(!os.exists(strings.concat(foldoutput, ".new")));
|
||||
assert(!os.exists(strings.concat(foldoutput, ".sepwork")));
|
||||
assert(!os.exists(strings.concat(foldwork, "/.wwtool.stamp")));
|
||||
assert(directoryisempty(foldwork));
|
||||
if (si == 0) { foldfilerefs[fi] = strings.dup(out.stderr); }
|
||||
else { assert(same(foldfilerefs[fi], out.stderr)); };
|
||||
fi += 1;
|
||||
};
|
||||
|
||||
let creationmodes: []str = ["direct", "recursive"];
|
||||
let creationfull: []str = ["", ""];
|
||||
let creationwant: str =
|
||||
"ww: case-insensitive file name collision: \"File.ww\" and \"file.ww\"\n";
|
||||
let ci: i32 = 0;
|
||||
for (ci < creationmodes.len) {
|
||||
let creationwork: str = strings.concat(root, "/", labels[si],
|
||||
"-fold-creation-", creationmodes[ci], "-work");
|
||||
let creationout: str = strings.concat(root, "/", labels[si],
|
||||
"-fold-creation-", creationmodes[ci]);
|
||||
assert(os.mkdir(creationwork, 448i32) == 0);
|
||||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||||
rewritefile(linkertrace, "");
|
||||
let directav: []str = [driver(stages[si]), "build", "-w",
|
||||
creationwork, "-I", foldcreationroot, "-o", creationout,
|
||||
foldcreation];
|
||||
if (ci != 0) { assert(os.mkdir(creationout, 448i32) == 0); };
|
||||
let recursiveav: []str = [driver(stages[si]), "build", "-w",
|
||||
creationwork, "-I", foldcreationroot, "-o",
|
||||
strings.concat(creationout, "/"),
|
||||
strings.concat(foldcreationroot, "/...")];
|
||||
let creationav: []str = directav;
|
||||
if (ci != 0) { creationav = recursiveav; };
|
||||
runcommandenv(root, strings.concat("filename-fold-creation-",
|
||||
labels[si], "-", creationmodes[ci]), creationav, env,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(out.stdout.len == 0);
|
||||
let creationfullwant: str = creationwant;
|
||||
if (ci != 0) {
|
||||
creationfullwant = strings.concat(creationwant, "FAIL ",
|
||||
foldcreation, " [main] (build exit 1)\n");
|
||||
};
|
||||
assert(same(out.stderr, creationfullwant));
|
||||
assert(readfile(compilertrace).len == 0);
|
||||
assert(readfile(assemblertrace).len == 0);
|
||||
assert(readfile(linkertrace).len == 0);
|
||||
if (ci == 0) { assert(!os.exists(creationout)); }
|
||||
else { assert(directoryisempty(creationout)); };
|
||||
assert(!os.exists(strings.concat(creationout, ".new")));
|
||||
assert(!os.exists(strings.concat(creationout, ".sepwork")));
|
||||
assert(directoryisempty(creationwork));
|
||||
creationfull[ci] = strings.dup(out.stderr);
|
||||
ci += 1;
|
||||
};
|
||||
if (si == 0) {
|
||||
foldcreationref = strings.dup(creationfull[0]);
|
||||
foldcreationrecursiveref = strings.dup(creationfull[1]);
|
||||
} else {
|
||||
assert(same(foldcreationref, creationfull[0]));
|
||||
assert(same(foldcreationrecursiveref, creationfull[1]));
|
||||
};
|
||||
|
||||
// Canonically equivalent Unicode normalization forms remain distinct.
|
||||
let nonormalwork: str = strings.concat(root, "/", labels[si],
|
||||
"-fold-nonormal-work");
|
||||
let nonormalout: str = strings.concat(root, "/", labels[si],
|
||||
"-fold-nonormal");
|
||||
assert(os.mkdir(nonormalwork, 448i32) == 0);
|
||||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||||
rewritefile(linkertrace, "");
|
||||
let nonormalav: []str = [driver(stages[si]), "build", "-w",
|
||||
nonormalwork, "-I", foldfiles, "-o", nonormalout, foldnonormal];
|
||||
runcommandenv(root, strings.concat("filename-no-normalization-",
|
||||
labels[si]), nonormalav, env,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||
let nonormalunit: str = readfile(strings.concat(nonormalwork,
|
||||
"/nonormal.unit.ww"));
|
||||
assert(has(nonormalunit, "fn composed() i32"));
|
||||
assert(has(nonormalunit, "fn decomposed() i32"));
|
||||
if (si == 0) { nonormalref = strings.dup(readfile(nonormalout)); }
|
||||
else { assert(same(nonormalref, readfile(nonormalout))); };
|
||||
if (si == 0) { nonormalunitref = strings.dup(nonormalunit); }
|
||||
else { assert(same(nonormalunitref, nonormalunit)); };
|
||||
|
||||
// A collision introduced after a committed generation is pre-tool and
|
||||
// cannot mutate any successful action, stamp, or published output. Its
|
||||
// removal restores exact warm reuse (only the normal relink remains).
|
||||
let warmname: str = strings.concat("warm", labels[si]);
|
||||
let warmdir: str = strings.concat(foldfiles, "/", warmname);
|
||||
mkdirall(warmdir);
|
||||
writefile(strings.concat(warmdir, "/File.ww"), strings.concat(
|
||||
"package main;\nfn upper() i32 = { return 1; };\n"));
|
||||
writefile(strings.concat(warmdir, "/main.ww"), strings.concat(
|
||||
"package main;\nfn main() i32 = { return upper() - 1; };\n"));
|
||||
let warmwork: str = strings.concat(root, "/", labels[si],
|
||||
"-fold-warm-work");
|
||||
let warmout: str = strings.concat(root, "/", labels[si],
|
||||
"-fold-warm");
|
||||
assert(os.mkdir(warmwork, 448i32) == 0);
|
||||
let warmav: []str = [driver(stages[si]), "build", "-w", warmwork,
|
||||
"-I", foldfiles, "-o", warmout, warmdir];
|
||||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||||
rewritefile(linkertrace, "");
|
||||
runcommandenv(root, strings.concat("filename-fold-warm-cold-",
|
||||
labels[si]), warmav, env,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
let warmsuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a",
|
||||
".init.unit.ww", ".init.s", ".init.o"];
|
||||
let warmrefs: []str = ["", "", "", "", "", "", "", ""];
|
||||
let wi: i32 = 0;
|
||||
for (wi < warmsuffixes.len) {
|
||||
warmrefs[wi] = strings.dup(readfile(strings.concat(warmwork, "/",
|
||||
warmname, warmsuffixes[wi])));
|
||||
wi += 1;
|
||||
};
|
||||
let warmbinref: str = strings.dup(readfile(warmout));
|
||||
let warmstampref: str = strings.dup(readfile(strings.concat(warmwork,
|
||||
"/.wwtool.stamp")));
|
||||
let warmtoolpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c",
|
||||
"/.wwtool.w6a"];
|
||||
let warmtoolrefs: []str = alloc([], warmtoolpaths.len: u64)!;
|
||||
wi = 0;
|
||||
for (wi < warmtoolpaths.len) {
|
||||
append(warmtoolrefs, strings.dup(readfile(strings.concat(warmwork,
|
||||
warmtoolpaths[wi]))));
|
||||
wi += 1;
|
||||
};
|
||||
let collidingfile: str = strings.concat(warmdir, "/file.ww");
|
||||
writefile(collidingfile,
|
||||
"package main;\nfn lower() i32 = { return 2; };\n");
|
||||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||||
rewritefile(linkertrace, "");
|
||||
runcommandenv(root, strings.concat("filename-fold-warm-added-",
|
||||
labels[si]), warmav, env,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(same(out.stderr,
|
||||
"ww: case-insensitive file name collision: \"File.ww\" and \"file.ww\"\n"));
|
||||
assert(readfile(compilertrace).len == 0);
|
||||
assert(readfile(assemblertrace).len == 0);
|
||||
assert(readfile(linkertrace).len == 0);
|
||||
wi = 0;
|
||||
for (wi < warmsuffixes.len) {
|
||||
assert(same(warmrefs[wi], readfile(strings.concat(warmwork, "/",
|
||||
warmname, warmsuffixes[wi]))));
|
||||
wi += 1;
|
||||
};
|
||||
wi = 0;
|
||||
for (wi < warmtoolpaths.len) {
|
||||
assert(same(warmtoolrefs[wi], readfile(strings.concat(warmwork,
|
||||
warmtoolpaths[wi]))));
|
||||
wi += 1;
|
||||
};
|
||||
assert(!directoryhasnew(warmwork));
|
||||
assert(same(warmbinref, readfile(warmout)));
|
||||
assert(!os.exists(strings.concat(warmout, ".new")));
|
||||
assert(!os.exists(strings.concat(warmout, ".sepwork")));
|
||||
assert(same(warmstampref, readfile(strings.concat(warmwork,
|
||||
"/.wwtool.stamp"))));
|
||||
assert(os.remove(collidingfile) == 0);
|
||||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||||
rewritefile(linkertrace, "");
|
||||
runcommandenv(root, strings.concat("filename-fold-warm-removed-",
|
||||
labels[si]), warmav, env,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(readfile(compilertrace).len == 0);
|
||||
assert(readfile(assemblertrace).len == 0);
|
||||
assert(occurrences(readfile(linkertrace), "\n") == 1);
|
||||
wi = 0;
|
||||
for (wi < warmsuffixes.len) {
|
||||
assert(same(warmrefs[wi], readfile(strings.concat(warmwork, "/",
|
||||
warmname, warmsuffixes[wi]))));
|
||||
wi += 1;
|
||||
};
|
||||
wi = 0;
|
||||
for (wi < warmtoolpaths.len) {
|
||||
assert(same(warmtoolrefs[wi], readfile(strings.concat(warmwork,
|
||||
warmtoolpaths[wi]))));
|
||||
wi += 1;
|
||||
};
|
||||
assert(!directoryhasnew(warmwork));
|
||||
assert(same(warmbinref, readfile(warmout)));
|
||||
assert(!os.exists(strings.concat(warmout, ".new")));
|
||||
assert(!os.exists(strings.concat(warmout, ".sepwork")));
|
||||
assert(same(warmstampref, readfile(strings.concat(warmwork,
|
||||
"/.wwtool.stamp"))));
|
||||
si += 1;
|
||||
};
|
||||
clean(root);
|
||||
@@ -8508,6 +9157,26 @@ fn runtimepath(relative: str) str = {
|
||||
let cleanroot: str = strings.concat(root, "/clean");
|
||||
let cleanclient: str = strings.concat(cleanroot, "/domain/app");
|
||||
let cleanordinary: str = strings.concat(cleanroot, "/lib/math");
|
||||
let foldvendorupper: str = strings.concat(source,
|
||||
"/domain/fold/vendor/lib/Foo");
|
||||
let foldvendorlower: str = strings.concat(source,
|
||||
"/domain/fold/vendor/lib/foo");
|
||||
let foldvendorleft: str = strings.concat(source, "/domain/fold/left");
|
||||
let foldvendorright: str = strings.concat(source, "/domain/fold/right");
|
||||
let foldvendorclient: str = strings.concat(source, "/domain/fold/client");
|
||||
let foldvendorreverseclient: str = strings.concat(source,
|
||||
"/domain/fold/reverseclient");
|
||||
let foldaliasvendorlib: str = strings.concat(source,
|
||||
"/domain/foldalias/vendor/lib");
|
||||
let foldaliasvendorupper: str = strings.concat(foldaliasvendorlib, "/Foo");
|
||||
let foldaliasvendorlower: str = strings.concat(foldaliasvendorlib, "/foo");
|
||||
let foldaliasvendorleft: str = strings.concat(source,
|
||||
"/domain/foldalias/left");
|
||||
let foldaliasvendorright: str = strings.concat(source,
|
||||
"/domain/foldalias/right");
|
||||
let foldaliasvendorclient: str = strings.concat(source,
|
||||
"/domain/foldalias/client");
|
||||
let foldaliasvendorvault: str = strings.concat(root, "/fold-vendor-vault");
|
||||
|
||||
let dirs: []str = [leaf, nearest, outer, rootvendor, ordinary, client,
|
||||
one, two, outerclient, rootclient, emptyclient,
|
||||
@@ -8523,6 +9192,9 @@ fn runtimepath(relative: str) str = {
|
||||
alpha, beta, strings.concat(alpha, "/vendor/lib"),
|
||||
strings.concat(beta, "/vendor/lib"), pairvault, boundaryclient,
|
||||
boundaryordinary, boundaryabove, cleanclient, cleanordinary,
|
||||
foldvendorupper, foldvendorlower, foldvendorleft, foldvendorright,
|
||||
foldvendorclient, foldvendorreverseclient, foldaliasvendorlib, foldaliasvendorleft,
|
||||
foldaliasvendorright, foldaliasvendorclient, foldaliasvendorvault,
|
||||
runtime, tools];
|
||||
let di: i32 = 0;
|
||||
for (di < dirs.len) { mkdirall(dirs[di]); di += 1; };
|
||||
@@ -8649,6 +9321,35 @@ fn runtimepath(relative: str) str = {
|
||||
writefile(strings.concat(cleanclient, "/main.ww"), mainbody);
|
||||
writefile(strings.concat(cleanordinary, "/math.ww"),
|
||||
"package math;\nexport fn value() i32 = { return 44; };\n");
|
||||
writefile(strings.concat(foldvendorupper, "/pkg.ww"),
|
||||
"package upper;\nexport fn value() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(foldvendorlower, "/pkg.ww"),
|
||||
"package lower;\nexport fn value() i32 = { return 2; };\n");
|
||||
writefile(strings.concat(foldvendorleft, "/pkg.ww"), strings.concat(
|
||||
"package left;\nimport _ lib.Foo;\n",
|
||||
"export fn value() i32 = { return 1; };\n"));
|
||||
writefile(strings.concat(foldvendorright, "/pkg.ww"), strings.concat(
|
||||
"package right;\nimport _ lib.foo;\n",
|
||||
"export fn value() i32 = { return 2; };\n"));
|
||||
writefile(strings.concat(foldvendorclient, "/main.ww"), strings.concat(
|
||||
"package main;\nimport _ domain.fold.left;\n",
|
||||
"import _ domain.fold.right;\nfn main() i32 = { return 0; };\n"));
|
||||
writefile(strings.concat(foldvendorreverseclient, "/main.ww"), strings.concat(
|
||||
"package main;\nimport _ domain.fold.right;\n",
|
||||
"import _ domain.fold.left;\nfn main() i32 = { return 0; };\n"));
|
||||
writefile(strings.concat(foldaliasvendorvault, "/pkg.ww"),
|
||||
"package shared;\nexport fn value() i32 = { return 1; };\n");
|
||||
assert(os.symlink(foldaliasvendorvault, foldaliasvendorupper) == 0);
|
||||
assert(os.symlink(foldaliasvendorvault, foldaliasvendorlower) == 0);
|
||||
writefile(strings.concat(foldaliasvendorleft, "/pkg.ww"), strings.concat(
|
||||
"package left;\nimport _ lib.Foo;\n",
|
||||
"export fn value() i32 = { return 1; };\n"));
|
||||
writefile(strings.concat(foldaliasvendorright, "/pkg.ww"), strings.concat(
|
||||
"package right;\nimport _ lib.foo;\n",
|
||||
"export fn value() i32 = { return 2; };\n"));
|
||||
writefile(strings.concat(foldaliasvendorclient, "/main.ww"), strings.concat(
|
||||
"package main;\nimport _ domain.foldalias.left;\n",
|
||||
"import _ domain.foldalias.right;\nfn main() i32 = { return 0; };\n"));
|
||||
|
||||
let compilerwrapper: str = strings.concat(tools, "/w6c.sh");
|
||||
let assemblerwrapper: str = strings.concat(tools, "/w6a.sh");
|
||||
@@ -8711,6 +9412,8 @@ fn runtimepath(relative: str) str = {
|
||||
let referencealiasdirectdiag: str = "";
|
||||
let referenceprefixdiag: str = "";
|
||||
let candidate_diag_refs: []str = ["", ""];
|
||||
let foldvendorrefs: []str = ["", "", ""];
|
||||
let foldvendormultiref: str = "";
|
||||
let supplementalrefs: []str = ["", "", "", "", "", "", "", "",
|
||||
""];
|
||||
let referencevendortestout: str = "";
|
||||
@@ -8764,9 +9467,112 @@ fn runtimepath(relative: str) str = {
|
||||
append(env, strings.concat("WW_VENDOR_REAL_LINKER=",
|
||||
driver(linkers[si])));
|
||||
|
||||
let out: commandout;
|
||||
let foldtargets: []str = [foldvendorclient, foldvendorreverseclient,
|
||||
foldaliasvendorclient];
|
||||
let foldlabels: []str = ["distinct", "reverse-imports", "one-dir"];
|
||||
let foldwants: []str = [
|
||||
"ww: case-insensitive import collision: \"domain.fold.vendor.lib.Foo\" and \"domain.fold.vendor.lib.foo\"\n",
|
||||
"ww: case-insensitive import collision: \"domain.fold.vendor.lib.Foo\" and \"domain.fold.vendor.lib.foo\"\n",
|
||||
"ww: case-insensitive import collision: \"domain.foldalias.vendor.lib.Foo\" and \"domain.foldalias.vendor.lib.foo\"\n",
|
||||
];
|
||||
let fi: i32 = 0;
|
||||
for (fi < foldtargets.len) {
|
||||
rewritefile(compilertraces[si], "");
|
||||
rewritefile(assemblertraces[si], "");
|
||||
rewritefile(linkertraces[si], "");
|
||||
let foldwork: str = strings.concat(root, "/vendor-fold-",
|
||||
foldlabels[fi], "-", stages[si], "-work");
|
||||
let foldout: str = strings.concat(root, "/vendor-fold-",
|
||||
foldlabels[fi], "-", stages[si]);
|
||||
assert(os.mkdir(foldwork, 448i32) == 0);
|
||||
let foldav: []str = [driver(stages[si]), "build", "-w",
|
||||
foldwork, "-I", source, "-o", foldout, foldtargets[fi]];
|
||||
runcommandenv(root, strings.concat("vendor-fold-", foldlabels[fi],
|
||||
"-", stages[si]), foldav, env,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(out.stdout.len == 0 && same(out.stderr, foldwants[fi]));
|
||||
assert(readfile(compilertraces[si]).len == 0);
|
||||
assert(readfile(assemblertraces[si]).len == 0);
|
||||
assert(readfile(linkertraces[si]).len == 0);
|
||||
assert(!os.exists(foldout));
|
||||
assert(!os.exists(strings.concat(foldout, ".new")));
|
||||
assert(!os.exists(strings.concat(foldout, ".sepwork")));
|
||||
assert(!os.exists(strings.concat(foldwork, "/.wwtool.stamp")));
|
||||
assert(directoryisempty(foldwork));
|
||||
if (si == 0) { foldvendorrefs[fi] = strings.dup(out.stderr); }
|
||||
else { assert(same(foldvendorrefs[fi], out.stderr)); };
|
||||
fi += 1;
|
||||
};
|
||||
assert(same(foldvendorrefs[0], foldvendorrefs[1]));
|
||||
|
||||
// A command-global multi-product request is one rejection transaction.
|
||||
// Reversing its descriptors cannot publish the unrelated product or
|
||||
// mutate either pre-existing status.
|
||||
let foldmultiwork: str = strings.concat(root, "/vendor-fold-multi-",
|
||||
stages[si], "-work");
|
||||
assert(os.mkdir(foldmultiwork, 448i32) == 0);
|
||||
let foldallowedout: str = strings.concat(root, "/vendor-fold-allowed-",
|
||||
stages[si]);
|
||||
let foldcollisionout: str = strings.concat(root,
|
||||
"/vendor-fold-collision-", stages[si]);
|
||||
let foldallowedstatus: str = strings.concat(foldallowedout, ".status");
|
||||
let foldcollisionstatus: str = strings.concat(foldcollisionout, ".status");
|
||||
writefile(foldallowedstatus, "sentinel-allowed\n");
|
||||
writefile(foldcollisionstatus, "sentinel-collision\n");
|
||||
let foldforward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||
foldmultiwork, "-I", source, "--ww-package-build",
|
||||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||||
foldallowedout, foldallowedstatus,
|
||||
"--ww-package-test", "build", "main", "main", "-", "-",
|
||||
foldvendorclient, foldcollisionout, foldcollisionstatus, client];
|
||||
let foldreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||
foldmultiwork, "-I", source, "--ww-package-build",
|
||||
"--ww-package-test", "build", "main", "main", "-", "-",
|
||||
foldvendorclient, foldcollisionout, foldcollisionstatus,
|
||||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||||
foldallowedout, foldallowedstatus, client];
|
||||
let foldrequests: [][]str = [foldforward, foldreverse];
|
||||
let foldmultidiags: []str = ["", ""];
|
||||
let foldorderlabels: []str = ["forward", "reverse"];
|
||||
let foi: i32 = 0;
|
||||
for (foi < foldrequests.len) {
|
||||
rewritefile(compilertraces[si], "");
|
||||
rewritefile(assemblertraces[si], "");
|
||||
rewritefile(linkertraces[si], "");
|
||||
runcommandenv(root, strings.concat("vendor-fold-multi-", stages[si],
|
||||
"-", foldorderlabels[foi]), foldrequests[foi], env,
|
||||
(180i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(out.stdout.len == 0 && same(out.stderr, foldwants[0]));
|
||||
foldmultidiags[foi] = 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(foldallowedstatus), "sentinel-allowed\n"));
|
||||
assert(same(readfile(foldcollisionstatus), "sentinel-collision\n"));
|
||||
assert(!os.exists(strings.concat(foldallowedstatus, ".new")));
|
||||
assert(!os.exists(strings.concat(foldcollisionstatus, ".new")));
|
||||
assert(!os.exists(foldallowedout));
|
||||
assert(!os.exists(strings.concat(foldallowedout, ".new")));
|
||||
assert(!os.exists(strings.concat(foldallowedout, ".sepwork")));
|
||||
assert(!os.exists(strings.concat(foldallowedout, ".result")));
|
||||
assert(!os.exists(strings.concat(foldallowedout, ".result.new")));
|
||||
assert(!os.exists(foldcollisionout));
|
||||
assert(!os.exists(strings.concat(foldcollisionout, ".new")));
|
||||
assert(!os.exists(strings.concat(foldcollisionout, ".sepwork")));
|
||||
assert(!os.exists(strings.concat(foldcollisionout, ".result")));
|
||||
assert(!os.exists(strings.concat(foldcollisionout, ".result.new")));
|
||||
assert(directoryisempty(foldmultiwork));
|
||||
foi += 1;
|
||||
};
|
||||
assert(same(foldmultidiags[0], foldmultidiags[1]));
|
||||
if (si == 0) { foldvendormultiref = strings.dup(foldmultidiags[0]); }
|
||||
else { assert(same(foldvendormultiref, foldmultidiags[0])); };
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user