test/wcc: carrier ownership repair and driver-contract adaptation
Every surviving carrier now owns its artifacts: checked mkdir/mkdtemp/ fopen acquisition, one all-exit cleanup funnel per carrier, ENOENT- tolerant checked unlinks, exact-path deletion (rm -rf only for an owned pid-keyed dir or a .sepwork beneath one), and cleanup failure fails a passing carrier without overwriting its diagnostic. In the same pass the carriers adapt to the driver contract this branch lands: --sep and WW_PKGCACHE are gone, -S and the /tmp/ww_run_<pid> scratch contract are asserted, and rows whose runtime or reject coverage moved to test/wcc/data fixtures or test/lang @test owners are trimmed to the byte/artifact/diagnostic observations only they can make. Repair and adaptation ride together because most files interleave both in the same hunks; splitting would manufacture intermediate carrier states that never existed and cannot run against either driver.
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
/*
|
||||
* 989_sepbuild_run — M3-tail commit-3 `ww build --sep` driver gate (#46).
|
||||
* 989_sepbuild_run — M3-tail commit-3 `ww build` driver gate (#46).
|
||||
*
|
||||
* Certifies build_one_sep (cmd/ww/main.c + selfhost/cmd/ww/main.ww twin)
|
||||
* END-TO-END through the REAL `ww` / `ww_ww` drivers on a REAL lib chain
|
||||
* (NOT the 989_m3sep synth fixture, which exercises the M3-CORE codegen
|
||||
* via a hand harness). The driver adds ONLY orchestration around that
|
||||
* proven core: discover_deps, reverse-topo, the transitive `w6c -c -I`
|
||||
* producer loop, and a flat `w6l`.
|
||||
* producer loop, and `w6l` over the root `.o` plus reverse-topo dependency
|
||||
* `.a` archives.
|
||||
*
|
||||
* Chosen chain (smallest real multi-package graph with a genuine
|
||||
* multi-level dep edge): root → os → { rt, time }. rt and time are
|
||||
@@ -19,13 +20,15 @@
|
||||
* - reverse-topo dep-first ordering (time/rt before os before root),
|
||||
* - the transitive-closure `.wwi` prepend tagged by dotted path,
|
||||
* - one-pass `w6c -c -I` (consumer + producer) per package,
|
||||
* - flat `w6l` of the `.o` set + libwwrt.a.
|
||||
* - `w6l` of the root `.o`, dependency `.a` set, and libwwrt.a.
|
||||
*
|
||||
* Asserts (all COLD — `<stem>.sepwork` scratch is wiped each run):
|
||||
* Asserts (fresh output stems under an invocation-owned root; retained
|
||||
* `<stem>.sepwork` is inspected, then removed by final carrier cleanup):
|
||||
* 1. Build + run, BOTH stages → exit EXPECT_EXIT (cross-boundary
|
||||
* os.getpid resolves + links across sep `.o`s; the program runs).
|
||||
* os.getpid resolves across the root `.o` and dependency `.a` boundary;
|
||||
* the program runs).
|
||||
* 2. cs==ww (rule 10): per-package `.s`/`.wwi`/`.unit.ww` AND the final
|
||||
* binary are byte-identical between `ww --sep` and `ww_ww --sep`.
|
||||
* binary are byte-identical between `ww` and `ww_ww`.
|
||||
* 3. KEYSTONE (load-bearing, §3.1) THROUGH the driver: for each non-leaf
|
||||
* package P, transform the driver's own `<P>.unit.ww` by substituting
|
||||
* each dep's `.wwi` section with that dep's full directory BODIES,
|
||||
@@ -33,8 +36,7 @@
|
||||
* the `.wwi` conveys exactly the dep facts P's codegen needs. (Leaves
|
||||
* rt/time have no deps → keystone is vacuous; checked on os + root.)
|
||||
*
|
||||
* Light wwstage-driver test (CLAUDE.md rule 14): all intermediates are
|
||||
* `-o`-redirected to /tmp, so it is phase-1 parallel-safe. Models
|
||||
* All intermediates are `-o`-redirected to /tmp for isolation. Models
|
||||
* 989_m3sep_run.c conventions; 989 prefix per the m3sep/m2wwi precedent.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
@@ -44,6 +46,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define EXPECT_EXIT 7
|
||||
|
||||
@@ -232,22 +235,10 @@ main(void)
|
||||
|
||||
snprintf(libdir, sizeof libdir, "%s/../../lib", bin);
|
||||
snprintf(td, sizeof td, "/tmp/wwsep_%d", getpid());
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
|
||||
runwait(cmd);
|
||||
mkdir(td, 0755);
|
||||
|
||||
/* This test byte-id compares the per-package INTERMEDIATES (.s, .unit.ww)
|
||||
* between the cs and ww legs, which a cache HIT legitimately skips
|
||||
* producing (a HIT copies only .wwi/.o and bypasses compose+w6c). So the
|
||||
* compare requires every package to COLD-compile: pin a private per-pid
|
||||
* cache to force misses. This is orthogonal to #104 — the atomic temp+
|
||||
* rename closed the torn-read race (by construction; #105 is the deferred
|
||||
* white-box guard). Concurrent shared-cache build correctness is smoked by
|
||||
* 989_pkgcache_concurrent_run. Here the pin is for intermediate-compare
|
||||
* isolation, not a torn-read mask. */
|
||||
char cachedir[80];
|
||||
snprintf(cachedir, sizeof cachedir, "%s/pkgcache", td);
|
||||
setenv("WW_PKGCACHE", cachedir, 1);
|
||||
if (mkdir(td, 0755) != 0) {
|
||||
fprintf(stderr, "sepbuild FAIL: cannot acquire %s\n", td);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char rootww[1024];
|
||||
snprintf(rootww, sizeof rootww, "%s/root.ww", td);
|
||||
@@ -263,10 +254,10 @@ main(void)
|
||||
for (int s = 0; s < 2; s++) {
|
||||
snprintf(stg[s].prog, sizeof stg[s].prog, "%s/prog.%s", td, stg[s].tag);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 240 %s/%s build --sep -o %s %s >/dev/null 2>&1",
|
||||
"timeout 240 %s/%s build -o %s %s >/dev/null 2>&1",
|
||||
bin, stg[s].drv, stg[s].prog, rootww);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "sepbuild FAIL: %s build --sep\n", stg[s].drv);
|
||||
fprintf(stderr, "sepbuild FAIL: %s build\n", stg[s].drv);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
@@ -352,21 +343,19 @@ main(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* run --sep symmetry: E3-C1 makes sep the sole compile path, so
|
||||
* `run` now genuinely sep-compiles-then-runs (the old "no sep run
|
||||
* path → loud-reject exit 2" is gone with the amalgamator). BOTH
|
||||
* stages must build+run the cross-boundary fixture identically
|
||||
* (rule 10) and exit EXPECT_EXIT — `--sep` is an accepted no-op. */
|
||||
/* `run` must compile and execute this cross-package graph through the
|
||||
* sole separate-compilation path. Both stages must agree and return
|
||||
* EXPECT_EXIT. */
|
||||
{
|
||||
int rc_cs, rc_ww;
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"%s/ww run --sep %s >/dev/null 2>&1", bin, rootww);
|
||||
"%s/ww run %s >/dev/null 2>&1", bin, rootww);
|
||||
rc_cs = runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"%s/ww_ww run --sep %s >/dev/null 2>&1", bin, rootww);
|
||||
"%s/ww_ww run %s >/dev/null 2>&1", bin, rootww);
|
||||
rc_ww = runwait(cmd);
|
||||
if (rc_cs != rc_ww || rc_cs != EXPECT_EXIT) {
|
||||
fprintf(stderr, "sepbuild FAIL: run --sep cs=%d ww=%d "
|
||||
fprintf(stderr, "sepbuild FAIL: run cs=%d ww=%d "
|
||||
"(both must run via sep, exit %d)\n", rc_cs, rc_ww,
|
||||
EXPECT_EXIT);
|
||||
fail++;
|
||||
@@ -374,8 +363,33 @@ main(void)
|
||||
}
|
||||
|
||||
out:
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
|
||||
runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"rm -rf -- '%s/prog.cs.sepwork' '%s/prog.ww.sepwork'", td, td);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "sepbuild FAIL: cannot clean .sepwork trees\n");
|
||||
fail++;
|
||||
}
|
||||
{
|
||||
char path[1200];
|
||||
snprintf(path, sizeof path, "%s/root.ww", td);
|
||||
if (unlink(path) != 0 && errno != ENOENT) { fprintf(stderr, "sepbuild FAIL: cannot remove %s\n", path); fail++; }
|
||||
snprintf(path, sizeof path, "%s/prog.cs", td);
|
||||
if (unlink(path) != 0 && errno != ENOENT) { fprintf(stderr, "sepbuild FAIL: cannot remove %s\n", path); fail++; }
|
||||
snprintf(path, sizeof path, "%s/prog.ww", td);
|
||||
if (unlink(path) != 0 && errno != ENOENT) { fprintf(stderr, "sepbuild FAIL: cannot remove %s\n", path); fail++; }
|
||||
snprintf(path, sizeof path, "%s/os.bodies.ww", td);
|
||||
if (unlink(path) != 0 && errno != ENOENT) { fprintf(stderr, "sepbuild FAIL: cannot remove %s\n", path); fail++; }
|
||||
snprintf(path, sizeof path, "%s/os.bodies.s", td);
|
||||
if (unlink(path) != 0 && errno != ENOENT) { fprintf(stderr, "sepbuild FAIL: cannot remove %s\n", path); fail++; }
|
||||
snprintf(path, sizeof path, "%s/__root.bodies.ww", td);
|
||||
if (unlink(path) != 0 && errno != ENOENT) { fprintf(stderr, "sepbuild FAIL: cannot remove %s\n", path); fail++; }
|
||||
snprintf(path, sizeof path, "%s/__root.bodies.s", td);
|
||||
if (unlink(path) != 0 && errno != ENOENT) { fprintf(stderr, "sepbuild FAIL: cannot remove %s\n", path); fail++; }
|
||||
}
|
||||
if (rmdir(td) != 0 && errno != ENOENT) {
|
||||
fprintf(stderr, "sepbuild FAIL: cannot remove %s\n", td);
|
||||
fail++;
|
||||
}
|
||||
if (fail) {
|
||||
fprintf(stderr, "sepbuild: %d check(s) failed\n", fail);
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user