diff --git a/docs/build-system.md b/docs/build-system.md index 8e277962..6b2ab0f6 100644 --- a/docs/build-system.md +++ b/docs/build-system.md @@ -2829,7 +2829,8 @@ and every component of the canonical import path. A selected command directory declares `package main` while retaining its complete canonical import identity. A source import of a command package from a different directory is rejected; the one same-directory exception is an -external `main_test` variant's canonical import of its production action. Two +external `main_test` variant's canonical import of its effective augmented +test action. Two ordinary logical identities for one physical directory are rejected rather than compiled twice; section 11.16 records the deliberate exception for distinct expanded vendor routes that converge through symlinks. @@ -2948,54 +2949,57 @@ supported manifest-free forms include `ww build DIR...`, `ww build DIR/...`, `ww test DIR...`, and `ww test DIR/...`, with overlapping direct and recursive roots. Test retains its existing `-run`, `-filter`, `-list`, `-timeout-ms`, `-j`, `-c`, and `-w` forms. `ww test -c -o test.bin DIR` names the result when -the selected directory has one test variant; the coordinator rejects one output -name for a multi-variant or recursive test request. Explicit `ww test FILE` -retains its compatibility path. +the request selects one canonical directory, including a directory with both +same-package and external-package test sources. The coordinator rejects one +output name only when it would fan out over multiple directory products. +Explicit `ww test FILE` retains its compatibility path. -The test coordinator still discovers requested directories, enumerates the -test package names, selects variants, executes independent binaries, and emits -captured results in byte-sorted package order. It no longer concatenates a +The test coordinator still discovers requested directories, classifies the +selected test package names, executes one binary per test-bearing canonical +directory, and emits captured results in byte-sorted directory order. It no +longer concatenates a generated production/test root, resolves imports, or starts one package graph -per binary or directory. Instead it sends one ordered build request containing -every selected directory/variant root to the Cstage or WWstage command. Its -semantic selections are only the directory and selected variant/package -identities; it also carries output destinations, coordinator-private completion -paths, import search roots, and the optional command-scoped work-directory -policy. The command owns source selection, package loading, compiler inputs, -archive construction, generated-main construction, and linking for every -package variant: +per declared test package. Instead it sends one ordered build request containing +one product descriptor per selected directory, with optional production, +same-package, and external-package selectors, to the Cstage or WWstage command. +It also carries output destinations, coordinator-private completion paths, +import search roots, and the optional command-scoped work-directory policy. +The command owns source selection, package loading, substitution, compiler +inputs, archive construction, the single generated main, and linking: - The ordinary production action selects the directory's byte-sorted non-test - files. A no-test requested product uses this action directly, and every - ordinary, internal-test, or external-test import of that package reuses it. + files. A no-test request uses this action directly. Ordinary builds and + dependencies outside a tested closure continue to use it. - The internal production-plus-test variant selects the byte-sorted production files followed by byte-sorted matching `package p` test files. It is distinct - from production so its private production declarations and test declarations - remain isolated to that test product. + from production, exports declarations contributed by internal test files, and + replaces the ordinary action throughout the applicable tested closure. - When production sources establish `p`, the external variant selects only matching `package p_test` files, where `p` is the production declaration rather than an import-path leaf. A test-only directory may establish its own - one consistent test package name, matching pinned `go/build` classification. - A real source import of the production package uses its canonical import - path and is a direct edge to the same ordinary production action used by - ordinary products and transitive imports; there is no external-production - role or artifact. + `p`, `p_test`, or valid `p`/`p_test` pair, matching pinned `go/build` + classification. The external action's import of the package under test binds + to the augmented internal action when it exists. Importers affected by that + replacement are copied and rewired transitively; an ordinary and augmented + instance of one canonical package never coexist in the linked test closure. - Generated main is a separate package action whose owner-only generated unit - declares `package main` and imports exactly the selected variant and test - support. It consumes those direct `.wwi` files, emits its own `.wwi/.o/.a`, - and alone receives compiler `-T --entry`. Its target edge also receives the - compiler-private `--test-target-package ` binding. That - private canonical-path qualifier prevents a tested command declared `main` - from colliding with generated main; it is not user alias syntax. + declares `package main` and imports every applicable internal/external target + plus test support. It consumes those direct `.wwi` files, emits its own + `.wwi/.o/.a`, and alone receives compiler `-T --entry`. Repeated byte-sorted + `--test-target-package ` arguments identify the target set. + Those compiler-private canonical qualifiers prevent declared-name collisions; + they are not user alias syntax. -The authoritative directory-action identity is the triple **(canonical -filesystem directory, canonical ordinary import path, semantic variant)**. -The semantic variants are production, internal production-plus-test, and -external `_test`; generated main and the reserved test-support alias are -explicit non-directory action classes. The declared package name is stored -separately and is not part of canonical directory/path interning. Requested-root -state, discovery role, product ordinal, output path, persistent artifact key, -and discovery order never enter the triple. +The coordinator groups one test product by canonical directory, but physical +location is not a compiler/package identity. The authoritative action key uses +the finalized canonical dotted import identity, semantic variant, role, and, +for a copy made by recompile-for-test, the owning canonical directory product's +stable test identity. The semantic variants are production, +production-plus-same-package-test, external `_test`, directory generated main, +and recompiled-for-test. The declared package name, local import binding, +request spelling, path leaf, source filename, artifact basename, product +ordinal, output path, and discovery order are presentation or source-location +state and never substitute for that key. A literal directory root may enter the interner before its full import spelling is known. It is provisionally interned by canonical directory and variant, and @@ -3059,19 +3063,19 @@ introduces a package registry. Artifact publication follows the semantic action instead of product order: production uses the full finalized ordinary identity, internal appends -`-internal-test`, external appends `_test-external-test`, and their generated -mains append `-main`. Generated package identities are likewise derived from -the full variant identity, for example `__wwtestmain.a.foo.internal.main` and -`__wwtestmain.b.foo_test.external.main`. Equivalent products therefore reuse an -already-interned directory action and generated-main action and publish through -the same persistent-workdir slots regardless of discovery or product order. +`-internal-test`, external appends `_test-external-test`, and the one +directory-owned generated main appends `-test-main`. Its package identity is +`__wwtestmain..main`, independent of either +declared test name. Equivalent roots therefore converge on one directory +product and reuse already interned actions and persistent-workdir slots +regardless of discovery or request order. The narrow raw single-file compatibility path alone retains `__root`. A deterministic dependency-first traversal of the complete command union invokes the compiler, assembler, and in-driver deterministic archiver once per interned action. This is compile-time interning, not linker-argument -deduplication. Each product is then linked separately from its generated-main -or executable root archive and the complete reachable archive closure. The +deduplication. Each canonical directory product is linked once from its single +generated-main root archive and the complete reachable archive closure. The shared plan remains package-test-specific; it is not a generalized scheduler, action schema, cache, or protocol. @@ -3095,15 +3099,19 @@ a rejected union build. After one successful union build, the completed test products share the coordinator's existing `-j` process bound; captured output is still emitted only in byte-sorted directory/package order. -Every source-imported dependency is a production variant, so dependency -`*_test.ww` files never enter the graph. Imports that occur only in selected -test files add edges only to that internal or external variant. Each compiler -unit contains only its action's owned source set, while its invocation receives -only the byte-sorted direct dependency `.wwi` artifacts as separate inputs. +Ordinary production loading never selects dependency `*_test.ww` files. +Imports that occur only in selected test files add edges only to the applicable +internal or external action. Recompile-for-test may create a product-scoped copy +of an ordinary transitive importer, but that copy retains the importer's +production source unit and changes only canonical dependency targets. Each +compiler unit contains only its action's owned source set, while its invocation +receives only the byte-sorted direct dependency `.wwi` artifacts as separate +inputs. Variant compiles receive `--test-package`, which validates and retains private `@test` declarations as compiler-only export metadata without synthesizing an -entry point. The distinct generated-main action consumes that metadata from its -direct variant export and synthesizes the dispatcher with `-T`. +entry point. The distinct directory generated-main action consumes that +metadata from every direct target export and synthesizes one dispatcher with +`-T`. The generated-main action, rather than the tested variant, owns the implicit direct test-runtime support edge. The command-scoped plan compiles the common @@ -3125,22 +3133,23 @@ coexist only because the former is explicitly rebound to the compiler-only qualifier `__wwtest`. This is the sole role-based directory alias and cannot be created by a source import. The separate expanded-vendor-route exception in section 11.16 is canonical source-tree identity, not a role alias. All ordinary -production actions, including an external test's colocated production -dependency, use canonical directory identity and the global bidirectional -import-path checks above. There is no role-based tolerance for duplicate -ordinary import identities and no late product-closure ambiguity to resolve. +and test actions use canonical action identity and the global bidirectional +import-path checks above. External self-import substitution changes the target +action, never the source spelling or file-local binding. There is no role-based +tolerance for duplicate ordinary import identities and no late product-closure +ambiguity to resolve. -The selected internal/external variants and a production action reached by -their imports or test-runtime closure are the sanctioned graph nodes that may -share a physical directory. This also lets a toolchain package's own tests -coexist with the production action required by the test runtime. An internal -variant already defines those production symbols, so the colocated production -archive is omitted from that product's final link while the production action's -dependency archives remain in the closure. The external product includes the -production archive. Variant-only archives are never linked into another -product. All ordinary logical and physical package-identity collision checks -remain unchanged; only distinct expanded vendor routes receive the section -11.16 symlink-convergence exception. +Production, internal, and external actions may select source from one physical +directory, but the final directory test closure is strict: wherever the +augmented internal action substitutes for production, every affected direct and +transitive edge is rewired before tools. A closure containing both ordinary +`p` and its augmented `ptest` is rejected rather than hidden by initialization +or linker filtering. The external action therefore sees internal-test exports +through `ptest`, and the one linked process owns exactly one package state. +Variant-only archives never leak into an unrelated directory product. All +ordinary logical and physical package-identity collision checks remain +unchanged; only distinct expanded vendor routes receive the section 11.16 +symlink-convergence exception. Both stage linkers receive the generated-main archive first, followed by the complete reverse-topological reachable package-archive closure, runtime, and @@ -3155,10 +3164,13 @@ one stable escape of the canonical discovery directory names the persistent command work directory. It contains neither a declared package leaf nor a product ordinal, so equivalent path spellings and reordered products select the same request state. -Every `*_test.ww` package variant is built even when a file only -declares helpers and contains no `@test`, so its package clause and imports are -still checked by the shared loader. Every built variant is run, and a successful -compiler-owned dispatcher with empty output is reported as `[no tests]`. +Every selected `*_test.ww` package variant is built even when its files only +declare helpers and contain no `@test`, so its package clause and imports are +still checked; that is a real zero-test package and runs one empty combined +harness. A directory with no selected test files instead follows the no-real-run +path: validate/compile ordinary production as needed, publish status only, and +create no support action, generated main, link, binary, result, or process. The +coordinator alone emits its `[no tests]` report. Persistent workdirs keep a global driver/compiler/assembler/stamp identity and per-action committed units. When that global identity is stale, the command @@ -3175,12 +3187,9 @@ stops as soon as a regenerated importer export is byte-identical. Stable semantic artifact keys make that behavior independent of which ordinary or test product first discovered the action. -This ownership split follows the pinned Go 1.26.5 implementation cited in -section 11.12: its loader reuses canonical cached package objects, its work -builder interns actions by mode and package, and its test loader reuses ordinary -production while constructing only the required internal, external, and -generated-main variants. WW borrows that command-global ownership boundary -without adding Go's build cache, import configuration format, or module system. +The completed topology and its pinned Go 1.26.5 evidence are specified in +section 11.22. WW borrows that command-global action boundary without adding +Go's build cache, import-configuration format, or module system. ### 11.8 Implemented exact package-tool invocation slice @@ -3389,7 +3398,8 @@ imports use their explicit compiler-owned bindings. Executable linking independently walks the full reachable package closure and passes archives, never interfaces. The same path handles an ordinary package, the production-plus-internal-test variant, the external test -package and its reused production package, compiler-generated test main, and +package and its effective production-or-augmented self dependency, +compiler-generated directory test main, and the reserved test-support package. Persistent workdirs compare a newly emitted export with its committed predecessor before allowing a direct importer to reuse owner-identical artifacts, retaining correctness without a new cache @@ -3411,8 +3421,9 @@ its non-`main` canonical identity in the unit, export, archive, compiler argv, and linker argv; the bootstrap self-rebuild independently proves the same rule for the real `w6a` and `w6l` command directories. A focused command-test row proves distinct production, internal, external, and generated-main actions, -colocated external-production reuse, parser-only command markers, stage-equal -unit/export/archive bytes, both test binaries' runtime behavior, and pre-tool +colocated external-to-augmented substitution, parser-only command markers, +stage-equal unit/export/archive bytes, the one combined test binary's runtime +behavior, and pre-tool rejection when another directory tries to import the command. The exact-argv regression uses the real diamond @@ -3424,9 +3435,9 @@ and byte-identical units, exports, archives, executables, and tool argument vectors across two clean Cstage builds and two clean WWstage builds. The existing directory-package variant regression checks separate production, internal, external, and -generated-main actions, exact generated-main direct variant/support exports, -canonical production reuse across ordinary and test products, and archive-only -link closures. It also reverses equivalent product descriptors and compares +directory-generated-main actions, exact generated-main direct target/support +exports, canonical recompile-for-test substitution, and one archive-only link +closure. It also reverses equivalent product descriptors and compares exact compiler and linker trace bytes, then changes a shared direct export in persistent workdirs to prove propagation through direct importers stops at the first byte-identical regenerated export. Both stages compile and run those @@ -3661,9 +3672,10 @@ over 255 bytes. They inspect the complete owner in units and exports, exact direct sorted/deduplicated `.wwi` compiler inputs, long mangled assembler symbols, complete archive-only link closures, runtime results, and independent Cstage/WWstage artifact bytes. Two deep outside-root directories declaring -the same leaf coexist under distinct reversible identities. A long command -package and its production, internal, external, and two generated-main actions -remain distinct. Logical and literal roots, dependency-first and root-first +the same leaf coexist under distinct reversible identities. A long command +package's internal, external, and one directory generated-main actions remain +distinct while retaining its canonical package identity. Logical and literal +roots, dependency-first and root-first discovery, equivalent and symlink spellings, reordered products, and warm requests reuse the same production action and persistent slot. The persistent diamond observer also changes a shared dependency export, proves rebuilding of @@ -3757,7 +3769,7 @@ lazily zero-extended `contextstate: []u8`. Products, load frames, and topological frames use typed dynamically allocated slices. `internal/wwpackage` continues to construct one union command for all selected directory-test groups, but now checks the complete -`12 + 6*products + 2*includes` builder argument count before allocating or +`12 + 9*products + 2*includes` builder argument count before allocating or starting the driver. All graph and product growth starts at capacity 8 and doubles until it covers @@ -3819,30 +3831,26 @@ byte-identical export. Closing the chain at `p299 -> p000` produces the complete stage-identical 300-node cycle diagnostic with empty compiler, assembler, and linker traces. -`dynamic_package_universe_crosses_former_boundary` first selects 309 package-test -products through 257 directory spellings in one direct request per stage. -Fifty-two real directories each produce ordinary production, internal -production-plus-test, external `_test`, and two generated-main actions; the -shared support closure brings that command-global universe to 270 actions. Two -hundred five explicit symlink spellings of `p000` add distinct valid products -and resolution contexts while reusing its one canonical internal action. The -observer reverses product and variant order between stages, proves one compile -per action, checks all 309 binaries exist, runs boundary products, validates -variant-owned units, and compares every action artifact plus representative -binaries byte-for-byte. +`dynamic_package_universe_crosses_former_boundary` selects 257 canonical +directory products in one direct request per stage. Fifty-two directories have +combined same/external tests and each produces `ptest`, `pxtest`, and one +directory `pmain`; 205 production-only directories take the no-test path. The +shared support closure brings the command-global universe to exactly 370 +compile actions and 422 assembler invocations, with 52 generated dispatchers, +links, binaries, and results. The 205 no-test products have only ordinary +production actions and statuses: no support-owned target, main, link, binary, +or result. The observer reverses all directory descriptors between stages, +proves one compile per action, runs a boundary combined binary, validates +variant-owned units and external self substitution, and compares representative +action artifacts, normalized traces, and binaries byte-for-byte. -The same observer then exercises the public `ww test /...` coordinator -path. The generated tree has 257 real directories: the 52 test-bearing -directories above plus 205 production-only directories. The coordinator forms -and passes 309 products and 258 contexts (including support) in one driver -request. Reusing the direct request's persistent semantic-action store preserves all 270 -existing actions and adds exactly 410 production/generated-main actions, for 680 -distinct actions. Both stages run every product, produce byte-identical ordered -coordinator output and all 680 action artifacts, and perform no compilation on a -second equivalent public request. It checks all 104 test-bearing result labels -and all 205 no-test result labels, while a builder-boundary observer records -exactly one invocation containing all 309 descriptors for each cold and warm -public request. Existing focused observers continue to prove +The same observer exercises the public `ww test /...` coordinator path +against that persistent action store. The coordinator passes exactly 257 +directory descriptors in one driver request, prints 52 combined `ok` reports +and 205 no-test `?` reports, and performs no new compilation. An unchanged warm +request again invokes no compiler or assembler; the 52 real products follow the +existing explicit-output relink convention while no-test products still create +no linker work. Existing focused observers continue to prove dependency-first/root-first canonical reuse, root-only/combined artifact identity, exact reordered-product trace bytes, and request-shape-independent persistent action reuse. @@ -4351,8 +4359,9 @@ selected test-only directory remains an unusable build root and fails. Build creates one production product per remaining directory. Test retains test-only directories and, after selection, constructs the already specified isolated production/no-test, internal production-plus-test, external `_test`, support, -and generated-main actions. Pattern expansion does not create those variants -and cannot make one variant visible to another. +recompiled-for-test, and one directory-generated-main action. Pattern expansion +does not create those actions; graph-owned substitution alone makes the +augmented package visible through applicable external and transitive edges. Vendor selection remains distinct from vendor import resolution. Selecting a directory below `vendor`, literally or through an explicitly vendor-rooted @@ -4592,14 +4601,14 @@ directory may establish one consistent package name from its test files, as in pinned `go/build`. External action identity remains the canonical production identity plus the existing external variant suffix where that production exists. Imports found only in internal or external test files belong only to -that variant. Support and generated-main actions retain their isolated +that action. Support and the directory generated-main retain isolated action identities and archive closures. A generated dispatcher privately binds its -tested target through -`--test-target-package ` so a command variant declared `main` +tested targets through repeated, byte-sorted +`--test-target-package ` arguments so a command variant declared `main` does not collide with the dispatcher's own synthesized `main`; this is -compiler-generated wiring distinct from ordinary source aliases. Zero-test -dispatchers mark their compiler-owned target/support metadata imports consumed. -That private binding is installed or marked consumed only in the generated +compiler-generated wiring distinct from ordinary source aliases. A real +zero-test directory product marks all compiler-owned target/support metadata +imports consumed. Those private bindings are installed or marked consumed only in the generated dispatcher's source section; an import from an earlier test-file section neither supplies nor satisfies it. @@ -5117,14 +5126,15 @@ the reachable archive closure and runtime/native inputs; no `.wwi`, alias, blank spelling, or dispatcher sidecar appears. Production, production-plus-internal-test, external `_test`, test support, and -generated main retain separate action identities. An internal product replaces -the colocated production task with its combined variant and includes test-only -blank edges/init declarations exactly once. An external product initializes -ordinary production and its production dependencies before the external task. -Support is an ordinary dependency task; generated main owns only the final -dispatcher call and cannot duplicate a tested task. Test-file-only imports, -runtime lets, and init functions never enter production. A blank import cannot -bypass imported-`main` rejection, including through vendor expansion. +directory generated main retain separate action identities. One canonical +directory product replaces colocated production with `ptest` wherever internal +tests augment it, rewires `pxtest` self-import and affected transitive importers +to that action, and includes test-only blank edges/init declarations exactly +once. Support is an ordinary dependency task; the one generated main owns only +the final dispatcher call and cannot duplicate a tested task. Test-file-only +imports, runtime lets, and init functions never enter production. A blank +import cannot bypass imported-`main` rejection, including through vendor +expansion. `.wwi` contains neither blank-only spelling, init declarations/bodies, hidden variable helpers, slice backing symbols, package tasks, nor dispatcher facts. @@ -5139,7 +5149,7 @@ first regenerated byte-identical semantic export. The owner source voucher remains `.unit.ww`; a linked product root also owns `.init.unit.ww` for its dispatcher unit, `.init.s`, `.init.o`, and -two-member archive. Current persistent formats are build 18 and test 17. Warm +two-member archive. Current persistent formats are build 18 and test 19. Warm 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 identity copies, stamp, library/executable publications, and test statuses are @@ -5184,9 +5194,10 @@ diamonds, independent lexical ties, aggregate and allocation initialization, multiple-cycle diagnostics, special-init rejection, test-variant isolation, canonical task/dispatcher/archive bytes, init-only invalidation, and warm invalid-init rollback across independent Cstage and WWstage roots. Direct and -recursive test legs compare variant dispatcher/archive bytes and observable -production-dependency order; exact task counts include support and generated -main. Rejection rows compare complete normalized diagnostics and prove cold +recursive test legs compare the one directory dispatcher/archive and observable +dependency, `ptest`, `pxtest`, then `pmain` order; every production and +dependency task runs exactly once. Exact task counts include support and the +single generated main. Rejection rows compare complete normalized diagnostics and prove cold scratch absence. The observer also uses a repository-built `setrlimit` launcher to find one shared bounded-memory ceiling at which both drivers fail before a producer, and proves no-follow atomic rejection of dangling driver staging and @@ -5305,7 +5316,7 @@ 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 +Persistent formats are build 18 and test 19 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 @@ -5340,9 +5351,242 @@ 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. +escape is also not exposed. Section 11.22 closes the formerly separate +directory test-product topology divergence without changing these filename +eligibility boundaries. + +### 11.22 Implemented one canonical directory package-test product + +`ww test` now owns one practical test product per canonical selected directory, +not one product per declared test package. Production, augmented internal test, +external test, support, recompiled dependency, and generated-main actions stay +separate compilation units; only their execution/publication ownership is +unified. This is the applicable Go 1.26.5 topology for WW's local, dotted, +manifest-free package model. + +#### Pinned Go evidence and pre-fix divergence + +The reference is official Go 1.26.5 at commit +`c19862e5f8415b4f24b189d065ed739517c548ba`: + +- `TestPackagesFor` defines one `pmain` test binary running `ptest` and optional + `pxtest`, with `ptest` equal to production plus same-package test files + ([`cmd/go/internal/load/test.go`, lines 85–101 and 175–226](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L85-L101)). +- External self-import is recognized before the external package is formed and + is rebound to `ptest`, not ordinary `p` + ([lines 144–161 and 228–266](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L144-L161)). +- One `pmain` receives both applicable targets, one sorted import set, and then + `recompileForTest` copy-on-write rewires every affected transitive importer + from ordinary production to the augmented package + ([lines 272–293, 342–376, and 421–490](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L272-L293)). +- The generated source scans both target classes into one function set and one + `testing.MainStart`/`Run` + ([lines 595–638 and 790–860](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L595-L638)). +- A directory with no selected test files takes the early ordinary-production + compile path and creates no test support, generated main, link, executable, + or subprocess; its print action reports `[no test files]` + ([`cmd/go/internal/test/test.go`, lines 1133–1170 and 1524–1557](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1170)). +- A real test package creates one generated source, one link action, and one + output-copy/install or run action, including `-c`/`-o` + ([lines 1200–1364](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1200-L1364)). +- Go's action cache distinguishes cloned package objects even when their import + paths match, compiler inputs come from the selected action's direct + dependencies, and the link closure selects one archive per import path + ([`cmd/go/internal/work/action.go`, lines 202–206, 437–447, and 628–658](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L202-L206), + [`exec.go`, lines 410–438 and 864–884](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/exec.go#L410-L438)). +- `go/build` classifies same-package and `_test` external files independently + and accepts directories containing only either class + ([`go/build/build.go`, lines 948–953, 1005–1036, and 1076–1082](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L948-L953)). + +Official command testdata closes the observable cases. `test_empty.txt` lines +3–24 and 30–53 accepts production-only, internal-only, external-only, combined, +test-only internal, test-only external, and mixed test-only directories +([source](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_empty.txt#L3-L24)). +`vendor_test_issue11864.txt` lines 8–9 and 64–80 proves an external test can use +an export declared only in an internal test source +([source](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/vendor_test_issue11864.txt#L64-L80)). +`list_test_imports.txt` lines 3–21 proves transitive rebuilding when a helper +imports the package under test +([source](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/list_test_imports.txt#L3-L21)). +`toolexec.txt` lines 27–50 observes distinct `ptest`, `pxtest`, and `pmain` +compiles but one main and linker call +([source](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/toolexec.txt#L27-L50)). +`test_no_tests.txt` lines 1–14 uses a panicking production initializer to prove +that no-test reporting does not execute a test process +([source](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_no_tests.txt#L1-L14)). + +Before this slice WW constructed one generated main, link, binary, result, and +report for each declared same/external test package. A combined directory ran +two processes, initialized production state twice, hid internal-test exports +from the external package, and made `-c -o` fail as a multi-product request. +Even a directory with no selected tests manufactured support, a main, a link, +and a temporary executable merely to print `[no tests]`. Test-only external +directories were mislabeled, a valid mixed test-only directory was rejected, +and a transitive importer was compiled against ordinary production. + +#### Final product and action topology + +For canonical directory product `P`, the action graph is: + +- `p`: ordinary production sources only; +- `ptest`: production plus same-package selected `*_test.ww` sources; +- `pxtest`: external-package selected `*_test.ww` sources only; +- product-scoped recompiled actions: unchanged source units whose direct target + set was rewritten by the `p -> ptest` substitution; +- support: one command-global production support action (or the reserved + `__wwtest` compiler binding when user `test` occupies that spelling); +- `pmain`: one directory-owned generated-main action importing every applicable + target and support. + +The source shapes produce these reachable roots: + +| Directory shape | Test closure and externally visible product | +| --- | --- | +| production, no selected tests | ordinary `p`; no support/main/link/run/output/result | +| production + internal | `ptest` + `pmain`; one binary/result | +| production + external | unaugmented `p` + `pxtest` + `pmain`; one binary/result | +| production + both | `ptest` + `pxtest -> ptest` + `pmain`; one binary/result | +| test-only same package | `ptest` + `pmain`; one binary/result | +| test-only external package | `pxtest` + `pmain`; one binary/result | +| test-only same + external | `ptest` + `pxtest -> ptest` + `pmain`; one binary/result | + +A selected helper-only `*_test.ww` file is still a real test source: it is +loaded and may yield an empty harness. The no-real-run branch is specifically +the absence of selected test files after platform filtering. Ordinary +`ww build` excludes test filenames before reading even their package clauses, +so malformed test-only content and test-only imports cannot affect production. + +The coordinator in `internal/wwpackage/package.ww` classifies all selected +files into one directory group. Classification is production-name-relative, +so a legitimate production package literally named `foo_test` has external +name `foo_test_test`; without production, a valid `p`/`p_test` pair is retained +as same/external rather than blindly stripping every suffix. The private driver +descriptor is an ordered directory record: + +``` +--ww-package-test KIND FAMILY PRODUCTION INTERNAL EXTERNAL DIR OUTPUT STATUS +``` + +Missing action selectors are `-`. One descriptor owns at most one output and +one status. Canonically duplicate products and pairwise output/status/staging +collisions reject before producer execution. Declared names and output stems do +not identify products or actions. + +The Cstage and WWstage drivers first load and validate the ordinary production +root. They form `ptest` and `pxtest` as separate source actions, bind external +self-import to `ptest`, seed `p -> ptest`, and copy/rewrite the affected +dependency closure. Both deduplicated dependency edges and every original +file-local import binding target are rewritten through action identity. The +strict closure validator rejects any leaked ordinary/augmented duplicate; no +initialization or archive-order fallback can hide an incomplete substitution. + +`pmain` stores a checked, byte-sorted unique target action set. Its generated +unit imports each target once plus support. The compiler argv repeats +`--test-target-package` in that same order, followed by the exact byte-sorted +direct `--import PATH WWI` set. Thus a combined shape has the essential form: + +``` +w6c --test-package ... -I PTEST.wwi -o PTEST.s PTEST.unit.ww +w6c --test-package ... --import P PTEST.wwi ... -I PXTEST.wwi ... +w6c -T --entry --test-support-module test \ + --test-target-package P --test-target-package P_test \ + --import P PTEST.wwi --import P_test PXTEST.wwi --import test TEST.wwi \ + -I PMAIN.wwi -o PMAIN.s PMAIN.unit.ww +``` + +Actual arguments also contain action-owned init and dispatcher symbols. The +assembler receives one source object per compiled action and exactly one +`pmain.init.s -> pmain.init.o` dispatcher. Test target archives contain only +their deterministic `pkg.o` member; the main archive contains `pkg.o` then +`init.o`. Link argv is root plus reachable archives only: + +``` +w6l -o OUTPUT.new PMAIN.a [PXTEST.a] [PTEST-or-P.a] ... TEST.a libwwrt.a +``` + +No `.wwi`, source filename, standalone semantic object, or linker-order choice +participates. Both native linkers already resolve archive members by symbols; +they required no topology-specific change. + +#### Import, export, initialization, and execution ownership + +Default, explicit-alias, and blank imports remain file-local source +occurrences. The package graph still uses one canonical edge per target and +preserves every occurrence for usage and legality diagnostics. Substitution +changes only the chosen canonical action. Consequently an external source +continues to spell `import P`, but its direct compiler export input is +`ptest.wwi`; an exported helper in an internal test source is ordinary augmented +package export data and needs no special reader format. Per-owner `@test` +metadata in `.wwi` remains sufficient. The writer/reader format did not change. + +`cmd/w6c/main.c`, `cmd/wcc/ww.h`, and `cmd/wcc/check.c`, with their self-hosted +twins, changed singular generated-target state into a target set. Every target +import is marked used, and every imported `@test` declaration receives its +canonical target qualifier. The coordinator controls target order; the checker +does not resolve directories. `wwdump` passes an empty target set on its +non-test path. + +One dependency-first dispatcher is generated from the substituted closure. +Each reachable canonical action contributes one initialization task; `pmain` +is the sole entry package that calls the dispatcher. A combined directory +therefore initializes dependencies, augmented production, external tests, +support, and main exactly once in one process. Filtering and listing operate on +the single deterministic enumeration containing both target sets; output has +one accounting block and one directory report. `ww test -c -o OUTPUT DIR` +publishes the one directory binary even when both target classes exist. + +#### Artifacts, persistence, invalidation, and rejection + +Semantic action artifacts remain separate: `.unit.ww`, `.wwi`, `.s`, `.o`, and +`.a` for each production/test/recompiled/main/support action, plus main init +unit/assembly/object. The directory product alone owns the binary, status, and +result. Test persistent-work format is `19`; semantic package storage is format +`3` and includes the product-scoped `for_test` identity for recompiled actions. +There is still no cache, CAS, manifest, registry, database, or result cache. + +Warm reuse compares owner-unit bytes and exact direct export bytes. An internal +helper body edit with stable `.wwi` rebuilds only `ptest` and relinks; external +and reverse compilation stop. An external body-only edit rebuilds only +`pxtest`. Adding or changing an exported internal helper changes `ptest.wwi`, +then rebuilds exactly affected recompiled importers, `pxtest`, and `pmain`. +Adding or removing a target class changes only the owning directory product and +newly reachable actions. Request/root order and equivalent directory spelling +do not change action bytes, target order, output, or reuse. + +Loader-owned source classification, import legality, canonical-product, +duplicate-closure, cycle, command-kind, and publication-path errors reject +before compiler, assembler, linker, support, or main work. Production failure +is diagnosed once even though an augmented action would contain the same +sources. Later compiler, assembler, generated-main, archive, link, staging, or +commit failure remains one request-wide transaction: no sibling product runs, +no status/result/binary or mixed generation is published, prior committed +bytes remain unchanged, and every staged `.new` is discarded. Both drivers use +checked dynamic allocation and transactional clone construction; allocation +failure cannot publish a partial action or leave cleanup to traverse +uninitialized storage. + +The native proof is primarily +`directory_package_graph_variants`, +`multi_directory_shared_package_plan`, +`empty_and_invalid_package_classes`, +`dynamic_package_universe_crosses_former_boundary`, +`platform_filename_source_selection`, +`vendor_directory_import_resolution`, and +`test_variant_initialization`, with late transaction ownership in +`sibling_test_variant_failures_are_isolated`. Together they compare +cold/persistent Cstage and WWstage units, exports, assembly, objects, exact +archive members, mains, binaries, diagnostics, runtime order, literal and +normalized compiler/assembler/linker argv, both root orders in each stage, +precise body/export and target-removal/re-addition invalidation, combined-graph +allocation failure, repeated late internal/external/main/link rollback, and a +complete work-directory sweep for staged residue. + +Deliberately unchanged or unsupported behavior includes the raw single-file +compatibility path, WW's `[no tests]` presentation text, Go modules and build +cache, network resolution, manifests, coverage/vet/fuzz/benchmark generation, +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. ## 12. Candidate architectures and hard-gate decision diff --git a/docs/spec.md b/docs/spec.md index e3a4c981..2e7b4a66 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -251,10 +251,12 @@ ImportName = ident . ImportPath = ident { "." ident } . ``` -- Every source file begins with a package clause. A directory of eligible - `.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 - canonical import identity. +- Every source file begins with a package clause. Within one semantic source + action, eligible `.ww` files share one declared package name. Ordinary + production and same-package test sources use `p`; external test sources may + use the related `p_test`, and the actions remain separate even though one + canonical directory owns their test product. The declared name need not equal + the directory name or the final component of its 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 @@ -286,10 +288,12 @@ ImportPath = ident { "." ident } . initialization. Repeated blank occurrences and blank plus default/explicit named occurrences of one path are valid; every named occurrence remains independently subject to duplicate-binding and unused checks. - The package dependency graph is the sorted, deduplicated union of the real - imports in all eligible files. Occurrences retain their owning file and - position, but equal canonical targets create one graph edge/package action. - Self-import is rejected. + Each semantic action's package dependency graph is the sorted, deduplicated + union of real imports in that action's eligible source set. Occurrences retain + their owning file and position, but equal canonical targets create one graph + 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. - 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 @@ -606,13 +610,21 @@ the program. Two placements, following the Go `foo` / `foo_test` model: ``` Production, production-plus-white-box-test, external black-box-test, test -support, and generated test main are distinct package actions. Before a test -function runs, the generated test product initializes its exact reachable -variant graph dependency-first and once per canonical action. A white-box -variant replaces the colocated production task rather than initializing both; -an external variant depends on ordinary production. Imports, runtime lets, and -init declarations found only in `*_test.ww` never enter an ordinary production -build. +support, recompiled-for-test dependencies, and generated test main are distinct +package actions. One canonical selected directory owns one test product and one +generated main, which imports every applicable same-package and external target +and produces one binary/result. The external action's import of the package +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. + +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 +ordinary production build. Test-only same-package, external-package, and valid +mixed directories are accepted from their selected test files. A directory +with no selected test file validates ordinary production but creates no test +support, generated main, link, binary, result, or process. --- diff --git a/docs/test-system-v2.md b/docs/test-system-v2.md index 3a505127..4fa3ff09 100644 --- a/docs/test-system-v2.md +++ b/docs/test-system-v2.md @@ -193,8 +193,9 @@ and nonempty-version-constant assertions. `ww test` delegates directory package requests to the native package coordinator. The coordinator owns request-pattern expansion, package grouping, -same-package and external-package variant selection, filtering, result -aggregation, and its internal temporary workspace. Test sources are exclusively +same-package and external-package action selection, one canonical +directory-owned product, filtering, result aggregation, and its internal +temporary workspace. Test sources are exclusively `*_test.ww` (Go's `_test.go` contract): a line-leading `@test` declaration in any other source is rejected loudly — by both driver stages at directory enumeration and by the coordinator at source classification. A local spelling @@ -204,8 +205,11 @@ directory prefix before the first wildcard. Recursive children beginning `.` or `_` and exact `testdata` subtrees are pruned; child directory symlinks are not followed. A wildcard cannot consume a non-terminal exact `vendor` element, while an explicitly vendor-rooted pattern remains legal. Every selected -test-bearing directory becomes one package run, and selected source-bearing -directories without tests report the usual `?` line. The coordinator first +test-bearing directory becomes exactly one package run, binary, and result even +when both internal and external actions exist. Selected source-bearing +directories without selected test files report the usual `?` line after +ordinary production validation, with no support, generated main, link, binary, +result, or process. The coordinator first launches one driver plan for the complete command-global package/action union. After that shared build completes, `-j N` schedules up to N successful selected test binaries concurrently under `os.exec` start/poll supervision (no threads); @@ -213,10 +217,11 @@ emission stays strictly in group order, so the byte stream is identical at every `-j` level, and `-j 1` — the default — matches the former sequential run loop exactly. Measured on the 31-package `lib/...` walk: 7.0s sequential, 2.4s at `-j 4`. With `-c`, it publishes each exact -`.test` binary in the package directory; the first output owns the one +`.test` directory binary; the first output owns the one shared cold sepwork containing the command-global action universe. Those become caller-owned artifacts. `-c -o ` -names that artifact instead of the fixed stem, for exactly one package: +names that artifact instead of the fixed stem for exactly one directory, +including a combined internal/external directory: the coordinator rejects a multi-package fan-out ("cannot use -o with multiple packages", Go's `go test -o` rule), and `-o` without `-c` is rejected at the driver ("needs -c for a package target") because a plain @@ -256,8 +261,10 @@ archive/action ownership, one canonical direct edge for repeated occurrences, stage-equal diagnostics and binaries, and clean rejection state. The declared-name observer proves imports are file-scoped while dependency edges are the package-wide sorted union; command and -production/internal/external/generated-main variants retain canonical action -ownership; vendor expansion changes identity but not the effective qualifier; +production/internal/external/directory-main actions retain canonical action +ownership while one canonical directory owns their product; external +self-imports bind the augmented internal action; vendor expansion changes +identity but not the effective qualifier; compiler argv contains only direct `.wwi` inputs; and its named rejected actions leave neither committed nor staged action artifacts or a published binary. It also forces a request transaction to reject after dependency work @@ -277,9 +284,15 @@ 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. +compiler fails. Wrong-target test files cannot add a target to the directory +main or create one for an otherwise no-test directory. The bounded-memory +package-initialization observer now pressures the combined directory topology +itself: production, augmented internal test, external self-import, a transitive +recompile-for-test clone, support, and the two-target generated main. Each stage +searches its own bounded address-space ceiling, while the accepted failure must +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. `test/sep/sepinit_test.ww` is the single focused package-initialization owner. It generates all source trees temporarily and runs independent cold/persistent @@ -287,15 +300,18 @@ Cstage and WWstage legs. Its matrix covers blank-only reachability; dependency, diamond, and independent ready-task order; runtime call/allocation and aggregate package lets; multiple init declarations and invalid forms; direct/qualified init invisibility; initialization cycles and diagnostic order; command, -internal-test, external-test, support, and generated-main ownership; canonical +internal-test, external-test, support, and one directory-generated-main +ownership; canonical task and dispatcher symbols; deterministic one- and two-member archives; `.wwi` exclusion; byte-identical artifacts/binaries; init-only invalidation; warm invalid-init rollback; direct/recursive variant equivalence; exact -production, support, and generated-main task ordering; complete normalized +production, support, ptest-before-pxtest-before-main task ordering, exactly-once +package/dependency initialization in one dispatcher; complete normalized rejection diagnostics; dangling staging/rollback no-follow rejection; and checked compiler-output failure/non-regular-destination rollback; and -bounded-memory allocation-failure parity under one shared ceiling supplied by -the repository-built `sep-limitexec` helper. +bounded-memory Cstage/WWstage allocation-failure parity across the complete +combined package-test graph, under independently discovered ceilings supplied +by the repository-built `sep-limitexec` helper. `ww build`, an explicit single-file `ww test -o `, and each successful directory-package `ww test -c` build publish `.sepwork` as a caller-owned @@ -330,9 +346,11 @@ create false reuse or a mixed generation; a successful executable link still reruns when required. One workdir serves one invocation at a time and may hold the complete command-global action universe for all selected roots and isolated variants; semantic identity, rather than request shape, controls reuse. Both -driver stages implement the identical contract. This is build staleness in the +driver stages implement the identical contract. Test-mode persistent workdirs +use format 19. This is build staleness in the Make/mk/Go sense, not a result cache: -tests always run, and the byte-identity and bootstrap gates keep building on +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/`. On a package or tree target, `ww build/test -w DIR` forwards that exact caller diff --git a/test/package/cases/pass/pass_test.ww b/test/package/cases/pass/pass_test.ww index 48e7aa10..62608e0c 100644 --- a/test/package/cases/pass/pass_test.ww +++ b/test/package/cases/pass/pass_test.ww @@ -3,7 +3,7 @@ package pass_test; import test; @test fn alpha_pass() void = { - assert(test.current() == "pass_test.alpha_pass"); + assert(test.current() == "pass.alpha_pass"); }; @test fn beta_skip() void = { diff --git a/test/package/package_test.ww b/test/package/package_test.ww index d288ddde..836f6620 100644 --- a/test/package/package_test.ww +++ b/test/package/package_test.ww @@ -362,10 +362,11 @@ fn digesthex(h: *hash.hash) str = { return strings.frombytes(out); }; -fn packagestoragekey(path: str, canon: str, variant: i32, role: i32) str = { +fn packagestoragekeyfor(path: str, canon: str, variant: i32, role: i32, + for_test: str) str = { let state: sha256.state = sha256.sha256(); let h: *hash.hash = (&state): *hash.hash; - hash.write(h, strings.toutf8("ww-package-storage-v2:")); + hash.write(h, strings.toutf8("ww-package-storage-v3:")); let tag: [4]u8; tag[0] = ('0': i32 + variant): u8; tag[1] = ':': u8; @@ -377,6 +378,10 @@ fn packagestoragekey(path: str, canon: str, variant: i32, role: i32) str = { zero[0] = 0u8; hash.write(h, zero[0:1]); hash.write(h, strings.toutf8(canon)); + hash.write(h, zero[0:1]); + if (for_test.len != 0) { + hash.write(h, strings.toutf8(for_test)); + }; let out: []u8 = alloc([], 81u64)!; let prefix: str = "__wwpkg.v"; let i: i32 = 0; @@ -389,6 +394,10 @@ fn packagestoragekey(path: str, canon: str, variant: i32, role: i32) str = { return strings.frombytes(out); }; +fn packagestoragekey(path: str, canon: str, variant: i32, role: i32) str = { + return packagestoragekeyfor(path, canon, variant, role, ""); +}; + fn importrelative(path: str) str = { let out: []u8 = alloc([], (path.len + 1): u64)!; let i: i32 = 0; @@ -442,6 +451,71 @@ fn normalizedtrace(trace: str, work: str, output: str) str = { }; }; +fn directoryhasnew(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; }; + if (len >= 4 && buf[off + 19 + len - 4] == '.' + && buf[off + 19 + len - 3] == 'n' + && buf[off + 19 + len - 2] == 'e' + && buf[off + 19 + len - 1] == 'w') { + assert(os.close(fd) == 0); + return true; + }; + off += reclen; + }; + n = os.getdents64(fd, buf.ptr, buf.len: u64); + }; + assert(n == 0i64); + assert(os.close(fd) == 0); + return false; +}; + +fn byteshasat(s: str, off: i32, want: str) bool = { + if (off < 0 || off + want.len > s.len) { return false; }; + let i: i32 = 0; + for (i < want.len) { + if (s[off + i] != want[i]) { return false; }; + i += 1; + }; + return true; +}; + +fn archivememberend(ar: str, off: i32, name: str) i32 = { + assert(byteshasat(ar, off, name)); + assert(byteshasat(ar, off + 58, "`\n")); + let size: i32 = 0; + let seen: bool = false; + let i: i32 = 0; + for (i < 10) { + let c: u8 = ar[off + 48 + i]; + if (c >= '0' && c <= '9') { + seen = true; + size = size * 10 + ((c - '0'): i32); + } else { assert(c == ' '); }; + i += 1; + }; + assert(seen); + return off + 60 + size + (size & 1); +}; + +fn checkactionarchive(ar: str, hasinit: bool) void = { + assert(byteshasat(ar, 0, "!\n")); + let off: i32 = archivememberend(ar, 8, "pkg.o/"); + if (hasinit) { off = archivememberend(ar, off, "init.o/"); }; + assert(off == ar.len); +}; + fn hexbytes(value: str) str = { let out: []u8 = alloc([], (value.len * 2 + 1): u64)!; let digits: str = "0123456789abcdef"; @@ -468,11 +542,11 @@ fn hexbytes(value: str) str = { expectexit(&out, 0); let first: i32 = pos(out.stdout, "routing.private_helper_first\n"); let second: i32 = pos(out.stdout, "routing.same_package\n"); - let third: i32 = pos(out.stdout, "routing_test.external_package\n"); + let third: i32 = pos(out.stdout, "routing.external_package\n"); assert(first >= 0 && first < second && second < third); assert(occurrences(out.stdout, "routing.private_helper_first\n") == 1); assert(occurrences(out.stdout, "routing.same_package\n") == 1); - assert(occurrences(out.stdout, "routing_test.external_package\n") == 1); + assert(occurrences(out.stdout, "routing.external_package\n") == 1); let rav: []str = [driver("ww"), "test", "-I", sourceroot, target]; runcommand(root, "run", rav, @@ -480,11 +554,11 @@ fn hexbytes(value: str) str = { expectexit(&out, 0); assert(has(out.stdout, "routing.private_helper_first ... ok\n")); assert(has(out.stdout, "routing.same_package ... ok\n")); - assert(has(out.stdout, "routing_test.external_package ... ok\n")); + assert(has(out.stdout, "routing.external_package ... ok\n")); assert(has(out.stdout, - "2 passed, 0 failed, 0 skipped, 0 harness errors\n")); - assert(has(out.stdout, - "1 passed, 0 failed, 0 skipped, 0 harness errors\n")); + "3 passed, 0 failed, 0 skipped, 0 harness errors\n")); + assert(occurrences(out.stdout, + "3 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1); let fav: []str = [driver("ww"), "test", "-run", "routing.private_helper_first", "-filter", "external_package", @@ -494,25 +568,23 @@ fn hexbytes(value: str) str = { expectexit(&out, 0); assert(has(out.stdout, "routing.private_helper_first ... ok\n")); assert(!has(out.stdout, "routing.same_package ...")); - assert(has(out.stdout, "routing_test.external_package ... ok\n")); + assert(has(out.stdout, "routing.external_package ... ok\n")); let nav: []str = [driver("ww"), "test", "-I", sourceroot, "-run", "no-such-*", target]; runcommand(root, "nomatch", nav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); - assert(occurrences(out.stdout, "[no matches]\n") == 2); + assert(occurrences(out.stdout, "[no matches]\n") == 1); assert(occurrences(out.stdout, - "1 discovered, 0 selected, 0 started, 0 completed\n") == 1); - assert(occurrences(out.stdout, - "2 discovered, 0 selected, 0 started, 0 completed\n") == 1); + "3 discovered, 0 selected, 0 started, 0 completed\n") == 1); let lnav: []str = [driver("ww"), "test", "-list", "-run", "no-such-*", "-I", sourceroot, target]; runcommand(root, "list-nomatch", lnav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); - assert(occurrences(out.stdout, "[no matches]\n") == 2); + assert(occurrences(out.stdout, "[no matches]\n") == 1); clean(root); }; @@ -684,8 +756,249 @@ fn hexbytes(value: str) str = { runcommand(root, "commented-test", commentedav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); - assert(has(out.stdout, " [no tests]\n")); + assert(has(out.stdout, "ok ")); + assert(!has(out.stdout, " [no tests]\n")); assert(!has(out.stdout, "phantom")); + + // Go's complete empty-package matrix: production-only, internal-only, + // external-only, combined, test-only same, test-only external, and honest + // mixed test-only directories are each one directory-owned result. + let prodonly: str = strings.concat(root, "/shapeprod"); + let internalonly: str = strings.concat(root, "/shapeinternal"); + let externalonly: str = strings.concat(root, "/shapeexternal"); + let both: str = strings.concat(root, "/shapeboth"); + let testonlysame: str = strings.concat(root, "/tosame"); + let testonlyexternal: str = strings.concat(root, + "/toexternal"); + let testonlymixed: str = strings.concat(root, "/tomixed"); + let shapes: []str = [prodonly, internalonly, externalonly, both, + testonlysame, testonlyexternal, testonlymixed]; + let shi: i32 = 0; + for (shi < shapes.len) { + assert(os.mkdir(shapes[shi], 448i32) == 0); + shi += 1; + }; + writefile(strings.concat(prodonly, "/prod.ww"), + "package shapeprod;\nexport fn value() i32 = { return 1; };\n"); + writefile(strings.concat(internalonly, "/prod.ww"), + "package shapeinternal;\nfn value() i32 = { return 1; };\n"); + writefile(strings.concat(internalonly, "/same_test.ww"), strings.concat( + "package shapeinternal;\n", + "@test fn internal_runs() void = { assert(value() == 1); };\n")); + writefile(strings.concat(externalonly, "/prod.ww"), + "package shapeexternal;\nexport fn value() i32 = { return 1; };\n"); + writefile(strings.concat(externalonly, "/external_test.ww"), strings.concat( + "package shapeexternal_test;\nimport shapeexternal;\n", + "@test fn external_runs() void = { assert(shapeexternal.value() == 1); };\n")); + writefile(strings.concat(both, "/prod.ww"), + "package shapeboth;\nexport fn value() i32 = { return 1; };\n"); + writefile(strings.concat(both, "/same_test.ww"), strings.concat( + "package shapeboth;\n", + "export fn helper() i32 = { return 2; };\n", + "@test fn same_runs() void = { assert(value() == 1); };\n")); + writefile(strings.concat(both, "/external_test.ww"), strings.concat( + "package shapeboth_test;\nimport shapeboth;\n", + "@test fn external_runs() void = { assert(shapeboth.helper() == 2); };\n")); + writefile(strings.concat(testonlysame, "/same_test.ww"), + "package tosame;\n@test fn same_only() void = { assert(true); };\n"); + writefile(strings.concat(testonlyexternal, "/external_test.ww"), + "package toexternal_test;\n@test fn external_only() void = { assert(true); };\n"); + writefile(strings.concat(testonlymixed, "/same_test.ww"), strings.concat( + "package tomixed;\nexport fn helper() i32 = { return 3; };\n", + "@test fn same_test_only() void = { assert(helper() == 3); };\n")); + writefile(strings.concat(testonlymixed, "/external_test.ww"), strings.concat( + "package tomixed_test;\nimport tomixed;\n", + "@test fn external_test_only() void = { assert(tomixed.helper() == 3); };\n")); + let shaperefs: []str = ["", "", "", "", "", "", ""]; + shi = 0; + for (shi < shapes.len) { + let stagei: i32 = 0; + for (stagei < stages.len) { + let shapeav: []str = [driver(stages[stagei]), "test", "-I", root, + shapes[shi]]; + runcommand(root, strings.concat("shape-", stages[stagei], "-", + boundarypkgname(shi)), shapeav, + (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + if (stagei == 0) { shaperefs[shi] = strings.dup(out.stdout); } + else { assert(same(shaperefs[shi], out.stdout)); }; + assert(out.stderr.len == 0); + stagei += 1; + }; + shi += 1; + }; + assert(has(shaperefs[0], " [no tests]\n")); + assert(occurrences(shaperefs[0], strings.concat( + "? ", prodonly, " [no tests]\n")) == 1); + assert(has(shaperefs[1], "shapeinternal.internal_runs ... ok\n")); + assert(has(shaperefs[2], "shapeexternal.external_runs ... ok\n")); + assert(occurrences(shaperefs[3], " ... ok\n") == 2); + assert(has(shaperefs[4], "tosame.same_only ... ok\n")); + assert(has(shaperefs[5], "toexternal.external_only ... ok\n")); + assert(has(shaperefs[6], "tomixed.same_test_only ... ok\n")); + assert(has(shaperefs[6], "tomixed.external_test_only ... ok\n")); + let shapefamilies: []str = ["shapeprod", "shapeinternal", + "shapeexternal", "shapeboth", "tosame", "toexternal", "tomixed"]; + shi = 1; + for (shi < shapes.len) { + assert(occurrences(shaperefs[shi], strings.concat( + "ok ", shapes[shi], " [", shapefamilies[shi], "]\n")) == 1); + shi += 1; + }; + + // The five non-combined real-test shapes have one structural product in + // both stages. The combined case is proved in full by the graph observer. + let shapecompilerwrapper: str = strings.concat(root, + "/shape-w6c-wrapper.sh"); + let shapelinkerwrapper: str = strings.concat(root, + "/shape-w6l-wrapper.sh"); + writeexecutable(shapecompilerwrapper, strings.concat( + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_SHAPE_COMPILER_TRACE\"\n", + "exec \"$WW_SHAPE_REAL_COMPILER\" \"$@\"\n")); + writeexecutable(shapelinkerwrapper, strings.concat( + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_SHAPE_LINKER_TRACE\"\n", + "exec \"$WW_SHAPE_REAL_LINKER\" \"$@\"\n")); + let shapeindices: []i32 = [1, 2, 4, 5, 6]; + let shapemains: []str = ["shapeinternal-test-main", + "shapeexternal-test-main", "tosame-test-main", + "toexternal-test-main", "tomixed-test-main"]; + let shapeordinary: []str = ["shapeinternal", "shapeexternal", "tosame", + "toexternal", "tomixed"]; + let shapeordinarypresent: []bool = [false, true, false, false, false]; + let shapeinternal: []str = ["shapeinternal-internal-test", + "shapeexternal-internal-test", "tosame-internal-test", + "toexternal-internal-test", "tomixed-internal-test"]; + let shapeinternalpresent: []bool = [true, false, true, false, true]; + let shapeexternal: []str = ["shapeinternal_test-external-test", + "shapeexternal_test-external-test", "tosame_test-external-test", + "toexternal_test-external-test", "tomixed_test-external-test"]; + let shapeexternalpresent: []bool = [false, true, false, true, true]; + let shapetargetfirst: []str = ["shapeinternal", "shapeexternal", "tosame", + "toexternal_test", "tomixed"]; + let shapetargetsecond: []str = ["", "shapeexternal_test", "", "", + "tomixed_test"]; + let shapecompilerrefs: []str = ["", "", "", "", ""]; + let shapelinkerrefs: []str = ["", "", "", "", ""]; + let shapemainrefs: []str = ["", "", "", "", ""]; + let shapebinrefs: []str = ["", "", "", "", ""]; + let shapecompilers: []str = ["w6c", "w6c_ww"]; + let shapelinkers: []str = ["w6l", "w6l_ww"]; + let shapebaseenv: []str = os.getenvs(); + let sj: i32 = 0; + for (sj < shapeindices.len) { + let index: i32 = shapeindices[sj]; + let shapebin: str = strings.concat(root, "/shape-product-", + boundarypkgname(sj), ".test"); + let shapework: str = strings.concat(shapebin, ".sepwork"); + let ctracepath: str = strings.concat(root, "/shape-compiler-", + boundarypkgname(sj), ".trace"); + let ltracepath: str = strings.concat(root, "/shape-linker-", + boundarypkgname(sj), ".trace"); + writefile(ctracepath, ""); + writefile(ltracepath, ""); + let stagei: i32 = 0; + for (stagei < stages.len) { + if (stagei != 0) { + rewritefile(ctracepath, ""); + rewritefile(ltracepath, ""); + }; + let env: []str = alloc([], (shapebaseenv.len + 6): u64)!; + let ei: i32 = 0; + for (ei < shapebaseenv.len) { + if (!strings.hasprefix(shapebaseenv[ei], "WW_W6C=") + && !strings.hasprefix(shapebaseenv[ei], "WW_W6L=") + && !strings.hasprefix(shapebaseenv[ei], + "WW_SHAPE_COMPILER_TRACE=") + && !strings.hasprefix(shapebaseenv[ei], + "WW_SHAPE_LINKER_TRACE=") + && !strings.hasprefix(shapebaseenv[ei], + "WW_SHAPE_REAL_COMPILER=") + && !strings.hasprefix(shapebaseenv[ei], + "WW_SHAPE_REAL_LINKER=")) { + append(env, shapebaseenv[ei]); + }; + ei += 1; + }; + append(env, strings.concat("WW_W6C=", shapecompilerwrapper)); + append(env, strings.concat("WW_W6L=", shapelinkerwrapper)); + append(env, strings.concat("WW_SHAPE_COMPILER_TRACE=", ctracepath)); + append(env, strings.concat("WW_SHAPE_LINKER_TRACE=", ltracepath)); + append(env, strings.concat("WW_SHAPE_REAL_COMPILER=", + driver(shapecompilers[stagei]))); + append(env, strings.concat("WW_SHAPE_REAL_LINKER=", + driver(shapelinkers[stagei]))); + let shapeav: []str = [driver(stages[stagei]), "test", "-c", "-o", + shapebin, "-I", root, shapes[index]]; + runcommandenv(root, strings.concat("shape-structure-", + stages[stagei], "-", boundarypkgname(sj)), shapeav, env, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(out.stderr.len == 0); + assert(os.exists(shapebin)); + assert(!os.exists(strings.concat(shapes[index], "/", + shapefamilies[index], ".test"))); + let ctrace: str = readfile(ctracepath); + let ltrace: str = readfile(ltracepath); + assert(occurrences(ctrace, "-test-main.unit.ww") == 1); + let mainline: str = linecontaining(ctrace, + strings.concat("/", shapemains[sj], ".unit.ww")); + let targetcount: i32 = 1; + if (shapetargetsecond[sj].len != 0) { targetcount = 2; }; + assert(occurrences(mainline, "--test-target-package") + == targetcount); + assert(has(mainline, strings.concat("--test-target-package ", + shapetargetfirst[sj], " "))); + if (shapetargetsecond[sj].len != 0) { + assert(pos(mainline, strings.concat("--test-target-package ", + shapetargetfirst[sj], " ")) < pos(mainline, strings.concat( + "--test-target-package ", shapetargetsecond[sj], " "))); + }; + assert(occurrences(ltrace, "\n") == 1); + let linkline: str = linecontaining(ltrace, + strings.concat("-o ", shapebin, ".new ")); + assert(occurrences(linkline, "-test-main.a") == 1); + assert(has(linkline, strings.concat("/", shapemains[sj], + ".a"))); + assert(!has(linkline, ".wwi")); + assert(os.exists(strings.concat(shapework, "/", shapemains[sj], + ".unit.ww"))); + let actionnames: []str = [shapeordinary[sj], shapeinternal[sj], + shapeexternal[sj]]; + let actionpresent: []bool = [shapeordinarypresent[sj], + shapeinternalpresent[sj], shapeexternalpresent[sj]]; + let actioni: i32 = 0; + for (actioni < actionnames.len) { + assert(os.exists(strings.concat(shapework, "/", + actionnames[actioni], ".unit.ww")) + == actionpresent[actioni]); + assert(has(linkline, strings.concat("/", actionnames[actioni], + ".a")) == actionpresent[actioni]); + actioni += 1; + }; + let nc: str = normalizedtrace(ctrace, + strings.concat(shapework, "/"), shapebin); + let nl: str = normalizedtrace(ltrace, + strings.concat(shapework, "/"), shapebin); + let mainbytes: str = readfile(strings.concat(shapework, "/", + shapemains[sj], ".unit.ww")); + let binbytes: str = readfile(shapebin); + if (stagei == 0) { + shapecompilerrefs[sj] = nc; + shapelinkerrefs[sj] = nl; + shapemainrefs[sj] = mainbytes; + shapebinrefs[sj] = binbytes; + } else { + assert(same(shapecompilerrefs[sj], nc)); + assert(same(shapelinkerrefs[sj], nl)); + assert(same(shapemainrefs[sj], mainbytes)); + assert(same(shapebinrefs[sj], binbytes)); + }; + clean(shapework); + clean(shapebin); + stagei += 1; + }; + sj += 1; + }; clean(root); }; @@ -806,11 +1119,11 @@ fn hexbytes(value: str) str = { runcommand(root, "pass", av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); - assert(has(out.stdout, "pass_test.alpha_pass ... ok\n")); + assert(has(out.stdout, "pass.alpha_pass ... ok\n")); assert(has(out.stdout, - "pass_test.beta_skip ... SKIP: synthetic unavailable feature\n")); - assert(has(out.stdout, "pass_test.gamma_expected_abort ... ok\n")); - assert(has(out.stdout, "pass_test.delta_pass ... ok\n")); + "pass.beta_skip ... SKIP: synthetic unavailable feature\n")); + assert(has(out.stdout, "pass.gamma_expected_abort ... ok\n")); + assert(has(out.stdout, "pass.delta_pass ... ok\n")); assert(has(out.stdout, "3 passed, 0 failed, 1 skipped, 0 harness errors\n")); assert(has(out.stdout, @@ -827,7 +1140,7 @@ fn hexbytes(value: str) str = { (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, - "assert_failure_test.assertion_failure ... FAIL (exit 1)\n")); + "assert_failure.assertion_failure ... FAIL (exit 1)\n")); assert(!has(out.stderr, "captures:")); let nonzeroav: []str = [driver("ww"), "test", @@ -836,7 +1149,7 @@ fn hexbytes(value: str) str = { (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, - "nonzero_test.deliberate_nonzero_exit ... FAIL (exit 7)\n")); + "nonzero.deliberate_nonzero_exit ... FAIL (exit 7)\n")); let prematureav: []str = [driver("ww"), "test", packagepath("cases/premature")]; @@ -844,7 +1157,7 @@ fn hexbytes(value: str) str = { (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, - "premature_test.clean_exit_without_completion ... HARNESS (incomplete result)\n")); + "premature.clean_exit_without_completion ... HARNESS (incomplete result)\n")); let signalav: []str = [driver("ww"), "test", packagepath("cases/signal")]; @@ -852,7 +1165,7 @@ fn hexbytes(value: str) str = { (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, - "signal_test.signal_is_not_exit ... FAIL (signal 15)\n")); + "signal.signal_is_not_exit ... FAIL (signal 15)\n")); let multiav: []str = [driver("ww"), "test", packagepath("cases/multi_fail")]; @@ -875,7 +1188,7 @@ fn hexbytes(value: str) str = { (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, - "timeout_test.timeout_clears_term_resistant_descendant ... FAIL(timeout)\n")); + "timeout.timeout_clears_term_resistant_descendant ... FAIL(timeout)\n")); assert(has(out.stdout, "1 discovered, 1 selected, 1 started, 1 completed\n")); @@ -885,7 +1198,7 @@ fn hexbytes(value: str) str = { (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, - "descendant_test.normal_return_clears_descendant ... ok\n")); + "descendant.normal_return_clears_descendant ... ok\n")); assert(!has(out.stdout, "HARNESS")); clean(root); }; @@ -941,12 +1254,14 @@ fn hexbytes(value: str) str = { (30i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); assert(has(outc.stdout, strings.concat(" -> ", route, "/route.test\n"))); - assert(has(outc.stdout, - strings.concat(" -> ", route, "/route_test.test\n"))); + assert(occurrences(outc.stdout, "built ") == 1); let cwhite: str = readfile(strings.concat(route, "/route.test.sepwork/route-internal-test.s")); let cexternal: str = readfile(strings.concat(route, "/route.test.sepwork/route_test-external-test.s")); + let cmain: str = readfile(strings.concat(route, + "/route.test.sepwork/route-test-main.s")); + let cbinary: str = readfile(strings.concat(route, "/route.test")); // The explicit package `-c` outputs are caller-owned artifacts. Release // the shared C-stage tree before asking the WW driver to acquire the same // stem; the driver never deletes a pre-existing `.sepwork` path. @@ -962,6 +1277,9 @@ fn hexbytes(value: str) str = { "/route.test.sepwork/route-internal-test.s")))); assert(same(cexternal, readfile(strings.concat(route, "/route.test.sepwork/route_test-external-test.s")))); + assert(same(cmain, readfile(strings.concat(route, + "/route.test.sepwork/route-test-main.s")))); + assert(same(cbinary, readfile(strings.concat(route, "/route.test")))); clean(root); }; @@ -969,14 +1287,16 @@ fn hexbytes(value: str) str = { let root: str = fresh(); let implementation: str = strings.concat(root, "/implementation"); let api: str = strings.concat(root, "/api"); - let testonly: str = strings.concat(root, "/__same"); - let externalonly: str = strings.concat(root, "/__external"); + let samedep: str = strings.concat(root, "/__same"); + let external: str = strings.concat(root, "/__external"); + let bridge: str = strings.concat(root, "/bridge"); let pkg: str = strings.concat(root, "/pkg"); - assert(os.mkdir(implementation, 448i32) == 0); - assert(os.mkdir(api, 448i32) == 0); - assert(os.mkdir(testonly, 448i32) == 0); - assert(os.mkdir(externalonly, 448i32) == 0); - assert(os.mkdir(pkg, 448i32) == 0); + let dirs: []str = [implementation, api, samedep, external, bridge, pkg]; + let di: i32 = 0; + for (di < dirs.len) { + assert(os.mkdir(dirs[di], 448i32) == 0); + di += 1; + }; writefile(strings.concat(implementation, "/implementation.ww"), strings.concat("package implementation;\n", "// IMPLEMENTATION_SOURCE\n", @@ -985,15 +1305,13 @@ fn hexbytes(value: str) str = { "package api;\nimport implementation;\n", "// API_SOURCE\n", "export fn value() i32 = { return implementation.value(); };\n")); - writefile(strings.concat(api, "/api_test.ww"), strings.concat( + writefile(strings.concat(api, "/ignored_test.ww"), strings.concat( "package api;\n", "TEST_DEPENDENCY_MUST_NOT_COMPILE\n")); - writefile(strings.concat(testonly, "/source.ww"), strings.concat( - "package __same;\n", - "export fn value() i32 = { return 1; };\n")); - writefile(strings.concat(externalonly, "/source.ww"), strings.concat( - "package __external;\n", - "export fn value() i32 = { return 1; };\n")); + writefile(strings.concat(samedep, "/same.ww"), + "package __same;\nexport fn value() i32 = { return 1; };\n"); + writefile(strings.concat(external, "/external.ww"), + "package __external;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(pkg, "/a.ww"), strings.concat( "package pkg;\nimport api;\n", "// PACKAGE_PRODUCTION_A\n", @@ -1003,19 +1321,69 @@ fn hexbytes(value: str) str = { "// PACKAGE_PRODUCTION_Z\n", "fn secret() i32 = { return 6; };\n", "export fn second() i32 = { return 9; };\n")); - writefile(strings.concat(pkg, "/same_test.ww"), strings.concat( + writefile(strings.concat(bridge, "/bridge.ww"), strings.concat( + "package bridge;\nimport pkg;\n", + "// TRANSITIVE_TEST_IMPORTER\n", + "export fn helper() i32 = { return pkg.internal_helper(); };\n")); + let samebody: str = strings.concat( "package pkg;\nimport __same;\n", "// SAME_TEST_SOURCE\n", + "export fn internal_helper() i32 = { return 6; };\n", "@test fn same_graph() void = {\n", " assert(value() + __same.value() == 42);\n", - " assert(secret() == 6);\n};\n")); - writefile(strings.concat(pkg, "/external_test.ww"), strings.concat( - "package pkg_test;\nimport __external;\nimport pkg;\n", + " assert(secret() == 6);\n", + "};\n"); + let samebodychanged: str = strings.concat( + "package pkg;\nimport __same;\n", + "// SAME_TEST_SOURCE_BODY_CHANGED\n", + "export fn internal_helper() i32 = { return 7; };\n", + "@test fn same_graph() void = {\n", + " assert(value() + __same.value() == 42);\n", + " assert(secret() == 6);\n", + "};\n"); + let samebodyexported: str = strings.concat( + "package pkg;\nimport __same;\n", + "// SAME_TEST_SOURCE_EXPORT_CHANGED\n", + "export fn internal_helper() i32 = { return 7; };\n", + "export fn second_helper() i32 = { return 8; };\n", + "@test fn same_graph() void = {\n", + " assert(value() + __same.value() == 42);\n", + " assert(secret() == 6);\n", + "};\n"); + let externalbody: str = strings.concat( + "package pkg_test;\nimport __external;\nimport bridge;\nimport pkg;\n", "// EXTERNAL_TEST_SOURCE\n", "@test fn external_graph() void = {\n", " assert(pkg.value() + __external.value() == 42);\n", - " assert(pkg.second() == 9);\n};\n")); + " assert(pkg.second() == 9);\n", + " assert(pkg.internal_helper() >= 6);\n", + " assert(bridge.helper() >= 6);\n", + "};\n"); + let externalbodychanged: str = strings.concat( + "package pkg_test;\nimport __external;\nimport bridge;\nimport pkg;\n", + "// EXTERNAL_TEST_SOURCE_BODY_CHANGED\n", + "@test fn external_graph() void = {\n", + " assert(pkg.value() + __external.value() == 42);\n", + " assert(pkg.second() == 9);\n", + " assert(pkg.internal_helper() >= 7);\n", + " assert(bridge.helper() >= 7);\n", + "};\n"); + let externalbodyreadded: str = strings.concat( + "package pkg_test;\nimport __external;\nimport bridge;\nimport pkg;\n", + "// EXTERNAL_TEST_SOURCE_READDED\n", + "@test fn external_graph() void = {\n", + " assert(pkg.value() + __external.value() == 42);\n", + " assert(pkg.second() == 9);\n", + " assert(pkg.internal_helper() >= 7);\n", + " assert(bridge.helper() >= 7);\n", + " assert(__external.value() == 1);\n", + "};\n"); + let samefile: str = strings.concat(pkg, "/same_test.ww"); + let externalfile: str = strings.concat(pkg, "/external_test.ww"); + writefile(samefile, samebody); + writefile(externalfile, externalbody); + // Ordinary builds neither parse nor load any test-only source or dependency. let prodout: str = strings.concat(root, "/production.a"); let prodav: []str = [driver("ww"), "build", "-I", root, "-o", prodout, pkg]; @@ -1024,1171 +1392,1261 @@ fn hexbytes(value: str) str = { (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let prodwork: str = strings.concat(prodout, ".sepwork/"); - let produnit: str = readfile(strings.concat(prodwork, - "pkg.unit.ww")); - assert(has(produnit, "//ww:module-reset pkg\n")); - assert(!has(produnit, "//ww:module ")); - assert(!has(produnit, "IMPLEMENTATION_SOURCE")); - assert(!has(produnit, "__same")); - assert(!has(produnit, "__external")); + let produnit: str = readfile(strings.concat(prodwork, "pkg.unit.ww")); + assert(has(produnit, "PACKAGE_PRODUCTION_A")); + assert(has(produnit, "PACKAGE_PRODUCTION_Z")); + assert(!has(produnit, "SAME_TEST_SOURCE")); + assert(!has(produnit, "internal_helper")); assert(!os.exists(strings.concat(prodwork, "__same.unit.ww"))); assert(!os.exists(strings.concat(prodwork, "__external.unit.ww"))); + assert(!os.exists(strings.concat(prodwork, "bridge.unit.ww"))); assert(!has(readfile(strings.concat(prodwork, "api.unit.ww")), "TEST_DEPENDENCY_MUST_NOT_COMPILE")); - let samebin: str = strings.concat(pkg, "/pkg.test"); - let externalbin: str = strings.concat(pkg, "/pkg_test.test"); - let sharedworkroot: str = strings.concat(samebin, ".sepwork"); - let sharedwork: str = strings.concat(sharedworkroot, "/"); - let compilertrace: str = strings.concat(root, "/compiler.trace"); - let linkertrace: str = strings.concat(root, "/linker.trace"); let compilerwrapper: str = strings.concat(root, "/trace-w6c.sh"); + let assemblerwrapper: str = strings.concat(root, "/trace-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/trace-w6l.sh"); - writefile(compilertrace, ""); - writefile(linkertrace, ""); writeexecutable(compilerwrapper, strings.concat( - "#!/bin/sh\n", - "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_COMPILER_TRACE\"\n", - "exec \"$WW_PACKAGE_W6C\" \"$@\"\n")); + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_GRAPH_COMPILER_TRACE\"\n", + "exec \"$WW_GRAPH_REAL_COMPILER\" \"$@\"\n")); + writeexecutable(assemblerwrapper, strings.concat( + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_GRAPH_ASSEMBLER_TRACE\"\n", + "exec \"$WW_GRAPH_REAL_ASSEMBLER\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( - "#!/bin/sh\n", - "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_LINKER_TRACE\"\n", - "exec \"$WW_PACKAGE_W6L\" \"$@\"\n")); - let baseenv: []str = os.getenvs(); - let traceenv: []str = alloc([], (baseenv.len + 6): u64)!; - let ei: i32 = 0; - for (ei < baseenv.len) { - if (!strings.hasprefix(baseenv[ei], "WW_W6C=") - && !strings.hasprefix(baseenv[ei], "WW_W6L=") - && !strings.hasprefix(baseenv[ei], - "WW_PACKAGE_COMPILER_TRACE=") - && !strings.hasprefix(baseenv[ei], - "WW_PACKAGE_LINKER_TRACE=") - && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C=") - && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6L=")) { - append(traceenv, baseenv[ei]); - }; - ei += 1; + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_GRAPH_LINKER_TRACE\"\n", + "exec \"$WW_GRAPH_REAL_LINKER\" \"$@\"\n")); + + let stages: []str = ["ww", "ww_ww"]; + let compilers: []str = ["w6c", "w6c_ww"]; + let assemblers: []str = ["w6a", "w6a_ww"]; + let linkers: []str = ["w6l", "w6l_ww"]; + let works: []str = [strings.concat(root, "/c-work"), + strings.concat(root, "/ww-work")]; + let bins: []str = [strings.concat(root, "/c-pkg.test"), + strings.concat(root, "/ww-pkg.test")]; + let statuses: []str = [strings.concat(root, "/c-pkg.status"), + strings.concat(root, "/ww-pkg.status")]; + let compilertraces: []str = [strings.concat(root, "/c-w6c.trace"), + strings.concat(root, "/ww-w6c.trace")]; + let assemblertraces: []str = [strings.concat(root, "/c-w6a.trace"), + strings.concat(root, "/ww-w6a.trace")]; + let linkertraces: []str = [strings.concat(root, "/c-w6l.trace"), + strings.concat(root, "/ww-w6l.trace")]; + let bridgeowner: str = strings.concat("pkg#", pkg); + let bridgeaction: str = packagestoragekeyfor( + "bridge", bridge, 4, 0, bridgeowner); + assert(strings.hasprefix(bridgeaction, "__wwpkg.v4.r0.h")); + let actions: []str = ["implementation", "api", "__same", "__external", + bridgeaction, "test", "pkg-internal-test", "pkg_test-external-test", + "pkg-test-main"]; + let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; + let references: []str = alloc([], + (actions.len * suffixes.len): u64)!; + let ai: i32 = 0; + for (ai < actions.len * suffixes.len) { + append(references, ""); + ai += 1; }; - append(traceenv, strings.concat("WW_W6C=", compilerwrapper)); - append(traceenv, strings.concat("WW_W6L=", linkerwrapper)); - append(traceenv, strings.concat("WW_PACKAGE_COMPILER_TRACE=", - compilertrace)); - append(traceenv, strings.concat("WW_PACKAGE_LINKER_TRACE=", linkertrace)); - append(traceenv, strings.concat("WW_PACKAGE_W6C=", driver("w6c"))); - append(traceenv, strings.concat("WW_PACKAGE_W6L=", driver("w6l"))); - let wwtraceenv: []str = alloc([], (baseenv.len + 6): u64)!; - ei = 0; - for (ei < baseenv.len) { - if (!strings.hasprefix(baseenv[ei], "WW_W6C=") - && !strings.hasprefix(baseenv[ei], "WW_W6L=") - && !strings.hasprefix(baseenv[ei], - "WW_PACKAGE_COMPILER_TRACE=") - && !strings.hasprefix(baseenv[ei], - "WW_PACKAGE_LINKER_TRACE=") - && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C=") - && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6L=")) { - append(wwtraceenv, baseenv[ei]); - }; - ei += 1; - }; - append(wwtraceenv, strings.concat("WW_W6C=", compilerwrapper)); - append(wwtraceenv, strings.concat("WW_W6L=", linkerwrapper)); - append(wwtraceenv, strings.concat("WW_PACKAGE_COMPILER_TRACE=", - compilertrace)); - append(wwtraceenv, strings.concat("WW_PACKAGE_LINKER_TRACE=", linkertrace)); - append(wwtraceenv, strings.concat("WW_PACKAGE_W6C=", driver("w6c_ww"))); - append(wwtraceenv, strings.concat("WW_PACKAGE_W6L=", driver("w6l_ww"))); - let drivers: []str = ["ww", "ww", "ww_ww"]; - let tags: []str = ["c1", "c2", "ww"]; + let referencebin: str = ""; let referenceout: str = ""; let referenceerr: str = ""; - let referencesame: str = ""; - let referenceexternal: str = ""; - let referencesameunit: str = ""; - let referenceexternalunit: str = ""; - let referenceproductionunit: str = ""; - let packageactions: []str = ["pkg-internal-test", - "pkg-internal-test-main", "pkg_test-external-test", - "pkg_test-external-test-main"]; - let referenceactionexports: []str = ["", "", "", ""]; - let referenceactionarchives: []str = ["", "", "", ""]; - let i: i32 = 0; - for (i < drivers.len) { - let av: []str = [driver(drivers[i]), "test", "-c", "-I", root, - pkg]; - if (i == 0) { - runcommandenv(root, strings.concat("variant-build-", tags[i]), av, - traceenv, (120i64 * (time.second: i64)): time.duration, - &out); - } else { - runcommand(root, strings.concat("variant-build-", tags[i]), av, - (120i64 * (time.second: i64)): time.duration, &out); + let referencecompiler: str = ""; + let referenceassembler: str = ""; + let referencelinker: str = ""; + let bodycompiler: str = ""; + let bodyassembler: str = ""; + let bodylinker: str = ""; + let externalcompiler: str = ""; + let externalassembler: str = ""; + let externallinker: str = ""; + let exportcompiler: str = ""; + let exportassembler: str = ""; + let exportlinker: str = ""; + let exportbridge: []str = ["", "", "", "", ""]; + let removecompiler: str = ""; + let removeassembler: str = ""; + let removelinker: str = ""; + let removebin: str = ""; + let removeout: str = ""; + let removeerr: str = ""; + let readdcompiler: str = ""; + let readdassembler: str = ""; + let readdlinker: str = ""; + let readdbin: str = ""; + let readdout: str = ""; + let readderr: str = ""; + let baseenv: []str = os.getenvs(); + let si: i32 = 0; + for (si < stages.len) { + assert(os.mkdir(works[si], 448i32) == 0); + writefile(compilertraces[si], ""); + writefile(assemblertraces[si], ""); + writefile(linkertraces[si], ""); + let env: []str = alloc([], (baseenv.len + 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_GRAPH_COMPILER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_ASSEMBLER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_LINKER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_REAL_COMPILER=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_REAL_ASSEMBLER=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_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_GRAPH_COMPILER_TRACE=", + compilertraces[si])); + append(env, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", + assemblertraces[si])); + append(env, strings.concat("WW_GRAPH_LINKER_TRACE=", + linkertraces[si])); + append(env, strings.concat("WW_GRAPH_REAL_COMPILER=", + driver(compilers[si]))); + append(env, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", + driver(assemblers[si]))); + append(env, strings.concat("WW_GRAPH_REAL_LINKER=", + driver(linkers[si]))); + + let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], + "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", + "pkg_test", pkg, bins[si], statuses[si], pkg]; + runcommandenv(root, strings.concat("graph-cold-", stages[si]), av, + env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); - let buildstdout: str = strings.dup(out.stdout); - let buildstderr: str = strings.dup(out.stderr); - let sameunit: str = readfile(strings.concat(sharedwork, - "pkg-internal-test.unit.ww")); - let externalunit: str = readfile(strings.concat(sharedwork, - "pkg_test-external-test.unit.ww")); - let externalproduction: str = readfile(strings.concat(sharedwork, - "pkg.unit.ww")); - let samemainunit: str = readfile(strings.concat(sharedwork, - "pkg-internal-test-main.unit.ww")); - let externalmainunit: str = readfile(strings.concat(sharedwork, - "pkg_test-external-test-main.unit.ww")); - assert(!os.exists(strings.concat(sharedwork, - "__ww-test-001-external-production.unit.ww"))); - assert(!os.exists(strings.concat(externalbin, ".sepwork"))); - assert(!has(sameunit, "//ww:module ")); - assert(os.exists(strings.concat(sharedwork, - "__external.unit.ww"))); - assert(has(sameunit, "PACKAGE_PRODUCTION_A")); - assert(has(sameunit, "PACKAGE_PRODUCTION_Z")); - assert(has(sameunit, "SAME_TEST_SOURCE")); - assert(!has(sameunit, "EXTERNAL_TEST_SOURCE")); - assert(!has(externalunit, "//ww:module ")); - assert(has(externalunit, "EXTERNAL_TEST_SOURCE")); - assert(!has(externalunit, "PACKAGE_PRODUCTION_A")); - assert(!has(externalunit, "PACKAGE_PRODUCTION_Z")); - assert(has(externalproduction, "PACKAGE_PRODUCTION_A")); - assert(has(externalproduction, "PACKAGE_PRODUCTION_Z")); - assert(!has(externalproduction, "//ww:module ")); - assert(!has(externalproduction, "SAME_TEST_SOURCE")); - assert(!has(externalproduction, "EXTERNAL_TEST_SOURCE")); - assert(strings.hasprefix(samemainunit, strings.concat( - "//ww:module-reset __wwtestmain.pkg.internal.main\n", - "package main;\nimport pkg;\nimport test;\n"))); - assert(strings.hasprefix(externalmainunit, strings.concat( - "//ww:module-reset __wwtestmain.pkg_test.external.main\n", - "package main;\nimport pkg_test;\nimport test;\n"))); - assert(has(samemainunit, "//ww:direct-export pkg ")); - assert(has(samemainunit, "//ww:direct-export test ")); - assert(has(externalmainunit, "//ww:direct-export pkg_test ")); - assert(has(externalmainunit, "//ww:direct-export test ")); - assert(!has(samemainunit, "PACKAGE_PRODUCTION")); - assert(!has(samemainunit, "SAME_TEST_SOURCE")); - assert(!has(externalmainunit, "EXTERNAL_TEST_SOURCE")); - assert(!has(readfile(strings.concat(sharedwork, "api.unit.ww")), - "TEST_DEPENDENCY_MUST_NOT_COMPILE")); - let artifacts: []str = ["implementation", "api", "pkg", - "__same", "__external", "test", "pkg-internal-test", - "pkg-internal-test-main", "pkg_test-external-test", - "pkg_test-external-test-main"]; - let ai: i32 = 0; - for (ai < artifacts.len) { - assert(os.exists(strings.concat(sharedwork, artifacts[ai], ".wwi"))); - assert(os.exists(strings.concat(sharedwork, artifacts[ai], ".a"))); - ai += 1; + assert(out.stdout.len == 0 && out.stderr.len == 0); + assert(os.exists(bins[si])); + assert(same(readfile(statuses[si]), "ok\n")); + assert(!os.exists(strings.concat(pkg, "/pkg_test.test"))); + assert(!os.exists(strings.concat(works[si], "/pkg.unit.ww"))); + assert(!os.exists(strings.concat(works[si], "/bridge.unit.ww"))); + + let ptestunit: str = readfile(strings.concat(works[si], + "/pkg-internal-test.unit.ww")); + let pxtestunit: str = readfile(strings.concat(works[si], + "/pkg_test-external-test.unit.ww")); + let mainunit: str = readfile(strings.concat(works[si], + "/pkg-test-main.unit.ww")); + assert(has(ptestunit, "PACKAGE_PRODUCTION_A")); + assert(has(ptestunit, "PACKAGE_PRODUCTION_Z")); + assert(has(ptestunit, "SAME_TEST_SOURCE")); + assert(has(ptestunit, "export fn internal_helper")); + assert(!has(ptestunit, "EXTERNAL_TEST_SOURCE")); + assert(has(pxtestunit, "EXTERNAL_TEST_SOURCE")); + assert(!has(pxtestunit, "PACKAGE_PRODUCTION_A")); + assert(!has(pxtestunit, "SAME_TEST_SOURCE")); + assert(strings.hasprefix(mainunit, strings.concat( + "//ww:module-reset __wwtestmain.pkg.main\n", + "package main;\nimport pkg;\nimport pkg_test;\nimport test;\n"))); + + let ctrace: str = readfile(compilertraces[si]); + let atrace: str = readfile(assemblertraces[si]); + let ltrace: str = readfile(linkertraces[si]); + assert(occurrences(ctrace, "pkg-internal-test.unit.new") == 1); + assert(occurrences(ctrace, + "pkg_test-external-test.unit.new") == 1); + assert(occurrences(ctrace, "pkg-test-main.unit.new") == 1); + assert(occurrences(ctrace, + "--test-target-package pkg --test-target-package pkg_test") == 1); + assert(!has(ctrace, + "--package-init-symbol __ww..pkg.p.pkg.v0.r0.init")); + let ptestcompile: str = linecontaining(ctrace, + "pkg-internal-test.unit.new"); + assert(has(ptestcompile, + "--package-init-symbol __ww..pkg.p.pkg.v1.r0.init")); + assert(pos(ptestcompile, "--import __same ") + < pos(ptestcompile, "--import api ")); + let bridgecompile: str = linecontaining(ctrace, + "--package-init-symbol __ww..pkg.p.bridge.v4.r0.init"); + assert(has(bridgecompile, "--import pkg ")); + assert(has(bridgecompile, "/pkg-internal-test.wwi.new")); + assert(!has(ctrace, + "--package-init-symbol __ww..pkg.p.bridge.v0.r0.init")); + let externalcompile: str = linecontaining(ctrace, + "pkg_test-external-test.unit.new"); + assert(pos(externalcompile, "--import __external ") + < pos(externalcompile, "--import bridge ")); + assert(pos(externalcompile, "--import bridge ") + < pos(externalcompile, "--import pkg ")); + assert(has(externalcompile, "/pkg-internal-test.wwi.new")); + assert(!has(externalcompile, "/pkg.wwi")); + let maincompile: str = linecontaining(ctrace, + "pkg-test-main.unit.new"); + assert(has(maincompile, strings.concat( + "-T --entry --test-support-module test ", + "--test-target-package pkg --test-target-package pkg_test ", + "--package-init-symbol ", + "__ww..pkg.p.__wwtestmain.pkg.main.v3.r2.init "))); + assert(pos(maincompile, "--import pkg ") + < pos(maincompile, "--import pkg_test ")); + assert(pos(maincompile, "--import pkg_test ") + < pos(maincompile, "--import test ")); + let workprefix: str = strings.concat(works[si], "/"); + assert(same(ptestcompile, strings.concat( + "--test-package --package-init-symbol ", + "__ww..pkg.p.pkg.v1.r0.init -c --import __same ", workprefix, + "__same.wwi.new --import api ", workprefix, + "api.wwi.new -I ", workprefix, + "pkg-internal-test.wwi.new -o ", workprefix, + "pkg-internal-test.s.new ", workprefix, + "pkg-internal-test.unit.new"))); + assert(same(bridgecompile, strings.concat( + "--package-init-symbol __ww..pkg.p.bridge.v4.r0.init -c ", + "--import pkg ", workprefix, + "pkg-internal-test.wwi.new -I ", workprefix, bridgeaction, + ".wwi.new -o ", workprefix, bridgeaction, ".s.new ", workprefix, + bridgeaction, ".unit.new"))); + assert(same(externalcompile, strings.concat( + "--test-package --package-init-symbol ", + "__ww..pkg.p.pkg_test.v2.r0.init -c --import __external ", + workprefix, "__external.wwi.new --import bridge ", workprefix, + bridgeaction, ".wwi.new --import pkg ", workprefix, + "pkg-internal-test.wwi.new -I ", workprefix, + "pkg_test-external-test.wwi.new -o ", workprefix, + "pkg_test-external-test.s.new ", workprefix, + "pkg_test-external-test.unit.new"))); + assert(same(maincompile, strings.concat( + "-T --entry --test-support-module test --test-target-package pkg ", + "--test-target-package pkg_test --package-init-symbol ", + "__ww..pkg.p.__wwtestmain.pkg.main.v3.r2.init ", + "--init-dispatch-symbol __ww..dispatch -c --import pkg ", + workprefix, "pkg-internal-test.wwi.new --import pkg_test ", + workprefix, "pkg_test-external-test.wwi.new --import test ", + workprefix, "test.wwi.new -I ", workprefix, + "pkg-test-main.wwi.new -o ", workprefix, + "pkg-test-main.s.new ", workprefix, "pkg-test-main.unit.new"))); + let supportcompile: str = linecontaining(ctrace, + "--test-support-module test --package-init-symbol"); + assert(same(supportcompile, strings.concat( + "--test-support-module test --package-init-symbol ", + "__ww..pkg.p.test.v0.r0.init -c --import fnmatch ", workprefix, + "fnmatch.wwi.new --import os ", workprefix, + "os.wwi.new --import time ", workprefix, + "time.wwi.new -I ", workprefix, "test.wwi.new -o ", workprefix, + "test.s.new ", workprefix, "test.unit.new"))); + assert(same(linecontaining(atrace, "/pkg-internal-test.o.new"), + strings.concat("-o ", works[si], + "/pkg-internal-test.o.new ", works[si], + "/pkg-internal-test.s.new"))); + assert(same(linecontaining(atrace, "/pkg-test-main.init.o.new"), + strings.concat("-o ", works[si], + "/pkg-test-main.init.o.new ", works[si], + "/pkg-test-main.init.s.new"))); + let exactassembled: []str = [bridgeaction, + "pkg_test-external-test", "pkg-test-main", "test"]; + let exacti: i32 = 0; + for (exacti < exactassembled.len) { + assert(same(linecontaining(atrace, strings.concat("/", + exactassembled[exacti], ".o.new")), strings.concat("-o ", + workprefix, exactassembled[exacti], ".o.new ", workprefix, + exactassembled[exacti], ".s.new"))); + exacti += 1; }; + assert(occurrences(ltrace, "\n") == 1); + let link: str = linecontaining(ltrace, + strings.concat("-o ", bins[si], ".new ")); + assert(has(link, "/pkg-test-main.a.new")); + assert(has(link, "/pkg-internal-test.a.new")); + assert(has(link, "/pkg_test-external-test.a.new")); + assert(has(link, "/__same.a.new")); + assert(has(link, "/__external.a.new")); + assert(has(link, "/api.a.new")); + assert(has(link, "/implementation.a.new")); + assert(has(link, "/test.a.new")); + assert(has(link, strings.concat("/", bridgeaction, ".a.new"))); + assert(!has(link, strings.concat(works[si], "/pkg.a"))); + assert(!has(link, ".wwi")); + assert(same(link, strings.concat("-o ", bins[si], ".new ", + workprefix, "pkg-test-main.a.new ", workprefix, "test.a.new ", + workprefix, "fnmatch.a.new ", workprefix, "ascii.a.new ", + workprefix, "strings.a.new ", workprefix, "os.a.new ", + workprefix, "time.a.new ", workprefix, "encoding.utf8.a.new ", + workprefix, "bytes.a.new ", workprefix, "types.a.new ", + workprefix, "pkg_test-external-test.a.new ", workprefix, + bridgeaction, ".a.new ", workprefix, "__external.a.new ", + workprefix, "pkg-internal-test.a.new ", workprefix, "api.a.new ", + workprefix, "implementation.a.new ", workprefix, "__same.a.new ", + repo(), "/out/bin/../lib/libwwrt.a"))); + ai = 0; - for (ai < packageactions.len) { - let ex: str = readfile(strings.concat(sharedwork, - packageactions[ai], ".wwi")); - let ar: str = readfile(strings.concat(sharedwork, - packageactions[ai], ".a")); - if (i == 0) { - referenceactionexports[ai] = strings.dup(ex); - referenceactionarchives[ai] = strings.dup(ar); - } else { - assert(same(referenceactionexports[ai], ex)); - assert(same(referenceactionarchives[ai], ar)); + for (ai < actions.len) { + let archive: str = readfile(strings.concat(works[si], "/", + actions[ai], ".a")); + checkactionarchive(archive, + strings.compare(actions[ai], "pkg-test-main") == 0); + let xi: i32 = 0; + for (xi < suffixes.len) { + let bytes: str = readfile(strings.concat(works[si], "/", + actions[ai], suffixes[xi])); + let index: i32 = ai * suffixes.len + xi; + if (si == 0) { references[index] = strings.dup(bytes); } + else { assert(same(references[index], bytes)); }; + xi += 1; }; ai += 1; }; - assert(os.exists(samebin)); - assert(os.exists(externalbin)); - if (i == 0) { - let ctrace: str = readfile(compilertrace); - assert(occurrences(ctrace, "implementation.unit.ww") == 1); - assert(occurrences(ctrace, "api.unit.ww") == 1); - assert(occurrences(ctrace, - "/pkg.unit.ww") == 1); - assert(occurrences(ctrace, "/__same.unit.ww") == 1); - assert(occurrences(ctrace, "/__external.unit.ww") == 1); - assert(occurrences(ctrace, "/test.unit.ww") == 1); - assert(occurrences(ctrace, - "pkg-internal-test.unit.ww") == 1); - assert(occurrences(ctrace, - "pkg_test-external-test.unit.ww") == 1); - assert(occurrences(ctrace, - "pkg-internal-test-main.unit.ww") == 1); - assert(occurrences(ctrace, - "pkg_test-external-test-main.unit.ww") == 1); - assert(occurrences(ctrace, "-T --entry --test-support-module") == 2); - assert(occurrences(ctrace, "--test-package") == 2); - let samecompile: str = linecontaining(ctrace, - "pkg-internal-test.unit.ww"); - let externalcompile: str = linecontaining(ctrace, - "pkg_test-external-test.unit.ww"); - let productioncompile: str = linecontaining(ctrace, - "/pkg.unit.ww"); - assert(same(samecompile, strings.concat( - "--test-package --package-init-symbol ", - "__ww..pkg.p.pkg.v1.r0.init -c --import __same ", - sharedwork, "__same.wwi --import api ", sharedwork, - "api.wwi -I ", sharedwork, "pkg-internal-test.wwi -o ", - sharedwork, "pkg-internal-test.s ", sharedwork, - "pkg-internal-test.unit.ww"))); - assert(same(externalcompile, strings.concat( - "--test-package --package-init-symbol ", - "__ww..pkg.p.pkg_test.v2.r0.init -c --import __external ", - sharedwork, "__external.wwi --import pkg ", sharedwork, - "pkg.wwi -I ", sharedwork, - "pkg_test-external-test.wwi -o ", sharedwork, - "pkg_test-external-test.s ", sharedwork, - "pkg_test-external-test.unit.ww"))); - let samemaincompile: str = linecontaining(ctrace, - "pkg-internal-test-main.unit.ww"); - let externalmaincompile: str = linecontaining(ctrace, - "pkg_test-external-test-main.unit.ww"); - assert(same(samemaincompile, strings.concat( - "-T --entry --test-support-module test ", - "--test-target-package pkg --package-init-symbol ", - "__ww..pkg.p.__wwtestmain.pkg.internal.main.v3.r2.init ", - "--init-dispatch-symbol __ww..dispatch -c --import pkg ", - sharedwork, "pkg-internal-test.wwi --import test ", - sharedwork, "test.wwi -I ", sharedwork, - "pkg-internal-test-main.wwi -o ", sharedwork, - "pkg-internal-test-main.s ", sharedwork, - "pkg-internal-test-main.unit.ww"))); - assert(same(externalmaincompile, strings.concat( - "-T --entry --test-support-module test ", - "--test-target-package pkg_test --package-init-symbol ", - "__ww..pkg.p.__wwtestmain.pkg_test.external.main.v3.r2.init ", - "--init-dispatch-symbol __ww..dispatch -c --import pkg_test ", - sharedwork, "pkg_test-external-test.wwi --import test ", - sharedwork, "test.wwi -I ", sharedwork, - "pkg_test-external-test-main.wwi -o ", sharedwork, - "pkg_test-external-test-main.s ", sharedwork, - "pkg_test-external-test-main.unit.ww"))); - assert(same(productioncompile, strings.concat( - "--package-init-symbol __ww..pkg.p.pkg.v0.r0.init ", - "-c --import api ", sharedwork, "api.wwi -I ", sharedwork, - "pkg.wwi -o ", sharedwork, - "pkg.s ", sharedwork, "pkg.unit.ww"))); - let ltrace: str = readfile(linkertrace); - assert(occurrences(ltrace, "\n") == 2); - let samelink: str = linecontaining(ltrace, - strings.concat("-o ", samebin, ".new ")); - let externallink: str = linecontaining(ltrace, - strings.concat("-o ", externalbin, ".new ")); - assert(has(samelink, strings.concat(sharedwork, "api.a"))); - assert(has(samelink, - strings.concat(sharedwork, "implementation.a"))); - assert(has(samelink, strings.concat(sharedwork, "__same.a"))); - assert(has(samelink, strings.concat(sharedwork, - "pkg-internal-test-main.a"))); - assert(has(samelink, strings.concat(sharedwork, - "pkg-internal-test.a"))); - assert(!has(samelink, strings.concat(sharedwork, - "pkg.a"))); - assert(!has(samelink, - strings.concat(sharedwork, "__external.a"))); - assert(has(externallink, strings.concat(sharedwork, - "pkg.a"))); - assert(has(externallink, strings.concat(sharedwork, - "pkg_test-external-test-main.a"))); - assert(has(externallink, strings.concat(sharedwork, - "pkg_test-external-test.a"))); - assert(!has(samelink, ".wwi")); - assert(!has(externallink, ".wwi")); - assert(has(externallink, strings.concat(sharedwork, "api.a"))); - assert(has(externallink, - strings.concat(sharedwork, "implementation.a"))); - assert(has(externallink, - strings.concat(sharedwork, "__external.a"))); - assert(!has(externallink, - strings.concat(sharedwork, "__same.a"))); + let normalizedcompiler: str = normalizedtrace(ctrace, + strings.concat(works[si], "/"), bins[si]); + let normalizedassembler: str = normalizedtrace(atrace, + strings.concat(works[si], "/"), bins[si]); + let normalizedlinker: str = normalizedtrace(ltrace, + strings.concat(works[si], "/"), bins[si]); + if (si == 0) { + referencebin = readfile(bins[si]); + referenceout = strings.dup(out.stdout); + referenceerr = strings.dup(out.stderr); + referencecompiler = normalizedcompiler; + referenceassembler = normalizedassembler; + referencelinker = normalizedlinker; + } else { + assert(same(referencebin, readfile(bins[si]))); + assert(same(referenceout, out.stdout)); + assert(same(referenceerr, out.stderr)); + assert(same(referencecompiler, normalizedcompiler)); + assert(same(referenceassembler, normalizedassembler)); + assert(same(referencelinker, normalizedlinker)); }; - let runav: []str = [samebin]; - runcommand(root, strings.concat("variant-run-same-", tags[i]), runav, + let runav: []str = [bins[si]]; + runcommand(root, strings.concat("graph-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); - assert(has(out.stdout, "same_graph ... ok\n")); - let externalrun: []str = [externalbin]; - runcommand(root, strings.concat("variant-run-external-", tags[i]), - externalrun, (60i64 * (time.second: i64)): time.duration, &out); + assert(occurrences(out.stdout, "same_graph ... ok\n") == 1); + assert(occurrences(out.stdout, "external_graph ... ok\n") == 1); + + // Warm action reuse is exact; linking republishes the requested output. + rewritefile(compilertraces[si], ""); + rewritefile(assemblertraces[si], ""); + rewritefile(linkertraces[si], ""); + runcommandenv(root, strings.concat("graph-warm-", stages[si]), av, + env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); - assert(has(out.stdout, "external_graph ... ok\n")); - if (i == 0) { - referenceout = buildstdout; - referenceerr = buildstderr; - referencesame = readfile(samebin); - referenceexternal = readfile(externalbin); - referencesameunit = strings.dup(sameunit); - referenceexternalunit = strings.dup(externalunit); - referenceproductionunit = strings.dup(externalproduction); + assert(readfile(compilertraces[si]).len == 0); + assert(readfile(assemblertraces[si]).len == 0); + assert(occurrences(readfile(linkertraces[si]), "\n") == 1); + si += 1; + }; + + // An internal helper body edit recompiles only ptest; its stable export + // stops reverse compilation of pxtest, the transitive clone, and pmain. + rewritefile(samefile, samebodychanged); + si = 0; + for (si < stages.len) { + let oldptestwwi: str = readfile(strings.concat(works[si], + "/pkg-internal-test.wwi")); + let oldptesta: str = readfile(strings.concat(works[si], + "/pkg-internal-test.a")); + let oldpxtestwwi: str = readfile(strings.concat(works[si], + "/pkg_test-external-test.wwi")); + let oldpxtesta: str = readfile(strings.concat(works[si], + "/pkg_test-external-test.a")); + let oldmainwwi: str = readfile(strings.concat(works[si], + "/pkg-test-main.wwi")); + let oldmaina: str = readfile(strings.concat(works[si], + "/pkg-test-main.a")); + rewritefile(compilertraces[si], ""); + rewritefile(assemblertraces[si], ""); + rewritefile(linkertraces[si], ""); + let env: []str = os.getenvs(); + let tracedenv: []str = alloc([], (env.len + 9): u64)!; + let ei: i32 = 0; + for (ei < env.len) { + if (!strings.hasprefix(env[ei], "WW_W6C=") + && !strings.hasprefix(env[ei], "WW_W6A=") + && !strings.hasprefix(env[ei], "WW_W6L=")) { + append(tracedenv, env[ei]); + }; + ei += 1; + }; + append(tracedenv, strings.concat("WW_W6C=", compilerwrapper)); + append(tracedenv, strings.concat("WW_W6A=", assemblerwrapper)); + append(tracedenv, strings.concat("WW_W6L=", linkerwrapper)); + append(tracedenv, strings.concat("WW_GRAPH_COMPILER_TRACE=", + compilertraces[si])); + append(tracedenv, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", + assemblertraces[si])); + append(tracedenv, strings.concat("WW_GRAPH_LINKER_TRACE=", + linkertraces[si])); + append(tracedenv, strings.concat("WW_GRAPH_REAL_COMPILER=", + driver(compilers[si]))); + append(tracedenv, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", + driver(assemblers[si]))); + append(tracedenv, strings.concat("WW_GRAPH_REAL_LINKER=", + driver(linkers[si]))); + let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], + "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", + "pkg_test", pkg, bins[si], statuses[si], pkg]; + runcommandenv(root, strings.concat("graph-body-", stages[si]), av, + tracedenv, (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let ct: str = readfile(compilertraces[si]); + let at: str = readfile(assemblertraces[si]); + let lt: str = readfile(linkertraces[si]); + assert(occurrences(ct, "\n") == 1); + assert(has(ct, "pkg-internal-test.unit.new")); + assert(!has(ct, "pkg_test-external-test.unit.new")); + assert(!has(ct, "pkg-test-main.unit.new")); + assert(!has(ct, ".bridge.v4.r0.init")); + assert(occurrences(at, "\n") == 1); + assert(has(at, "pkg-internal-test.o.new")); + assert(occurrences(lt, "\n") == 1); + assert(same(oldptestwwi, readfile(strings.concat(works[si], + "/pkg-internal-test.wwi")))); + assert(!same(oldptesta, readfile(strings.concat(works[si], + "/pkg-internal-test.a")))); + assert(same(oldpxtestwwi, readfile(strings.concat(works[si], + "/pkg_test-external-test.wwi")))); + assert(same(oldpxtesta, readfile(strings.concat(works[si], + "/pkg_test-external-test.a")))); + assert(same(oldmainwwi, readfile(strings.concat(works[si], + "/pkg-test-main.wwi")))); + assert(same(oldmaina, readfile(strings.concat(works[si], + "/pkg-test-main.a")))); + let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"), + bins[si]); + let na: str = normalizedtrace(at, strings.concat(works[si], "/"), + bins[si]); + let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"), + bins[si]); + if (si == 0) { + bodycompiler = nc; bodyassembler = na; bodylinker = nl; } else { - assert(same(referencesame, readfile(samebin))); - assert(same(referenceexternal, readfile(externalbin))); - assert(same(referencesameunit, sameunit)); - assert(same(referenceexternalunit, externalunit)); - assert(same(referenceproductionunit, externalproduction)); - assert(same(referenceout, buildstdout)); - assert(same(referenceerr, buildstderr)); + assert(same(bodycompiler, nc)); + assert(same(bodyassembler, na)); + assert(same(bodylinker, nl)); + assert(same(readfile(bins[0]), readfile(bins[1]))); }; - if (i + 1 < drivers.len) { - clean(sharedworkroot); - clean(samebin); clean(externalbin); - }; - i += 1; + si += 1; }; - // Canonical variant keys stay disjoint from valid packages literally named - // __same and __external, including warm reuse. - let warmroot: str = strings.concat(root, "/warm"); - assert(os.mkdir(warmroot, 448i32) == 0); - let warmav: []str = [driver("ww"), "test", "-w", warmroot, - "-I", root, pkg]; - runcommandenv(root, "variant-warm-cold", warmav, traceenv, - (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - let warmstdout: str = strings.dup(out.stdout); - let warmstderr: str = strings.dup(out.stderr); - let warmtrace: str = readfile(compilertrace); - let warmwork: str = strings.concat(warmroot, "/"); - assert(has(readfile(strings.concat(warmwork, "__same.unit.ww")), - "package __same;")); - assert(has(readfile(strings.concat(warmwork, - "pkg-internal-test.unit.ww")), "SAME_TEST_SOURCE")); - assert(has(readfile(strings.concat(warmwork, "__external.unit.ww")), - "package __external;")); - assert(has(readfile(strings.concat(warmwork, - "pkg_test-external-test.unit.ww")), "EXTERNAL_TEST_SOURCE")); - assert(has(readfile(strings.concat(warmwork, - "pkg.unit.ww")), - "PACKAGE_PRODUCTION_A")); - assert(!os.exists(strings.concat(warmwork, - "__ww-test-001-external-production.unit.ww"))); - // Equivalent hidden product order is presentation-only. The driver sorts - // and interns before compilation, so reversing the descriptors reuses the - // same persistent actions and leaves every artifact byte unchanged. - let orderwork: str = strings.concat(root, "/order-work"); - assert(os.mkdir(orderwork, 448i32) == 0); - let orderav: []str = [driver("ww"), "test", "-c", "-w", orderwork, - "-I", root, - "--ww-package-test", "external", "pkg_test", pkg, - externalbin, strings.concat(root, "/order-external.status"), - "--ww-package-test", "same", "pkg", pkg, - samebin, strings.concat(root, "/order-same.status"), pkg]; - let cordertracebefore: str = strings.dup(readfile(compilertrace)); - let corderlinkbefore: str = strings.dup(readfile(linkertrace)); - runcommandenv(root, "variant-order-reversed", orderav, traceenv, - (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - let cordertraceafter: str = strings.dup(readfile(compilertrace)); - let corderlinkafter: str = strings.dup(readfile(linkertrace)); - let creversedtrace: str = strings.dup(strings.sub(cordertraceafter, - cordertracebefore.len, cordertraceafter.len)); - let creversedlink: str = strings.dup(strings.sub(corderlinkafter, - corderlinkbefore.len, corderlinkafter.len)); - let orderworkdir: str = strings.concat(orderwork, "/"); - let orderactions: []str = ["pkg", "pkg-internal-test", - "pkg_test-external-test", "pkg-internal-test-main", - "pkg_test-external-test-main"]; - let oi: i32 = 0; - for (oi < orderactions.len) { - let suffixes: []str = [".unit.ww", ".wwi", ".a"]; + + // An external test body edit is confined to pxtest when its export is stable. + rewritefile(externalfile, externalbodychanged); + si = 0; + for (si < stages.len) { + rewritefile(compilertraces[si], ""); + rewritefile(assemblertraces[si], ""); + rewritefile(linkertraces[si], ""); + let env: []str = os.getenvs(); + let tracedenv: []str = alloc([], (env.len + 9): u64)!; + let ei: i32 = 0; + for (ei < env.len) { + if (!strings.hasprefix(env[ei], "WW_W6C=") + && !strings.hasprefix(env[ei], "WW_W6A=") + && !strings.hasprefix(env[ei], "WW_W6L=")) { + append(tracedenv, env[ei]); + }; + ei += 1; + }; + append(tracedenv, strings.concat("WW_W6C=", compilerwrapper)); + append(tracedenv, strings.concat("WW_W6A=", assemblerwrapper)); + append(tracedenv, strings.concat("WW_W6L=", linkerwrapper)); + append(tracedenv, strings.concat("WW_GRAPH_COMPILER_TRACE=", + compilertraces[si])); + append(tracedenv, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", + assemblertraces[si])); + append(tracedenv, strings.concat("WW_GRAPH_LINKER_TRACE=", + linkertraces[si])); + append(tracedenv, strings.concat("WW_GRAPH_REAL_COMPILER=", + driver(compilers[si]))); + append(tracedenv, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", + driver(assemblers[si]))); + append(tracedenv, strings.concat("WW_GRAPH_REAL_LINKER=", + driver(linkers[si]))); + let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], + "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", + "pkg_test", pkg, bins[si], statuses[si], pkg]; + runcommandenv(root, strings.concat("graph-external-", stages[si]), av, + tracedenv, (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let ct: str = readfile(compilertraces[si]); + let at: str = readfile(assemblertraces[si]); + let lt: str = readfile(linkertraces[si]); + assert(occurrences(ct, "\n") == 1); + assert(has(ct, "pkg_test-external-test.unit.new")); + assert(!has(ct, "pkg-internal-test.unit.new")); + assert(!has(ct, "pkg-test-main.unit.new")); + assert(!has(ct, ".bridge.v4.r0.init")); + assert(occurrences(at, "\n") == 1); + assert(has(at, "pkg_test-external-test.o.new")); + assert(occurrences(lt, "\n") == 1); + let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"), + bins[si]); + let na: str = normalizedtrace(at, strings.concat(works[si], "/"), + bins[si]); + let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"), + bins[si]); + if (si == 0) { + externalcompiler = nc; externalassembler = na; + externallinker = nl; + } else { + assert(same(externalcompiler, nc)); + assert(same(externalassembler, na)); + assert(same(externallinker, nl)); + assert(same(readfile(bins[0]), readfile(bins[1]))); + }; + si += 1; + }; + + // An internal exported-helper interface edit reaches the variant-4 + // importer, pxtest, and pmain, but still never introduces ordinary p. + rewritefile(samefile, samebodyexported); + si = 0; + for (si < stages.len) { + let oldwwi: str = readfile(strings.concat(works[si], + "/pkg-internal-test.wwi")); + rewritefile(compilertraces[si], ""); + rewritefile(assemblertraces[si], ""); + rewritefile(linkertraces[si], ""); + let env: []str = os.getenvs(); + let tracedenv: []str = alloc([], (env.len + 9): u64)!; + let ei: i32 = 0; + for (ei < env.len) { + if (!strings.hasprefix(env[ei], "WW_W6C=") + && !strings.hasprefix(env[ei], "WW_W6A=") + && !strings.hasprefix(env[ei], "WW_W6L=")) { + append(tracedenv, env[ei]); + }; + ei += 1; + }; + append(tracedenv, strings.concat("WW_W6C=", compilerwrapper)); + append(tracedenv, strings.concat("WW_W6A=", assemblerwrapper)); + append(tracedenv, strings.concat("WW_W6L=", linkerwrapper)); + append(tracedenv, strings.concat("WW_GRAPH_COMPILER_TRACE=", + compilertraces[si])); + append(tracedenv, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", + assemblertraces[si])); + append(tracedenv, strings.concat("WW_GRAPH_LINKER_TRACE=", + linkertraces[si])); + append(tracedenv, strings.concat("WW_GRAPH_REAL_COMPILER=", + driver(compilers[si]))); + append(tracedenv, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", + driver(assemblers[si]))); + append(tracedenv, strings.concat("WW_GRAPH_REAL_LINKER=", + driver(linkers[si]))); + let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], + "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", + "pkg_test", pkg, bins[si], statuses[si], pkg]; + runcommandenv(root, strings.concat("graph-export-", stages[si]), av, + tracedenv, (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let ct: str = readfile(compilertraces[si]); + let at: str = readfile(assemblertraces[si]); + let lt: str = readfile(linkertraces[si]); + assert(!same(oldwwi, readfile(strings.concat(works[si], + "/pkg-internal-test.wwi")))); + assert(occurrences(ct, "pkg-internal-test.unit.new") == 1); + assert(occurrences(ct, + "--package-init-symbol __ww..pkg.p.bridge.v4.r0.init") == 1); + assert(occurrences(ct, + "pkg_test-external-test.unit.new") == 1); + assert(occurrences(ct, "pkg-test-main.unit.new") == 1); + assert(!has(ct, + "--package-init-symbol __ww..pkg.p.pkg.v0.r0.init")); + assert(occurrences(lt, "\n") == 1); + let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"), + bins[si]); + let na: str = normalizedtrace(at, strings.concat(works[si], "/"), + bins[si]); + let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"), + bins[si]); + if (si == 0) { + exportcompiler = nc; exportassembler = na; exportlinker = nl; + } else { + assert(same(exportcompiler, nc)); + assert(same(exportassembler, na)); + assert(same(exportlinker, nl)); + assert(same(readfile(bins[0]), readfile(bins[1]))); + }; let xi: i32 = 0; for (xi < suffixes.len) { - assert(same(readfile(strings.concat(warmwork, orderactions[oi], - suffixes[xi])), readfile(strings.concat(orderworkdir, - orderactions[oi], suffixes[xi])))); + let bytes: str = readfile(strings.concat(works[si], "/", + bridgeaction, suffixes[xi])); + if (si == 0) { exportbridge[xi] = strings.dup(bytes); } + else { assert(same(exportbridge[xi], bytes)); }; xi += 1; }; - oi += 1; + let runav: []str = [bins[si]]; + runcommand(root, strings.concat("graph-export-run-", stages[si]), + runav, (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(occurrences(out.stdout, "same_graph ... ok\n") == 1); + assert(occurrences(out.stdout, "external_graph ... ok\n") == 1); + si += 1; }; - clean(orderwork); - assert(os.mkdir(orderwork, 448i32) == 0); - let orderforwardav: []str = [driver("ww"), "test", "-c", "-w", - orderwork, "-I", root, - "--ww-package-test", "same", "pkg", pkg, - samebin, strings.concat(root, "/order-same.status"), - "--ww-package-test", "external", "pkg_test", pkg, - externalbin, strings.concat(root, "/order-external.status"), pkg]; - let cforwardtracebefore: str = strings.dup(readfile(compilertrace)); - let cforwardlinkbefore: str = strings.dup(readfile(linkertrace)); - runcommandenv(root, "variant-order-forward", orderforwardav, traceenv, - (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - let cforwardtraceafter: str = strings.dup(readfile(compilertrace)); - let cforwardlinkafter: str = strings.dup(readfile(linkertrace)); - assert(same(creversedtrace, strings.sub(cforwardtraceafter, - cforwardtracebefore.len, cforwardtraceafter.len))); - assert(same(creversedlink, strings.sub(cforwardlinkafter, - cforwardlinkbefore.len, cforwardlinkafter.len))); - let ordertrace: str = readfile(compilertrace); - let coldcounts: []i32 = [occurrences(ordertrace, "/pkg.unit.new"), - occurrences(ordertrace, "/pkg-internal-test.unit.new"), - occurrences(ordertrace, "/pkg_test-external-test.unit.new")]; - assert(coldcounts[0] == 3 && coldcounts[1] == 3 && coldcounts[2] == 3); - let orderwwwork: str = strings.concat(root, "/order-ww-work"); - let orderwwsame: str = strings.concat(root, "/order-ww-same.test"); - let orderwwexternal: str = strings.concat(root, "/order-ww-external.test"); - assert(os.mkdir(orderwwwork, 448i32) == 0); - let orderwwav: []str = [driver("ww_ww"), "test", "-c", "-w", - orderwwwork, "-I", root, - "--ww-package-test", "external", "pkg_test", pkg, - orderwwexternal, strings.concat(root, "/order-ww-external.status"), - "--ww-package-test", "same", "pkg", pkg, - orderwwsame, strings.concat(root, "/order-ww-same.status"), pkg]; - let wordertracebefore: str = strings.dup(readfile(compilertrace)); - let worderlinkbefore: str = strings.dup(readfile(linkertrace)); - runcommandenv(root, "variant-order-reversed-ww", orderwwav, wwtraceenv, - (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - let wordertraceafter: str = strings.dup(readfile(compilertrace)); - let worderlinkafter: str = strings.dup(readfile(linkertrace)); - let wreversedtrace: str = strings.dup(strings.sub(wordertraceafter, - wordertracebefore.len, wordertraceafter.len)); - let wreversedlink: str = strings.dup(strings.sub(worderlinkafter, - worderlinkbefore.len, worderlinkafter.len)); - clean(orderwwwork); - assert(os.mkdir(orderwwwork, 448i32) == 0); - let orderwwforward: []str = [driver("ww_ww"), "test", "-c", "-w", - orderwwwork, "-I", root, - "--ww-package-test", "same", "pkg", pkg, - orderwwsame, strings.concat(root, "/order-ww-same.status"), - "--ww-package-test", "external", "pkg_test", pkg, - orderwwexternal, strings.concat(root, "/order-ww-external.status"), pkg]; - let wforwardtracebefore: str = strings.dup(readfile(compilertrace)); - let wforwardlinkbefore: str = strings.dup(readfile(linkertrace)); - runcommandenv(root, "variant-order-forward-ww", orderwwforward, wwtraceenv, - (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - let wforwardtraceafter: str = strings.dup(readfile(compilertrace)); - let wforwardlinkafter: str = strings.dup(readfile(linkertrace)); - assert(same(wreversedtrace, strings.sub(wforwardtraceafter, - wforwardtracebefore.len, wforwardtraceafter.len))); - assert(same(wreversedlink, strings.sub(wforwardlinkafter, - wforwardlinkbefore.len, wforwardlinkafter.len))); - oi = 0; - for (oi < orderactions.len) { - let suffixes: []str = [".unit.ww", ".wwi", ".a"]; - let xi: i32 = 0; - for (xi < suffixes.len) { - assert(same(readfile(strings.concat(warmwork, orderactions[oi], - suffixes[xi])), readfile(strings.concat(orderwwwork, "/", - orderactions[oi], suffixes[xi])))); - xi += 1; + + // Removing the external variant changes only the directory main's target + // set. The now-unreachable pxtest and transitive clone remain intact in the + // persistent store, while the linked process runs the internal target once. + assert(os.remove(externalfile) == 0); + si = 0; + for (si < stages.len) { + let oldptest: str = readfile(strings.concat(works[si], + "/pkg-internal-test.a")); + let oldpxtest: str = readfile(strings.concat(works[si], + "/pkg_test-external-test.a")); + let oldbridge: str = readfile(strings.concat(works[si], "/", + bridgeaction, ".a")); + rewritefile(compilertraces[si], ""); + rewritefile(assemblertraces[si], ""); + rewritefile(linkertraces[si], ""); + 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_GRAPH_COMPILER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_ASSEMBLER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_LINKER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_REAL_COMPILER=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_REAL_ASSEMBLER=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_REAL_LINKER=")) { + append(env, baseenv[ei]); + }; + ei += 1; }; - oi += 1; - }; - let tracedorders: str = readfile(compilertrace); - coldcounts[0] = occurrences(tracedorders, "/pkg.unit.new"); - coldcounts[1] = occurrences(tracedorders, "/pkg-internal-test.unit.new"); - coldcounts[2] = occurrences(tracedorders, - "/pkg_test-external-test.unit.new"); - runcommandenv(root, "variant-warm-reuse", warmav, traceenv, - (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - assert(same(warmstdout, out.stdout)); - assert(same(warmstderr, out.stderr)); - let reusedtrace: str = readfile(compilertrace); - assert(occurrences(reusedtrace, "/pkg.unit.new") == coldcounts[0]); - assert(occurrences(reusedtrace, "/pkg-internal-test.unit.new") - == coldcounts[1]); - assert(occurrences(reusedtrace, "/pkg_test-external-test.unit.new") - == coldcounts[2]); - let aliasunit: str = strings.concat(root, "/alias.unit.ww"); - writefile(aliasunit, strings.concat( - "//ww:module-reset __wwtest\n", - "package test;\nexport fn value() i32 = { return 1; };\n")); - let aliasstages: []str = ["w6c", "w6c_ww"]; - let aliaserrs: []str = ["", ""]; - i = 0; - for (i < aliasstages.len) { - let asmout: str = strings.concat(root, "/alias-", aliasstages[i], - ".s"); - let rawav: []str = [driver(aliasstages[i]), "-c", "-o", asmout, - aliasunit]; - runcommand(root, strings.concat("alias-unowned-", aliasstages[i]), - rawav, (30i64 * (time.second: i64)): time.duration, &out); + 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_GRAPH_COMPILER_TRACE=", + compilertraces[si])); + append(env, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", + assemblertraces[si])); + append(env, strings.concat("WW_GRAPH_LINKER_TRACE=", + linkertraces[si])); + append(env, strings.concat("WW_GRAPH_REAL_COMPILER=", + driver(compilers[si]))); + append(env, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", + driver(assemblers[si]))); + append(env, strings.concat("WW_GRAPH_REAL_LINKER=", + driver(linkers[si]))); + let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], + "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", + "-", pkg, bins[si], statuses[si], pkg]; + runcommandenv(root, strings.concat("graph-remove-external-", stages[si]), + av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); - assert(os.exists(asmout)); - let badav: []str = [driver(aliasstages[i]), "-c", - "--test-support-module", "arbitrary", "-o", asmout, aliasunit]; - runcommand(root, strings.concat("alias-arbitrary-", aliasstages[i]), - badav, (30i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 2); - assert(has(out.stderr, "invalid --test-support-module")); - aliaserrs[i] = strings.dup(out.stderr); - i += 1; + assert(same(readfile(statuses[si]), "ok\n")); + let ct: str = readfile(compilertraces[si]); + let at: str = readfile(assemblertraces[si]); + let lt: str = readfile(linkertraces[si]); + assert(occurrences(ct, "\n") == 1); + assert(has(ct, "pkg-test-main.unit.new")); + assert(!has(ct, "pkg-internal-test.unit.new")); + assert(!has(ct, "pkg_test-external-test.unit.new")); + assert(!has(ct, strings.concat("/", bridgeaction, ".unit.new"))); + let maincompile: str = linecontaining(ct, + "pkg-test-main.unit.new"); + assert(occurrences(maincompile, "--test-target-package") == 1); + assert(has(maincompile, "--test-target-package pkg ")); + assert(!has(maincompile, "--test-target-package pkg_test ")); + assert(occurrences(at, "\n") == 2); + assert(has(at, "/pkg-test-main.o.new")); + assert(has(at, "/pkg-test-main.init.o.new")); + assert(occurrences(lt, "\n") == 1); + let link: str = linecontaining(lt, + strings.concat("-o ", bins[si], ".new ")); + assert(has(link, "/pkg-test-main.a.new")); + assert(has(link, "/pkg-internal-test.a")); + assert(!has(link, "/pkg_test-external-test.a")); + assert(!has(link, strings.concat("/", bridgeaction, ".a"))); + assert(!has(link, "/__external.a")); + assert(same(oldptest, readfile(strings.concat(works[si], + "/pkg-internal-test.a")))); + assert(same(oldpxtest, readfile(strings.concat(works[si], + "/pkg_test-external-test.a")))); + assert(same(oldbridge, readfile(strings.concat(works[si], "/", + bridgeaction, ".a")))); + let runav: []str = [bins[si]]; + runcommand(root, strings.concat("graph-remove-run-", stages[si]), + runav, (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(occurrences(out.stdout, "same_graph ... ok\n") == 1); + assert(!has(out.stdout, "external_graph ... ok\n")); + let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"), + bins[si]); + let na: str = normalizedtrace(at, strings.concat(works[si], "/"), + bins[si]); + let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"), + bins[si]); + if (si == 0) { + removecompiler = nc; removeassembler = na; removelinker = nl; + removebin = readfile(bins[si]); + removeout = strings.dup(out.stdout); + removeerr = strings.dup(out.stderr); + } else { + assert(same(removecompiler, nc)); + assert(same(removeassembler, na)); + assert(same(removelinker, nl)); + assert(same(removebin, readfile(bins[si]))); + assert(same(removeout, out.stdout)); + assert(same(removeerr, out.stderr)); + }; + si += 1; + }; + + // Re-adding the external variant with a body-only edit rebuilds pxtest and + // the target-set main. Its stable export stops at the unchanged ptest and + // transitive clone, whose complete persisted bytes remain identical. + writefile(externalfile, externalbodyreadded); + si = 0; + for (si < stages.len) { + let oldptest: str = readfile(strings.concat(works[si], + "/pkg-internal-test.a")); + let oldpxtestwwi: str = readfile(strings.concat(works[si], + "/pkg_test-external-test.wwi")); + let oldpxtesta: str = readfile(strings.concat(works[si], + "/pkg_test-external-test.a")); + let oldbridge: []str = ["", "", "", "", ""]; + let xi: i32 = 0; + for (xi < suffixes.len) { + oldbridge[xi] = readfile(strings.concat(works[si], "/", + bridgeaction, suffixes[xi])); + xi += 1; + }; + rewritefile(compilertraces[si], ""); + rewritefile(assemblertraces[si], ""); + rewritefile(linkertraces[si], ""); + 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_GRAPH_COMPILER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_ASSEMBLER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_LINKER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_REAL_COMPILER=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_REAL_ASSEMBLER=") + && !strings.hasprefix(baseenv[ei], + "WW_GRAPH_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_GRAPH_COMPILER_TRACE=", + compilertraces[si])); + append(env, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", + assemblertraces[si])); + append(env, strings.concat("WW_GRAPH_LINKER_TRACE=", + linkertraces[si])); + append(env, strings.concat("WW_GRAPH_REAL_COMPILER=", + driver(compilers[si]))); + append(env, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", + driver(assemblers[si]))); + append(env, strings.concat("WW_GRAPH_REAL_LINKER=", + driver(linkers[si]))); + let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], + "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", + "pkg_test", pkg, bins[si], statuses[si], pkg]; + runcommandenv(root, strings.concat("graph-readd-external-", stages[si]), + av, env, (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(out.stdout.len == 0 && out.stderr.len == 0); + let ct: str = readfile(compilertraces[si]); + let at: str = readfile(assemblertraces[si]); + let lt: str = readfile(linkertraces[si]); + assert(occurrences(ct, "\n") == 2); + assert(occurrences(ct, + "pkg_test-external-test.unit.new") == 1); + assert(occurrences(ct, "pkg-test-main.unit.new") == 1); + assert(!has(ct, "pkg-internal-test.unit.new")); + assert(!has(ct, strings.concat("/", bridgeaction, ".unit.new"))); + let maincompile: str = linecontaining(ct, + "pkg-test-main.unit.new"); + assert(occurrences(maincompile, "--test-target-package") == 2); + assert(pos(maincompile, "--test-target-package pkg ") + < pos(maincompile, "--test-target-package pkg_test ")); + let externalcompile: str = linecontaining(ct, + "pkg_test-external-test.unit.new"); + assert(has(externalcompile, "/pkg-internal-test.wwi")); + assert(has(externalcompile, strings.concat("/", bridgeaction, + ".wwi"))); + assert(occurrences(at, "\n") == 3); + assert(occurrences(lt, "\n") == 1); + let link: str = linecontaining(lt, + strings.concat("-o ", bins[si], ".new ")); + assert(has(link, "/pkg-test-main.a.new")); + assert(has(link, "/pkg_test-external-test.a.new")); + assert(has(link, "/pkg-internal-test.a")); + assert(has(link, strings.concat("/", bridgeaction, ".a"))); + assert(same(oldptest, readfile(strings.concat(works[si], + "/pkg-internal-test.a")))); + assert(same(oldpxtestwwi, readfile(strings.concat(works[si], + "/pkg_test-external-test.wwi")))); + assert(!same(oldpxtesta, readfile(strings.concat(works[si], + "/pkg_test-external-test.a")))); + xi = 0; + for (xi < suffixes.len) { + assert(same(oldbridge[xi], readfile(strings.concat(works[si], "/", + bridgeaction, suffixes[xi])))); + xi += 1; + }; + let runav: []str = [bins[si]]; + runcommand(root, strings.concat("graph-readd-run-", stages[si]), + runav, (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(occurrences(out.stdout, "same_graph ... ok\n") == 1); + assert(occurrences(out.stdout, "external_graph ... ok\n") == 1); + let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"), + bins[si]); + let na: str = normalizedtrace(at, strings.concat(works[si], "/"), + bins[si]); + let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"), + bins[si]); + if (si == 0) { + readdcompiler = nc; readdassembler = na; readdlinker = nl; + readdbin = readfile(bins[si]); + readdout = strings.dup(out.stdout); + readderr = strings.dup(out.stderr); + } else { + assert(same(readdcompiler, nc)); + assert(same(readdassembler, na)); + assert(same(readdlinker, nl)); + assert(same(readdbin, readfile(bins[si]))); + assert(same(readdout, out.stdout)); + assert(same(readderr, out.stderr)); + }; + si += 1; + }; + + // The public coordinator accepts one explicit output for the combined + // directory in both stages because variants are actions, not products. + let directbin: str = strings.concat(root, "/direct.test"); + let directbytes: str = ""; + let directout: str = ""; + let directerr: str = ""; + si = 0; + for (si < stages.len) { + let directav: []str = [driver(stages[si]), "test", "-c", "-o", + directbin, "-I", root, pkg]; + runcommand(root, strings.concat("graph-direct-product-", stages[si]), + directav, (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(occurrences(out.stdout, "built ") == 1); + assert(os.exists(directbin)); + if (si == 0) { + directbytes = readfile(directbin); + directout = strings.dup(out.stdout); + directerr = strings.dup(out.stderr); + } else { + assert(same(directbytes, readfile(directbin))); + assert(same(directout, out.stdout)); + assert(same(directerr, out.stderr)); + }; + clean(strings.concat(directbin, ".sepwork")); + clean(directbin); + si += 1; }; - assert(same(aliaserrs[0], aliaserrs[1])); clean(root); }; @test fn multi_directory_shared_package_plan() void = { let root: str = fresh(); + let suite: str = strings.concat(root, "/suite"); let leaf: str = strings.concat(root, "/leaf"); let common: str = strings.concat(root, "/common"); - let suite: str = strings.concat(root, "/suite"); let alpha: str = strings.concat(suite, "/alpha"); let beta: str = strings.concat(suite, "/beta"); let gamma: str = strings.concat(suite, "/gamma"); - let alphasame: str = strings.concat(alpha, "/_alpha_same"); - let alphaexternal: str = strings.concat(alpha, "/_alpha_external"); - let betasame: str = strings.concat(beta, "/_beta_same"); - let betaexternal: str = strings.concat(beta, "/_beta_external"); - let alphashadow: str = strings.concat(root, "/_alpha_same"); - let betashadow: str = strings.concat(root, "/_beta_same"); - let dirs: []str = [leaf, common, suite, alpha, beta, gamma, alphasame, - alphaexternal, betasame, betaexternal, alphashadow, betashadow]; + let alphasame: str = strings.concat(suite, "/_alpha_same"); + let alphaexternal: str = strings.concat(suite, "/_alpha_external"); + let betasame: str = strings.concat(suite, "/_beta_same"); + let betaexternal: str = strings.concat(suite, "/_beta_external"); + let dirs: []str = [suite, leaf, common, alpha, beta, gamma, + alphasame, alphaexternal, betasame, betaexternal]; let di: i32 = 0; for (di < dirs.len) { assert(os.mkdir(dirs[di], 448i32) == 0); di += 1; }; writefile(strings.concat(leaf, "/leaf.ww"), strings.concat( - "package leaf;\n", - "// MULTIDIR_LEAF\n", + "package leaf;\n// MULTIDIR_LEAF\n", "export fn value() i32 = { return 40; };\n")); writefile(strings.concat(common, "/common.ww"), strings.concat( - "package common;\nimport leaf;\n", - "// MULTIDIR_COMMON\n", - "export fn value() i32 = { return leaf.value(); };\n")); - writefile(strings.concat(common, "/common_test.ww"), strings.concat( - "package common;\n", - "DEPENDENCY_TEST_FILE_MUST_NOT_COMPILE\n")); - writefile(strings.concat(alphasame, "/source.ww"), - "package _alpha_same;\nexport fn value() i32 = { return 1; };\n"); - writefile(strings.concat(alphaexternal, "/source.ww"), - "package _alpha_external;\nexport fn value() i32 = { return 1; };\n"); - writefile(strings.concat(betasame, "/source.ww"), - "package _beta_same;\nexport fn value() i32 = { return 2; };\n"); - writefile(strings.concat(betaexternal, "/source.ww"), - "package _beta_external;\nexport fn value() i32 = { return 2; };\n"); - // The explicit -I root contains deliberately wrong shadows. Each selected - // directory must retain its own first lookup root inside the union plan. - writefile(strings.concat(alphashadow, "/source.ww"), - "package _alpha_same;\nexport fn value() i32 = { return 99; };\n"); - writefile(strings.concat(betashadow, "/source.ww"), - "package _beta_same;\nexport fn value() i32 = { return 99; };\n"); + "package common;\nimport leaf;\n// MULTIDIR_COMMON\n", + "export fn value() i32 = { return leaf.value() + 1; };\n")); + writefile(strings.concat(alphasame, "/dep.ww"), + "package _alpha_same;\nexport fn value() i32 = { return 0; };\n"); + writefile(strings.concat(alphaexternal, "/dep.ww"), + "package _alpha_external;\nexport fn value() i32 = { return 0; };\n"); + writefile(strings.concat(betasame, "/dep.ww"), + "package _beta_same;\nexport fn value() i32 = { return 0; };\n"); + writefile(strings.concat(betaexternal, "/dep.ww"), + "package _beta_external;\nexport fn value() i32 = { return 0; };\n"); writefile(strings.concat(alpha, "/a.ww"), strings.concat( - "package alpha;\nimport common;\n", - "// MULTIDIR_ALPHA_A\n", - "export fn value() i32 = { return common.value(); };\n")); - writefile(strings.concat(alpha, "/z.ww"), strings.concat( - "package alpha;\n", - "// MULTIDIR_ALPHA_Z\n", - "fn secret() i32 = { return 1; };\n", - "export fn exported() i32 = { return 1; };\n")); + "package alpha;\nimport common;\n// MULTIDIR_ALPHA\n", + "export fn value() i32 = { return common.value() + 1; };\n")); writefile(strings.concat(alpha, "/same_test.ww"), strings.concat( - "package alpha;\nimport _alpha_same;\n", - "// MULTIDIR_ALPHA_SAME\n", + "package alpha;\nimport _alpha_same;\n// MULTIDIR_ALPHA_SAME\n", + "export fn test_helper() i32 = { return 1; };\n", "@test fn alpha_same_runs() void = {\n", - " assert(value() + secret() + _alpha_same.value() == 42);\n};\n")); + " assert(value() + _alpha_same.value() == 42);\n", + "};\n")); writefile(strings.concat(alpha, "/external_test.ww"), strings.concat( - "package alpha_test;\nimport alpha;\nimport _alpha_external;\n", + "package alpha_test;\nimport _alpha_external;\nimport alpha;\n", "// MULTIDIR_ALPHA_EXTERNAL\n", "@test fn alpha_external_runs() void = {\n", - " assert(alpha.value() + alpha.exported()", - " + _alpha_external.value() == 42);\n};\n")); - // A no-test directory is an ordinary production product. Beta also imports - // it, proving that root/dependency discovery shares the same action. + " assert(alpha.value() + _alpha_external.value() == 42);\n", + " assert(alpha.test_helper() == 1);\n", + "};\n")); writefile(strings.concat(gamma, "/gamma.ww"), strings.concat( - "package gamma;\nimport common;\n", - "// MULTIDIR_GAMMA\n", - "export fn value() i32 = { return common.value(); };\n")); + "package gamma;\n// MULTIDIR_GAMMA\n", + "export fn value() i32 = { return 0; };\n")); writefile(strings.concat(beta, "/beta.ww"), strings.concat( - "package beta;\nimport gamma;\nimport alpha;\n", - "// MULTIDIR_BETA\n", - "fn secret() i32 = { return 2; };\n", - "export fn value() i32 = { return gamma.value(); };\n", - "export fn exported() i32 = { return alpha.exported() + 1; };\n")); + "package beta;\nimport alpha;\nimport gamma;\n// MULTIDIR_BETA\n", + "export fn value() i32 = { return alpha.value() + gamma.value(); };\n")); writefile(strings.concat(beta, "/same_test.ww"), strings.concat( - "package beta;\nimport _beta_same;\n", - "// MULTIDIR_BETA_SAME\n", + "package beta;\nimport _beta_same;\n// MULTIDIR_BETA_SAME\n", + "export fn test_helper() i32 = { return 2; };\n", "@test fn beta_same_runs() void = {\n", - " assert(value() + secret() + _beta_same.value() == 44);\n};\n")); + " assert(value() + _beta_same.value() == 42);\n", + "};\n")); writefile(strings.concat(beta, "/external_test.ww"), strings.concat( - "package beta_test;\nimport beta;\nimport _beta_external;\n", + "package beta_test;\nimport _beta_external;\nimport beta;\n", "// MULTIDIR_BETA_EXTERNAL\n", "@test fn beta_external_runs() void = {\n", - " assert(beta.value() + beta.exported()", - " + _beta_external.value() == 44);\n};\n")); + " assert(beta.value() + _beta_external.value() == 42);\n", + " assert(beta.test_helper() == 2);\n", + "};\n")); - let compilertrace: str = strings.concat(root, "/compiler.trace"); - let linkertrace: str = strings.concat(root, "/linker.trace"); let compilerwrapper: str = strings.concat(root, "/trace-w6c.sh"); + let assemblerwrapper: str = strings.concat(root, "/trace-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/trace-w6l.sh"); - writefile(compilertrace, ""); - writefile(linkertrace, ""); writeexecutable(compilerwrapper, strings.concat( - "#!/bin/sh\n", - "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_COMPILER_TRACE\"\n", - "exec \"$WW_PACKAGE_W6C\" \"$@\"\n")); + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MULTI_COMPILER_TRACE\"\n", + "exec \"$WW_MULTI_REAL_COMPILER\" \"$@\"\n")); + writeexecutable(assemblerwrapper, strings.concat( + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MULTI_ASSEMBLER_TRACE\"\n", + "exec \"$WW_MULTI_REAL_ASSEMBLER\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( - "#!/bin/sh\n", - "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_LINKER_TRACE\"\n", - "exec \"$WW_PACKAGE_W6L\" \"$@\"\n")); - let baseenv: []str = os.getenvs(); - let traceenv: []str = alloc([], (baseenv.len + 6): u64)!; - let ei: i32 = 0; - for (ei < baseenv.len) { - if (!strings.hasprefix(baseenv[ei], "WW_W6C=") - && !strings.hasprefix(baseenv[ei], "WW_W6L=") - && !strings.hasprefix(baseenv[ei], - "WW_PACKAGE_COMPILER_TRACE=") - && !strings.hasprefix(baseenv[ei], - "WW_PACKAGE_LINKER_TRACE=") - && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C=") - && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6L=")) { - append(traceenv, baseenv[ei]); - }; - ei += 1; - }; - append(traceenv, strings.concat("WW_W6C=", compilerwrapper)); - append(traceenv, strings.concat("WW_W6L=", linkerwrapper)); - append(traceenv, strings.concat("WW_PACKAGE_COMPILER_TRACE=", - compilertrace)); - append(traceenv, strings.concat("WW_PACKAGE_LINKER_TRACE=", linkertrace)); - append(traceenv, strings.concat("WW_PACKAGE_W6C=", driver("w6c"))); - append(traceenv, strings.concat("WW_PACKAGE_W6L=", driver("w6l"))); - // Exercise default sibling discovery independently of the exact-path - // override contract covered below: the copied driver sits beside narrow - // tracing wrappers for its real compiler and linker actions. - let wwtracebin: str = strings.concat(repo(), "/out/package-trace-", - workescape(root)); - assert(os.mkdir(wwtracebin, 448i32) == 0); - let wwtracedriver: str = strings.concat(wwtracebin, "/ww_ww"); - let wwcompilertrace: str = strings.concat(root, "/ww-compiler.trace"); - let wwlinkertrace: str = strings.concat(root, "/ww-linker.trace"); - writefile(wwcompilertrace, ""); - writefile(wwlinkertrace, ""); - writeexecutable(wwtracedriver, readfile(driver("ww_ww"))); - writeexecutable(strings.concat(wwtracebin, "/wwtest"), - readfile(driver("wwtest"))); - writeexecutable(strings.concat(wwtracebin, "/w6c_ww"), strings.concat( - "#!/bin/sh\n", - "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_WW_COMPILER_TRACE\"\n", - "exec \"$WW_PACKAGE_W6C_WW\" \"$@\"\n")); - writeexecutable(strings.concat(wwtracebin, "/w6a_ww"), strings.concat( - "#!/bin/sh\nexec \"$WW_PACKAGE_W6A_WW\" \"$@\"\n")); - writeexecutable(strings.concat(wwtracebin, "/w6l_ww"), strings.concat( - "#!/bin/sh\n", - "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_WW_LINKER_TRACE\"\n", - "exec \"$WW_PACKAGE_W6L_WW\" \"$@\"\n")); - let wwtraceenv: []str = alloc([], (baseenv.len + 5): u64)!; - ei = 0; - for (ei < baseenv.len) { - if (!strings.hasprefix(baseenv[ei], "WW_PACKAGE_WW_COMPILER_TRACE=") - && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_WW_LINKER_TRACE=") - && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C_WW=") - && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6A_WW=") - && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6L_WW=")) { - append(wwtraceenv, baseenv[ei]); - }; - ei += 1; - }; - append(wwtraceenv, strings.concat("WW_PACKAGE_WW_COMPILER_TRACE=", - wwcompilertrace)); - append(wwtraceenv, strings.concat("WW_PACKAGE_WW_LINKER_TRACE=", - wwlinkertrace)); - append(wwtraceenv, strings.concat("WW_PACKAGE_W6C_WW=", driver("w6c_ww"))); - append(wwtraceenv, strings.concat("WW_PACKAGE_W6A_WW=", driver("w6a_ww"))); - append(wwtraceenv, strings.concat("WW_PACKAGE_W6L_WW=", driver("w6l_ww"))); + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MULTI_LINKER_TRACE\"\n", + "exec \"$WW_MULTI_REAL_LINKER\" \"$@\"\n")); - let bins: []str = [strings.concat(alpha, "/alpha.test"), - strings.concat(alpha, "/alpha_test.test"), - strings.concat(beta, "/beta.test"), - strings.concat(beta, "/beta_test.test"), - strings.concat(gamma, "/gamma.test")]; - let rootkeys: []str = ["alpha-internal-test", - "alpha_test-external-test", "beta-internal-test", - "beta_test-external-test", "gamma"]; - let mainkeys: []str = ["alpha-internal-test-main", - "alpha_test-external-test-main", "beta-internal-test-main", - "beta_test-external-test-main", "gamma-main"]; - let alphaartifact: str = "alpha"; - let betaartifact: str = "beta"; - let expectedtests: []str = ["alpha_same_runs ... ok\n", - "alpha_external_runs ... ok\n", "beta_same_runs ... ok\n", - "beta_external_runs ... ok\n"]; - let sharedworkroot: str = strings.concat(bins[0], ".sepwork"); - let sharedwork: str = strings.concat(sharedworkroot, "/"); - let referencebins: []str = ["", "", "", "", ""]; - let referenceroots: []str = ["", "", "", "", ""]; - let referenceout: str = ""; - let referenceerr: str = ""; - let referencecommonwwi: str = ""; - let referencecommona: str = ""; - let referencealphaa: str = ""; - let stages: []str = ["ww", "ww", "ww_ww"]; - let tags: []str = ["c1", "c2", "ww"]; + let stages: []str = ["ww", "ww_ww"]; + let compilers: []str = ["w6c", "w6c_ww"]; + let assemblers: []str = ["w6a", "w6a_ww"]; + let linkers: []str = ["w6l", "w6l_ww"]; + let works: []str = [strings.concat(root, "/c-work"), + strings.concat(root, "/ww-work")]; + let compilertraces: []str = [strings.concat(root, "/c-w6c.trace"), + strings.concat(root, "/ww-w6c.trace")]; + let assemblertraces: []str = [strings.concat(root, "/c-w6a.trace"), + strings.concat(root, "/ww-w6a.trace")]; + let linkertraces: []str = [strings.concat(root, "/c-w6l.trace"), + strings.concat(root, "/ww-w6l.trace")]; + let bins: []str = [strings.concat(root, "/alpha-product.test"), + strings.concat(root, "/beta-product.test")]; + let statuses: []str = [strings.concat(root, "/alpha.status"), + strings.concat(root, "/beta.status"), + strings.concat(root, "/gamma.status")]; + let gammabin: str = strings.concat(root, "/gamma-product.test"); + let actions: []str = ["leaf", "common", "alpha", "gamma", + "_alpha_same", "_alpha_external", "_beta_same", "_beta_external", + "test", "alpha-internal-test", "alpha_test-external-test", + "alpha-test-main", "beta-internal-test", + "beta_test-external-test", "beta-test-main"]; + let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; + let references: []str = alloc([], + (actions.len * suffixes.len): u64)!; + let ri: i32 = 0; + for (ri < actions.len * suffixes.len) { + append(references, ""); + ri += 1; + }; + let referencealpha: str = ""; + let referencebeta: str = ""; + let referencecompiler: str = ""; + let referenceassembler: str = ""; + let referencelinker: str = ""; + let baseenv: []str = os.getenvs(); let out: commandout; let si: i32 = 0; for (si < stages.len) { - let stagepath: str = driver(stages[si]); - if (si == 2) { stagepath = wwtracedriver; }; - let av: []str = [stagepath, "test", "-c", "-j", "1", - "-I", suite, "-I", root, strings.concat(suite, "/...")]; - if (si == 0) { - runcommandenv(root, "multidir-c1", av, traceenv, - (120i64 * (time.second: i64)): time.duration, &out); - } else { if (si == 2) { - runcommandenv(root, strings.concat("multidir-", tags[si]), av, - wwtraceenv, - (120i64 * (time.second: i64)): time.duration, &out); - } else { - runcommand(root, strings.concat("multidir-", tags[si]), av, - (120i64 * (time.second: i64)): time.duration, &out); - }; }; - expectexit(&out, 0); - let buildstdout: str = strings.dup(out.stdout); - let buildstderr: str = strings.dup(out.stderr); - let commonunit: str = readfile(strings.concat(sharedwork, - "common.unit.ww")); - let alphaunit: str = readfile(strings.concat(sharedwork, - alphaartifact, ".unit.ww")); - let betaunit: str = readfile(strings.concat(sharedwork, - betaartifact, ".unit.ww")); - let gammaunit: str = readfile(strings.concat(sharedwork, - "gamma.unit.ww")); - assert(has(commonunit, "//ww:module-reset common\n")); - assert(!has(commonunit, "//ww:module ")); - assert(!has(alphaunit, "//ww:module ")); - assert(has(alphaunit, "MULTIDIR_ALPHA_A")); - assert(has(alphaunit, "MULTIDIR_ALPHA_Z")); - assert(!has(betaunit, "//ww:module ")); - assert(has(betaunit, "MULTIDIR_BETA")); - assert(!has(gammaunit, "//ww:module ")); - assert(has(gammaunit, "MULTIDIR_GAMMA")); - assert(!has(gammaunit, "MULTIDIR_COMMON")); - assert(!has(commonunit, "DEPENDENCY_TEST_FILE_MUST_NOT_COMPILE")); - let roots: []str = [readfile(strings.concat(sharedwork, - rootkeys[0], ".unit.ww")), readfile(strings.concat(sharedwork, - rootkeys[1], ".unit.ww")), readfile(strings.concat(sharedwork, - rootkeys[2], ".unit.ww")), readfile(strings.concat(sharedwork, - rootkeys[3], ".unit.ww")), readfile(strings.concat(sharedwork, - rootkeys[4], ".unit.ww"))]; - assert(!has(roots[0], "//ww:module ")); - assert(has(roots[0], "MULTIDIR_ALPHA_A")); - assert(has(roots[0], "MULTIDIR_ALPHA_Z")); - assert(has(roots[0], "MULTIDIR_ALPHA_SAME")); - assert(!has(roots[0], "MULTIDIR_ALPHA_EXTERNAL")); - assert(!has(roots[1], "//ww:module ")); - assert(has(roots[1], "MULTIDIR_ALPHA_EXTERNAL")); - assert(!has(roots[1], "MULTIDIR_ALPHA_A")); - assert(!has(roots[2], "//ww:module ")); - assert(has(roots[2], "MULTIDIR_BETA")); - assert(has(roots[2], "MULTIDIR_BETA_SAME")); - assert(!has(roots[3], "//ww:module ")); - assert(has(roots[3], "MULTIDIR_BETA_EXTERNAL")); - assert(!has(roots[3], "MULTIDIR_BETA_SAME")); - assert(has(roots[4], "MULTIDIR_GAMMA")); - assert(!has(roots[4], "MULTIDIR_COMMON")); - let products: []str = ["leaf", "common", alphaartifact, betaartifact, - "gamma", "_alpha_same", "_alpha_external", "_beta_same", - "_beta_external", "test", "alpha-internal-test", - "alpha_test-external-test", "beta-internal-test", - "beta_test-external-test", "alpha-internal-test-main", - "alpha_test-external-test-main", "beta-internal-test-main", - "beta_test-external-test-main", "gamma-main"]; - let pi: i32 = 0; - for (pi < products.len) { - assert(os.exists(strings.concat(sharedwork, products[pi], - ".unit.ww"))); - assert(os.exists(strings.concat(sharedwork, products[pi], ".wwi"))); - assert(os.exists(strings.concat(sharedwork, products[pi], ".a"))); - pi += 1; - }; - let ri: i32 = 0; - for (ri < rootkeys.len) { - assert(os.exists(strings.concat(sharedwork, rootkeys[ri], ".o"))); - assert(os.exists(strings.concat(sharedwork, rootkeys[ri], ".wwi"))); - assert(os.exists(strings.concat(sharedwork, rootkeys[ri], ".a"))); - assert(os.exists(strings.concat(sharedwork, mainkeys[ri], ".unit.ww"))); - assert(os.exists(strings.concat(sharedwork, mainkeys[ri], ".wwi"))); - assert(os.exists(strings.concat(sharedwork, mainkeys[ri], ".a"))); - assert(os.exists(bins[ri])); - if (ri > 0) { - assert(!os.exists(strings.concat(bins[ri], ".sepwork"))); + assert(os.mkdir(works[si], 448i32) == 0); + writefile(compilertraces[si], ""); + writefile(assemblertraces[si], ""); + writefile(linkertraces[si], ""); + 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_MULTI_COMPILER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_MULTI_ASSEMBLER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_MULTI_LINKER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_MULTI_REAL_COMPILER=") + && !strings.hasprefix(baseenv[ei], + "WW_MULTI_REAL_ASSEMBLER=") + && !strings.hasprefix(baseenv[ei], + "WW_MULTI_REAL_LINKER=")) { + append(env, baseenv[ei]); }; - let runav: []str = [bins[ri]]; - runcommand(root, strings.concat("multidir-run-", tags[si], "-", - rootkeys[ri]), runav, - (60i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - if (ri < expectedtests.len) { - assert(has(out.stdout, expectedtests[ri])); - } else { - assert(out.stdout.len == 0 && out.stderr.len == 0); + 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_MULTI_COMPILER_TRACE=", + compilertraces[si])); + append(env, strings.concat("WW_MULTI_ASSEMBLER_TRACE=", + assemblertraces[si])); + append(env, strings.concat("WW_MULTI_LINKER_TRACE=", + linkertraces[si])); + append(env, strings.concat("WW_MULTI_REAL_COMPILER=", + driver(compilers[si]))); + append(env, strings.concat("WW_MULTI_REAL_ASSEMBLER=", + driver(assemblers[si]))); + append(env, strings.concat("WW_MULTI_REAL_LINKER=", + driver(linkers[si]))); + + let forward: []str = [driver(stages[si]), "test", "-c", "-w", + works[si], "-I", suite, "-I", root, + "--ww-package-test", "test", "alpha", "alpha", "alpha", + "alpha_test", alpha, bins[0], statuses[0], + "--ww-package-test", "test", "beta", "beta", "beta", + "beta_test", beta, bins[1], statuses[1], + "--ww-package-test", "test", "gamma", "gamma", "-", "-", + gamma, gammabin, statuses[2], alpha]; + let reverse: []str = [driver(stages[si]), "test", "-c", "-w", + works[si], "-I", suite, "-I", root, + "--ww-package-test", "test", "gamma", "gamma", "-", "-", + gamma, gammabin, statuses[2], + "--ww-package-test", "test", "beta", "beta", "beta", + "beta_test", beta, bins[1], statuses[1], + "--ww-package-test", "test", "alpha", "alpha", "alpha", + "alpha_test", alpha, bins[0], statuses[0], alpha]; + if (si == 0) { + runcommandenv(root, "multi-forward-c", forward, env, + (180i64 * (time.second: i64)): time.duration, &out); + } else { + runcommandenv(root, "multi-reverse-ww", reverse, env, + (180i64 * (time.second: i64)): time.duration, &out); + }; + expectexit(&out, 0); + assert(out.stdout.len == 0 && out.stderr.len == 0); + assert(os.exists(bins[0]) && os.exists(bins[1])); + assert(!os.exists(gammabin)); + assert(same(readfile(statuses[0]), "ok\n")); + assert(same(readfile(statuses[1]), "ok\n")); + assert(same(readfile(statuses[2]), "ok\n")); + assert(!os.exists(strings.concat(works[si], "/beta.unit.ww"))); + assert(!os.exists(strings.concat(works[si], "/gamma-main.unit.ww"))); + assert(!os.exists(strings.concat(works[si], + "/alpha-internal-test-main.unit.ww"))); + assert(!os.exists(strings.concat(works[si], + "/alpha_test-external-test-main.unit.ww"))); + + let ctrace: str = readfile(compilertraces[si]); + let atrace: str = readfile(assemblertraces[si]); + let ltrace: str = readfile(linkertraces[si]); + assert(occurrences(ctrace, "/alpha.unit.new") == 1); + assert(occurrences(ctrace, "/gamma.unit.new") == 1); + assert(occurrences(ctrace, "/beta.unit.new") == 0); + assert(occurrences(ctrace, + "/alpha-internal-test.unit.new") == 1); + assert(occurrences(ctrace, + "/alpha_test-external-test.unit.new") == 1); + assert(occurrences(ctrace, "/alpha-test-main.unit.new") == 1); + assert(occurrences(ctrace, + "/beta-internal-test.unit.new") == 1); + assert(occurrences(ctrace, + "/beta_test-external-test.unit.new") == 1); + assert(occurrences(ctrace, "/beta-test-main.unit.new") == 1); + assert(occurrences(ctrace, "--test-target-package") == 4); + let alphamain: str = linecontaining(ctrace, + "/alpha-test-main.unit.new"); + assert(occurrences(alphamain, "--test-target-package") == 2); + assert(pos(alphamain, "--test-target-package alpha ") + < pos(alphamain, "--test-target-package alpha_test ")); + let betamain: str = linecontaining(ctrace, + "/beta-test-main.unit.new"); + assert(occurrences(betamain, "--test-target-package") == 2); + assert(pos(betamain, "--test-target-package beta ") + < pos(betamain, "--test-target-package beta_test ")); + + assert(occurrences(ltrace, "\n") == 2); + let alink: str = linecontaining(ltrace, + strings.concat("-o ", bins[0], ".new ")); + assert(has(alink, "/alpha-test-main.a.new")); + assert(has(alink, "/alpha-internal-test.a.new")); + assert(has(alink, "/alpha_test-external-test.a.new")); + assert(!has(alink, strings.concat(works[si], "/alpha.a"))); + assert(!has(alink, "/beta-internal-test.a.new")); + assert(!has(alink, "/gamma.a.new")); + assert(!has(alink, ".wwi")); + let blink: str = linecontaining(ltrace, + strings.concat("-o ", bins[1], ".new ")); + assert(has(blink, "/beta-test-main.a.new")); + assert(has(blink, "/beta-internal-test.a.new")); + assert(has(blink, "/beta_test-external-test.a.new")); + assert(!has(blink, strings.concat(works[si], "/beta.a"))); + assert(has(blink, strings.concat(works[si], "/alpha.a.new"))); + assert(has(blink, strings.concat(works[si], "/gamma.a.new"))); + assert(!has(blink, "/alpha-internal-test.a.new")); + assert(!has(blink, "/alpha_test-external-test.a.new")); + assert(!has(blink, ".wwi")); + + let runav: []str = [bins[0]]; + runcommand(root, strings.concat("multi-alpha-run-", stages[si]), + runav, (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(occurrences(out.stdout, "alpha_same_runs ... ok\n") == 1); + assert(occurrences(out.stdout, "alpha_external_runs ... ok\n") == 1); + let betarunav: []str = [bins[1]]; + runcommand(root, strings.concat("multi-beta-run-", stages[si]), + betarunav, (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(occurrences(out.stdout, "beta_same_runs ... ok\n") == 1); + assert(occurrences(out.stdout, "beta_external_runs ... ok\n") == 1); + + ri = 0; + for (ri < actions.len) { + let xi: i32 = 0; + for (xi < suffixes.len) { + let bytes: str = readfile(strings.concat(works[si], "/", + actions[ri], suffixes[xi])); + let index: i32 = ri * suffixes.len + xi; + if (si == 0) { references[index] = strings.dup(bytes); } + else { assert(same(references[index], bytes)); }; + xi += 1; }; ri += 1; }; + let nc: str = normalizedtrace(ctrace, + strings.concat(works[si], "/"), bins[0]); + let na: str = normalizedtrace(atrace, + strings.concat(works[si], "/"), bins[0]); + let nl: str = normalizedtrace(ltrace, + strings.concat(works[si], "/"), bins[0]); if (si == 0) { - let ctrace: str = readfile(compilertrace); - let once: []str = ["leaf", "common", alphaartifact, betaartifact, - "gamma", - "_alpha_same", "_alpha_external", "_beta_same", - "_beta_external", "test"]; - pi = 0; - for (pi < once.len) { - assert(occurrences(ctrace, strings.concat("/", once[pi], - ".unit.ww")) == 1); - pi += 1; - }; - // alpha is both external-test production and a direct dependency of - // beta. The canonical production action above is compiled once and no - // role-qualified production action exists. - assert(occurrences(ctrace, "/alpha.unit.ww") == 1); - assert(occurrences(ctrace, "/gamma.unit.ww") == 1); - assert(occurrences(ctrace, "external-production") == 0); - ri = 0; - for (ri < rootkeys.len) { - assert(occurrences(ctrace, strings.concat(rootkeys[ri], - ".unit.ww")) == 1); - assert(occurrences(ctrace, strings.concat(mainkeys[ri], - ".unit.ww")) == 1); - ri += 1; - }; - assert(pos(ctrace, "/leaf.unit.ww") - < pos(ctrace, "/common.unit.ww")); - assert(pos(ctrace, "/common.unit.ww") - < pos(ctrace, strings.concat("/", alphaartifact, - ".unit.ww"))); - assert(pos(ctrace, "/common.unit.ww") - < pos(ctrace, strings.concat("/", betaartifact, - ".unit.ww"))); - let ltrace: str = readfile(linkertrace); - assert(occurrences(ltrace, "\n") == 5); - ri = 0; - for (ri < bins.len) { - let link: str = linecontaining(ltrace, - strings.concat("-o ", bins[ri], ".new ")); - assert(has(link, strings.concat(sharedwork, mainkeys[ri], ".a"))); - assert(has(link, strings.concat(sharedwork, rootkeys[ri], ".a"))); - assert(!has(link, ".wwi")); - assert(has(link, strings.concat(sharedwork, "common.a"))); - assert(has(link, strings.concat(sharedwork, "leaf.a"))); - assert(has(link, strings.concat(sharedwork, "test.a"))); - if (ri >= 2) { - assert(has(link, strings.concat(sharedwork, "gamma.a"))); - } else { - assert(!has(link, strings.concat(sharedwork, "gamma.a"))); - }; - if (ri == 4) { - assert(!has(link, strings.concat(sharedwork, - alphaartifact, ".a"))); - assert(!has(link, strings.concat(sharedwork, - betaartifact, ".a"))); - assert(!has(link, strings.concat(sharedwork, "_alpha_same.a"))); - assert(!has(link, strings.concat(sharedwork, - "_alpha_external.a"))); - assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); - assert(!has(link, strings.concat(sharedwork, - "_beta_external.a"))); - } else { if (ri < 2) { - assert(!has(link, strings.concat(sharedwork, - betaartifact, ".a"))); - assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); - assert(!has(link, strings.concat(sharedwork, - "_beta_external.a"))); - } else { - assert(has(link, strings.concat(sharedwork, - alphaartifact, ".a"))); - assert(!has(link, strings.concat(sharedwork, "_alpha_same.a"))); - assert(!has(link, strings.concat(sharedwork, - "_alpha_external.a"))); - }; - if (ri == 0) { - assert(has(link, strings.concat(sharedwork, "_alpha_same.a"))); - assert(!has(link, strings.concat(sharedwork, - alphaartifact, ".a"))); - assert(!has(link, strings.concat(sharedwork, - "_alpha_external.a"))); - } else { if (ri == 1) { - assert(has(link, strings.concat(sharedwork, - alphaartifact, ".a"))); - assert(has(link, strings.concat(sharedwork, - "_alpha_external.a"))); - assert(!has(link, strings.concat(sharedwork, "_alpha_same.a"))); - } else { if (ri == 2) { - assert(has(link, strings.concat(sharedwork, "_beta_same.a"))); - assert(!has(link, strings.concat(sharedwork, - betaartifact, ".a"))); - assert(!has(link, strings.concat(sharedwork, - "_beta_external.a"))); - } else { - assert(has(link, strings.concat(sharedwork, - betaartifact, ".a"))); - assert(has(link, strings.concat(sharedwork, - "_beta_external.a"))); - assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); - }; }; }; }; - ri += 1; - }; - referenceout = buildstdout; - referenceerr = buildstderr; - referencecommonwwi = strings.dup(readfile(strings.concat(sharedwork, - "common.wwi"))); - referencecommona = strings.dup(readfile(strings.concat(sharedwork, - "common.a"))); - referencealphaa = strings.dup(readfile(strings.concat(sharedwork, - alphaartifact, ".a"))); - ri = 0; - for (ri < bins.len) { - referencebins[ri] = strings.dup(readfile(bins[ri])); - referenceroots[ri] = strings.dup(roots[ri]); - ri += 1; - }; + referencealpha = readfile(bins[0]); + referencebeta = readfile(bins[1]); + referencecompiler = nc; + referenceassembler = na; + referencelinker = nl; } else { - assert(same(referenceout, buildstdout)); - assert(same(referenceerr, buildstderr)); - assert(same(referencecommonwwi, - readfile(strings.concat(sharedwork, "common.wwi")))); - assert(same(referencecommona, - readfile(strings.concat(sharedwork, "common.a")))); - assert(same(referencealphaa, - readfile(strings.concat(sharedwork, alphaartifact, ".a")))); - ri = 0; - for (ri < bins.len) { - assert(same(referencebins[ri], readfile(bins[ri]))); - assert(same(referenceroots[ri], roots[ri])); - ri += 1; - }; + assert(same(referencealpha, readfile(bins[0]))); + assert(same(referencebeta, readfile(bins[1]))); + assert(same(referencecompiler, nc)); + assert(same(referenceassembler, na)); + assert(same(referencelinker, nl)); }; - if (si == 2) { - let wwtrace: str = readfile(wwcompilertrace); - let once: []str = ["leaf", "common", alphaartifact, betaartifact, - "gamma", - "_alpha_same", "_alpha_external", "_beta_same", - "_beta_external", "test"]; - pi = 0; - for (pi < once.len) { - assert(occurrences(wwtrace, strings.concat("/", once[pi], - ".unit.ww")) == 1); - pi += 1; - }; - assert(occurrences(wwtrace, "/alpha.unit.ww") == 1); - assert(occurrences(wwtrace, "/gamma.unit.ww") == 1); - assert(occurrences(wwtrace, "external-production") == 0); - ri = 0; - for (ri < rootkeys.len) { - assert(occurrences(wwtrace, strings.concat(rootkeys[ri], - ".unit.ww")) == 1); - assert(occurrences(wwtrace, strings.concat(mainkeys[ri], - ".unit.ww")) == 1); - ri += 1; - }; - assert(occurrences(readfile(wwlinkertrace), "\n") == 5); - }; - if (si + 1 < stages.len) { - clean(sharedworkroot); - ri = 0; - for (ri < bins.len) { clean(bins[ri]); ri += 1; }; + + // Every unchanged action is warm-reused. Only the two requested + // directory binaries are linked again; gamma remains compile-only. + // The opposite descriptor order is warm in each stage, so product + // order cannot enter action identity or invalidation. + let warmalpha: str = readfile(bins[0]); + let warmbeta: str = readfile(bins[1]); + rewritefile(compilertraces[si], ""); + rewritefile(assemblertraces[si], ""); + rewritefile(linkertraces[si], ""); + if (si == 0) { + runcommandenv(root, "multi-warm-reverse-c", reverse, env, + (180i64 * (time.second: i64)): time.duration, &out); + } else { + runcommandenv(root, "multi-warm-forward-ww", forward, env, + (180i64 * (time.second: i64)): time.duration, &out); }; + expectexit(&out, 0); + assert(readfile(compilertraces[si]).len == 0); + assert(readfile(assemblertraces[si]).len == 0); + assert(occurrences(readfile(linkertraces[si]), "\n") == 2); + assert(same(warmalpha, readfile(bins[0]))); + assert(same(warmbeta, readfile(bins[1]))); si += 1; }; - clean(sharedworkroot); - let bi: i32 = 0; - for (bi < bins.len) { clean(bins[bi]); bi += 1; }; - let warmroot: str = strings.concat(root, "/warm-c"); - let warmwwroot: str = strings.concat(root, "/warm-ww"); - assert(os.mkdir(warmroot, 448i32) == 0); - assert(os.mkdir(warmwwroot, 448i32) == 0); - let warmc: []str = [driver("ww"), "test", "-j", "1", "-w", - warmroot, "-I", suite, "-I", root, - strings.concat(suite, "/...")]; - runcommandenv(root, "multidir-warm-cold", warmc, traceenv, - (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - let warmstdout: str = strings.dup(out.stdout); - let warmstderr: str = strings.dup(out.stderr); - let warmtrace: str = readfile(compilertrace); - let warmparallel: []str = [driver("ww"), "test", "-j", "4", "-w", - warmroot, "-I", suite, "-I", root, - strings.concat(suite, "/...")]; - let warmequivalentc: []str = [driver("ww"), "test", "-j", "4", "-w", - warmroot, "-I", suite, "-I", root, - strings.concat(suite, "/./...")]; - runcommandenv(root, "multidir-warm-reuse", warmequivalentc, traceenv, - (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - assert(same(warmstderr, out.stderr)); - assert(same(warmtrace, readfile(compilertrace))); - let warmkey: str = "/"; - let warmwork: str = strings.concat(warmroot, warmkey); - assert(os.exists(strings.concat(warmwork, - "alpha-internal-test.unit.ww"))); - assert(os.exists(strings.concat(warmwork, - "beta_test-external-test.unit.ww"))); - let warmww: []str = [wwtracedriver, "test", "-j", "4", "-w", - warmwwroot, "-I", suite, "-I", root, - strings.concat(suite, "/...")]; - runcommandenv(root, "multidir-warm-ww", warmww, wwtraceenv, - (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - assert(same(warmstdout, out.stdout)); - assert(same(warmstderr, out.stderr)); - let warmwwtrace: str = strings.dup(readfile(wwcompilertrace)); - let warmwwequivalent: []str = [wwtracedriver, "test", "-j", "4", "-w", - warmwwroot, "-I", suite, "-I", root, - strings.concat(suite, "/./...")]; - runcommandenv(root, "multidir-warm-ww-reuse", warmwwequivalent, wwtraceenv, - (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - assert(same(warmstderr, out.stderr)); - assert(same(warmwwtrace, readfile(wwcompilertrace))); - let warmwwwork: str = strings.concat(warmwwroot, warmkey); - assert(same(readfile(strings.concat(warmwork, "common.wwi")), - readfile(strings.concat(warmwwwork, "common.wwi")))); - assert(same(readfile(strings.concat(warmwork, "common.a")), - readfile(strings.concat(warmwwwork, "common.a")))); - assert(same(readfile(strings.concat(warmwork, - "alpha-internal-test.unit.ww")), readfile(strings.concat(warmwwwork, - "alpha-internal-test.unit.ww")))); - // Change one shared dependency's public export in place. Each stage must - // rebuild that canonical action and its one direct importer exactly once; - // common's unchanged regenerated export stops propagation to gamma/alpha. - let oldleafwwi: str = readfile(strings.concat(warmwork, "leaf.wwi")); - let oldcommonwwi: str = readfile(strings.concat(warmwork, "common.wwi")); - let oldcommona: str = readfile(strings.concat(warmwork, "common.a")); - let oldgammaa: str = readfile(strings.concat(warmwork, "gamma.a")); - assert(os.remove(strings.concat(leaf, "/leaf.ww")) == 0); - writefile(strings.concat(leaf, "/leaf.ww"), strings.concat( - "package leaf;\n// MULTIDIR_LEAF\n", - "export fn value() i32 = { return 40; };\n", - "export fn newly_visible() i32 = { return 2; };\n")); - let cbeforechange: str = strings.dup(readfile(compilertrace)); - let wbeforechange: str = strings.dup(readfile(wwcompilertrace)); - runcommandenv(root, "multidir-warm-c-export-change", warmparallel, - traceenv, (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - assert(same(warmstdout, out.stdout)); - assert(same(warmstderr, out.stderr)); - let cafterchange: str = strings.dup(readfile(compilertrace)); - assert(occurrences(cafterchange, "/leaf.unit.new") - == occurrences(cbeforechange, "/leaf.unit.new") + 1); - assert(occurrences(cafterchange, "/common.unit.new") - == occurrences(cbeforechange, "/common.unit.new") + 1); - assert(occurrences(cafterchange, "/gamma.unit.new") - == occurrences(cbeforechange, "/gamma.unit.new")); - assert(occurrences(cafterchange, "/alpha-internal-test.unit.new") - == occurrences(cbeforechange, "/alpha-internal-test.unit.new")); - assert(!same(oldleafwwi, readfile(strings.concat(warmwork, "leaf.wwi")))); - assert(same(oldcommonwwi, readfile(strings.concat(warmwork, "common.wwi")))); - assert(same(oldcommona, readfile(strings.concat(warmwork, "common.a")))); - assert(same(oldgammaa, readfile(strings.concat(warmwork, "gamma.a")))); - let changedleafunit: str = readfile(strings.concat(warmwork, - "leaf.unit.ww")); - let changedleafwwi: str = readfile(strings.concat(warmwork, "leaf.wwi")); - let changedleafa: str = readfile(strings.concat(warmwork, "leaf.a")); - let changedcommonunit: str = readfile(strings.concat(warmwork, - "common.unit.ww")); - runcommandenv(root, "multidir-warm-ww-export-change", warmww, - wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - assert(same(warmstdout, out.stdout)); - assert(same(warmstderr, out.stderr)); - let wafterchange: str = strings.dup(readfile(wwcompilertrace)); - assert(occurrences(wafterchange, "/leaf.unit.new") - == occurrences(wbeforechange, "/leaf.unit.new") + 1); - assert(occurrences(wafterchange, "/common.unit.new") - == occurrences(wbeforechange, "/common.unit.new") + 1); - assert(occurrences(wafterchange, "/gamma.unit.new") - == occurrences(wbeforechange, "/gamma.unit.new")); - assert(occurrences(wafterchange, "/alpha-internal-test.unit.new") - == occurrences(wbeforechange, "/alpha-internal-test.unit.new")); - assert(same(changedleafunit, - readfile(strings.concat(warmwwwork, "leaf.unit.ww")))); - assert(same(changedleafwwi, - readfile(strings.concat(warmwwwork, "leaf.wwi")))); - assert(same(changedleafa, - readfile(strings.concat(warmwwwork, "leaf.a")))); - assert(same(changedcommonunit, - readfile(strings.concat(warmwwwork, "common.unit.ww")))); - assert(same(readfile(strings.concat(warmwork, "common.wwi")), - readfile(strings.concat(warmwwwork, "common.wwi")))); - assert(same(readfile(strings.concat(warmwork, "common.a")), - readfile(strings.concat(warmwwwork, "common.a")))); - // A second unchanged request in either stage performs no compilation. - runcommandenv(root, "multidir-warm-c-export-reuse", warmparallel, - traceenv, (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - assert(same(cafterchange, readfile(compilertrace))); - runcommandenv(root, "multidir-warm-ww-export-reuse", warmww, - wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - assert(same(wafterchange, readfile(wwcompilertrace))); - - // Exercise the inverse discovery order explicitly. aaroot is selected and - // loaded before zimporter, which later imports it; gamma above is first - // discovered through beta and only later visited as a selected root. Both - // orders must compile one production action, and a root-only build must emit - // byte-identical owner unit, export, and archive. - let aaroot: str = strings.concat(root, "/aaroot"); - let zimporter: str = strings.concat(root, "/zimporter"); - assert(os.mkdir(aaroot, 448i32) == 0); - assert(os.mkdir(zimporter, 448i32) == 0); - writefile(strings.concat(aaroot, "/aaroot.ww"), strings.concat( - "package aaroot;\n// ROOT_FIRST_OWNER\n", - "export fn value() i32 = { return 42; };\n")); - writefile(strings.concat(zimporter, "/zimporter.ww"), strings.concat( - "package zimporter;\nimport aaroot;\n", - "export fn value() i32 = { return aaroot.value(); };\n")); - let rootrefs: []str = ["", "", ""]; - let rootstages: []str = ["ww", "ww_ww"]; - let rsi: i32 = 0; - for (rsi < rootstages.len) { - let uniondriver: str = driver(rootstages[rsi]); - if (rsi == 1) { uniondriver = wwtracedriver; }; - let rootout: str = strings.concat(root, "/root-only-", - rootstages[rsi], ".a"); - let rootav: []str = [driver(rootstages[rsi]), "build", - "-I", root, "-o", rootout, aaroot]; - runcommand(root, strings.concat("root-only-", rootstages[rsi]), rootav, - (60i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - let rootwork: str = strings.concat(rootout, ".sepwork/"); - let depbin: str = strings.concat(root, "/root-first-", - rootstages[rsi], "-dep.test"); - let userbin: str = strings.concat(root, "/root-first-", - rootstages[rsi], "-user.test"); - let unionav: []str = [uniondriver, "test", "-c", - "-I", root, - "--ww-package-test", "production", "aaroot", aaroot, depbin, - strings.concat(root, "/root-first-", rootstages[rsi], - "-dep.status"), - "--ww-package-test", "production", "zimporter", zimporter, - userbin, strings.concat(root, "/root-first-", rootstages[rsi], - "-user.status"), aaroot]; - let beforetrace: str = ""; - if (rsi == 0) { beforetrace = strings.dup(readfile(compilertrace)); } - else { beforetrace = strings.dup(readfile(wwcompilertrace)); }; - if (rsi == 0) { - runcommandenv(root, "root-first-c", unionav, traceenv, - (120i64 * (time.second: i64)): time.duration, &out); + // Public root order is presentation-only: two directories publish one + // binary each, while the selected no-test directory publishes no binary. + let publicalpha: str = strings.concat(alpha, "/alpha.test"); + let publicbeta: str = strings.concat(beta, "/beta.test"); + let publicgamma: str = strings.concat(gamma, "/gamma.test"); + let publicworks: []str = [strings.concat(publicalpha, ".sepwork"), + strings.concat(publicbeta, ".sepwork"), + strings.concat(publicgamma, ".sepwork")]; + let publicout: str = ""; + let publicerr: str = ""; + let publicalphabytes: str = ""; + let publicbetabytes: str = ""; + let publiclabels: []str = ["c-forward", "c-reverse", + "ww-forward", "ww-reverse"]; + let pi: i32 = 0; + for (pi < 4) { + let publicdriver: str = driver("ww"); + if (pi >= 2) { publicdriver = driver("ww_ww"); }; + let publicav: []str = alloc([], 16u64)!; + append(publicav, publicdriver); + append(publicav, "test"); append(publicav, "-c"); + append(publicav, "-j"); append(publicav, "1"); + append(publicav, "-I"); append(publicav, suite); + append(publicav, "-I"); append(publicav, root); + if (pi % 2 == 0) { + append(publicav, alpha); append(publicav, beta); + append(publicav, gamma); } else { - runcommandenv(root, "root-first-ww", unionav, wwtraceenv, - (120i64 * (time.second: i64)): time.duration, &out); + append(publicav, gamma); append(publicav, beta); + append(publicav, alpha); }; + runcommand(root, strings.concat("multi-public-order-", + publiclabels[pi]), publicav, + (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); - let aftertrace: str = ""; - if (rsi == 0) { aftertrace = strings.dup(readfile(compilertrace)); } - else { aftertrace = strings.dup(readfile(wwcompilertrace)); }; - assert(occurrences(aftertrace, "/aaroot.unit.ww") - == occurrences(beforetrace, "/aaroot.unit.ww") + 1); - let unionwork: str = strings.concat(depbin, ".sepwork/"); - let suffixes: []str = [".unit.ww", ".wwi", ".a"]; - let xi: i32 = 0; - for (xi < suffixes.len) { - let rootbytes: str = readfile(strings.concat(rootwork, "aaroot", - suffixes[xi])); - assert(same(rootbytes, readfile(strings.concat(unionwork, "aaroot", - suffixes[xi])))); - if (rsi == 0) { rootrefs[xi] = strings.dup(rootbytes); } - else { assert(same(rootrefs[xi], rootbytes)); }; - xi += 1; + assert(occurrences(out.stdout, "built ") == 2); + assert(has(out.stdout, strings.concat( + "? ", gamma, " [no tests]\n"))); + assert(os.exists(publicalpha) && os.exists(publicbeta)); + assert(!os.exists(publicgamma)); + assert(!os.exists(strings.concat(alpha, "/alpha_test.test"))); + assert(!os.exists(strings.concat(beta, "/beta_test.test"))); + if (pi == 0) { + publicout = strings.dup(out.stdout); + publicerr = strings.dup(out.stderr); + publicalphabytes = readfile(publicalpha); + publicbetabytes = readfile(publicbeta); + } else { + assert(same(publicout, out.stdout)); + assert(same(publicerr, out.stderr)); + assert(same(publicalphabytes, readfile(publicalpha))); + assert(same(publicbetabytes, readfile(publicbeta))); }; - rsi += 1; + let wi: i32 = 0; + for (wi < publicworks.len) { clean(publicworks[wi]); wi += 1; }; + clean(publicalpha); clean(publicbeta); clean(publicgamma); + pi += 1; }; - clean(wwtracebin); clean(root); }; @@ -2355,17 +2813,13 @@ fn hexbytes(value: str) str = { append(linkenv, strings.concat("WW_MIXED_LINK_TRACE=", linktrace)); append(linkenv, strings.concat("WW_MIXED_W6L=", driver("w6l"))); let bins: []str = [strings.concat(named, "/test.test"), - strings.concat(named, "/test_test.test"), - strings.concat(consumer, "/zconsumer.test"), - strings.concat(consumer, "/zconsumer_test.test")]; + strings.concat(consumer, "/zconsumer.test")]; let keys: []str = ["test-internal-test", "test_test-external-test", - "zconsumer-internal-test", "zconsumer_test-external-test"]; - let expected: []str = ["named_same_runs ... ok\n", - "named_external_runs ... ok\n", "consumer_same_runs ... ok\n", - "consumer_external_runs ... ok\n"]; + "test-test-main", "zconsumer-internal-test", + "zconsumer_test-external-test", "zconsumer-test-main"]; let workroot: str = strings.concat(bins[0], ".sepwork"); let work: str = strings.concat(workroot, "/"); - let referenceunits: []str = ["", "", "", ""]; + let referenceunits: []str = ["", "", "", "", "", ""]; let stages: []str = ["ww", "ww_ww"]; let out: commandout; let si: i32 = 0; @@ -2380,13 +2834,18 @@ fn hexbytes(value: str) str = { (120i64 * (time.second: i64)): time.duration, &out); }; expectexit(&out, 0); - let namedprod: str = readfile(strings.concat(work, "test.unit.ww")); + assert(occurrences(out.stdout, "built ") == 2); + let namedprod: str = readfile(strings.concat(work, + "test-internal-test.unit.ww")); let support: str = readfile(strings.concat(work, "__wwtest.unit.ww")); assert(has(namedprod, "MIXED_USER_TEST_PRODUCTION")); assert(has(namedprod, "//ww:module-reset test\n")); assert(has(support, "//ww:module-reset __wwtest\n")); let suffixes: []str = [".unit.ww", ".wwi", ".a"]; - let actions: []str = ["test", "__wwtest"]; + let actions: []str = ["__wwtest", "test-internal-test", + "test_test-external-test", "test-test-main", + "zconsumer-internal-test", "zconsumer_test-external-test", + "zconsumer-test-main"]; let ai: i32 = 0; for (ai < actions.len) { let xi: i32 = 0; @@ -2400,35 +2859,45 @@ fn hexbytes(value: str) str = { for (ri < keys.len) { let unit: str = readfile(strings.concat(work, keys[ri], ".unit.ww")); assert(!has(unit, "//ww:module ")); - let runav: []str = [bins[ri]]; - runcommand(root, strings.concat("mixed-run-", stages[si], "-", - keys[ri]), runav, - (60i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - assert(has(out.stdout, expected[ri])); if (si == 0) { referenceunits[ri] = strings.dup(unit); } else { assert(same(referenceunits[ri], unit)); }; ri += 1; }; + let namedrun: []str = [bins[0]]; + runcommand(root, strings.concat("mixed-run-", stages[si], "-named"), + namedrun, (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(has(out.stdout, "named_same_runs ... ok\n")); + assert(has(out.stdout, "named_external_runs ... ok\n")); + let consumerrun: []str = [bins[1]]; + runcommand(root, strings.concat("mixed-run-", stages[si], "-consumer"), + consumerrun, (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(has(out.stdout, "consumer_same_runs ... ok\n")); + assert(has(out.stdout, "consumer_external_runs ... ok\n")); if (si == 0) { let links: str = readfile(linktrace); - assert(occurrences(links, "\n") == 4); - let namedsame: str = linecontaining(links, + assert(occurrences(links, "\n") == 2); + let namedlink: str = linecontaining(links, strings.concat("-o ", bins[0], ".new ")); - let namedexternal: str = linecontaining(links, + let consumerlink: str = linecontaining(links, strings.concat("-o ", bins[1], ".new ")); - let consumersame: str = linecontaining(links, - strings.concat("-o ", bins[2], ".new ")); - let consumerexternal: str = linecontaining(links, - strings.concat("-o ", bins[3], ".new ")); - assert(has(namedsame, strings.concat(work, "__wwtest.a"))); - assert(!has(namedsame, strings.concat(work, "test.a"))); - assert(has(namedexternal, strings.concat(work, "test.a"))); - assert(has(namedexternal, strings.concat(work, "__wwtest.a"))); - assert(has(consumersame, strings.concat(work, "__wwtest.a"))); - assert(!has(consumersame, strings.concat(work, "test.a"))); - assert(has(consumerexternal, strings.concat(work, "__wwtest.a"))); - assert(!has(consumerexternal, strings.concat(work, "test.a"))); + assert(has(namedlink, strings.concat(work, "__wwtest.a"))); + assert(has(namedlink, strings.concat(work, + "test-test-main.a"))); + assert(has(namedlink, strings.concat(work, + "test-internal-test.a"))); + assert(has(namedlink, strings.concat(work, + "test_test-external-test.a"))); + assert(!has(namedlink, strings.concat(work, "test.a"))); + assert(has(consumerlink, strings.concat(work, "__wwtest.a"))); + assert(has(consumerlink, strings.concat(work, + "zconsumer-test-main.a"))); + assert(has(consumerlink, strings.concat(work, + "zconsumer-internal-test.a"))); + assert(has(consumerlink, strings.concat(work, + "zconsumer_test-external-test.a"))); + assert(!has(consumerlink, strings.concat(work, "test.a"))); }; if (si == 0) { clean(workroot); @@ -2888,58 +3357,43 @@ fn hexbytes(value: str) str = { let root: str = fresh(); let suite: str = strings.concat(root, "/suite"); assert(os.mkdir(suite, 448i32) == 0); - let directorycount: i32 = 52; - let aliascount: i32 = 205; - let totaldirectorycount: i32 = directorycount + aliascount; - let productcount: i32 = directorycount * 2 + aliascount; - // Each selected directory contributes production, internal, external, and - // two generated-main actions. The shared test support action and its eight - // ordinary dependencies are command-global actions too. - let directactioncount: i32 = directorycount * 5 + 9; - let coordinatoractioncount: i32 = directactioncount + aliascount * 2; + let testcount: i32 = 52; + let notestcount: i32 = 205; + let productcount: i32 = testcount + notestcount; + let actioncount: i32 = testcount * 3 + notestcount + 9; + let assemblycount: i32 = actioncount + testcount; let i: i32 = 0; - for (i < directorycount) { + for (i < productcount) { let name: str = boundarypkgname(i); let dir: str = strings.concat(suite, "/", name); assert(os.mkdir(dir, 448i32) == 0); - writefile(strings.concat(dir, "/a.ww"), strings.concat( + writefile(strings.concat(dir, "/prod.ww"), strings.concat( "package ", name, ";\n// DYNAMIC_PRODUCTION_", name, "\nexport fn value() i32 = { return 42; };\n")); - writefile(strings.concat(dir, "/same_test.ww"), strings.concat( - "package ", name, ";\n// DYNAMIC_INTERNAL_", name, - "\n@test fn same_value() void = { assert(value() == 42); };\n")); - writefile(strings.concat(dir, "/external_test.ww"), strings.concat( - "package ", name, "_test;\nimport ", name, - ";\n// DYNAMIC_EXTERNAL_", name, - "\n@test fn external_value() void = { assert(", name, - ".value() == 42); };\n")); - i += 1; - }; - // Production-only directories make the recursive coordinator request own - // 257 resolution contexts and 309 products. Each contributes a production - // action plus its generated production test-main action. - for (i < totaldirectorycount) { - let name: str = boundarypkgname(i); - let dir: str = strings.concat(suite, "/", name); - assert(os.mkdir(dir, 448i32) == 0); - writefile(strings.concat(dir, "/a.ww"), strings.concat( - "package ", name, ";\n// DYNAMIC_PRODUCTION_ONLY_", name, - "\nexport fn value() i32 = { return 42; };\n")); - i += 1; - }; - i = 0; - for (i < aliascount) { - let alias: str = strings.concat(suite, "/alias-", boundarypkgname(i)); - assert(os.symlink(strings.concat(suite, "/p000"), alias) == 0); + if (i < testcount) { + writefile(strings.concat(dir, "/same_test.ww"), strings.concat( + "package ", name, ";\n// DYNAMIC_INTERNAL_", name, + "\n@test fn same_value() void = { assert(value() == 42); };\n")); + writefile(strings.concat(dir, "/external_test.ww"), strings.concat( + "package ", name, "_test;\nimport ", name, + ";\n// DYNAMIC_EXTERNAL_", name, + "\n@test fn external_value() void = { assert(", name, + ".value() == 42); };\n")); + }; i += 1; }; let compilerwrapper: str = strings.concat(root, "/universe-w6c.sh"); + let assemblerwrapper: str = strings.concat(root, "/universe-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/universe-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> ", "\"$WW_UNIVERSE_COMPILER_TRACE\"\n", "exec \"$WW_UNIVERSE_W6C\" \"$@\"\n")); + writeexecutable(assemblerwrapper, strings.concat( + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> ", + "\"$WW_UNIVERSE_ASSEMBLER_TRACE\"\n", + "exec \"$WW_UNIVERSE_W6A\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> ", "\"$WW_UNIVERSE_LINKER_TRACE\"\n", @@ -2960,29 +3414,38 @@ fn hexbytes(value: str) str = { "done\nprintf '%s\\n' \"$count\" >> ", "\"$WW_UNIVERSE_BUILDER_TRACE\"\n", "exec \"$WW_UNIVERSE_DRIVER\" \"$@\"\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 workroots: []str = [strings.concat(root, "/universe-c-workroot"), - strings.concat(root, "/universe-ww-workroot")]; - let works: []str = [workroots[0], workroots[1]]; - let compilertraces: []str = [strings.concat(root, "/universe-c-compiler"), - strings.concat(root, "/universe-ww-compiler")]; - let linkertraces: []str = [strings.concat(root, "/universe-c-linker"), - strings.concat(root, "/universe-ww-linker")]; + let works: []str = [strings.concat(root, "/universe-c-work"), + strings.concat(root, "/universe-ww-work")]; + let compilertraces: []str = [strings.concat(root, "/universe-c-w6c"), + strings.concat(root, "/universe-ww-w6c")]; + let assemblertraces: []str = [strings.concat(root, "/universe-c-w6a"), + strings.concat(root, "/universe-ww-w6a")]; + let linkertraces: []str = [strings.concat(root, "/universe-c-w6l"), + strings.concat(root, "/universe-ww-w6l")]; let buildertraces: []str = [strings.concat(root, "/universe-c-builder"), strings.concat(root, "/universe-ww-builder")]; - let baseenv: []str = os.getenvs(); + let referencecompiler: str = ""; + let referenceassembler: str = ""; + let referencelinker: str = ""; + let referencebinary: str = ""; + let referenceartifacts: []str = ["", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", ""]; let coordinatoroutputs: []str = ["", ""]; + let baseenv: []str = os.getenvs(); let out: commandout; let si: i32 = 0; for (si < stages.len) { - assert(os.mkdir(workroots[si], 448i32) == 0); + assert(os.mkdir(works[si], 448i32) == 0); writefile(compilertraces[si], ""); + writefile(assemblertraces[si], ""); writefile(linkertraces[si], ""); writefile(buildertraces[si], ""); - let env: []str = alloc([], (baseenv.len + 14): u64)!; + let env: []str = alloc([], (baseenv.len + 17): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6C=") @@ -2990,9 +3453,12 @@ fn hexbytes(value: str) str = { && !strings.hasprefix(baseenv[ei], "WW_W6L=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_COMPILER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_UNIVERSE_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_W6C=") + && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_W6A=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_W6L=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_WWTEST=") && !strings.hasprefix(baseenv[ei], @@ -3008,15 +3474,20 @@ fn hexbytes(value: str) str = { ei += 1; }; append(env, strings.concat("WW_W6C=", compilerwrapper)); - append(env, strings.concat("WW_W6A=", driver(assemblers[si]))); + append(env, strings.concat("WW_W6A=", assemblerwrapper)); append(env, strings.concat("WW_W6L=", linkerwrapper)); append(env, strings.concat("WW_UNIVERSE_COMPILER_TRACE=", compilertraces[si])); + append(env, strings.concat("WW_UNIVERSE_ASSEMBLER_TRACE=", + assemblertraces[si])); append(env, strings.concat("WW_UNIVERSE_LINKER_TRACE=", linkertraces[si])); append(env, strings.concat("WW_UNIVERSE_W6C=", driver(compilers[si]))); - append(env, strings.concat("WW_UNIVERSE_W6L=", driver(linkers[si]))); + append(env, strings.concat("WW_UNIVERSE_W6A=", + driver(assemblers[si]))); + append(env, strings.concat("WW_UNIVERSE_W6L=", + driver(linkers[si]))); append(env, strings.concat("WW_LIB=", repo(), "/out/bin/../lib")); append(env, strings.concat("WW_SRCLIB=", repo(), "/lib")); append(env, strings.concat("WW_WWTEST=", coordinatorproxy)); @@ -3028,274 +3499,198 @@ fn hexbytes(value: str) str = { append(env, strings.concat("WW_UNIVERSE_BUILDER_TRACE=", buildertraces[si])); - let av: []str = alloc([], (8 + productcount * 6): u64)!; + let av: []str = alloc([], (8 + productcount * 9): u64)!; append(av, driver(stages[si])); append(av, "test"); append(av, "-c"); append(av, "-w"); append(av, works[si]); append(av, "-I"); append(av, suite); if (si == 0) { i = 0; - for (i < directorycount) { + for (i < productcount) { let name: str = boundarypkgname(i); let dir: str = strings.concat(suite, "/", name); - append(av, "--ww-package-test"); append(av, "same"); - append(av, name); append(av, dir); - append(av, strings.concat(root, "/c-same-", name)); - append(av, strings.concat(root, "/c-same-", name, ".status")); - append(av, "--ww-package-test"); append(av, "external"); - append(av, strings.concat(name, "_test")); append(av, dir); - append(av, strings.concat(root, "/c-external-", name)); - append(av, strings.concat(root, "/c-external-", name, - ".status")); - i += 1; - }; - i = 0; - for (i < aliascount) { - let aname: str = boundarypkgname(i); - let alias: str = strings.concat(suite, "/alias-", aname); - append(av, "--ww-package-test"); append(av, "same"); - append(av, "p000"); append(av, alias); - append(av, strings.concat(root, "/c-alias-", aname)); - append(av, strings.concat(root, "/c-alias-", aname, - ".status")); + append(av, "--ww-package-test"); append(av, "test"); + append(av, name); append(av, name); + if (i < testcount) { + append(av, name); + append(av, strings.concat(name, "_test")); + } else { + append(av, "-"); append(av, "-"); + }; + append(av, dir); + append(av, strings.concat(root, "/product-", name, ".test")); + append(av, strings.concat(root, "/product-", name, ".status")); i += 1; }; } else { - // Reverse both the product groups and variant order. The driver must - // derive the same canonical action universe independent of request order. - i = aliascount - 1; - for (i >= 0) { - let aname: str = boundarypkgname(i); - let alias: str = strings.concat(suite, "/alias-", aname); - append(av, "--ww-package-test"); append(av, "same"); - append(av, "p000"); append(av, alias); - append(av, strings.concat(root, "/ww-alias-", aname)); - append(av, strings.concat(root, "/ww-alias-", aname, - ".status")); - i -= 1; - }; - i = directorycount - 1; + i = productcount - 1; for (i >= 0) { let name: str = boundarypkgname(i); let dir: str = strings.concat(suite, "/", name); - append(av, "--ww-package-test"); append(av, "external"); - append(av, strings.concat(name, "_test")); append(av, dir); - append(av, strings.concat(root, "/ww-external-", name)); - append(av, strings.concat(root, "/ww-external-", name, - ".status")); - append(av, "--ww-package-test"); append(av, "same"); - append(av, name); append(av, dir); - append(av, strings.concat(root, "/ww-same-", name)); - append(av, strings.concat(root, "/ww-same-", name, ".status")); + append(av, "--ww-package-test"); append(av, "test"); + append(av, name); append(av, name); + if (i < testcount) { + append(av, name); + append(av, strings.concat(name, "_test")); + } else { + append(av, "-"); append(av, "-"); + }; + append(av, dir); + append(av, strings.concat(root, "/product-", name, ".test")); + append(av, strings.concat(root, "/product-", name, ".status")); i -= 1; }; }; append(av, strings.concat(suite, "/p000")); - assert(av.len == 8 + productcount * 6); + assert(av.len == 8 + productcount * 9); runcommandenv(root, strings.concat("dynamic-universe-", stages[si]), av, - env, (600i64 * (time.second: i64)): time.duration, &out); + env, (1200i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let ctrace: str = readfile(compilertraces[si]); + let atrace: str = readfile(assemblertraces[si]); let ltrace: str = readfile(linkertraces[si]); - assert(occurrences(ctrace, "\n") == directactioncount); - assert(occurrences(ltrace, "\n") == productcount); + assert(occurrences(ctrace, "\n") == actioncount); + assert(occurrences(atrace, "\n") == assemblycount); + assert(occurrences(ltrace, "\n") == testcount); assert(!has(ltrace, ".wwi")); assert(occurrences(ctrace, "/test.unit.new") == 1); i = 0; - for (i < directorycount) { + for (i < productcount) { let name: str = boundarypkgname(i); - let internal: str = strings.concat(name, "-internal-test"); - let external: str = strings.concat(name, - "_test-external-test"); - let internalmain: str = strings.concat(internal, "-main"); - let externalmain: str = strings.concat(external, "-main"); - assert(occurrences(ctrace, strings.concat("/", name, - ".unit.new")) == 1); - assert(occurrences(ctrace, strings.concat("/", internal, - ".unit.new")) == 1); - assert(occurrences(ctrace, strings.concat("/", external, - ".unit.new")) == 1); - assert(occurrences(ctrace, strings.concat("/", internalmain, - ".unit.new")) == 1); - assert(occurrences(ctrace, strings.concat("/", externalmain, - ".unit.new")) == 1); - let actions: []str = [name, internal, external, internalmain, - externalmain]; - let ai: i32 = 0; - for (ai < actions.len) { - assert(os.exists(strings.concat(works[si], "/", actions[ai], - ".unit.ww"))); - assert(os.exists(strings.concat(works[si], "/", actions[ai], - ".wwi"))); - assert(os.exists(strings.concat(works[si], "/", actions[ai], - ".s"))); - assert(os.exists(strings.concat(works[si], "/", actions[ai], - ".o"))); - assert(os.exists(strings.concat(works[si], "/", actions[ai], - ".a"))); - ai += 1; + if (i < testcount) { + assert(occurrences(ctrace, strings.concat("/", name, + "-internal-test.unit.new")) == 1); + assert(occurrences(ctrace, strings.concat("/", name, + "_test-external-test.unit.new")) == 1); + assert(occurrences(ctrace, strings.concat("/", name, + "-test-main.unit.new")) == 1); + assert(occurrences(ctrace, strings.concat("/", name, + ".unit.new")) == 0); + assert(os.exists(strings.concat(root, "/product-", name, + ".test"))); + } else { + assert(occurrences(ctrace, strings.concat("/", name, + ".unit.new")) == 1); + assert(occurrences(ctrace, strings.concat("/", name, + "-test-main.unit.new")) == 0); + assert(!os.exists(strings.concat(root, "/product-", name, + ".test"))); }; - let prefix: str = "c"; - if (si == 1) { prefix = "ww"; }; - assert(os.exists(strings.concat(root, "/", prefix, "-same-", name))); - assert(os.exists(strings.concat(root, "/", prefix, "-external-", - name))); + assert(same(readfile(strings.concat(root, "/product-", name, + ".status")), "ok\n")); i += 1; }; - i = 0; - for (i < aliascount) { - let name: str = boundarypkgname(i); - let prefix: str = "c"; - if (si == 1) { prefix = "ww"; }; - assert(os.exists(strings.concat(root, "/", prefix, "-alias-", name))); - i += 1; + let samplemain: str = linecontaining(ctrace, + "/p051-test-main.unit.new"); + assert(has(samplemain, + "--test-target-package p051 --test-target-package p051_test")); + let sampleexternal: str = linecontaining(ctrace, + "/p051_test-external-test.unit.new"); + assert(has(sampleexternal, + strings.concat("--import p051 ", works[si], + "/p051-internal-test.wwi.new"))); + let samplelink: str = linecontaining(ltrace, + strings.concat("-o ", root, "/product-p051.test.new ")); + assert(has(samplelink, "/p051-test-main.a.new")); + assert(has(samplelink, "/p051-internal-test.a.new")); + assert(has(samplelink, "/p051_test-external-test.a.new")); + assert(!has(samplelink, strings.concat(works[si], "/p051.a"))); + assert(!has(samplelink, ".wwi")); + let runav: []str = [strings.concat(root, "/product-p051.test")]; + runcommand(root, strings.concat("dynamic-run-", stages[si]), runav, + (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(has(out.stdout, "same_value ... ok\n")); + assert(has(out.stdout, "external_value ... ok\n")); + + let samples: []str = ["p051-internal-test", + "p051_test-external-test", "p051-test-main", "p256"]; + let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; + let sri: i32 = 0; + for (sri < samples.len) { + let xi: i32 = 0; + for (xi < suffixes.len) { + let bytes: str = readfile(strings.concat(works[si], "/", + samples[sri], suffixes[xi])); + let index: i32 = sri * suffixes.len + xi; + if (si == 0) { + referenceartifacts[index] = strings.dup(bytes); + } else { + assert(same(referenceartifacts[index], bytes)); + }; + xi += 1; + }; + sri += 1; }; - let prefix: str = "c"; - if (si == 1) { prefix = "ww"; }; - let samples: []str = [strings.concat(root, "/", prefix, "-same-p051"), - strings.concat(root, "/", prefix, "-external-p051"), - strings.concat(root, "/", prefix, "-alias-p204")]; - i = 0; - for (i < samples.len) { - let runav: []str = [samples[i]]; - runcommand(root, strings.concat("dynamic-run-", stages[si], "-", - boundarypkgname(i)), runav, - (60i64 * (time.second: i64)): time.duration, &out); - expectexit(&out, 0); - assert(has(out.stdout, "_value ... ok\n")); - i += 1; + let impossible: str = strings.concat(root, "/not-an-output"); + let nc: str = normalizedtrace(ctrace, + strings.concat(works[si], "/"), impossible); + let na: str = normalizedtrace(atrace, + strings.concat(works[si], "/"), impossible); + let nl: str = normalizedtrace(ltrace, + strings.concat(works[si], "/"), impossible); + if (si == 0) { + referencecompiler = nc; + referenceassembler = na; + referencelinker = nl; + referencebinary = readfile(strings.concat(root, + "/product-p051.test")); + } else { + assert(same(referencecompiler, nc)); + assert(same(referenceassembler, na)); + assert(same(referencelinker, nl)); + assert(same(referencebinary, readfile(strings.concat(root, + "/product-p051.test")))); }; - // Exercise the public coordinator path with the same persistent package - // universe. It must construct and pass all 309 descriptors in one argv, - // retain the 270 existing actions, and add exactly the 410 actions owned by - // the 205 production-only directories. - let cbefore: i32 = readfile(compilertraces[si]).len; - let lbefore: i32 = readfile(linkertraces[si]).len; - let recursive: str = strings.concat(suite, "/..."); + // The complete >256 action universe is reused on an unchanged request. + rewritefile(compilertraces[si], ""); + rewritefile(assemblertraces[si], ""); + rewritefile(linkertraces[si], ""); + runcommandenv(root, strings.concat("dynamic-warm-", stages[si]), av, + env, (1200i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(readfile(compilertraces[si]).len == 0); + assert(readfile(assemblertraces[si]).len == 0); + assert(occurrences(readfile(linkertraces[si]), "\n") == testcount); + + // The public recursive coordinator emits the same 257 canonical + // directory products in one delegated request, runs 52 binaries, and + // takes the no-real-run path for all remaining directories. + rewritefile(compilertraces[si], ""); + rewritefile(assemblertraces[si], ""); + rewritefile(linkertraces[si], ""); let coordinatorav: []str = [driver(stages[si]), "test", "-j", "1", - "-w", workroots[si], "-I", suite, recursive]; + "-w", works[si], "-I", suite, strings.concat(suite, "/...")]; runcommandenv(root, strings.concat("dynamic-coordinator-", stages[si]), coordinatorav, env, (1200i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); coordinatoroutputs[si] = strings.dup(out.stdout); - let cafter: str = readfile(compilertraces[si]); - let lafter: str = readfile(linkertraces[si]); - assert(occurrences(cafter, "\n") == coordinatoractioncount); - assert(occurrences(strings.sub(cafter, cbefore, cafter.len), "\n") - == aliascount * 2); - assert(occurrences(strings.sub(lafter, lbefore, lafter.len), "\n") - == productcount); - assert(!has(strings.sub(lafter, lbefore, lafter.len), ".wwi")); - assert(same(readfile(buildertraces[si]), "309\n")); - assert(occurrences(out.stdout, "ok ") == directorycount * 2); - assert(occurrences(out.stdout, "? ") == aliascount); - i = 0; - for (i < directorycount) { - let name: str = boundarypkgname(i); - assert(has(out.stdout, strings.concat("ok ", suite, "/", name, - " [", name, ", same-package]\n"))); - assert(has(out.stdout, strings.concat("ok ", suite, "/", name, - " [", name, "_test, external]\n"))); - i += 1; - }; - i = directorycount; - for (i < totaldirectorycount) { - let name: str = boundarypkgname(i); - assert(has(out.stdout, strings.concat("? ", suite, "/", name, - " [no tests]\n"))); - i += 1; - }; + assert(readfile(compilertraces[si]).len == 0); + assert(readfile(assemblertraces[si]).len == 0); + assert(occurrences(readfile(linkertraces[si]), "\n") == testcount); + assert(same(readfile(buildertraces[si]), "257\n")); + assert(occurrences(out.stdout, "ok ") == testcount); + assert(occurrences(out.stdout, "? ") == notestcount); + assert(has(out.stdout, strings.concat("ok ", suite, + "/p051 [p051]\n"))); + assert(has(out.stdout, strings.concat("? ", suite, + "/p256 [no tests]\n"))); - // Repeating the public request may relink products, but every canonical - // action in the >256 universe must be warm. - let warmcompilerlen: i32 = cafter.len; - runcommandenv(root, strings.concat("dynamic-coordinator-warm-", stages[si]), - coordinatorav, env, + let beforecompiler: i32 = readfile(compilertraces[si]).len; + runcommandenv(root, strings.concat("dynamic-coordinator-warm-", + stages[si]), coordinatorav, env, (1200i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); - assert(out.stderr.len == 0); - assert(same(out.stdout, coordinatoroutputs[si])); - assert(readfile(compilertraces[si]).len == warmcompilerlen); - assert(same(readfile(buildertraces[si]), "309\n309\n")); + assert(same(coordinatoroutputs[si], out.stdout)); + assert(readfile(compilertraces[si]).len == beforecompiler); + assert(same(readfile(buildertraces[si]), "257\n257\n")); si += 1; }; assert(same(coordinatoroutputs[0], coordinatoroutputs[1])); - - let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; - i = 0; - for (i < directorycount) { - let name: str = boundarypkgname(i); - let actions: []str = [name, strings.concat(name, "-internal-test"), - strings.concat(name, "_test-external-test"), - strings.concat(name, "-internal-test-main"), - strings.concat(name, "_test-external-test-main")]; - let ai: i32 = 0; - for (ai < actions.len) { - let xi: i32 = 0; - for (xi < suffixes.len) { - assert(same(readfile(strings.concat(works[0], "/", actions[ai], - suffixes[xi])), readfile(strings.concat(works[1], "/", - actions[ai], suffixes[xi])))); - xi += 1; - }; - ai += 1; - }; - i += 1; - }; - i = directorycount; - for (i < totaldirectorycount) { - let name: str = boundarypkgname(i); - let actions: []str = [name, strings.concat(name, "-main")]; - let ai: i32 = 0; - for (ai < actions.len) { - let xi: i32 = 0; - for (xi < suffixes.len) { - assert(same(readfile(strings.concat(works[0], "/", actions[ai], - suffixes[xi])), readfile(strings.concat(works[1], "/", - actions[ai], suffixes[xi])))); - xi += 1; - }; - ai += 1; - }; - i += 1; - }; - let supportactions: []str = ["ascii", "bytes", "encoding.utf8", - "fnmatch", "os", "strings", "test", "time", "types"]; - let ai: i32 = 0; - for (ai < supportactions.len) { - i = 0; - for (i < suffixes.len) { - assert(same(readfile(strings.concat(works[0], "/", - supportactions[ai], suffixes[i])), readfile(strings.concat(works[1], - "/", supportactions[ai], suffixes[i])))); - i += 1; - }; - ai += 1; - }; - assert(same(readfile(strings.concat(root, "/c-same-p051")), - readfile(strings.concat(root, "/ww-same-p051")))); - assert(same(readfile(strings.concat(root, "/c-external-p051")), - readfile(strings.concat(root, "/ww-external-p051")))); - assert(same(readfile(strings.concat(root, "/c-alias-p204")), - readfile(strings.concat(root, "/ww-alias-p204")))); - let internalunit: str = readfile(strings.concat(works[0], - "/p051-internal-test.unit.ww")); - let externalunit: str = readfile(strings.concat(works[0], - "/p051_test-external-test.unit.ww")); - let productionunit: str = readfile(strings.concat(works[0], - "/p051.unit.ww")); - assert(has(internalunit, "DYNAMIC_PRODUCTION_p051")); - assert(has(internalunit, "DYNAMIC_INTERNAL_p051")); - assert(!has(internalunit, "DYNAMIC_EXTERNAL_p051")); - assert(has(externalunit, "DYNAMIC_EXTERNAL_p051")); - assert(!has(externalunit, "DYNAMIC_PRODUCTION_p051")); - assert(has(productionunit, "DYNAMIC_PRODUCTION_p051")); - assert(!has(productionunit, "DYNAMIC_INTERNAL_p051")); - assert(!has(productionunit, "DYNAMIC_EXTERNAL_p051")); clean(root); }; @@ -3347,12 +3742,12 @@ fn hexbytes(value: str) str = { assert(has(out.stderr, "has no exported declaration 'hidden'")); assert(has(out.stderr, strings.concat("FAIL ", alpha, " [alpha] (build exit 1)\n"))); - assert(has(out.stderr, strings.concat("FAIL ", alpha, - " [alpha_test] (build exit 1)\n"))); assert(has(out.stderr, strings.concat("FAIL ", beta, " [beta] (build exit 1)\n"))); - assert(has(out.stderr, strings.concat("FAIL ", beta, - " [beta_test] (build exit 1)\n"))); + assert(occurrences(out.stderr, strings.concat("FAIL ", alpha)) == 1); + assert(occurrences(out.stderr, strings.concat("FAIL ", beta)) == 1); + assert(!has(out.stderr, "[alpha_test]")); + assert(!has(out.stderr, "[beta_test]")); let alphapos: i32 = pos(out.stderr, strings.concat("FAIL ", alpha, " [alpha] (build exit 1)\n")); let betapos: i32 = pos(out.stderr, strings.concat("FAIL ", beta, @@ -3415,9 +3810,9 @@ fn hexbytes(value: str) str = { assert(has(out.stderr, strings.concat("FAIL ", badexternal, " [badexternal] (build exit 1)\n"))); - assert(has(out.stderr, - strings.concat("FAIL ", badexternal, - " [badexternal_test] (build exit 1)\n"))); + assert(occurrences(out.stderr, + strings.concat("FAIL ", badexternal)) == 1); + assert(!has(out.stderr, "[badexternal_test]")); externaldiagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); let sameav: []str = [driver(stages[i]), "test", "-I", root, @@ -3429,8 +3824,8 @@ fn hexbytes(value: str) str = { assert(has(out.stderr, "undefined: missing")); assert(has(out.stderr, strings.concat("FAIL ", badsame, " [badsame] (build exit 1)\n"))); - assert(has(out.stderr, strings.concat("FAIL ", badsame, - " [badsame_test] (build exit 1)\n"))); + assert(occurrences(out.stderr, strings.concat("FAIL ", badsame)) == 1); + assert(!has(out.stderr, "[badsame_test]")); samediagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); i += 1; }; @@ -3542,6 +3937,367 @@ fn hexbytes(value: str) str = { "/badexternal_test-external-test.unit.ww"))); assert(!os.exists(strings.concat(wwwarmroot, "/.wwtool.stamp"))); clean(wwtracebin); + + // Late failures are request-wide transactions. Start from a committed + // combined product, stage a changed dependency first, and then fail each + // family action or the linker twice. Every retry must reconsider the same + // work and the committed generation must remain byte-identical. + let latedep: str = strings.concat(root, "/late_dep"); + let latebridge: str = strings.concat(root, "/late_bridge"); + let late: str = strings.concat(root, "/late"); + assert(os.mkdir(latedep, 448i32) == 0); + assert(os.mkdir(latebridge, 448i32) == 0); + assert(os.mkdir(late, 448i32) == 0); + let depfile: str = strings.concat(latedep, "/dep.ww"); + let latefile: str = strings.concat(late, "/late.ww"); + let latebridgefile: str = strings.concat(latebridge, "/bridge.ww"); + let latesamefile: str = strings.concat(late, "/same_test.ww"); + let lateexternalfile: str = strings.concat(late, "/external_test.ww"); + let depbase: str = strings.concat("package late_dep;\n", + "export fn value() i32 = { return 40; };\n"); + let depchanged: str = strings.concat("package late_dep;\n", + "export fn value() i32 = { return 39 + 1; };\n"); + let samebase: str = strings.concat("package late;\n", + "export fn internal_helper() i32 = { return 41; };\n", + "@test fn internal_base() void = { assert(value() == 41); };\n"); + let samechanged: str = strings.concat("package late;\n", + "export fn internal_helper() i32 = { return 40 + 1; };\n", + "@test fn internal_base() void = { assert(value() == 41); };\n"); + let externalbase: str = strings.concat( + "package late_test;\nimport late;\nimport late_bridge;\n", + "@test fn external_base() void = {\n", + " assert(late.internal_helper() == 41);\n", + " assert(late_bridge.helper() == 41);\n};\n"); + let externalchanged: str = strings.concat( + "package late_test;\nimport late;\nimport late_bridge;\n", + "@test fn external_base() void = {\n", + " assert(late.internal_helper() >= 41);\n", + " assert(late_bridge.helper() >= 41);\n};\n"); + let externalmainchanged: str = strings.concat( + "package late_test;\nimport late;\nimport late_bridge;\n", + "@test fn external_renamed() void = {\n", + " assert(late.internal_helper() == 41);\n", + " assert(late_bridge.helper() == 41);\n};\n"); + writefile(depfile, depbase); + writefile(latefile, strings.concat("package late;\nimport late_dep;\n", + "export fn value() i32 = { return late_dep.value() + 1; };\n")); + writefile(latebridgefile, strings.concat( + "package late_bridge;\nimport late;\n", + "export fn helper() i32 = { return late.internal_helper(); };\n")); + writefile(latesamefile, samebase); + writefile(lateexternalfile, externalbase); + let latecompilerwrapper: str = strings.concat(root, "/late-w6c.sh"); + let lateassemblerwrapper: str = strings.concat(root, "/late-w6a.sh"); + let latelinkerwrapper: str = strings.concat(root, "/late-w6l.sh"); + writeexecutable(latecompilerwrapper, strings.concat( + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_LATE_COMPILER_TRACE\"\n", + "if test -n \"$WW_LATE_FAIL_FRAGMENT\"; then\n", + " for arg do case \"$arg\" in *\"$WW_LATE_FAIL_FRAGMENT\"*)\n", + " printf 'injected w6c failure: %s\\n' ", + "\"$WW_LATE_FAIL_FRAGMENT\" >&2\n exit 97;; esac; done\nfi\n", + "exec \"$WW_LATE_REAL_COMPILER\" \"$@\"\n")); + writeexecutable(lateassemblerwrapper, strings.concat( + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_LATE_ASSEMBLER_TRACE\"\n", + "exec \"$WW_LATE_REAL_ASSEMBLER\" \"$@\"\n")); + writeexecutable(latelinkerwrapper, strings.concat( + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_LATE_LINKER_TRACE\"\n", + "if test \"$WW_LATE_FAIL_LINK\" = 1; then\n", + " printf 'injected w6l failure\\n' >&2\n exit 97\nfi\n", + "exec \"$WW_LATE_REAL_LINKER\" \"$@\"\n")); + let lateworks: []str = [strings.concat(root, "/late-c-work"), + strings.concat(root, "/late-ww-work")]; + let latebins: []str = [strings.concat(root, "/late-c.test"), + strings.concat(root, "/late-ww.test")]; + let latestatuses: []str = [strings.concat(root, "/late-c.status"), + strings.concat(root, "/late-ww.status")]; + let latecompilers: []str = ["w6c", "w6c_ww"]; + let lateassemblers: []str = ["w6a", "w6a_ww"]; + let latelinkers: []str = ["w6l", "w6l_ww"]; + let latebridgeowner: str = strings.concat("late#", late); + let latebridgeaction: str = packagestoragekeyfor( + "late_bridge", latebridge, 4, 0, latebridgeowner); + let laterelative: []str = [".wwtool.ww", ".wwtool.w6c", ".wwtool.w6a", + ".wwtool.stamp", "late_dep.unit.ww", "late_dep.wwi", "late_dep.s", + "late_dep.o", "late_dep.a", + strings.concat(latebridgeaction, ".unit.ww"), + strings.concat(latebridgeaction, ".wwi"), + strings.concat(latebridgeaction, ".s"), + strings.concat(latebridgeaction, ".o"), + strings.concat(latebridgeaction, ".a"), + "late-internal-test.unit.ww", "late-internal-test.wwi", + "late-internal-test.s", "late-internal-test.o", "late-internal-test.a", + "late_test-external-test.unit.ww", "late_test-external-test.wwi", + "late_test-external-test.s", "late_test-external-test.o", + "late_test-external-test.a", "late-test-main.unit.ww", + "late-test-main.wwi", "late-test-main.s", "late-test-main.o", + "late-test-main.a", "late-test-main.init.unit.ww", + "late-test-main.init.s", "late-test-main.init.o", "test.unit.ww", + "test.wwi", "test.s", "test.o", "test.a"]; + let latesnapshots: []str = alloc([], + (stages.len * laterelative.len): u64)!; + let snapshoti: i32 = 0; + for (snapshoti < stages.len * laterelative.len) { + append(latesnapshots, ""); snapshoti += 1; + }; + let latebinrefs: []str = ["", ""]; + let latestatusrefs: []str = ["", ""]; + let lateseedbaseenv: []str = os.getenvs(); + i = 0; + for (i < stages.len) { + assert(os.mkdir(lateworks[i], 448i32) == 0); + let seedctrace: str = strings.concat(root, "/late-seed-", tags[i], + "-w6c.trace"); + let seedatrace: str = strings.concat(root, "/late-seed-", tags[i], + "-w6a.trace"); + let seedltrace: str = strings.concat(root, "/late-seed-", tags[i], + "-w6l.trace"); + writefile(seedctrace, ""); writefile(seedatrace, ""); + writefile(seedltrace, ""); + let seedenv: []str = alloc([], (lateseedbaseenv.len + 11): u64)!; + ei = 0; + for (ei < lateseedbaseenv.len) { + if (!strings.hasprefix(lateseedbaseenv[ei], "WW_W6C=") + && !strings.hasprefix(lateseedbaseenv[ei], "WW_W6A=") + && !strings.hasprefix(lateseedbaseenv[ei], "WW_W6L=") + && !strings.hasprefix(lateseedbaseenv[ei], + "WW_LATE_COMPILER_TRACE=") + && !strings.hasprefix(lateseedbaseenv[ei], + "WW_LATE_ASSEMBLER_TRACE=") + && !strings.hasprefix(lateseedbaseenv[ei], + "WW_LATE_LINKER_TRACE=") + && !strings.hasprefix(lateseedbaseenv[ei], + "WW_LATE_REAL_COMPILER=") + && !strings.hasprefix(lateseedbaseenv[ei], + "WW_LATE_REAL_ASSEMBLER=") + && !strings.hasprefix(lateseedbaseenv[ei], + "WW_LATE_REAL_LINKER=") + && !strings.hasprefix(lateseedbaseenv[ei], + "WW_LATE_FAIL_FRAGMENT=") + && !strings.hasprefix(lateseedbaseenv[ei], + "WW_LATE_FAIL_LINK=")) { + append(seedenv, lateseedbaseenv[ei]); + }; + ei += 1; + }; + append(seedenv, strings.concat("WW_W6C=", latecompilerwrapper)); + append(seedenv, strings.concat("WW_W6A=", lateassemblerwrapper)); + append(seedenv, strings.concat("WW_W6L=", latelinkerwrapper)); + append(seedenv, strings.concat("WW_LATE_COMPILER_TRACE=", seedctrace)); + append(seedenv, strings.concat("WW_LATE_ASSEMBLER_TRACE=", seedatrace)); + append(seedenv, strings.concat("WW_LATE_LINKER_TRACE=", seedltrace)); + append(seedenv, strings.concat("WW_LATE_REAL_COMPILER=", + driver(latecompilers[i]))); + append(seedenv, strings.concat("WW_LATE_REAL_ASSEMBLER=", + driver(lateassemblers[i]))); + append(seedenv, strings.concat("WW_LATE_REAL_LINKER=", + driver(latelinkers[i]))); + append(seedenv, "WW_LATE_FAIL_FRAGMENT="); + append(seedenv, "WW_LATE_FAIL_LINK=0"); + let seedav: []str = [driver(stages[i]), "test", "-c", + "-w", lateworks[i], "-I", root, "--ww-package-test", "test", + "late", "late", "late", "late_test", late, latebins[i], + latestatuses[i], late]; + runcommandenv(root, strings.concat("late-seed-", tags[i]), seedav, + seedenv, (180i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(out.stdout.len == 0 && out.stderr.len == 0); + assert(same(readfile(latestatuses[i]), "ok\n")); + let runav: []str = [latebins[i]]; + runcommand(root, strings.concat("late-seed-run-", tags[i]), runav, + (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(occurrences(out.stdout, "internal_base ... ok\n") == 1); + assert(occurrences(out.stdout, "external_base ... ok\n") == 1); + latebinrefs[i] = readfile(latebins[i]); + latestatusrefs[i] = readfile(latestatuses[i]); + snapshoti = 0; + for (snapshoti < laterelative.len) { + latesnapshots[i * laterelative.len + snapshoti] = readfile( + strings.concat(lateworks[i], "/", laterelative[snapshoti])); + snapshoti += 1; + }; + i += 1; + }; + assert(same(latebinrefs[0], latebinrefs[1])); + let phaselabels: []str = ["internal", "external", "main", "link"]; + let phasefragments: []str = ["/late-internal-test.unit.new", + "/late_test-external-test.unit.new", "/late-test-main.unit.new", ""]; + let phaseowners: []str = ["late", "late_test", + "__wwtestmain.late.main", ""]; + let phasecompiles: []i32 = [2, 2, 3, 2]; + let phaseassembles: []i32 = [1, 1, 2, 2]; + let phaselinks: []i32 = [0, 0, 0, 1]; + let phasectrefs: []str = ["", "", "", ""]; + let phaseatrefs: []str = ["", "", "", ""]; + let phaseltrefs: []str = ["", "", "", ""]; + let phasediagnosticrefs: []str = ["", "", "", ""]; + let lateactions: []str = ["late_dep", latebridgeaction, + "late-internal-test", "late_test-external-test", "late-test-main", + "test"]; + let latestagesuffixes: []str = [".unit.new", ".wwi.new", ".s.new", + ".o.new", ".a.new", ".init.unit.new", ".init.s.new", + ".init.o.new"]; + let latebaseenv: []str = os.getenvs(); + let phasei: i32 = 0; + for (phasei < phaselabels.len) { + rewritefile(depfile, depchanged); + if (phasei == 0 || phasei == 3) { + rewritefile(latesamefile, samechanged); + } else { + rewritefile(latesamefile, samebase); + }; + if (phasei == 1) { + rewritefile(lateexternalfile, externalchanged); + } else { if (phasei == 2) { + rewritefile(lateexternalfile, externalmainchanged); + } else { + rewritefile(lateexternalfile, externalbase); + }; }; + i = 0; + for (i < stages.len) { + let retry: i32 = 0; + for (retry < 2) { + let retrylabel: str = "first"; + if (retry != 0) { retrylabel = "second"; }; + let ctracepath: str = strings.concat(root, "/late-", + phaselabels[phasei], "-", tags[i], "-", retrylabel, + "-w6c.trace"); + let atracepath: str = strings.concat(root, "/late-", + phaselabels[phasei], "-", tags[i], "-", retrylabel, + "-w6a.trace"); + let ltracepath: str = strings.concat(root, "/late-", + phaselabels[phasei], "-", tags[i], "-", retrylabel, + "-w6l.trace"); + writefile(ctracepath, ""); writefile(atracepath, ""); + writefile(ltracepath, ""); + let attemptbin: str = strings.concat(root, "/late-attempt-", + phaselabels[phasei], "-", tags[i], "-", retrylabel, + ".test"); + let attemptstatus: str = strings.concat(root, "/late-attempt-", + phaselabels[phasei], "-", tags[i], "-", retrylabel, + ".status"); + let env: []str = alloc([], (latebaseenv.len + 11): u64)!; + ei = 0; + for (ei < latebaseenv.len) { + if (!strings.hasprefix(latebaseenv[ei], "WW_W6C=") + && !strings.hasprefix(latebaseenv[ei], "WW_W6A=") + && !strings.hasprefix(latebaseenv[ei], "WW_W6L=") + && !strings.hasprefix(latebaseenv[ei], + "WW_LATE_COMPILER_TRACE=") + && !strings.hasprefix(latebaseenv[ei], + "WW_LATE_ASSEMBLER_TRACE=") + && !strings.hasprefix(latebaseenv[ei], + "WW_LATE_LINKER_TRACE=") + && !strings.hasprefix(latebaseenv[ei], + "WW_LATE_REAL_COMPILER=") + && !strings.hasprefix(latebaseenv[ei], + "WW_LATE_REAL_ASSEMBLER=") + && !strings.hasprefix(latebaseenv[ei], + "WW_LATE_REAL_LINKER=") + && !strings.hasprefix(latebaseenv[ei], + "WW_LATE_FAIL_FRAGMENT=") + && !strings.hasprefix(latebaseenv[ei], + "WW_LATE_FAIL_LINK=")) { + append(env, latebaseenv[ei]); + }; + ei += 1; + }; + append(env, strings.concat("WW_W6C=", latecompilerwrapper)); + append(env, strings.concat("WW_W6A=", lateassemblerwrapper)); + append(env, strings.concat("WW_W6L=", latelinkerwrapper)); + append(env, strings.concat("WW_LATE_COMPILER_TRACE=", ctracepath)); + append(env, strings.concat("WW_LATE_ASSEMBLER_TRACE=", atracepath)); + append(env, strings.concat("WW_LATE_LINKER_TRACE=", ltracepath)); + append(env, strings.concat("WW_LATE_REAL_COMPILER=", + driver(latecompilers[i]))); + append(env, strings.concat("WW_LATE_REAL_ASSEMBLER=", + driver(lateassemblers[i]))); + append(env, strings.concat("WW_LATE_REAL_LINKER=", + driver(latelinkers[i]))); + append(env, strings.concat("WW_LATE_FAIL_FRAGMENT=", + phasefragments[phasei])); + let faillink: str = "0"; + if (phasei == 3) { faillink = "1"; }; + append(env, strings.concat("WW_LATE_FAIL_LINK=", faillink)); + let failav: []str = [driver(stages[i]), "test", "-c", + "-w", lateworks[i], "-I", root, "--ww-package-test", "test", + "late", "late", "late", "late_test", late, attemptbin, + attemptstatus, late]; + runcommandenv(root, strings.concat("late-failure-", + phaselabels[phasei], "-", tags[i], "-", retrylabel), failav, + env, (180i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 1); + assert(out.stdout.len == 0); + if (phasei == 3) { + assert(has(out.stderr, "injected w6l failure\n")); + assert(has(out.stderr, "ww: w6l failed\n")); + } else { + assert(has(out.stderr, strings.concat( + "injected w6c failure: ", phasefragments[phasei], "\n"))); + assert(has(out.stderr, strings.concat( + "ww: w6c failed for ", phaseowners[phasei], "\n"))); + }; + assert(!os.exists(attemptbin)); + assert(!os.exists(strings.concat(attemptbin, ".new"))); + assert(!os.exists(strings.concat(attemptbin, ".result"))); + assert(!os.exists(strings.concat(attemptbin, ".result.new"))); + assert(!os.exists(attemptstatus)); + assert(!os.exists(strings.concat(attemptstatus, ".new"))); + let ct: str = readfile(ctracepath); + let at: str = readfile(atracepath); + let lt: str = readfile(ltracepath); + assert(occurrences(ct, "\n") == phasecompiles[phasei]); + assert(occurrences(at, "\n") == phaseassembles[phasei]); + assert(occurrences(lt, "\n") == phaselinks[phasei]); + assert(pos(ct, "/late_dep.unit.new") >= 0); + if (phasei != 3) { + assert(pos(ct, "/late_dep.unit.new") + < pos(ct, phasefragments[phasei])); + }; + assert(!directoryhasnew(lateworks[i])); + let actioni: i32 = 0; + for (actioni < lateactions.len) { + let suffixi: i32 = 0; + for (suffixi < latestagesuffixes.len) { + assert(!os.exists(strings.concat(lateworks[i], "/", + lateactions[actioni], latestagesuffixes[suffixi]))); + suffixi += 1; + }; + actioni += 1; + }; + snapshoti = 0; + for (snapshoti < laterelative.len) { + assert(same(latesnapshots[i * laterelative.len + snapshoti], + readfile(strings.concat(lateworks[i], "/", + laterelative[snapshoti])))); + snapshoti += 1; + }; + assert(same(latebinrefs[i], readfile(latebins[i]))); + assert(same(latestatusrefs[i], readfile(latestatuses[i]))); + let nc: str = normalizedtrace(ct, + strings.concat(lateworks[i], "/"), attemptbin); + let na: str = normalizedtrace(at, + strings.concat(lateworks[i], "/"), attemptbin); + let nl: str = normalizedtrace(lt, + strings.concat(lateworks[i], "/"), attemptbin); + let nd: str = normalizedtrace(out.stderr, + strings.concat(lateworks[i], "/"), attemptbin); + if (i == 0 && retry == 0) { + phasectrefs[phasei] = nc; phaseatrefs[phasei] = na; + phaseltrefs[phasei] = nl; + phasediagnosticrefs[phasei] = nd; + } else { + assert(same(phasectrefs[phasei], nc)); + assert(same(phaseatrefs[phasei], na)); + assert(same(phaseltrefs[phasei], nl)); + assert(same(phasediagnosticrefs[phasei], nd)); + }; + retry += 1; + }; + i += 1; + }; + phasei += 1; + }; clean(root); }; @@ -3824,9 +4580,9 @@ fn hexbytes(value: str) str = { let referencediagnostic: str = ""; let referencetestout: str = ""; let referencetesterr: str = ""; - let referencetestartifacts: []str = alloc([], 12u64)!; + let referencetestartifacts: []str = alloc([], 9u64)!; ri = 0; - for (ri < 12) { append(referencetestartifacts, ""); ri += 1; }; + for (ri < 9) { append(referencetestartifacts, ""); ri += 1; }; let allowedreferences: []str = ["", "", "", ""]; let testrejectreferences: []str = ["", ""]; let orderreference: str = ""; @@ -4129,8 +4885,7 @@ fn hexbytes(value: str) str = { let testwork: str = strings.concat(testworkroot, "/"); let testactions: []str = ["domain.testclient-internal-test", "domain.testclient_test-external-test", - "domain.testclient-internal-test-main", - "domain.testclient_test-external-test-main"]; + "domain.testclient-test-main"]; ai = 0; for (ai < testactions.len) { let testsuffixes: []str = [".unit.ww", ".wwi", ".a"]; @@ -4158,15 +4913,26 @@ fn hexbytes(value: str) str = { "domain.internal.secret.wwi") == 1); assert(!has(sameline, "domain.leaf.wwi")); assert(!has(externalline, "domain.leaf.wwi")); - let samemainline: str = linecontaining(testcompiletrace, - "domain.testclient-internal-test-main.unit"); - let externalmainline: str = linecontaining(testcompiletrace, - "domain.testclient_test-external-test-main.unit"); - assert(!has(samemainline, "domain.internal.secret.wwi")); - assert(!has(externalmainline, "domain.internal.secret.wwi")); + assert(has(externalline, strings.concat("<--import>", + "<", testwork, + "domain.testclient-internal-test.wwi.new>"))); + let mainline: str = linecontaining(testcompiletrace, + "domain.testclient-test-main.unit"); + assert(!has(mainline, "domain.internal.secret.wwi")); + assert(occurrences(mainline, "<--test-target-package>") == 2); + assert(pos(mainline, + "<--test-target-package>") + < pos(mainline, + "<--test-target-package>")); + assert(has(mainline, strings.concat("<--import><", + testwork, "domain.testclient-internal-test.wwi.new>"))); + assert(has(mainline, strings.concat( + "<--import><", testwork, + "domain.testclient_test-external-test.wwi.new>"))); assert(occurrences(testcompiletrace, "--test-package") == 2); assert(occurrences(testcompiletrace, - "<-T><--entry><--test-support-module>") == 2); + "<-T><--entry><--test-support-module>") == 1); + assert(occurrences(readfile(linkertraces[si]), "\n") == 1); assert(!has(readfile(linkertraces[si]), ".wwi")); let testrejects: []str = [outsame, outexternal]; @@ -4204,19 +4970,19 @@ fn hexbytes(value: str) str = { assert(os.mkdir(orderwork, 448i32) == 0); let orderforward: []str = [driver(stages[si]), "test", "-c", "-w", orderwork, "-I", source, - "--ww-package-test", "production", "main", client, + "--ww-package-test", "test", "main", "main", "-", "-", client, strings.concat(root, "/order-client-", stages[si]), strings.concat(root, "/order-client-", stages[si], ".status"), - "--ww-package-test", "production", "main", outsider, + "--ww-package-test", "test", "main", "main", "-", "-", outsider, strings.concat(root, "/order-outsider-", stages[si]), strings.concat(root, "/order-outsider-", stages[si], ".status"), client]; let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w", orderwork, "-I", source, - "--ww-package-test", "production", "main", outsider, + "--ww-package-test", "test", "main", "main", "-", "-", outsider, strings.concat(root, "/order-outsider-", stages[si]), strings.concat(root, "/order-outsider-", stages[si], ".status"), - "--ww-package-test", "production", "main", client, + "--ww-package-test", "test", "main", "main", "-", "-", client, strings.concat(root, "/order-client-", stages[si]), strings.concat(root, "/order-client-", stages[si], ".status"), client]; @@ -4641,10 +5407,12 @@ fn hexbytes(value: str) str = { clean(assemblertrace); writefile(assemblertrace, ""); clean(linkertrace); writefile(linkertrace, ""); let combinedav: []str = [driver(stages[si]), "test", "-c", "-I", - source, - "--ww-package-test", "production", "paritybase", base, + source, "--ww-package-build", + "--ww-package-test", "build", "paritybase", "paritybase", + "-", "-", base, combinedbase, combinedbasestatus, - "--ww-package-test", "production", "parityleft", left, + "--ww-package-test", "build", "parityleft", "parityleft", + "-", "-", left, combinedleft, combinedleftstatus, left]; runcommandenv(root, strings.concat("long-combined-", tags[si]), combinedav, env, (120i64 * (time.second: i64)): time.duration, &out); @@ -4827,24 +5595,15 @@ fn hexbytes(value: str) str = { let compilers: []str = ["w6c", "w6c_ww"]; let internalid: str = commandid; let externalid: str = strings.concat(commandid, "_test"); - let internalmainid: str = strings.concat("__wwtestmain.", commandid, - ".internal.main"); - let externalmainid: str = strings.concat("__wwtestmain.", externalid, - ".external.main"); - let actionids: []str = [commandid, internalid, externalid, - internalmainid, externalmainid]; - let artifacts: []str = [packagestoragekey(commandid, target, 0, 0), - packagestoragekey(internalid, target, 1, 0), + let mainid: str = strings.concat("__wwtestmain.", commandid, ".main"); + let actionids: []str = [internalid, externalid, mainid]; + let ordinaryartifact: str = packagestoragekey(commandid, target, 0, 0); + let artifacts: []str = [packagestoragekey(internalid, target, 1, 0), packagestoragekey(externalid, target, 2, 0), - packagestoragekey(internalmainid, - strings.concat(target, "#internal-test-main"), 3, 2), - packagestoragekey(externalmainid, - strings.concat(target, "#external-test-main"), 3, 2)]; + packagestoragekey(mainid, + strings.concat(target, "#directory-test-main"), 3, 2)]; let suffixes: []str = [".unit.ww", ".wwi", ".a"]; - let reference: []str = ["", "", "", "", "", "", "", "", "", - "", "", "", "", "", ""]; - let samebin: str = strings.concat(target, "/main.test"); - let externalbin: str = strings.concat(target, "/main_test.test"); + let reference: []str = ["", "", "", "", "", "", "", "", ""]; let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { @@ -4877,14 +5636,14 @@ fn hexbytes(value: str) str = { expectexit(&out, 0); assert(out.stderr.len == 0); assert(has(out.stdout, "main.command_internal ... ok\n")); - assert(has(out.stdout, "main_test.command_external ... ok\n")); + assert(has(out.stdout, "main.command_external ... ok\n")); let ctrace: str = readfile(trace); let ai: i32 = 0; for (ai < artifacts.len) { let line: str = linecontaining(ctrace, strings.concat(artifacts[ai], ".unit.new")); - if (ai < 3) { assert(has(line, "<--command-package>")); } + if (ai < 2) { assert(has(line, "<--command-package>")); } else { assert(has(line, "<-T><--entry>")); }; let sj: i32 = 0; for (sj < suffixes.len) { @@ -4905,28 +5664,28 @@ fn hexbytes(value: str) str = { ai += 1; }; let externalline: str = linecontaining(ctrace, - strings.concat(artifacts[2], ".unit.new")); + strings.concat(artifacts[1], ".unit.new")); assert(has(externalline, strings.concat("<--import><", commandid, "><", work, artifacts[0], ".wwi.new>"))); - let internalmainline: str = linecontaining(ctrace, - strings.concat(artifacts[3], ".unit.new")); - let externalmainline: str = linecontaining(ctrace, - strings.concat(artifacts[4], ".unit.new")); - assert(has(internalmainline, strings.concat("<--import><", internalid, + let mainline: str = linecontaining(ctrace, + strings.concat(artifacts[2], ".unit.new")); + assert(has(mainline, strings.concat("<--import><", internalid, + "><", work, artifacts[0], ".wwi.new>"))); + assert(has(mainline, strings.concat("<--import><", externalid, "><", work, artifacts[1], ".wwi.new>"))); - assert(has(internalmainline, strings.concat( + assert(has(mainline, strings.concat( "<--test-target-package><", internalid, ">"))); - assert(has(externalmainline, strings.concat("<--import><", externalid, - "><", work, artifacts[2], ".wwi.new>"))); - assert(has(externalmainline, strings.concat( + assert(has(mainline, strings.concat( "<--test-target-package><", externalid, ">"))); - assert(occurrences(ctrace, strings.concat("<", work, artifacts[0], - ".unit.new>")) == 1); + assert(pos(mainline, strings.concat("<--test-target-package><", + internalid, ">")) < pos(mainline, strings.concat( + "<--test-target-package><", externalid, ">"))); + assert(!os.exists(strings.concat(work, ordinaryartifact, ".unit.ww"))); assert(has(readfile(strings.concat(work, artifacts[0], ".s")), strings.concat(commandid, ".value"))); - assert(has(readfile(strings.concat(work, artifacts[1], ".s")), + assert(has(readfile(strings.concat(work, artifacts[0], ".s")), strings.concat(commandid, ".command_internal"))); - assert(has(readfile(strings.concat(work, artifacts[2], ".s")), + assert(has(readfile(strings.concat(work, artifacts[1], ".s")), strings.concat(externalid, ".command_external"))); let warmrefs: []str = alloc([], reference.len: u64)!; warmrefs.len = reference.len; @@ -4944,7 +5703,7 @@ fn hexbytes(value: str) str = { env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "main.command_internal ... ok\n")); - assert(has(out.stdout, "main_test.command_external ... ok\n")); + assert(has(out.stdout, "main.command_external ... ok\n")); assert(same(ctrace, readfile(trace))); ai = 0; for (ai < artifacts.len) { @@ -4973,8 +5732,6 @@ fn hexbytes(value: str) str = { assert(readfile(trace).len == 0); clean(workroot); - clean(samebin); - clean(externalbin); si += 1; }; clean(root); @@ -5286,13 +6043,13 @@ fn hexbytes(value: str) str = { "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", + let testartifactnames: []str = ["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"]; + "pkg-test-main.unit.ww", "pkg-test-main.wwi", "pkg-test-main.a"]; let testartifactrefs: []str = ["", "", "", "", "", "", "", "", - "", "", ""]; + ""]; let binref: str = ""; let compilerref: str = ""; let assemblerref: str = ""; @@ -5615,7 +6372,19 @@ fn hexbytes(value: str) str = { assert(occurrences(sameline, "<--import>") == 1); assert(occurrences(externalline, "<--import>") == 1); assert(occurrences(externalline, "<--import>") == 1); + assert(has(externalline, strings.concat("<--import><", testwork, + "/pkg-internal-test.wwi.new>"))); + let mainline: str = linecontaining(testctrace, + "/pkg-test-main.unit.new"); + assert(occurrences(mainline, "<--test-target-package>") == 2); + assert(pos(mainline, "<--test-target-package>") + < pos(mainline, "<--test-target-package>")); + assert(occurrences(testltrace, "\n") == 1); + assert(has(testltrace, "/pkg-test-main.a.new>")); + assert(has(testltrace, "/pkg-internal-test.a.new>")); + assert(has(testltrace, "/pkg_test-external-test.a.new>")); assert(!has(testltrace, ".wwi")); + assert(!os.exists(strings.concat(testwork, "/pkg.unit.ww"))); let sameunit: str = readfile(strings.concat(testwork, "/pkg-internal-test.unit.ww")); let externalunit: str = readfile(strings.concat(testwork, @@ -5653,7 +6422,7 @@ fn hexbytes(value: str) str = { assert(same(testoutref, out.stdout)); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); - assert(occurrences(readfile(linkertrace), "\n") == 2); + assert(occurrences(readfile(linkertrace), "\n") == 1); assert(same(stabletestunit, readfile(strings.concat(testwork, "/pkg-internal-test.unit.ww")))); rewritefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"), @@ -6540,15 +7309,15 @@ fn hexbytes(value: str) str = { assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "ww: no main packages to build\n")); assert(has(seq, strings.concat("ok ", tree, - " [rootpkg, same-package]\n"))); + " [rootpkg]\n"))); assert(has(seq, strings.concat("ok ", tree, - "/alpha [alpha_test, external]\n"))); + "/alpha [alpha]\n"))); assert(has(seq, strings.concat("ok ", tree, - "/app [app, same-package]\n"))); + "/app [app]\n"))); assert(has(seq, strings.concat("ok ", tree, - "/beta/inner [inner, same-package]\n"))); + "/beta/inner [inner]\n"))); assert(has(seq, strings.concat("ok ", tree, - "/domain/app [app, same-package]\n"))); + "/domain/app [app]\n"))); assert(has(seq, strings.concat("? ", tree, "/gamma [no tests]\n"))); assert(has(seq, strings.concat("? ", tree, @@ -6556,15 +7325,15 @@ fn hexbytes(value: str) str = { assert(has(seq, strings.concat("? ", tree, "/vendorx [no tests]\n"))); let rootreport: i32 = pos(seq, strings.concat("ok ", tree, - " [rootpkg, same-package]\n")); + " [rootpkg]\n")); let alphareport: i32 = pos(seq, strings.concat("ok ", tree, - "/alpha [alpha_test, external]\n")); + "/alpha [alpha]\n")); let appreport: i32 = pos(seq, strings.concat("ok ", tree, - "/app [app, same-package]\n")); + "/app [app]\n")); let innerreport: i32 = pos(seq, strings.concat("ok ", tree, - "/beta/inner [inner, same-package]\n")); + "/beta/inner [inner]\n")); let domainreport: i32 = pos(seq, strings.concat("ok ", tree, - "/domain/app [app, same-package]\n")); + "/domain/app [app]\n")); let gammareport: i32 = pos(seq, strings.concat("? ", tree, "/gamma [no tests]\n")); let vendorreport: i32 = pos(seq, strings.concat("? ", tree, @@ -6577,13 +7346,13 @@ fn hexbytes(value: str) str = { && gammareport < vendorreport && vendorreport < vendorxreport); assert(occurrences(seq, strings.concat(tree, " [")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, - "/alpha [alpha_test, external]\n")) == 1); + "/alpha [alpha]\n")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, - "/app [app, same-package]\n")) == 1); + "/app [app]\n")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, - "/beta/inner [inner, same-package]\n")) == 1); + "/beta/inner [inner]\n")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, - "/domain/app [app, same-package]\n")) == 1); + "/domain/app [app]\n")) == 1); assert(occurrences(seq, strings.concat("? ", tree, "/gamma [no tests]\n")) == 1); assert(occurrences(seq, strings.concat("? ", tree, @@ -6690,9 +7459,9 @@ fn hexbytes(value: str) str = { assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stdout, strings.concat("ok ", tree, - "/alpha [alpha_test, external]\n"))); + "/alpha [alpha]\n"))); assert(!has(outc.stdout, strings.concat("ok ", tree, - " [rootpkg, same-package]\n"))); + " [rootpkg]\n"))); // The recursive exclusions are traversal rules, not bans on direct roots. let directc: []str = [driver("ww"), "test", "-I", tree, @@ -6710,11 +7479,11 @@ fn hexbytes(value: str) str = { assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stdout, strings.concat("ok ", tree, - "/.hidden [hid, same-package]\n"))); + "/.hidden [hid]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, - "/_skip [_skip, same-package]\n"))); + "/_skip [_skip]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, - "/testdata/bad [bad, same-package]\n"))); + "/testdata/bad [bad]\n"))); // A generic wildcard selects a code-bearing terminal vendor directory but // cannot consume it. Rooting the pattern at vendor selects its descendants; @@ -6733,7 +7502,7 @@ fn hexbytes(value: str) str = { assert(has(outc.stdout, strings.concat("? ", tree, "/vendor [no tests]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, - "/vendor/bad [bad, same-package]\n"))); + "/vendor/bad [bad]\n"))); assert(!has(outc.stdout, "/vendor/bad/vendor/deep")); let vendordirectc: []str = [driver("ww"), "test", "-I", tree, @@ -6773,7 +7542,7 @@ fn hexbytes(value: str) str = { assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stdout, strings.concat("ok ", outside, - " [outside, same-package]\n"))); + " [outside]\n"))); let oc: []str = [driver("ww"), "test", "-o", strings.concat(root, "/stem"), spec]; @@ -6822,6 +7591,27 @@ fn hexbytes(value: str) str = { let alphaunit: str = readfile(strings.concat(wdkey, "/alpha.unit.ww")); assert(pos(alphaunit, "export fn value()") < pos(alphaunit, "export fn linked()")); + assert(same(readfile(strings.concat(wdkey, "/.wwtool.stamp")), + "ww workdir fmt 19 mode test asm 0\n")); + let directorymains: []str = [strings.concat(localidentity(tree, + "rootpkg"), "-test-main"), + "alpha-test-main", "app-test-main", "beta.inner-test-main", + "domain.app-test-main"]; + let dmi: i32 = 0; + for (dmi < directorymains.len) { + assert(os.exists(strings.concat(wdkey, "/", directorymains[dmi], + ".unit.ww"))); + dmi += 1; + }; + let notestactions: []str = ["gamma", "vendor", "vendorx"]; + let nti: i32 = 0; + for (nti < notestactions.len) { + assert(os.exists(strings.concat(wdkey, "/", notestactions[nti], + ".unit.ww"))); + assert(!os.exists(strings.concat(wdkey, "/", notestactions[nti], + "-test-main.unit.ww"))); + nti += 1; + }; runcommand(root, "tree-w-ww", wdw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); @@ -7249,9 +8039,9 @@ fn hexbytes(value: str) str = { expectexit(&out, 0); let cold: str = strings.dup(out.stdout); assert(has(cold, strings.concat("ok ", a, - " [foo, same-package]\n"))); + " [foo]\n"))); assert(has(cold, strings.concat("ok ", b, - " [foo, same-package]\n"))); + " [foo]\n"))); let ckey: str = workc; assert(os.exists(ckey)); let aaction: str = "a.foo-internal-test"; @@ -7294,9 +8084,9 @@ fn hexbytes(value: str) str = { (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, strings.concat("ok ", a, - " [foo, same-package]\n"))); + " [foo]\n"))); assert(has(out.stdout, strings.concat("ok ", b, - " [foo, same-package]\n"))); + " [foo]\n"))); assert(os.exists(wwkey)); assert(same(aunit, readfile(strings.concat(wwkey, "/", aaction, ".unit.ww")))); @@ -7333,9 +8123,12 @@ fn hexbytes(value: str) str = { let rightout: str = strings.concat(root, "/local-", stages[si], "-right.test"); let lav: []str = [driver(stages[si]), "test", "-c", - "--ww-package-test", "production", "foo", localleft, leftout, + "--ww-package-build", + "--ww-package-test", "build", "foo", "foo", "-", "-", + localleft, leftout, strings.concat(root, "/local-", stages[si], "-left.status"), - "--ww-package-test", "production", "foo", localright, rightout, + "--ww-package-test", "build", "foo", "foo", "-", "-", + localright, rightout, strings.concat(root, "/local-", stages[si], "-right.status"), localleft]; runcommand(root, strings.concat("local-command-union-", stages[si]), @@ -8260,7 +9053,7 @@ fn runtimepath(relative: str) str = { beta, pairvault]; let combinedpackages: []str = ["main", "main", "main", "main", "main", "main", "math"]; - let combinedcodes: []i32 = [0, 0, 0, 0, 0, 0, 0]; + let combinedcodes: []i32 = [11, 11, 11, 22, 61, 61]; let combinedoutputs: []str = [ strings.concat(root, "/combined-client-", stages[si]), strings.concat(root, "/combined-one-", stages[si]), @@ -8277,16 +9070,21 @@ fn runtimepath(relative: str) str = { strings.concat(combinedoutputs[4], ".status"), strings.concat(combinedoutputs[5], ".status"), strings.concat(combinedoutputs[6], ".status")]; - let combinedav: []str = alloc([], (8 + combinedtargets.len * 6): u64)!; + let combinedav: []str = alloc([], + (9 + combinedtargets.len * 9): u64)!; append(combinedav, driver(stages[si])); append(combinedav, "test"); append(combinedav, "-c"); append(combinedav, "-w"); append(combinedav, combinedwork); append(combinedav, "-I"); append(combinedav, source); + append(combinedav, "--ww-package-build"); let ui: i32 = 0; for (ui < combinedtargets.len) { append(combinedav, "--ww-package-test"); - append(combinedav, "production"); + append(combinedav, "build"); append(combinedav, combinedpackages[ui]); + append(combinedav, combinedpackages[ui]); + append(combinedav, "-"); + append(combinedav, "-"); append(combinedav, combinedtargets[ui]); append(combinedav, combinedoutputs[ui]); append(combinedav, combinedstatuses[ui]); @@ -8333,7 +9131,7 @@ fn runtimepath(relative: str) str = { assert(os.exists(strings.concat(combinedwork, "/domain.vendor.lib.math.a"))); ui = 0; - for (ui < combinedoutputs.len) { + for (ui < combinedcodes.len) { let cav: []str = [combinedoutputs[ui]]; runcommand(root, strings.concat("vendor-combined-run-", stages[si], "-", labels[ui]), cav, @@ -8341,6 +9139,8 @@ fn runtimepath(relative: str) str = { expectexit(&out, combinedcodes[ui]); ui += 1; }; + assert(!os.exists(combinedoutputs[6])); + assert(same(readfile(combinedstatuses[6]), "ok\n")); // A target interned by the allowed product is still checked at the // forbidden source edge. Reversing product descriptors changes neither @@ -8358,16 +9158,16 @@ fn runtimepath(relative: str) str = { writefile(orderallowedstatus, "sentinel-allowed\n"); writefile(orderforbiddenstatus, "sentinel-forbidden\n"); let orderforward: []str = [driver(stages[si]), "test", "-c", "-w", - orderwork, "-I", source, - "--ww-package-test", "production", "main", client, + orderwork, "-I", source, "--ww-package-build", + "--ww-package-test", "build", "main", "main", "-", "-", client, orderallowed, orderallowedstatus, - "--ww-package-test", "production", "main", outsider, + "--ww-package-test", "build", "main", "main", "-", "-", outsider, orderforbidden, orderforbiddenstatus, client]; let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w", - orderwork, "-I", source, - "--ww-package-test", "production", "main", outsider, + orderwork, "-I", source, "--ww-package-build", + "--ww-package-test", "build", "main", "main", "-", "-", outsider, orderforbidden, orderforbiddenstatus, - "--ww-package-test", "production", "main", client, + "--ww-package-test", "build", "main", "main", "-", "-", client, orderallowed, orderallowedstatus, client]; let orderrequests: [][]str = [orderforward, orderreverse]; let orderdiags: []str = ["", ""]; @@ -8453,15 +9253,30 @@ fn runtimepath(relative: str) str = { "<--import-map><", expanded, ">")) == 1); assert(occurrences(externalline, strings.concat( "<--import-map><", expanded, ">")) == 1); + assert(occurrences(externalline, strings.concat( + "<--import><", testwork, + "domain.app.testclient-internal-test.wwi.new>")) == 1); assert(!has(internalline, "lib.leaf.wwi")); assert(!has(externalline, "lib.leaf.wwi")); - let internalmainline: str = linecontaining(testtrace, - "domain.app.testclient-internal-test-main.unit.new"); - let externalmainline: str = linecontaining(testtrace, - "domain.app.testclient_test-external-test-main.unit.new"); - assert(!has(internalmainline, expanded)); - assert(!has(externalmainline, expanded)); - assert(!has(readfile(linkertraces[si]), ".wwi>")); + let mainline: str = linecontaining(testtrace, + "domain.app.testclient-test-main.unit.new"); + assert(!has(mainline, expanded)); + assert(occurrences(mainline, "<--test-target-package>") == 2); + assert(pos(mainline, + "<--test-target-package>") + < pos(mainline, + "<--test-target-package>")); + assert(has(mainline, strings.concat( + "<--import><", testwork, + "domain.app.testclient-internal-test.wwi.new>"))); + assert(has(mainline, strings.concat( + "<--import><", testwork, + "domain.app.testclient_test-external-test.wwi.new>"))); + let testlinktrace: str = readfile(linkertraces[si]); + assert(occurrences(testlinktrace, "\n") == 1); + assert(has(testlinktrace, + "/domain.app.testclient-test-main.a.new>")); + assert(!has(testlinktrace, ".wwi>")); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); @@ -8473,6 +9288,7 @@ fn runtimepath(relative: str) str = { assert(same(referencetesterr, out.stderr)); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); + assert(occurrences(readfile(linkertraces[si]), "\n") == 1); // A directly selected vendored test root is legal, and its external // source still performs the ordinary nearest-vendor search first. @@ -8559,7 +9375,7 @@ fn runtimepath(relative: str) str = { assert(occurrences(supporttrace, "/test.vendor.fnmatch.unit.new") == 1); let supportmainline: str = linecontaining(supporttrace, - "supportcase-internal-test-main.unit.new"); + "supportcase-test-main.unit.new"); assert(!has(supportmainline, "test.vendor.fnmatch")); assert(!has(readfile(linkertraces[si]), ".wwi>")); let supportsuffixes: []str = [".unit.ww", ".wwi", ".a"]; @@ -9108,26 +9924,46 @@ fn runtimepath(relative: str) str = { assert(out.stderr.len == 0); if (si == 0) { testoutref = strings.dup(out.stdout); } else { assert(same(testoutref, out.stdout)); }; - let productionunit: str = readfile(strings.concat(testwork, - "/testpkg.codec.unit.ww")); + assert(has(out.stdout, strings.concat("ok ", testcodec, + " [wire]\n"))); + assert(!os.exists(strings.concat(testwork, + "/testpkg.codec.unit.ww"))); let internalunit: str = readfile(strings.concat(testwork, "/testpkg.codec-internal-test.unit.ww")); let externalunit: str = readfile(strings.concat(testwork, "/testpkg.codec_test-external-test.unit.ww")); - assert(!has(productionunit, "testonly.helper")); + assert(has(internalunit, "export type Thing = i32;")); + assert(has(internalunit, "export fn value()")); assert(has(internalunit, "import assist testonly.helper;")); assert(!has(externalunit, "testonly.helper")); assert(has(externalunit, "package wire_test;")); + assert(has(externalunit, + "//ww:direct-export testpkg.codec ")); assert(os.exists(strings.concat(testwork, + "/testpkg.codec-test-main.a"))); + assert(!os.exists(strings.concat(testwork, "/testpkg.codec-internal-test-main.a"))); - assert(os.exists(strings.concat(testwork, + assert(!os.exists(strings.concat(testwork, "/testpkg.codec_test-external-test-main.a"))); assert(os.exists(strings.concat(testwork, "/test.a"))); - assert(!has(readfile(strings.concat(testwork, - "/testpkg.codec-internal-test-main.unit.ww")), "testonly.helper")); - assert(!has(readfile(strings.concat(testwork, - "/testpkg.codec_test-external-test-main.unit.ww")), - "testonly.helper")); + let testtrace: str = readfile(traces[si]); + let externalline: str = linecontaining(testtrace, + "/testpkg.codec_test-external-test.unit.new"); + assert(has(externalline, strings.concat( + "<--import><", testwork, + "/testpkg.codec-internal-test.wwi.new>"))); + let mainline: str = linecontaining(testtrace, + "/testpkg.codec-test-main.unit.new"); + assert(occurrences(mainline, "<--test-target-package>") == 2); + assert(pos(mainline, "<--test-target-package>") + < pos(mainline, + "<--test-target-package>")); + let mainunit: str = readfile(strings.concat(testwork, + "/testpkg.codec-test-main.unit.ww")); + assert(has(mainunit, "import testpkg.codec;")); + assert(has(mainunit, "import testpkg.codec_test;")); + assert(has(mainunit, "import test;")); + assert(!has(mainunit, "testonly.helper")); let recursivebuildwork: str = strings.concat(root, "/recursive-build-", stages[si]); diff --git a/test/sep/sepinit_test.ww b/test/sep/sepinit_test.ww index 8d2aa387..c3cc60a4 100644 --- a/test/sep/sepinit_test.ww +++ b/test/sep/sepinit_test.ww @@ -736,7 +736,11 @@ fn rejectrow(td: str, label: str, src: str, expected: str) void = { let td: str = testenv.fresh(); let tree: str = strings.concat(td, "/tree"); assert(os.mkdir(tree, 493) == 0); - let body: str = "package main;\n"; + let target: str = strings.concat(tree, "/target"); + let bridge: str = strings.concat(tree, "/bridge"); + assert(os.mkdir(target, 493) == 0); + assert(os.mkdir(bridge, 493) == 0); + let body: str = "package target;\n"; let i: i32 = 0; for (i < 600) { let name: str = allocpkgname(i); @@ -747,63 +751,98 @@ fn rejectrow(td: str, label: str, src: str, expected: str) void = { body = strings.concat(body, "import _ ", name, ";\n"); i += 1; }; - body = strings.concat(body, "fn main() i32 = { return 0; };\n"); - let src: str = strings.concat(td, "/main.ww"); - testenv.writefile(src, body); - let wrapper: str = strings.concat(td, "/w6c-trace.sh"); + body = strings.concat(body, + "export fn value() i32 = { return 41; };\n"); + testenv.writefile(strings.concat(target, "/target.ww"), body); + testenv.writefile(strings.concat(target, "/same_test.ww"), strings.concat( + "package target;\n", + "export fn internal_helper() i32 = { return value(); };\n", + "@test fn internal_runs() void = { assert(value() == 41); };\n")); + testenv.writefile(strings.concat(bridge, "/bridge.ww"), strings.concat( + "package bridge;\nimport target;\n", + "export fn helper() i32 = { return target.internal_helper(); };\n")); + testenv.writefile(strings.concat(target, "/external_test.ww"), + strings.concat("package target_test;\nimport bridge;\nimport target;\n", + "@test fn external_runs() void = {\n", + " assert(target.internal_helper() == 41);\n", + " assert(bridge.helper() == 41);\n};\n")); + let wrapper: str = strings.concat(td, "/tool-trace.sh"); testenv.writeexecutable(wrapper, strings.concat("#!/bin/sh\n", - "printf x >> \"$WW_SEPINIT_W6C_TRACE\"\nexit 99\n")); + "printf x >> \"$WW_SEPINIT_TOOL_TRACE\"\nexit 99\n")); let stages: []str = ["ww", "ww_ww"]; - let compilers: []str = ["w6c", "w6c_ww"]; let launcher: str = testenv.driver("sep-limitexec"); - let found: bool = false; - let last: str = ""; - let limit: i64 = 2621440i64; - let li: i32 = 0; - // Both implementations must reject under the same address-space ceiling. - // Search a bounded same-limit ladder because loader mappings differ across - // supported hosts; the 600-action graph makes the driver-allocation window - // broad, while the compiler trace pins failure before the first tool. - for (limit <= 16777216i64 && !found) { - let limitstr: str = strconv.i64tos(limit, strconv.base.DEC); - let good: [2]bool; - let diags: [2]str; - i = 0; - for (i < stages.len) { + let accepted: [2]str; + let stagei: i32 = 0; + // Address-space layouts differ, so each implementation searches its own + // bounded ceiling. The accepted point must fail while constructing the + // combined p/ptest/pxtest/pmain and transitive-clone graph, before any tool. + for (stagei < stages.len) { + let found: bool = false; + let last: str = ""; + let limit: i64 = 2621440i64; + let li: i32 = 0; + for (limit <= 33554432i64 && !found) { let tag: str = allocpkgname(li); - let trace: str = strings.concat(td, "/alloc-", stages[i], "-", + let trace: str = strings.concat(td, "/alloc-", stages[stagei], "-", tag, ".trace"); + let work: str = strings.concat(td, "/alloc-work-", stages[stagei], + "-", tag); + let output: str = strings.concat(td, "/alloc-output-", + stages[stagei], "-", tag); + let status: str = strings.concat(td, "/alloc-status-", + stages[stagei], "-", tag); testenv.writefile(trace, ""); - let env: []str = compilertraceenv(wrapper, - testenv.driver(compilers[i]), trace); - let output: str = strings.concat(td, "/alloc-", stages[i], "-", - tag); - let av: []str = [launcher, limitstr, testenv.driver(stages[i]), - "build", "-I", tree, "-o", output, src]; + assert(os.mkdir(work, 493) == 0); + let base: []str = os.getenvs(); + let env: []str = alloc([], (base.len + 4): u64)!; + i = 0; + for (i < base.len) { + if (!strings.hasprefix(base[i], "WW_W6C=") + && !strings.hasprefix(base[i], "WW_W6A=") + && !strings.hasprefix(base[i], "WW_W6L=") + && !strings.hasprefix(base[i], + "WW_SEPINIT_TOOL_TRACE=")) { + append(env, base[i]); + }; + i += 1; + }; + append(env, strings.concat("WW_W6C=", wrapper)); + append(env, strings.concat("WW_W6A=", wrapper)); + append(env, strings.concat("WW_W6L=", wrapper)); + append(env, strings.concat("WW_SEPINIT_TOOL_TRACE=", trace)); + let limitstr: str = strconv.i64tos(limit, strconv.base.DEC); + let av: []str = [launcher, limitstr, testenv.driver(stages[stagei]), + "test", "-c", "-w", work, "-I", tree, + "--ww-package-test", "test", "target", "target", "target", + "target_test", target, output, status, target]; let out: testenv.commandout; - runenv(td, strings.concat("allocation-", stages[i], "-", tag), + runenv(td, strings.concat("allocation-", stages[stagei], "-", tag), av, env, &out); last = strings.dup(out.stderr); - good[i] = exitcode(&out) == 1 && testenv.same(out.stderr, - "ww: out of memory\n") - && testenv.readfile(trace).len == 0; - diags[i] = strings.dup(out.stderr); - if (testenv.exists(output) || testenv.exists(strings.concat(output, - ".sepwork"))) { + if (testenv.listdir(work).len != 0 + || nofollowexists(output) + || nofollowexists(strings.concat(output, ".new")) + || nofollowexists(strings.concat(output, ".sepwork")) + || nofollowexists(status) + || nofollowexists(strings.concat(status, ".new"))) { fail("allocation", "failed pressure leg published state"); }; - i += 1; - }; - if (good[0] && good[1]) { - if (!testenv.same(diags[0], diags[1])) { - fail("allocation", "C/WW allocation diagnostic mismatch"); + if (exitcode(&out) == 1 && out.stdout.len == 0 + && testenv.same(out.stderr, "ww: out of memory\n") + && testenv.readfile(trace).len == 0) { + accepted[stagei] = strings.dup(out.stderr); + found = true; }; - found = true; + testenv.clean(work); + limit += 131072i64; + li += 1; }; - limit += 131072i64; - li += 1; + if (!found) { fail("allocation", last); }; + stagei += 1; + }; + if (!testenv.same(accepted[0], accepted[1])) { + fail("allocation", "C/WW allocation diagnostic mismatch"); }; - if (!found) { fail("allocation", last); }; testenv.clean(td); }; @@ -932,16 +971,12 @@ fn rejectrow(td: str, label: str, src: str, expected: str) void = { let tags: []str = ["c", "ww"]; let reference: str = ""; let variantnames: []str = [ - "widget-internal-test-main.init.unit.ww", - "widget-internal-test-main.init.s", - "widget-internal-test-main.init.o", - "widget-internal-test-main.a", - "widget_test-external-test-main.init.unit.ww", - "widget_test-external-test-main.init.s", - "widget_test-external-test-main.init.o", - "widget_test-external-test-main.a", + "widget-test-main.init.unit.ww", + "widget-test-main.init.s", + "widget-test-main.init.o", + "widget-test-main.a", ]; - let variantrefs: []str = ["", "", "", "", "", "", "", ""]; + let variantrefs: []str = ["", "", "", ""]; let i: i32 = 0; for (i < 2) { let work: str = strings.concat(td, "/test-work-", tags[i]); @@ -967,61 +1002,45 @@ fn rejectrow(td: str, label: str, src: str, expected: str) void = { let rr: i32 = testenv.pos(out.stdout, ""); let ii: i32 = testenv.pos(out.stdout, ""); let tt: i32 = testenv.pos(out.stdout, ""); - let tail: str = ""; - if (tt >= 0) { tail = strings.sub(out.stdout, tt + 3, out.stdout.len); }; - let de: i32 = testenv.pos(tail, ""); - let pe: i32 = testenv.pos(tail, "

"); - let w: i32 = testenv.pos(tail, ""); - let e: i32 = testenv.pos(tail, ""); - let x: i32 = testenv.pos(tail, ""); - if (!(d >= 0 && d < q && q < rr && rr < p && p < ii && ii < tt - && de >= 0 && de < pe && pe < w && w < e && e < x)) { + let w: i32 = testenv.pos(out.stdout, ""); + let e: i32 = testenv.pos(out.stdout, ""); + let x: i32 = testenv.pos(out.stdout, ""); + if (!(d >= 0 && d < q && q < rr && rr < p && p < ii && ii < w + && w < e && e < tt && tt < x)) { fail("variants", strings.concat("test init order: ", out.stdout)); }; if (testenv.occurrences(out.stdout, "") != 1 - || testenv.occurrences(out.stdout, "") != 2 + || testenv.occurrences(out.stdout, "") != 1 || testenv.occurrences(out.stdout, "") != 1 || testenv.occurrences(out.stdout, "") != 1 || testenv.occurrences(out.stdout, "") != 1 || testenv.occurrences(out.stdout, "") != 1 - || testenv.occurrences(out.stdout, "

") != 2) { + || testenv.occurrences(out.stdout, "

") != 1) { fail("variants", "variant initialization count"); }; - let internal: str = testenv.readfile(strings.concat(work, - "/widget-internal-test-main.init.unit.ww")); - if (testenv.occurrences(internal, + let dispatcher: str = testenv.readfile(strings.concat(work, + "/widget-test-main.init.unit.ww")); + if (testenv.occurrences(dispatcher, "__ww..pkg.p.testside.v0.r0.init") != 1 - || testenv.occurrences(internal, + || testenv.occurrences(dispatcher, "__ww..pkg.p.prodside.v0.r0.init") != 1 - || testenv.occurrences(internal, + || testenv.occurrences(dispatcher, "__ww..pkg.p.widget.v1.r0.init") != 1 - || testenv.occurrences(internal, - "__ww..pkg.p.test.v0.r0.init") != 1 - || testenv.occurrences(internal, - "//ww:init-call __ww..pkg.p.__wwtestmain.widget.internal.main.v3.r2.init") != 1 - || testenv.pos(internal, "__ww..pkg.p.test.v0.r0.init") - >= testenv.pos(internal, - "//ww:init-call __ww..pkg.p.__wwtestmain.widget.internal.main.v3.r2.init") - || testenv.has(internal, "__ww..pkg.p.widget.v0.r0.init")) { - fail("variants", "internal variant dispatcher ownership"); - }; - let external: str = testenv.readfile(strings.concat(work, - "/widget_test-external-test-main.init.unit.ww")); - if (testenv.occurrences(external, - "__ww..pkg.p.widget.v0.r0.init") != 1 - || testenv.occurrences(external, - "__ww..pkg.p.prodside.v0.r0.init") != 1 - || testenv.occurrences(external, + || testenv.occurrences(dispatcher, "__ww..pkg.p.widget_test.v2.r0.init") != 1 - || testenv.occurrences(external, + || testenv.occurrences(dispatcher, "__ww..pkg.p.test.v0.r0.init") != 1 - || testenv.occurrences(external, - "//ww:init-call __ww..pkg.p.__wwtestmain.widget_test.external.main.v3.r2.init") != 1 - || testenv.pos(external, "__ww..pkg.p.test.v0.r0.init") - >= testenv.pos(external, - "//ww:init-call __ww..pkg.p.__wwtestmain.widget_test.external.main.v3.r2.init") - || testenv.has(external, "testside")) { - fail("variants", "external variant dispatcher ownership"); + || testenv.occurrences(dispatcher, + "//ww:init-call __ww..pkg.p.__wwtestmain.widget.main.v3.r2.init") != 1 + || testenv.pos(dispatcher, "__ww..pkg.p.widget.v1.r0.init") + >= testenv.pos(dispatcher, + "__ww..pkg.p.widget_test.v2.r0.init") + || testenv.pos(dispatcher, + "__ww..pkg.p.widget_test.v2.r0.init") + >= testenv.pos(dispatcher, + "//ww:init-call __ww..pkg.p.__wwtestmain.widget.main.v3.r2.init") + || testenv.has(dispatcher, "__ww..pkg.p.widget.v0.r0.init")) { + fail("variants", "directory test dispatcher ownership"); }; let directstdout: str = strings.dup(out.stdout); let recursivework: str = strings.concat(td, "/test-recursive-work-", @@ -1100,7 +1119,7 @@ fn rejectrow(td: str, label: str, src: str, expected: str) void = { av, env, tmo(), &out); if (exitcode(&out) == 0 || !testenv.has(out.stderr, - "ww: dependency cycle in initialization closure") + "ww: dependency cycle: p -> q -> r -> p") || testenv.readfile(trace).len != 0 || testenv.listdir(work).len != 0) { fail("test-cycle", out.stderr);