/* * 993_ww_ww — phase-10 marker for the ww-side driver port. * * Diffs the C-built `ww` against the ww-built `ww_ww`. Both drivers * orchestrate the same w6c/w6a/w6l, so the resulting binaries must be * byte-identical for every program in the corpus. The corpus mixes: * - a tiny single-file program (no -I flags) * - a multi-import build (selfhost/cmd/wwdump/main.ww), exercising * `use` resolution + the -I search path + the libwwrt.a link. * * Each build runs from a per-driver scratch directory so the * intermediate .combined.ww/.s/.o files don't collide. */ #include #include #include #include #include #include #include "wwtestpkg.h" static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; if (WIFEXITED(rc)) return WEXITSTATUS(rc); return 1; } static const char * absbin(void) { const char *b = getenv("BIN"); if (!b) b = "out/bin"; if (b[0] == '/') return b; static char buf[2048]; char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return NULL; snprintf(buf, sizeof buf, "%s/%s", cwd, b); return buf; } static int slurp_eq(const char *a, const char *b) { FILE *fa = fopen(a, "rb"); FILE *fb = fopen(b, "rb"); if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; } int rc = 0; for (;;) { int ca = fgetc(fa); int cb = fgetc(fb); if (ca != cb) { rc = -1; break; } if (ca == EOF) break; } fclose(fa); fclose(fb); return rc; } /* Build `src` via the named driver, expecting output binary `out` in * the build directory. `incs` may be a colon-separated list of include * dirs (the ww driver accepts -I path1:path2:path3). Returns 0 on * success. */ static int build_via(const char *bin, const char *driver, const char *src, const char *workdir, const char *incs, const char *out_basename) { char cmd[4096]; /* -o / keeps the binary where the diff expects it AND * routes the .combined.ww/.s/.o into the workdir (T3) — no longer * next to the source, so concurrent driver builds never collide. */ if (incs && incs[0]) { snprintf(cmd, sizeof cmd, "cd %s && timeout 180 %s/%s build -o %s/%s -I %s %s 2>/dev/null", workdir, bin, driver, workdir, out_basename, incs, src); } else { snprintf(cmd, sizeof cmd, "cd %s && timeout 180 %s/%s build -o %s/%s %s 2>/dev/null", workdir, bin, driver, workdir, out_basename, src); } return runwait(cmd); } static int diff_one(const char *bin, const char *cwd, const char *label, const char *src, const char *out_basename, const char *incs) { char dc[64], dw[64]; snprintf(dc, sizeof dc, "/tmp/ww_d_%d_c", getpid()); snprintf(dw, sizeof dw, "/tmp/ww_d_%d_w", getpid()); char cmd[256]; snprintf(cmd, sizeof cmd, "rm -rf %s %s && mkdir -p %s %s", dc, dw, dc, dw); if (runwait(cmd) != 0) return -1; if (build_via(bin, "ww", src, dc, incs, out_basename) != 0) { fprintf(stderr, "ww_ww FAIL: C ww errored on %s\n", label); return -1; } if (build_via(bin, "ww_ww", src, dw, incs, out_basename) != 0) { fprintf(stderr, "ww_ww FAIL: ww ww errored on %s\n", label); return -1; } char co[1024], wo[1024]; snprintf(co, sizeof co, "%s/%s", dc, out_basename); snprintf(wo, sizeof wo, "%s/%s", dw, out_basename); int rc = 0; if (slurp_eq(co, wo) != 0) { fprintf(stderr, "ww_ww FAIL: %s — driver outputs differ\n", label); rc = -1; } snprintf(cmd, sizeof cmd, "rm -rf %s %s", dc, dw); runwait(cmd); (void)cwd; return rc; } /* Stage a program whose main() returns `code`, build+exec it via the * named driver's `run` subcommand, and return the propagated exit code * (-1 on staging/spawn failure). #16: ww_ww must report the child's real * exit code, not collapse every non-zero exit to 1. */ static int run_exit_code(const char *bin, const char *driver, int code) { char src[64]; snprintf(src, sizeof src, "/tmp/ww_rc_%d_%d.ww", getpid(), code); FILE *f = fopen(src, "w"); if (!f) return -1; fprintf(f, "package main;\nexport fn main() i32 = {\n\treturn %d;\n};\n", code); fclose(f); char cmd[256]; snprintf(cmd, sizeof cmd, "%s/%s run %s 2>/dev/null", bin, driver, src); int rc = runwait(cmd); unlink(src); return rc; } /* Stage a program that fails to compile, run it via the named driver, * and return the exit code. Pins the build-step caller contract: a * failing build must still report failure (non-zero) — the #16 fix that * lets procrun return the real exit code must not regress this, since the * w6c/w6a/w6l callers only test `!= 0`. */ static int run_build_fail(const char *bin, const char *driver) { char src[64]; snprintf(src, sizeof src, "/tmp/ww_badbuild_%d.ww", getpid()); FILE *f = fopen(src, "w"); if (!f) return -1; wwtest_fputs("export fn main() i32 = {\n\treturn 1\n", f); /* no ; no } */ fclose(f); char cmd[256]; snprintf(cmd, sizeof cmd, "%s/%s run %s 2>/dev/null", bin, driver, src); int rc = runwait(cmd); unlink(src); return rc; } /* #16 ENFORCE-driver: an unresolvable import is a hard error, not a silent * skip — the driver must exit nonzero AND print "ww: cannot find package * ". Both drivers must agree (rule-10 wording). Returns 0 if the * driver loud-fails as expected, 1 otherwise. (The ww_ww half is why this * lives in 993, the rule-14-safe wwstage-driver range.) */ static int run_missing_pkg(const char *bin, const char *driver) { char tmpdir[80], src[160], errf[160], outbin[160], rmcmd[200]; snprintf(tmpdir, sizeof tmpdir, "/tmp/ww_mp_%s_%d", driver, getpid()); mkdir(tmpdir, 0755); snprintf(src, sizeof src, "%s/mp.ww", tmpdir); snprintf(errf, sizeof errf, "%s/mp.err", tmpdir); snprintf(outbin, sizeof outbin, "%s/mp", tmpdir); snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); FILE *f = fopen(src, "w"); if (!f) { runwait(rmcmd); return 1; } wwtest_fputs("import nosuchpkg;\nexport fn main() i32 = { return 0; };\n", f); fclose(f); /* #8: the sep scratch is mkdir'd before the import-resolution failure, * so a no-o build leaks /tmp/ww_mp_*.sepwork. -o routes it under tmpdir; * rm -rf clears it. The missing-pkg error wording is -o-independent. */ char cmd[512]; snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s 2>%s", bin, driver, outbin, src, errf); int rc = runwait(cmd); int found = 0; FILE *e = fopen(errf, "rb"); if (e) { char buf[4096]; size_t n = fread(buf, 1, sizeof buf - 1, e); fclose(e); buf[n] = '\0'; found = strstr(buf, "cannot find package nosuchpkg") != NULL; } runwait(rmcmd); if (rc == 0 || !found) { fprintf(stderr, "ww_ww FAIL: %s missing-pkg rc=%d found=%d " "(want nonzero + 'cannot find package')\n", driver, rc, found); return 1; } return 0; } int main(void) { const char *bin = absbin(); if (!bin) return 1; char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return 1; /* Stage a tiny program in a place both drivers can reach. * pid-keyed so concurrent runs of this test (or shards of it) don't * race on the staging file. */ char hello_src[64], hello_out[64]; snprintf(hello_src, sizeof hello_src, "/tmp/ww_d_hello_%d.ww", getpid()); snprintf(hello_out, sizeof hello_out, "ww_d_hello_%d", getpid()); { FILE *f = fopen(hello_src, "w"); if (!f) return 1; wwtest_fputs("import os;\n\n" "export fn main() i32 = {\n" "\tos.write(1, \"hi\\n\".ptr, 3u64);\n" "\treturn 0;\n" "};\n", f); fclose(f); } struct { const char *label; const char *src; /* may be relative to cwd */ const char *out; /* basename of expected output */ const char *incs; /* colon-separated -I list, may be NULL */ } cases[] = { { "hello", NULL, NULL, "" }, /* filled in below */ { "wwdump", NULL, "main", NULL }, /* filled in below */ { NULL, NULL, NULL, NULL }, }; cases[0].src = hello_src; cases[0].out = hello_out; /* wwdump case: absolute paths so the driver finds the imports * regardless of the per-driver workdir. */ static char wwdump_src[2048], wwdump_incs[4096]; snprintf(wwdump_src, sizeof wwdump_src, "%s/selfhost/cmd/wwdump/main.ww", cwd); snprintf(wwdump_incs, sizeof wwdump_incs, "%s/lib/ww:%s/lib/encoding/utf8:%s/selfhost/cmd/wcc", cwd, cwd, cwd); cases[1].src = wwdump_src; cases[1].incs = wwdump_incs; int fail = 0; int n = 0; for (int i = 0; cases[i].label; i++) { if (diff_one(bin, cwd, cases[i].label, cases[i].src, cases[i].out, cases[i].incs) != 0) fail++; n++; } unlink(hello_src); /* #16: `ww_ww run` must propagate the child program's real exit code * (the shared procrun helper was collapsing every non-zero exit to 1). * Table-driven; the non-zero rows fail under the old collapse-to-1. * 255 is the single-byte boundary (WEXITSTATUS max). The C ww driver * is the reference (do_run -> WEXITSTATUS); assert ww_ww matches it * row-for-row. */ static const int exit_codes[] = { 0, 7, 42, 255 }; int nrc = (int)(sizeof exit_codes / sizeof exit_codes[0]); int rcfail = 0; for (int i = 0; i < nrc; i++) { int want = exit_codes[i]; int cww = run_exit_code(bin, "ww", want); int wwww = run_exit_code(bin, "ww_ww", want); if (cww != want) { fprintf(stderr, "ww_ww FAIL: C ww run returned %d, " "want %d\n", cww, want); rcfail++; } if (wwww != want) { fprintf(stderr, "ww_ww FAIL: ww_ww run returned %d, " "want %d (exit-code propagation, #16)\n", wwww, want); rcfail++; } } /* Build-step caller contract: a failing build must still report * failure (non-zero), and both drivers must agree. */ int cbad = run_build_fail(bin, "ww"); int wbad = run_build_fail(bin, "ww_ww"); if (cbad == 0 || wbad == 0 || cbad != wbad) { fprintf(stderr, "ww_ww FAIL: build-fail exit C ww=%d ww_ww=%d " "(want equal and non-zero)\n", cbad, wbad); rcfail++; } /* #16 ENFORCE-driver: a missing import package loud-fails (nonzero + * "cannot find package") on BOTH drivers — identical wording. */ rcfail += run_missing_pkg(bin, "ww"); rcfail += run_missing_pkg(bin, "ww_ww"); if (fail || rcfail) { fprintf(stderr, "ww_ww: %d/%d diff(s) failed, %d exit-code " "row(s) failed\n", fail, n, rcfail); return 1; } printf("ww_ww: byte-identical to C ww on %d corpus builds " "(single-file + selfhost/wwdump multi-import); " "run exit-code propagated on %d rows + build-fail parity (#16)\n", n, nrc); return 0; }