Files
ww/test/wcc/989_enumcap_run.c
Hojun-Cho a95a7a316b 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.
2026-08-07 23:21:04 +09:00

247 lines
7.2 KiB
C

/*
* 989_enumcap_run (#65, F13 c3) — the driver's directory enumerator must
* bundle EVERY eligible *.ww source, not silently cap at 256.
*
* THE BUG (wwstage driver only, cat-A silent wrong unit under rc=0):
* enumeratedir (selfhost/cmd/ww/main.ww) capped at maxnames=256 and the
* `if (n < maxnames)` guard silently discarded every further eligible file —
* no diagnostic. cstage enumerate_dir_ww (cmd/ww/main.c:209) grows via
* realloc doubling, unbounded. A module dir with >256 sources bundled all of
* them under cstage vs 256 under wwstage, a silently divergent package unit
* (and binary) under rc=0 — invisible to the byte-id gates (no in-tree
* module nears 256 files). THE FIX (CAP-SEMANTICS rule: the cstage twin grows,
* so grow-dynamic): mirror the realloc-doubling growth in enumeratedir.
*
* #94 sep layout: separate compilation stops emitting a single
* <stem>.combined.ww; each package is enumerated into its OWN
* <stem>.sepwork/<pkg>.unit.ww
* (the bigmod dir → bigmod.unit.ww, holding one `package bigmod;` clause per
* enumerated source file — the exact set the combined.ww used to hold for that
* package). So the gate now drives `ww build` / `ww_ww build` and
* re-targets the enumeration check onto bigmod.unit.ww: each stage must enroll
* `nfiles` package clauses, and the two stages' bigmod.unit.ww must be byte-
* identical. A cap-drop on one side shrinks that stage's bigmod.unit.ww — the
* count check AND the cs/ww byte-compare both redden (the cap can't pass blind).
*
* row | files | result (cs==ww)
* --------+-------+------------------------------------------
* big_300 | 300 | both enroll 300, bigmod.unit.ww byte-id
* small_5 | 5 | both enroll 5, bigmod.unit.ww byte-id (control)
*
* big_300 was RED pre-c3 (ww enrolled 256, cs 300 — divergent unit).
* small_5 pins the common ≤cap path stays byte-identical.
*
* All intermediates redirect under each stage's -o stem in /tmp, so out/
* and the source tree stay clean and the gate runs parallel-safe.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
/* Count '^package bigmod;' lines in a file. -1 on open failure. */
static int
count_pkgs(const char *path)
{
FILE *f = fopen(path, "rb");
if (!f) return -1;
char line[256];
int n = 0;
while (fgets(line, sizeof line, f))
if (strncmp(line, "package bigmod;", 15) == 0) n++;
fclose(f);
return n;
}
/* Returns 0 if both driver builds enrolled `nfiles` package clauses into
* their bigmod.unit.ww AND the two units are byte-identical; non-zero
* (and prints) otherwise. */
static int
one_row(const char *label, const char *cdrv, const char *wdrv, int nfiles,
int have_ww)
{
char dir[64], bigdir[128], main_ww[128], cstem[128], wstem[128];
char cunit[192], wunit[192], cmd[1024], fn[160];
snprintf(dir, sizeof dir, "/tmp/encap_%d_%s", getpid(), label);
if (mkdir(dir, 0755) != 0) {
perror(dir);
return 1;
}
int fail = 0, have_bigdir = 0;
snprintf(bigdir, sizeof bigdir, "%s/bigmod", dir);
if (mkdir(bigdir, 0755) != 0) {
perror(bigdir);
fail++;
goto cleanup;
}
have_bigdir = 1;
for (int i = 0; i < nfiles; i++) {
snprintf(fn, sizeof fn, "%s/bigmod/f%03d.ww", dir, i);
FILE *f = fopen(fn, "wb");
if (!f) {
perror(fn);
fail++;
goto cleanup;
}
int werr = fprintf(f, "package bigmod;\nexport fn f%03d() i32 = "
"{ return %d; };\n", i, i) < 0;
if (fclose(f) != 0) werr = 1;
if (werr) {
perror(fn);
fail++;
goto cleanup;
}
}
snprintf(main_ww, sizeof main_ww, "%s/main.ww", dir);
FILE *mf = fopen(main_ww, "wb");
if (!mf) {
perror(main_ww);
fail++;
goto cleanup;
}
int werr = fputs("package main;\nimport bigmod;\nfn main() void = {};\n", mf) == EOF;
if (fclose(mf) != 0) werr = 1;
if (werr) {
perror(main_ww);
fail++;
goto cleanup;
}
snprintf(cstem, sizeof cstem, "%s/X_c", dir);
snprintf(wstem, sizeof wstem, "%s/X_w", dir);
/* the bigmod dir enumerates into its own per-package sep unit */
snprintf(cunit, sizeof cunit, "%s.sepwork/bigmod.unit.ww", cstem);
snprintf(wunit, sizeof wunit, "%s.sepwork/bigmod.unit.ww", wstem);
snprintf(cmd, sizeof cmd,
"%s build -o %s %s 2>/dev/null",
cdrv, cstem, main_ww);
if (runwait(cmd) != 0) {
fprintf(stderr, "enumcap[cstage][%s]: build failed\n", label);
fail++;
}
int cn = count_pkgs(cunit);
if (cn != nfiles) {
fprintf(stderr, "enumcap[cstage][%s]: enrolled %d want %d\n",
label, cn, nfiles);
fail++;
}
if (have_ww) {
snprintf(cmd, sizeof cmd,
"%s build -o %s %s 2>/dev/null",
wdrv, wstem, main_ww);
if (runwait(cmd) != 0) {
fprintf(stderr, "enumcap[wwstage][%s]: build failed\n", label);
fail++;
}
int wn = count_pkgs(wunit);
if (wn != nfiles) {
fprintf(stderr, "enumcap[wwstage][%s]: enrolled %d want %d "
"(silent enumeratedir cap — #65)\n", label, wn, nfiles);
fail++;
}
/* the decisive rule-10 assertion: identical per-package unit —
* a cap-drop on either side shrinks its bigmod.unit.ww */
snprintf(cmd, sizeof cmd, "cmp -s %s %s", cunit, wunit);
if (runwait(cmd) != 0) {
fprintf(stderr, "enumcap[%s]: cs/ww bigmod.unit.ww differ (#65)\n",
label);
fail++;
}
}
cleanup:
if (have_bigdir) {
for (int i = 0; i < nfiles; i++) {
snprintf(fn, sizeof fn, "%s/bigmod/f%03d.ww", dir, i);
if (unlink(fn) != 0 && errno != ENOENT) {
perror(fn);
fail++;
}
}
}
snprintf(main_ww, sizeof main_ww, "%s/main.ww", dir);
if (unlink(main_ww) != 0 && errno != ENOENT) {
perror(main_ww);
fail++;
}
snprintf(cstem, sizeof cstem, "%s/X_c", dir);
snprintf(wstem, sizeof wstem, "%s/X_w", dir);
if (unlink(cstem) != 0 && errno != ENOENT) {
perror(cstem);
fail++;
}
if (unlink(wstem) != 0 && errno != ENOENT) {
perror(wstem);
fail++;
}
char work[192];
snprintf(work, sizeof work, "%s.sepwork", cstem);
snprintf(cmd, sizeof cmd, "rm -rf %s", work);
if (runwait(cmd) != 0) {
fprintf(stderr, "enumcap[%s]: cleanup failed: %s\n", label, work);
fail++;
}
snprintf(work, sizeof work, "%s.sepwork", wstem);
snprintf(cmd, sizeof cmd, "rm -rf %s", work);
if (runwait(cmd) != 0) {
fprintf(stderr, "enumcap[%s]: cleanup failed: %s\n", label, work);
fail++;
}
if (have_bigdir && rmdir(bigdir) != 0) {
perror(bigdir);
fail++;
}
if (rmdir(dir) != 0) {
perror(dir);
fail++;
}
return fail;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int have_ww = (access(wdrv, X_OK) == 0);
if (!have_ww)
fprintf(stderr, "enumcap: skip wwstage (no %s)\n", wdrv);
int fail = 0, total = 2;
fail += one_row("big_300", cdrv, wdrv, 300, have_ww);
fail += one_row("small_5", cdrv, wdrv, 5, have_ww);
if (fail) {
fprintf(stderr, "enumcap_run: %d check(s) failed\n", fail);
return 1;
}
printf("enumcap_run: %d/%d ok\n", total, total);
return 0;
}