/* * 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 * .combined.ww; each package is enumerated into its OWN * .sepwork/.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 #include #include #include #include #include #include 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; }