/* * 995_self_rebuild — the wwstage rebuilds itself. * * Drives ww_ww (the ww-side driver, which shells to w6c_ww/w6a_ww/w6l_ww) * over each wwstage tool's source and diffs the result byte-for-byte * against the cstage-built binary in $BIN. A green run means the * toolchain can recompile itself without touching cc, modulo the * cold-start binary — which is the v1.0 lock from PLAN.md. * * This is stricter than `make bootstrap`: that loop only proves the * wwdump cgen self-stabilises; this proves every wwstage tool round- * trips through the wwstage pipeline. */ #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; } 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; } /* Each tool builds via `ww_ww build -I -I lib/ww -I selfhost/cmd/wcc src`. * lib/ww holds the language introspection (lex/tok/ast/parse/typ/sym); * selfhost/cmd/wcc holds the compiler internals (mem/check/cgen*). * Some tools have a local module dir (w6a, w6l with sibling .ww files). * inc_local is "" for tools without one (w6c, ww, wwdump). */ static int rebuild_one(const char *bin, const char *cwd, const char *tool, const char *src_rel, const char *inc_local) { char workdir[64]; snprintf(workdir, sizeof workdir, "/tmp/wwsr_%d_%s", getpid(), tool); char cmd[4096]; snprintf(cmd, sizeof cmd, "rm -rf %s && mkdir -p %s", workdir, workdir); if (runwait(cmd) != 0) return -1; if (inc_local && inc_local[0]) { snprintf(cmd, sizeof cmd, "cd %s && %s/ww_ww build -I %s/%s -I %s/lib/ww -I %s/lib/ww/lex -I %s/lib/ww/parse -I %s/selfhost/cmd/wcc " "%s/%s >/dev/null 2>&1", workdir, bin, cwd, inc_local, cwd, cwd, cwd, cwd, cwd, src_rel); } else { snprintf(cmd, sizeof cmd, "cd %s && %s/ww_ww build -I %s/lib/ww -I %s/lib/ww/lex -I %s/lib/ww/parse -I %s/selfhost/cmd/wcc " "%s/%s >/dev/null 2>&1", workdir, bin, cwd, cwd, cwd, cwd, cwd, src_rel); } if (runwait(cmd) != 0) { fprintf(stderr, "self-rebuild FAIL: ww_ww build errored on %s\n", tool); return -1; } char rebuilt[256], canonical[256]; snprintf(rebuilt, sizeof rebuilt, "%s/main", workdir); snprintf(canonical, sizeof canonical, "%s/%s_ww", bin, tool); int rc = slurp_eq(rebuilt, canonical); if (rc != 0) { fprintf(stderr, "self-rebuild FAIL: %s rebuilt != cstage %s\n", tool, canonical); } /* Leave the driver's intermediates (.s/.o/.combined.ww) next to the * source — 991/992/994 read those fixtures, and `make wwstage` had * already produced byte-identical copies anyway. */ snprintf(cmd, sizeof cmd, "rm -rf %s", workdir); runwait(cmd); return rc; } int main(void) { const char *bin = absbin(); if (!bin) return 1; char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return 1; struct { const char *tool; const char *src; const char *inc_local; } tools[] = { { "w6c", "selfhost/cmd/w6c/main.ww", "" }, { "w6a", "selfhost/cmd/w6a/main.ww", "selfhost/cmd/w6a" }, { "w6l", "selfhost/cmd/w6l/main.ww", "selfhost/cmd/w6l" }, { "ww", "selfhost/cmd/ww/main.ww", "" }, { "wwdump", "selfhost/cmd/wwdump/main.ww", "" }, { NULL, NULL, NULL }, }; int fail = 0, n = 0; for (int i = 0; tools[i].tool; i++) { if (rebuild_one(bin, cwd, tools[i].tool, tools[i].src, tools[i].inc_local) != 0) fail++; n++; } if (fail) { fprintf(stderr, "self-rebuild: %d/%d tool(s) diverged\n", fail, n); return 1; } printf("self-rebuild: %d wwstage tool(s) round-trip byte-identical " "through ww_ww + w6c_ww + w6a_ww + w6l_ww\n", n); return 0; }