test: prove platform filename selection

This commit is contained in:
2026-08-14 21:44:26 +09:00
parent f138844737
commit 5c0e468f53
4 changed files with 857 additions and 34 deletions

View File

@@ -260,11 +260,12 @@ until `ww init` gives it a stable module/package identity.
### 3.1 One directory, one package ### 3.1 One directory, one package
A package directory contains its immediate regular source files only. The build A package directory contains its immediate regular source files and source-name
does not follow source symlinks. Nested directories are separate packages. Every symlinks that resolve to regular files. A source-name symlink to a directory is
selected production source MUST begin with the same canonical `package name;` ignored. Nested directories are separate packages. Every selected production
clause. The declared name MUST be a valid WW identifier. It need not repeat the source MUST begin with the same canonical `package name;` clause. The declared
directory leaf because identity and source qualifier are separate concepts. name MUST be a valid WW identifier. It need not repeat the directory leaf
because identity and source qualifier are separate concepts.
The following current forms are errors after the migration: The following current forms are errors after the migration:
@@ -279,41 +280,47 @@ still the smallest package and needs no extra metadata.
### 3.2 File membership and target selection ### 3.2 File membership and target selection
Production candidates are immediate regular files ending `.ww`, excluding The current toolchain has one honest target, `linux/amd64`. Production
`*_test.ww` and generated outputs. Names are byte-sorted after UTF-8 validity and candidates are immediate visible names ending `.ww`, excluding selected
normalization checks. A source symlink, duplicate normalized name, case-fold `*_test.ww` files. Directory entries beginning `.` or `_` are ignored. Candidate
collision, or non-regular candidate is a loud error on every host. names are byte-sorted before any selected source is opened, parsed, or checked.
Target variants use this only convention: WW applies Go 1.26.5's filename suffix algorithm to the portion of the basename
before its first dot. A final `_test` token is removed for this decision. If the
last two remaining underscore-delimited tokens are a known OS followed by a
known architecture, both must match `linux/amd64`. Otherwise a final known OS
or architecture must match. A known mismatch excludes the file; an unknown or
misplaced token leaves it ordinary. The pinned known sets are:
```text ```text
stem[+os][+arch][+environment].ww OS: aix android darwin dragonfly freebsd hurd illumos ios js linux nacl
netbsd openbsd plan9 solaris wasip1 windows zos
ARCH: 386 amd64 amd64p32 arm armbe arm64 arm64be loong64 mips mipsle
mips64 mips64le mips64p32 mips64p32le ppc ppc64 ppc64le riscv
riscv64 s390 s390x sparc sparc64 wasm
``` ```
Recognized tags come from the selected target descriptor, not from the host. The suffix requires a nonempty prefix and an underscore. Thus `linux.ww` and
Files are grouped by `stem`. The matching member with the greatest number of `plan9_test.ww` are ordinary files, `x_plan9_test.ww` is excluded,
tags wins; the untagged member is the fallback. Two equally specific matches are `x_linux_amd64.ww` is selected, and `x_windows_amd64.ww` is excluded. The first
an error. Examples are `poll.ww`, `poll+linux.ww`, and dot ends inspection: `x.extra_windows.ww` is ordinary. Pair recognition takes
`poll+linux+amd64+gnu.ww`. This is replacement selection, not additive feature precedence over the final single token; `x_windows_amd64.ww` does not match just
selection; additive code uses a distinct stem. Unknown tags are errors. There because `amd64` does. Conversely `x_amd64_linux.ww` has no OS/architecture pair
are no boolean selectors, glob expressions, or manifest-defined tag meanings. and matches its final single `linux` token, exactly as Go does.
Test variants put the same tags before the reserved suffix, for example Selection is additive, not replacement-based: every matching file belongs to
`poll+linux_test.ww`; their grammar is `stem[+tags]_test.ww` and the identical the package. The production variant then excludes `*_test.ww`; internal and
most-specific rule applies within the test set. A production stem ending external test classification uses only the already platform-selected test
`_test` is reserved and rejected, preventing a tagged test from being mistaken files. An excluded file creates no source occurrence, import, dependency edge,
for production source. 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.
The selected file-name list is itself an action-key input. Therefore adding or WW implements no source-level build expressions, user tags, target descriptor,
removing a more-specific file invalidates the package even when the old files' `UseAllFiles` escape, `+tag` replacement scheme, or manifest-defined selector.
bytes do not change. Those would introduce a second build language or a manifest model and are
outside the local, manifest-free product.
CPU features and optimization mode always enter the compile key but do not add
another WW source-selection language. WW-level specialization uses compiler
intrinsics/runtime dispatch or a distinct package; CPU/float-ABI/PIC-sensitive C
or assembly uses the finite native `when` constraints in section 8.4. This keeps
ordinary source membership conventional while still making exceptional native
selection exact and inspectable.
### 3.3 Imports, names, and resolution ### 3.3 Imports, names, and resolution
@@ -5132,7 +5139,7 @@ first regenerated byte-identical semantic export.
The owner source voucher remains `<action>.unit.ww`; a linked product root also The owner source voucher remains `<action>.unit.ww`; a linked product root also
owns `<root>.init.unit.ww` for its dispatcher unit, `.init.s`, `.init.o`, and owns `<root>.init.unit.ww` for its dispatcher unit, `.init.s`, `.init.o`, and
two-member archive. Current persistent formats are build 17 and test 16. Warm two-member archive. Current persistent formats are build 18 and test 17. Warm
consumers select a dependency's staged `.wwi.new` or `.a.new` when that exact consumers select a dependency's staged `.wwi.new` or `.a.new` when that exact
action changed in the same request. All action artifacts, init artifacts, tool action changed in the same request. All action artifacts, init artifacts, tool
identity copies, stamp, library/executable publications, and test statuses are identity copies, stamp, library/executable publications, and test statuses are
@@ -5192,6 +5199,151 @@ persistent-workdir, rejection-state, byte-identity, and bootstrap observers own
their unchanged broader boundaries. Grouped imports, quoted imports, and dot their unchanged broader boundaries. Grouped imports, quoted imports, and dot
imports remain deliberately unimplemented. imports remain deliberately unimplemented.
### 11.21 Implemented Go platform filename eligibility
Directory packages now apply Go 1.26.5's OS/architecture filename rule before
a source can enter WW's production or test graph. This closes a loader-wide
divergence rather than adding a syntax feature: WW remains a local,
manifest-free toolchain with unquoted dotted imports and one supported target,
`linux/amd64`.
#### Pinned Go evidence and pre-fix divergence
The reference is official Go 1.26.5 at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- `Context.matchFile` first rejects leading-dot/underscore names and unrelated
extensions, calls `goodOSArchFile`, and only then joins and opens the source
([`go/build/build.go`, lines 14381509](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1438-L1509)).
- `goodOSArchFile` cuts at the first dot, requires an underscore-prefixed
suffix, removes a final `test` token, gives a known OS/architecture pair
precedence over a final known single token, and treats every other suffix as
ordinary
([`go/build/build.go`, lines 19802027](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1980-L2027)).
Its match operation is against the selected `GOOS`/`GOARCH`, with only the
documented Android/Linux, illumos/Solaris, and iOS/Darwin aliases
([`go/build/build.go`, lines 19331977](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1933-L1977)).
- The exact past, present, and future filename-recognition sets are
`syslist.KnownOS` and `syslist.KnownArch`; they are intentionally broader than
currently supported targets and explicitly must not lose old names
([`internal/syslist/syslist.go`, lines 1436 and 5683](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/syslist/syslist.go#L14-L83)).
- `go/build` requires sorted directory presentation, its ordinary reader uses
byte-sorted names, and package classification consumes that order
([`go/build/build.go`, lines 108111 and 193207](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L108-L207),
[lines 859914](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L859-L914),
[`os/dir.go`, lines 109125](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/dir.go#L109-L125)).
- The official `TestMatchFile` table accepts `android.go`, `plan9.go`, and
`plan9_test.go` as whole-name ordinary files, accepts matching architecture
and Android/Linux aliases, and rejects a mismatching `foo_darwin.go`
([`go/build/build_test.go`, lines 381425](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build_test.go#L381-L425)).
Command testdata independently proves that a selected Linux suffix contributes
its file and import on `linux/amd64` and both disappear on Darwin
([`cmd/go/testdata/script/list_constraints.txt`, lines 129 and 5760](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/list_constraints.txt#L1-L60)).
An explicit package whose files are all excluded is rejected
([`build_no_go.txt`, lines 117 and 3141](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/build_no_go.txt#L1-L41)).
Before this slice, both drivers accepted every visible `.ww` entry apart from
the production/test partition, and `internal/wwpackage` discovered every such
entry recursively. They opened and parsed candidates in raw filesystem order.
On Linux/amd64, a malformed `bad_windows.ww` therefore rejected the request;
an otherwise valid `platform_windows_arm64.ww` could add imports, actions,
direct `.wwi` inputs, archives, linker inputs, initialization, and runtime
behavior; wrong-target internal and external tests ran; a directory containing
only `only_windows.ww` was selected recursively; and editing an ineligible file
recompiled its owner. Cstage and WWstage agreed with each other but were both
wrong.
#### Final source and graph ownership
The basename predicate is exact and allocation-free. It examines the stem
before the first dot, removes final `_test` for suffix analysis, then recognizes
only the pinned Go `KnownOS`/`KnownArch` sets. A recognized pair must be
`linux_amd64`; a recognized final single must be `linux` or `amd64`. Unknown or
misplaced tokens remain ordinary. There is no alias-, declared-name-, path-leaf-,
physical-directory-, artifact-, or request-order input to this decision.
Both drivers first collect every visible `.ww` basename in checked dynamically
grown storage, byte-sort the names, then apply target and production/test
eligibility before source stat/open/parse and package validation. This removes
the former filesystem-order diagnostic race and adds no fixed file bound. The
shared coordinator already sorts directory entries; its source predicate now
removes a mismatching basename before it is appended to discovery or grouped
into a package/test product. A recursive pattern skips a directory with no
eligible sources. An explicit directory with no eligible production source
retains WW's stable `directory contains no WW package sources` rejection.
Eligibility owns whether a source occurrence exists. For a selected file, the
parser and checker retain its exact file-local imports, aliases, blank
occurrences, positions, and declarations, and the package graph deduplicates
their resolved canonical targets exactly as before. For an excluded file there
is no occurrence to resolve: missing, self, cycle, final-`internal`, vendor, and
imported-`main` validation do not run, and the file contributes no canonical
dependency or action. This is source/file ownership before package-graph
ownership, never another identity dimension.
#### Build, test, artifacts, and execution
Production sees all matching non-test sources. The internal-test variant sees
that production category followed by matching same-package `*_test.ww` files;
the external variant sees only matching external `*_test.ww` files. The suffix
rule therefore removes wrong-target test-only imports and initialization before
variant construction, support generation, or generated-main generation.
`plan9_test.ww` remains ordinary because the suffix has no nonempty prefix;
`x_plan9_test.ww` is excluded; first-dot and pair-precedence cases behave like
the pinned Go table.
No checker, interface writer, assembler, archiver, or linker protocol changed.
The drivers simply stop excluded bytes before those owners. Each selected
package unit still contains its category-ordered source files and exact import
occurrences. The compiler still receives one byte-sorted direct `.wwi` input per
canonical edge; `.wwi` still contains only semantic exports; archives still
contain only their canonical package action (plus the command root dispatcher
member where applicable); and the linker still receives the root plus reachable
archive-only closure. An import found only in an excluded file therefore
creates no `.wwi`, object, archive, init task, dispatcher edge, linker argument,
binary effect, or test execution.
#### Persistence, rejection, and stage responsibility
Persistent formats are build 18 and test 17 so a pre-slice workdir performs one
complete reachable-action refresh under the new membership contract. Thereafter
an excluded-file add, removal, or content edit changes no unit voucher, `.wwi`,
assembly, object, archive, dispatcher, test status, or reverse action. Existing
product policy may still relink an explicitly requested executable from its
unchanged archives. A selected private implementation edit rebuilds its owner;
if its `.wwi` is byte-identical, reverse compilation stops and only affected
products relink.
Wrong-target malformed sources and wrong-target structural import sites are
ignored without producers. Selected structural failures are reported in
byte-sorted filename order before producers. Any later selected-source compiler
failure remains inside the existing request transaction: staged dependency
changes are discarded, all prior actions/tool records/stamps/publications stay
byte-identical, no `.new` generation survives, and no mixed package or test
result is published.
`cmd/ww/main.c` and `selfhost/cmd/ww/main.ww` mechanically mirror direct
enumeration, sorting, target filtering, and checked allocation. The shared
`internal/wwpackage/package.ww` predicate owns recursive build/test discovery.
The compiler/checker/writer consume only selected units and require no special
case; `w6a` and `w6l` remain unchanged. The focused native
`platform_filename_source_selection` observer generates independent cold and
persistent Cstage/WWstage work roots and proves exact suffix edge cases,
sorted diagnostics, direct and recursive build/test selection, production/test
isolation, repeated-edge canonicalization, exact compiler/assembler/linker
argv, archive-only closure, artifact/assembly/binary equality, reversed-root
independence, runtime results, ignored-edit reuse, `.wwi`-stable reverse
propagation, and late-failure rollback. Existing dynamic-allocation,
no-follow, byte-identity, bootstrap, internal, vendor, and imported-command
observers retain their broader ownership.
Source-level `//go:build`/`+build` equivalents, arbitrary tags, cross-target
selection, grouped/quoted/dot imports, modules, manifests, registries, and a
programmable build language remain deliberately unsupported. Go's `UseAllFiles`
escape is also not exposed. The separate remaining Go test-product topology
divergence—one generated main for combined internal and external variants—is
not hidden or changed by this slice.
## 12. Candidate architectures and hard-gate decision ## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins. Five candidates were developed as coherent systems, not as feature bins.

View File

@@ -255,6 +255,20 @@ ImportPath = ident { "." ident } .
`.ww` files sharing one declared package name compiles as one package. The `.ww` files sharing one declared package name compiles as one package. The
declared name need not equal the directory name or the final component of its declared name need not equal the directory name or the final component of its
canonical import identity. canonical import identity.
- Directory source eligibility uses Go 1.26.5 filename suffix semantics for
WW's fixed `linux/amd64` target. In the basename stem before the first dot, a
final `_test` token is ignored for platform matching. A final known OS or
architecture token must match `linux` or `amd64`; a final known OS followed
by a known architecture takes precedence and both must match. The known-name
sets are Go 1.26.5's `syslist.KnownOS` and `syslist.KnownArch`. Unknown or
misplaced suffixes are ordinary, and a platform word without a nonempty
underscore prefix is ordinary (`linux.ww` and `plan9_test.ww` are selected;
`x_windows.ww` and `x_plan9_test.ww` are not). Leading-dot/underscore entries
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.
- `import acme.codec;` loads the canonical package `acme.codec`. If that - `import acme.codec;` loads the canonical package `acme.codec`. If that
package declares `package wire;`, the importing file sees its exported names package declares `package wire;`, the importing file sees its exported names
as `wire.Name`; `codec.Name` is not an additional binding. An explicit alias as `wire.Name`; `codec.Name` is not an additional binding. An explicit alias

View File

@@ -224,6 +224,19 @@ run always executes from the temp root. Without `-c`, it removes the
temporary binary and scratch with its workspace. The language runtime temporary binary and scratch with its workspace. The language runtime
owns individual `@test` functions. owns individual `@test` functions.
Before package grouping, both directory drivers and the shared recursive
coordinator apply Go 1.26.5's filename OS/architecture rule for WW's fixed
`linux/amd64` target. The basename stem ends at its first dot; a final `_test`
token is removed for matching; a recognized OS/architecture pair takes
precedence over a recognized final single token; and unknown or misplaced
tokens remain ordinary. Wrong-target production, same-package test, and
external-test files therefore create no imports, variants, generated-main
inputs, runtime tests, artifacts, or persistent invalidation. Names are
byte-sorted before selected-source validation, so diagnostics do not depend on
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.
Separate compilation is the only driver build path; no compatibility mode Separate compilation is the only driver build path; no compatibility mode
switch remains. switch remains.
@@ -255,6 +268,19 @@ exact-argv, command, and persistent-workdir observers, the package suite proves
archive-only link argv and exact warm/rejection-state behavior without archive-only link argv and exact warm/rejection-state behavior without
duplicating those broader mechanisms in this observer. duplicating those broader mechanisms in this observer.
The same package owner contains the focused
`platform_filename_source_selection` observer. It independently generates the
suffix matrix, production/internal/external sources, wrong-target import and
malformed sentinels, wrong-target-only directories, and reverse-created
diagnostic files. Across cold and persistent Cstage/WWstage roots it compares
normalized compiler/assembler/linker argv, units, `.wwi`, assembly, objects,
archives, generated-main archives, binaries, runtime/test output, direct and
recursive behavior, reversed roots, ignored-edit reuse, selected private-change
propagation, and request rollback after a dependency has staged and the root
compiler fails. The existing bounded-memory package-initialization observer
continues to own allocation-failure parity for the shared dynamically grown
action universe.
`test/sep/sepinit_test.ww` is the single focused package-initialization owner. `test/sep/sepinit_test.ww` is the single focused package-initialization owner.
It generates all source trees temporarily and runs independent cold/persistent It generates all source trees temporarily and runs independent cold/persistent
Cstage and WWstage legs. Its matrix covers blank-only reachability; dependency, Cstage and WWstage legs. Its matrix covers blank-only reachability; dependency,

View File

@@ -5118,6 +5118,637 @@ fn hexbytes(value: str) str = {
clean(root); clean(root);
}; };
@test fn platform_filename_source_selection() void = {
let root: str = fresh();
let source: str = strings.concat(root, "/source");
let app: str = strings.concat(source, "/app");
let good: str = strings.concat(source, "/dep/good");
let pkg: str = strings.concat(source, "/pkg");
let testgood: str = strings.concat(source, "/test/good");
let wrongonly: str = strings.concat(source, "/wrongonly");
let notests: str = strings.concat(source, "/notests");
let internal: str = strings.concat(source, "/foreign/internal/secret");
let command: str = strings.concat(source, "/command");
let vendored: str = strings.concat(app, "/vendor/vendored");
let order: str = strings.concat(root, "/order");
let tools: str = strings.concat(root, "/tools");
mkdirall(app); mkdirall(good); mkdirall(pkg); mkdirall(testgood);
mkdirall(wrongonly); mkdirall(notests); mkdirall(internal);
mkdirall(command); mkdirall(vendored); mkdirall(order); mkdirall(tools);
let good5: str = strings.concat("package good;\n",
"export fn value() i32 = { return 5; };\n");
let good6: str = strings.concat("package good;\n",
"export fn value() i32 = { return 6; };\n");
let good7: str = strings.concat("package good;\n",
"export fn value() i32 = { return 7; };\n");
let appmain: str = strings.concat(
"package main;\nimport dep.good;\n",
"fn main() i32 = { return good.value() + wholelinux() + ",
"afterdot() + arch() + reversed() + future() + osname() + pair(); };\n");
let wholelinux: str =
"package main;\nfn wholelinux() i32 = { return 1; };\n";
let afterdot: str =
"package main;\nfn afterdot() i32 = { return 1; };\n";
let arch: str =
"package main;\nfn arch() i32 = { return 1; };\n";
let reversed: str =
"package main;\nfn reversed() i32 = { return 1; };\n";
let future: str =
"package main;\nfn future() i32 = { return 1; };\n";
let osname: str = strings.concat(
"package main;\nimport _ dep.good;\n",
"fn osname() i32 = { return 1; };\n");
let pair: str =
"package main;\nfn pair() i32 = { return 1; };\n";
let ignoredparse: str =
"this wrong-target source is deliberately malformed\n";
let ignoredparseedit: str =
"a different malformed wrong-target edit {\n";
let ignoreddep: str = strings.concat(
"package main;\nimport dep.wrong;\n",
"fn wrong_dep() i32 = { return wrong.value(); };\n");
let ignoredinternal: str = strings.concat(
"package main;\nimport foreign.internal.secret;\n",
"fn wrong_internal() i32 = { return secret.value(); };\n");
let ignoredmain: str = strings.concat(
"package main;\nimport command;\n",
"fn wrong_main() i32 = { return 0; };\n");
let ignoredvendor: str = strings.concat(
"package main;\nimport vendored;\n",
"fn wrong_vendor() i32 = { return vendored.value(); };\n");
writefile(strings.concat(good, "/good.ww"), good5);
writefile(strings.concat(app, "/main.ww"), appmain);
writefile(strings.concat(app, "/linux.ww"), wholelinux);
writefile(strings.concat(app, "/platform.extra_windows.ww"), afterdot);
writefile(strings.concat(app, "/platform_amd64.ww"), arch);
writefile(strings.concat(app, "/platform_amd64_linux.ww"), reversed);
writefile(strings.concat(app, "/platform_future.ww"), future);
writefile(strings.concat(app, "/platform_linux.ww"), osname);
writefile(strings.concat(app, "/platform_linux_amd64.ww"), pair);
writefile(strings.concat(app, "/badparse_windows.ww"), ignoredparse);
writefile(strings.concat(app, "/baddep_arm64.ww"), ignoreddep);
writefile(strings.concat(app, "/badinternal_windows_amd64.ww"),
ignoredinternal);
writefile(strings.concat(app, "/badmain_linux_arm64.ww"), ignoredmain);
writefile(strings.concat(app, "/badvendor_windows.ww"), ignoredvendor);
writefile(strings.concat(internal, "/secret.ww"), strings.concat(
"package secret;\n",
"export fn value() i32 = { return 19; };\n"));
writefile(strings.concat(command, "/main.ww"),
"package main;\nfn main() i32 = { return 0; };\n");
writefile(strings.concat(vendored, "/vendored.ww"), strings.concat(
"package vendored;\n",
"export fn value() i32 = { return 23; };\n"));
let pkgbase: str = strings.concat("package pkg;\n",
"export fn value() i32 = { return 7; };\n");
let samepair: str = strings.concat(
"package pkg;\nimport test.good;\n",
"@test fn selected_internal_pair() void = { ",
"assert(value() + good.value() == 18); };\n");
let samewhole: str = strings.concat(
"package pkg;\n",
"@test fn selected_whole_plan9_name() void = { assert(value() == 7); };\n");
let samereversed: str = strings.concat(
"package pkg;\n",
"@test fn selected_reversed_suffix() void = { assert(value() == 7); };\n");
let externalpair: str = strings.concat(
"package pkg_test;\nimport pkg;\nimport test.good;\n",
"@test fn selected_external_pair() void = { ",
"assert(pkg.value() + good.value() == 18); };\n");
let externaldot: str = strings.concat(
"package pkg_test;\nimport pkg;\n",
"@test fn selected_external_after_dot() void = { ",
"assert(pkg.value() == 7); };\n");
let wrongtest: str = strings.concat(
"package pkg;\nimport test.wrong;\n",
"@test fn wrong_target_test() void = { abort(\"wrong test ran\"); };\n");
let wrongtestedit: str =
"this edited wrong-target test remains deliberately malformed\n";
writefile(strings.concat(pkg, "/base.ww"), pkgbase);
writefile(strings.concat(pkg, "/internal_linux_amd64_test.ww"), samepair);
writefile(strings.concat(pkg, "/plan9_test.ww"), samewhole);
writefile(strings.concat(pkg, "/reverse_amd64_linux_test.ww"),
samereversed);
writefile(strings.concat(pkg, "/external_linux_amd64_test.ww"),
externalpair);
writefile(strings.concat(pkg, "/external.extra_windows_test.ww"),
externaldot);
writefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"),
wrongtest);
writefile(strings.concat(pkg, "/external_linux_arm64_test.ww"),
wrongtest);
writefile(strings.concat(pkg, "/x_plan9_test.ww"), wrongtest);
writefile(strings.concat(testgood, "/good.ww"), strings.concat(
"package good;\n",
"export fn value() i32 = { return 11; };\n"));
writefile(strings.concat(wrongonly, "/only_windows.ww"), ignoredparse);
writefile(strings.concat(notests, "/base.ww"),
"package notests;\nexport fn value() i32 = { return 1; };\n");
writefile(strings.concat(notests, "/only_windows_test.ww"), ignoredparse);
// Creation order is deliberately the inverse of byte order.
writefile(strings.concat(order, "/z_linux.ww"),
"package order;\n@test fn z() void = { };\n");
writefile(strings.concat(order, "/a_linux.ww"),
"package order;\n@test fn a() void = { };\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_PLATFORM_COMPILER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_PLATFORM_COMPILER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_PLATFORM_COMPILER_TRACE\"\n",
"exec \"$WW_PLATFORM_REAL_COMPILER\" \"$@\"\n"));
writeexecutable(assemblerwrapper, strings.concat(
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PLATFORM_ASSEMBLER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_PLATFORM_ASSEMBLER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_PLATFORM_ASSEMBLER_TRACE\"\n",
"exec \"$WW_PLATFORM_REAL_ASSEMBLER\" \"$@\"\n"));
writeexecutable(linkerwrapper, strings.concat(
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PLATFORM_LINKER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_PLATFORM_LINKER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_PLATFORM_LINKER_TRACE\"\n",
"exec \"$WW_PLATFORM_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 labels: []str = ["c", "ww"];
let artifactnames: []str = ["app.unit.ww", "app.wwi", "app.s",
"app.o", "app.a", "app.init.unit.ww", "app.init.s", "app.init.o",
"dep.good.unit.ww", "dep.good.wwi", "dep.good.s", "dep.good.o",
"dep.good.a"];
let artifactrefs: []str = ["", "", "", "", "", "", "", "", "",
"", "", "", ""];
let testartifactnames: []str = ["pkg.unit.ww", "pkg.wwi", "pkg.a",
"pkg-internal-test.unit.ww", "pkg-internal-test.wwi",
"pkg-internal-test.a", "pkg_test-external-test.unit.ww",
"pkg_test-external-test.wwi", "pkg_test-external-test.a",
"pkg-internal-test-main.a", "pkg_test-external-test-main.a"];
let testartifactrefs: []str = ["", "", "", "", "", "", "", "",
"", "", ""];
let binref: str = "";
let compilerref: str = "";
let assemblerref: str = "";
let linkerref: str = "";
let prodarchiveref: str = "";
let prodwwiref: str = "";
let testoutref: str = "";
let testerrref: str = "";
let faildiagref: str = "";
let recursiveoutref: str = "";
let recursiveerrref: str = "";
let baseenv: []str = os.getenvs();
let si: i32 = 0;
for (si < stages.len) {
rewritefile(strings.concat(good, "/good.ww"), good5);
rewritefile(strings.concat(app, "/platform_future.ww"), future);
rewritefile(strings.concat(app, "/badparse_windows.ww"), ignoredparse);
rewritefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"),
wrongtest);
let work: str = strings.concat(root, "/", labels[si], "-work");
let reversework: str = strings.concat(root, "/", labels[si],
"-reverse-work");
let outdir: str = strings.concat(root, "/", labels[si], "-out");
let reverseout: str = strings.concat(root, "/", labels[si],
"-reverse-out");
let compilertrace: str = strings.concat(root, "/", labels[si],
"-compiler");
let assemblertrace: str = strings.concat(root, "/", labels[si],
"-assembler");
let linkertrace: str = strings.concat(root, "/", labels[si],
"-linker");
assert(os.mkdir(work, 448i32) == 0);
assert(os.mkdir(reversework, 448i32) == 0);
assert(os.mkdir(outdir, 448i32) == 0);
assert(os.mkdir(reverseout, 448i32) == 0);
writefile(compilertrace, ""); writefile(assemblertrace, "");
writefile(linkertrace, "");
let env: []str = alloc([], (baseenv.len + 9): 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_PLATFORM_COMPILER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_PLATFORM_ASSEMBLER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_PLATFORM_LINKER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_PLATFORM_REAL_COMPILER=")
&& !strings.hasprefix(baseenv[ei],
"WW_PLATFORM_REAL_ASSEMBLER=")
&& !strings.hasprefix(baseenv[ei],
"WW_PLATFORM_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_PLATFORM_COMPILER_TRACE=", compilertrace));
append(env, strings.concat("WW_PLATFORM_ASSEMBLER_TRACE=", assemblertrace));
append(env, strings.concat("WW_PLATFORM_LINKER_TRACE=", linkertrace));
append(env, strings.concat("WW_PLATFORM_REAL_COMPILER=",
driver(compilers[si])));
append(env, strings.concat("WW_PLATFORM_REAL_ASSEMBLER=",
driver(assemblers[si])));
append(env, strings.concat("WW_PLATFORM_REAL_LINKER=",
driver(linkers[si])));
let forward: []str = [driver(stages[si]), "build", "-w", work,
"-I", source, "-o", strings.concat(outdir, "/"), app, good];
let reverse: []str = [driver(stages[si]), "build", "-w", reversework,
"-I", source, "-o", strings.concat(reverseout, "/"), good, app];
let out: commandout;
runcommandenv(root, strings.concat("platform-cold-", labels[si]),
forward, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
let ctrace: str = readfile(compilertrace);
let atrace: str = readfile(assemblertrace);
let ltrace: str = readfile(linkertrace);
assert(occurrences(ctrace, "\n") == 2);
assert(occurrences(atrace, "\n") == 3);
assert(occurrences(ltrace, "\n") == 1);
let appline: str = linecontaining(ctrace, "/app.unit.new");
assert(occurrences(appline, "<--import><dep.good>") == 1);
assert(occurrences(appline, "/dep.good.wwi.new>") == 1);
assert(!has(ctrace, "dep.wrong"));
assert(!has(ctrace, "foreign.internal.secret"));
assert(!has(ctrace, "vendored"));
assert(!has(ctrace, "command.wwi"));
assert(occurrences(ltrace, strings.concat(work, "/app.a.new")) == 1);
assert(occurrences(ltrace, strings.concat(work,
"/dep.good.a.new")) == 1);
assert(!has(ltrace, ".wwi"));
assert(!has(ltrace, "dep.wrong"));
let unitprefix: str = strings.concat(
"//ww:module-reset app\n", wholelinux, "\n",
"//ww:module-reset app\n", appmain, "\n",
"//ww:module-reset app\n", afterdot, "\n",
"//ww:module-reset app\n", arch, "\n",
"//ww:module-reset app\n", reversed, "\n",
"//ww:module-reset app\n", future, "\n",
"//ww:module-reset app\n", osname, "\n",
"//ww:module-reset app\n", pair, "\n");
let appunit: str = readfile(strings.concat(work, "/app.unit.ww"));
assert(strings.hasprefix(appunit, unitprefix));
assert(has(appunit, "//ww:direct-export dep.good "));
assert(!has(appunit, "wrong_dep"));
assert(!has(appunit, "wrong_internal"));
assert(!has(appunit, "wrong_main"));
assert(!has(appunit, "wrong_vendor"));
assert(!os.exists(strings.concat(work, "/dep.wrong.unit.ww")));
let bin: str = strings.concat(outdir, "/app");
let runav: []str = [bin];
runcommand(root, strings.concat("platform-run-", labels[si]), runav,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 12);
let ai: i32 = 0;
for (ai < artifactnames.len) {
let bytes: str = readfile(strings.concat(work, "/",
artifactnames[ai]));
if (si == 0) { artifactrefs[ai] = strings.dup(bytes); }
else { assert(same(artifactrefs[ai], bytes)); };
ai += 1;
};
let normalizedcompiler: str = normalizedtrace(ctrace,
strings.concat(work, "/"), outdir);
let normalizedassembler: str = normalizedtrace(atrace,
strings.concat(work, "/"), outdir);
let normalizedlinker: str = normalizedtrace(ltrace,
strings.concat(work, "/"), outdir);
if (si == 0) {
compilerref = strings.dup(normalizedcompiler);
assemblerref = strings.dup(normalizedassembler);
linkerref = strings.dup(normalizedlinker);
binref = strings.dup(readfile(bin));
} else {
assert(same(compilerref, normalizedcompiler));
assert(same(assemblerref, normalizedassembler));
assert(same(linkerref, normalizedlinker));
assert(same(binref, readfile(bin)));
};
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
runcommandenv(root, strings.concat("platform-reverse-", labels[si]),
reverse, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(same(readfile(bin), readfile(strings.concat(reverseout, "/app"))));
assert(same(normalizedcompiler, normalizedtrace(readfile(compilertrace),
strings.concat(reversework, "/"), reverseout)));
assert(same(normalizedassembler, normalizedtrace(readfile(assemblertrace),
strings.concat(reversework, "/"), reverseout)));
assert(same(normalizedlinker, normalizedtrace(readfile(linkertrace),
strings.concat(reversework, "/"), reverseout)));
ai = 0;
for (ai < artifactnames.len) {
assert(same(readfile(strings.concat(work, "/", artifactnames[ai])),
readfile(strings.concat(reversework, "/", artifactnames[ai]))));
ai += 1;
};
let stableunit: str = strings.dup(readfile(strings.concat(work,
"/app.unit.ww")));
let stablewwi: str = strings.dup(readfile(strings.concat(work,
"/app.wwi")));
let stablearchive: str = strings.dup(readfile(strings.concat(work,
"/app.a")));
let stabledepwwi: str = strings.dup(readfile(strings.concat(work,
"/dep.good.wwi")));
let stablebin: str = strings.dup(readfile(bin));
let stablestamp: str = strings.dup(readfile(strings.concat(work,
"/.wwtool.stamp")));
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
runcommandenv(root, strings.concat("platform-warm-", labels[si]),
forward, 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);
rewritefile(strings.concat(app, "/badparse_windows.ww"),
ignoredparseedit);
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
runcommandenv(root, strings.concat("platform-ignored-edit-", labels[si]),
forward, 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);
assert(same(stableunit, readfile(strings.concat(work, "/app.unit.ww"))));
assert(same(stablewwi, readfile(strings.concat(work, "/app.wwi"))));
assert(same(stablearchive, readfile(strings.concat(work, "/app.a"))));
assert(same(stabledepwwi, readfile(strings.concat(work,
"/dep.good.wwi"))));
assert(same(stablebin, readfile(bin)));
assert(same(stablestamp, readfile(strings.concat(work,
"/.wwtool.stamp"))));
rewritefile(strings.concat(good, "/good.ww"), good6);
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
runcommandenv(root, strings.concat("platform-private-edit-", labels[si]),
forward, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(occurrences(readfile(compilertrace), "\n") == 1);
assert(has(readfile(compilertrace), "/dep.good.unit.new"));
assert(!has(readfile(compilertrace), "/app.unit.new"));
assert(occurrences(readfile(assemblertrace), "\n") == 1);
assert(occurrences(readfile(linkertrace), "\n") == 1);
assert(same(stabledepwwi, readfile(strings.concat(work,
"/dep.good.wwi"))));
assert(same(stableunit, readfile(strings.concat(work, "/app.unit.ww"))));
assert(same(stablewwi, readfile(strings.concat(work, "/app.wwi"))));
assert(same(stablearchive, readfile(strings.concat(work, "/app.a"))));
assert(!same(stablebin, readfile(bin)));
let runchanged: []str = [bin];
runcommand(root, strings.concat("platform-run-edited-", labels[si]),
runchanged, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 13);
let committeddepunit: str = strings.dup(readfile(strings.concat(work,
"/dep.good.unit.ww")));
let committeddepa: str = strings.dup(readfile(strings.concat(work,
"/dep.good.a")));
let committedbin: str = strings.dup(readfile(bin));
rewritefile(strings.concat(good, "/good.ww"), good7);
rewritefile(strings.concat(app, "/platform_future.ww"),
"package main;\nfn future() i32 = { return missing_name; };\n");
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
runcommandenv(root, strings.concat("platform-late-failure-", labels[si]),
forward, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "undefined: missing_name"));
let normalizedfailure: str = normalizedtrace(out.stderr,
strings.concat(work, "/"), bin);
if (si == 0) { faildiagref = strings.dup(normalizedfailure); }
else { assert(same(faildiagref, normalizedfailure)); };
assert(occurrences(readfile(compilertrace), "\n") == 2);
assert(occurrences(readfile(assemblertrace), "\n") == 1);
assert(readfile(linkertrace).len == 0);
assert(same(committeddepunit, readfile(strings.concat(work,
"/dep.good.unit.ww"))));
assert(same(committeddepa, readfile(strings.concat(work,
"/dep.good.a"))));
assert(same(committedbin, readfile(bin)));
assert(same(stablestamp, readfile(strings.concat(work,
"/.wwtool.stamp"))));
assert(!os.exists(strings.concat(work, "/dep.good.unit.new")));
assert(!os.exists(strings.concat(work, "/dep.good.wwi.new")));
assert(!os.exists(strings.concat(work, "/dep.good.a.new")));
assert(!os.exists(strings.concat(work, "/app.unit.new")));
assert(!os.exists(strings.concat(work, "/app.wwi.new")));
assert(!os.exists(strings.concat(work, "/app.a.new")));
rewritefile(strings.concat(good, "/good.ww"), good6);
rewritefile(strings.concat(app, "/platform_future.ww"), future);
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
runcommandenv(root, strings.concat("platform-restored-", labels[si]),
forward, 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);
assert(same(committedbin, readfile(bin)));
rewritefile(strings.concat(good, "/good.ww"), good5);
rewritefile(strings.concat(app, "/badparse_windows.ww"), ignoredparse);
let prodwork: str = strings.concat(root, "/", labels[si], "-prod-work");
let prodout: str = strings.concat(root, "/", labels[si], "-pkg.a");
assert(os.mkdir(prodwork, 448i32) == 0);
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
let prodav: []str = [driver(stages[si]), "build", "-w", prodwork,
"-I", source, "-o", prodout, pkg];
runcommandenv(root, strings.concat("platform-production-", labels[si]),
prodav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(occurrences(readfile(compilertrace), "\n") == 1);
assert(!has(readfile(compilertrace), "test.good"));
assert(!has(readfile(strings.concat(prodwork, "/pkg.unit.ww")),
"selected_internal"));
if (si == 0) {
prodarchiveref = strings.dup(readfile(prodout));
prodwwiref = strings.dup(readfile(strings.concat(prodout, ".wwi")));
} else {
assert(same(prodarchiveref, readfile(prodout)));
assert(same(prodwwiref, readfile(strings.concat(prodout, ".wwi"))));
};
let testwork: str = strings.concat(root, "/", labels[si], "-test-work");
assert(os.mkdir(testwork, 448i32) == 0);
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
let testav: []str = [driver(stages[si]), "test", "-w", testwork,
"-I", source, pkg];
runcommandenv(root, strings.concat("platform-test-", labels[si]), testav,
env, (180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(has(out.stdout, "selected_internal_pair ... ok\n"));
assert(has(out.stdout, "selected_whole_plan9_name ... ok\n"));
assert(has(out.stdout, "selected_reversed_suffix ... ok\n"));
assert(has(out.stdout, "selected_external_pair ... ok\n"));
assert(has(out.stdout, "selected_external_after_dot ... ok\n"));
assert(!has(out.stdout, "wrong_target_test"));
assert(!has(out.stderr, "wrong-target"));
let testctrace: str = readfile(compilertrace);
let testltrace: str = readfile(linkertrace);
assert(!has(testctrace, "test.wrong"));
let sameline: str = linecontaining(testctrace,
"/pkg-internal-test.unit.new");
let externalline: str = linecontaining(testctrace,
"/pkg_test-external-test.unit.new");
assert(occurrences(sameline, "<--import><test.good>") == 1);
assert(occurrences(externalline, "<--import><pkg>") == 1);
assert(occurrences(externalline, "<--import><test.good>") == 1);
assert(!has(testltrace, ".wwi"));
let sameunit: str = readfile(strings.concat(testwork,
"/pkg-internal-test.unit.ww"));
let externalunit: str = readfile(strings.concat(testwork,
"/pkg_test-external-test.unit.ww"));
assert(has(sameunit, "selected_internal_pair"));
assert(has(sameunit, "selected_whole_plan9_name"));
assert(has(sameunit, "selected_reversed_suffix"));
assert(!has(sameunit, "wrong_target_test"));
assert(has(externalunit, "selected_external_pair"));
assert(has(externalunit, "selected_external_after_dot"));
assert(!has(externalunit, "wrong_target_test"));
ai = 0;
for (ai < testartifactnames.len) {
let bytes: str = readfile(strings.concat(testwork, "/",
testartifactnames[ai]));
if (si == 0) { testartifactrefs[ai] = strings.dup(bytes); }
else { assert(same(testartifactrefs[ai], bytes)); };
ai += 1;
};
if (si == 0) {
testoutref = strings.dup(out.stdout);
testerrref = strings.dup(out.stderr);
} else {
assert(same(testoutref, out.stdout));
assert(same(testerrref, out.stderr));
};
let stabletestunit: str = strings.dup(sameunit);
rewritefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"),
wrongtestedit);
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
runcommandenv(root, strings.concat("platform-test-warm-", labels[si]),
testav, env, (180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(same(testoutref, out.stdout));
assert(readfile(compilertrace).len == 0);
assert(readfile(assemblertrace).len == 0);
assert(occurrences(readfile(linkertrace), "\n") == 2);
assert(same(stabletestunit, readfile(strings.concat(testwork,
"/pkg-internal-test.unit.ww"))));
rewritefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"),
wrongtest);
let onlywork: str = strings.concat(root, "/", labels[si], "-only-work");
let onlyout: str = strings.concat(root, "/", labels[si], "-only-bin");
assert(os.mkdir(onlywork, 448i32) == 0);
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
let onlyav: []str = [driver(stages[si]), "build", "-w", onlywork,
"-o", onlyout, wrongonly];
runcommandenv(root, strings.concat("platform-only-", labels[si]), onlyav,
env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(same(out.stderr, strings.concat("ww: ", wrongonly,
": directory contains no WW package sources\n")));
assert(readfile(compilertrace).len == 0);
assert(readfile(assemblertrace).len == 0);
assert(readfile(linkertrace).len == 0);
assert(!os.exists(onlyout));
assert(!os.exists(strings.concat(onlywork, "/.wwtool.stamp")));
let orderwork: str = strings.concat(root, "/", labels[si],
"-order-work");
assert(os.mkdir(orderwork, 448i32) == 0);
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
let orderav: []str = [driver(stages[si]), "build", "-w", orderwork,
"-o", strings.concat(root, "/", labels[si], "-order.a"), order];
runcommandenv(root, strings.concat("platform-order-", labels[si]),
orderav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(same(out.stderr, strings.concat("ww: ", order,
"/a_linux.ww: @test declaration outside *_test.ww\n")));
assert(readfile(compilertrace).len == 0);
assert(readfile(assemblertrace).len == 0);
assert(readfile(linkertrace).len == 0);
assert(!os.exists(strings.concat(orderwork, "/.wwtool.stamp")));
let recursivework: str = strings.concat(root, "/", labels[si],
"-recursive-work");
let recursiveout: str = strings.concat(root, "/", labels[si],
"-recursive-out");
assert(os.mkdir(recursivework, 448i32) == 0);
assert(os.mkdir(recursiveout, 448i32) == 0);
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
let recursiveav: []str = [driver(stages[si]), "build", "-w",
recursivework, "-I", source, "-o", strings.concat(recursiveout,
"/"), strings.concat(source, "/...")];
runcommandenv(root, strings.concat("platform-recursive-build-",
labels[si]), recursiveav, env,
(180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(!has(readfile(compilertrace), "wrongonly"));
assert(!has(readfile(compilertrace), "dep.wrong"));
let recbin: str = strings.concat(recursiveout, "/app");
let recrun: []str = [recbin];
runcommand(root, strings.concat("platform-recursive-run-", labels[si]),
recrun, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 12);
assert(same(binref, readfile(recbin)));
let recursivetestwork: str = strings.concat(root, "/", labels[si],
"-recursive-test-work");
assert(os.mkdir(recursivetestwork, 448i32) == 0);
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
rewritefile(linkertrace, "");
let recursivetestav: []str = [driver(stages[si]), "test", "-w",
recursivetestwork, "-I", source, strings.concat(source, "/...")];
runcommandenv(root, strings.concat("platform-recursive-test-",
labels[si]), recursivetestav, env,
(240i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(has(out.stdout, "selected_internal_pair ... ok\n"));
assert(has(out.stdout, "selected_external_pair ... ok\n"));
assert(has(out.stdout, strings.concat("? ", notests,
" [no tests]\n")));
assert(!has(out.stdout, "wrong_target_test"));
assert(!has(out.stderr, "only_windows"));
assert(!has(readfile(compilertrace), "dep.wrong"));
if (si == 0) {
recursiveoutref = strings.dup(out.stdout);
recursiveerrref = strings.dup(out.stderr);
} else {
assert(same(recursiveoutref, out.stdout));
assert(same(recursiveerrref, out.stderr));
};
si += 1;
};
clean(root);
};
// Invalid @test attribute shapes reject at build with stable text on // Invalid @test attribute shapes reject at build with stable text on
// BOTH frontends (the -T synth checker owns them; the fixture corpus // BOTH frontends (the -T synth checker owns them; the fixture corpus
// cannot reach -T, so these rows live here). Fragments only — the // cannot reach -T, so these rows live here). Fragments only — the