test/wcc: extend carrier ownership to the bootstrap and platform gates

Same contract as the 27 repaired byte/artifact survivors: checked
acquisition, one all-exit cleanup funnel per carrier, ENOENT-tolerant
checked unlinks, exact-path deletion, and cleanup failure fails a
passing carrier without overwriting its diagnostic. Also closes the
vacuous-green channels: empty-artifact pairs no longer byte-compare
equal, capture and staging failures fail their row loudly, 950 requires
a clean checker exit on no-error rows and hard-fails on a missing
wwdump_ww, 992 keys its scratch per invocation so a failed cleanup
cannot leak stale objects into the next tool's compare, and 995 no
longer pre-sweeps a workdir it never created. No assertion is weakened;
990_selfhost is retired in the next commit rather than repaired.
This commit is contained in:
2026-08-07 23:28:49 +09:00
parent a87d931096
commit cdc8bda721
7 changed files with 332 additions and 105 deletions

View File

@@ -9,32 +9,48 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
static int
runcap(const char *cmd, char **outerr, size_t *outlen)
runcap(const char *cmd, char **outerr, size_t *outlen, int *outrc)
{
char tmp[256];
snprintf(tmp, sizeof tmp, "/tmp/wwd_sc_%d", getpid());
char full[2048];
snprintf(full, sizeof full, "%s 2>%s 1>/dev/null", cmd, tmp);
int ret = -1;
FILE *f = NULL;
char *b = NULL;
long n = 0;
int rc = system(full);
FILE *f = fopen(tmp, "rb");
if (!f) { unlink(tmp); return -1; }
fseek(f, 0, SEEK_END);
long n = ftell(f);
fseek(f, 0, SEEK_SET);
char *b = malloc((size_t)n + 1);
if (!b) { fclose(f); unlink(tmp); return -1; }
if (fread(b, 1, (size_t)n, f) != (size_t)n) {
free(b); fclose(f); unlink(tmp); return -1;
}
if (rc == -1 || !WIFEXITED(rc)) goto cleanup;
f = fopen(tmp, "rb");
if (!f) { perror(tmp); goto cleanup; }
if (fseek(f, 0, SEEK_END) != 0) { perror(tmp); goto cleanup; }
n = ftell(f);
if (n < 0) { perror(tmp); goto cleanup; }
if (fseek(f, 0, SEEK_SET) != 0) { perror(tmp); goto cleanup; }
b = malloc((size_t)n + 1);
if (!b) goto cleanup;
if (fread(b, 1, (size_t)n, f) != (size_t)n) goto cleanup;
b[n] = '\0';
fclose(f); unlink(tmp);
*outerr = b; *outlen = (size_t)n;
(void)rc;
ret = 0;
cleanup:
/* the shell may never have created tmp (fork failure). */
if (f) fclose(f);
if (unlink(tmp) != 0 && errno != ENOENT) {
perror(tmp);
ret = -1;
}
if (ret != 0) {
free(b);
return -1;
}
*outerr = b; *outlen = (size_t)n; *outrc = WEXITSTATUS(rc);
return 0;
}
@@ -44,7 +60,15 @@ writefile(const char *path, const char *src)
FILE *f = fopen(path, "wb");
if (!f) return -1;
wwtest_fputs(src, f);
fclose(f);
/* wwtest_fputs is void; ferror + fclose's flush surface a short
* write (ENOSPC) that would otherwise compile a truncated fixture. */
int werr = ferror(f) != 0;
if (fclose(f) != 0 || werr) {
perror(path);
if (unlink(path) != 0 && errno != ENOENT)
perror(path);
return -1;
}
return 0;
}
@@ -168,9 +192,15 @@ main(void)
if (!bin) bin = "out/bin";
char wwdump[2048];
snprintf(wwdump, sizeof wwdump, "%s/wwdump_ww", bin);
/* bootstrap gate: a missing selfhost checker must FAIL, not skip. */
if (access(wwdump, X_OK) != 0) {
fprintf(stderr, "selfcheck: %s missing or not executable\n",
wwdump);
return 1;
}
int n = sizeof rows / sizeof rows[0];
int fail = 0;
int fail = 0, cleanfail = 0;
for (int i = 0; i < n; i++) {
char path[64];
snprintf(path, sizeof path, "/tmp/wwd_sc_%d_%d.ww", getpid(), i);
@@ -178,7 +208,12 @@ main(void)
char cmd[2048];
snprintf(cmd, sizeof cmd, "%s -r %s", wwdump, path);
char *err = NULL; size_t elen = 0;
runcap(cmd, &err, &elen);
int xrc = -1;
if (runcap(cmd, &err, &elen, &xrc) != 0) {
fprintf(stderr, "row %d: capture failed\n", i);
fail++;
goto rowcleanup;
}
const char *want = rows[i].expect;
int got_match = (err && want && strstr(err, want) != NULL);
int expected_no_err = (want == NULL);
@@ -201,7 +236,9 @@ main(void)
err_present = err_present || (err && strstr(err,
"return: not assignable") != NULL);
int ok;
if (expected_no_err) ok = !err_present;
/* a checker that dies with empty stderr must not pass a
* no-error row: require a clean exit too. */
if (expected_no_err) ok = !err_present && xrc == 0;
else ok = got_match;
if (!ok) {
fprintf(stderr,
@@ -209,13 +246,21 @@ main(void)
i, want ? want : "(no error)", err ? err : "(empty)");
fail++;
}
rowcleanup:
free(err);
unlink(path);
if (unlink(path) != 0 && errno != ENOENT) {
perror(path);
cleanfail = 1;
}
}
if (fail) {
fprintf(stderr, "%d/%d selfcheck rows failed\n", fail, n);
return 1;
}
if (cleanfail) {
fprintf(stderr, "selfcheck: cleanup failed\n");
return 1;
}
printf("selfcheck: %d/%d ok\n", n, n);
return 0;
}

View File

@@ -14,6 +14,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <glob.h>
#include <sys/wait.h>
@@ -63,34 +64,47 @@ static int
diff_one(const char *bin, const char *src)
{
char co[64], wo[64], cmd[2048];
char *bc = NULL, *bw = NULL;
size_t nc = 0, nw = 0;
int rc = -1;
snprintf(co, sizeof co, "/tmp/wwa_%d_c.o", getpid());
snprintf(wo, sizeof wo, "/tmp/wwa_%d_w.o", getpid());
snprintf(cmd, sizeof cmd, "timeout 180 %s/w6a -o %s %s 2>/dev/null", bin, co, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6a_ww FAIL: C w6a errored on %s\n", src);
unlink(co);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "timeout 180 %s/w6a_ww -o %s %s 2>/dev/null", bin, wo, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6a_ww FAIL: ww w6a errored on %s\n", src);
unlink(co); unlink(wo);
return -1;
goto cleanup;
}
char *bc = NULL, *bw = NULL;
size_t nc = 0, nw = 0;
int rc = 0;
if (slurp(co, &bc, &nc) < 0 || slurp(wo, &bw, &nw) < 0) {
fprintf(stderr, "w6a_ww FAIL: cannot read .o for %s\n", src);
rc = -1;
} else if (nc == 0) {
fprintf(stderr, "w6a_ww FAIL: empty .o for %s\n", src);
} else if (nc != nw || memcmp(bc, bw, nc) != 0) {
fprintf(stderr, "w6a_ww FAIL: %s — C %zu vs ww %zu bytes\n",
src, nc, nw);
rc = -1;
}
} else
rc = 0;
cleanup:
free(bc); free(bw);
unlink(co); unlink(wo);
/* co/wo are reused across corpus files; a stale .o left behind
* would be slurped as a later file's output. */
int cleanfail = 0;
if (unlink(co) != 0 && errno != ENOENT) {
perror(co);
cleanfail = 1;
}
if (unlink(wo) != 0 && errno != ENOENT) {
perror(wo);
cleanfail = 1;
}
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -112,6 +126,9 @@ main(void)
* intentionally contains package main plus legacy file-import packages,
* so it is not a directory-package root. */
char stem[256], cmd[4096];
int fail = 0;
int n = 0;
int gen = 0;
snprintf(stem, sizeof stem, "/tmp/wwa_%d_sh", getpid());
snprintf(cmd, sizeof cmd,
"timeout 400 %s/ww build -o %s "
@@ -120,7 +137,9 @@ main(void)
if (runwait(cmd) != 0) {
fprintf(stderr,
"w6a_ww FAIL: cannot sep-build selfhost/cmd/w6a/main.ww\n");
return 1;
/* a failed build can leave a partial stem/.sepwork tree. */
fail = 1;
goto cleanup;
}
/* hand-written runtime — small, covers MOVQ/LEAQ/CALL/SYSCALL/RET;
@@ -129,8 +148,6 @@ main(void)
"rt/start.s", "rt/syscall.s", "rt/abort.s",
"rt/alloc.s", "rt/streq.s", NULL,
};
int fail = 0;
int n = 0;
for (int i = 0; rtfiles[i]; i++) {
char p[2048];
snprintf(p, sizeof p, "%s/%s", cwd, rtfiles[i]);
@@ -152,17 +169,32 @@ main(void)
if (sz <= 0) continue;
if (diff_one(bin, g.gl_pathv[i]) != 0) fail++;
n++;
gen++;
}
globfree(&g);
}
/* the PASS banner claims generated-asm coverage; an empty glob
* (or all-empty units) must not go green on the rt corpus alone. */
if (gen == 0) {
fprintf(stderr,
"w6a_ww FAIL: no non-empty generated .s under %s.sepwork\n",
stem);
fail++;
}
cleanup:
snprintf(cmd, sizeof cmd, "rm -rf %s.sepwork %s", stem, stem);
if (system(cmd)) {}
int cleanfail = runwait(cmd) != 0;
if (cleanfail)
fprintf(stderr, "w6a_ww: cleanup of %s{,.sepwork} failed\n",
stem);
if (fail) {
fprintf(stderr, "w6a_ww: %d/%d diff(s) failed\n", fail, n);
return 1;
}
if (cleanfail)
return 1;
printf("w6a_ww: byte-identical to C w6a on %d corpus files "
"(rt/*.s + selfhost/cmd/w6a sep-built .s)\n", n);
return 0;

View File

@@ -12,6 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
@@ -68,11 +69,15 @@ slurp(const char *path, char **outbuf, size_t *outlen)
* + reverse-topo .a resolution — a stronger link than the old single
* main.o. */
static int
build_and_diff(const char *bin, const char *cwd, const char *tool)
build_and_diff(const char *bin, const char *cwd, const char *tool, int id)
{
char cstem[256], wstem[256], cmd[4096];
snprintf(cstem, sizeof cstem, "/tmp/wwl_%d_c", getpid());
snprintf(wstem, sizeof wstem, "/tmp/wwl_%d_w", getpid());
snprintf(cstem, sizeof cstem, "/tmp/wwl_%d_%d_c", getpid(), id);
snprintf(wstem, sizeof wstem, "/tmp/wwl_%d_%d_w", getpid(), id);
char *bc = NULL, *bw = NULL;
size_t nc = 0, nw = 0;
int rc = 0;
snprintf(cmd, sizeof cmd,
"timeout 300 %s/ww build -o %s "
@@ -80,7 +85,8 @@ build_and_diff(const char *bin, const char *cwd, const char *tool)
bin, cstem, cwd, tool);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww FAIL: C-link sep-build of %s\n", tool);
return -1;
rc = -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd,
"WW_W6L=%s/w6l_ww timeout 300 %s/ww build "
@@ -88,25 +94,43 @@ build_and_diff(const char *bin, const char *cwd, const char *tool)
bin, bin, wstem, cwd, tool);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww FAIL: ww-link sep-build of %s\n", tool);
snprintf(cmd, sizeof cmd, "rm -rf %s %s.sepwork", cstem, cstem);
if (system(cmd)) {}
return -1;
rc = -1;
goto cleanup;
}
char *bc = NULL, *bw = NULL;
size_t nc = 0, nw = 0;
int rc = 0;
if (slurp(cstem, &bc, &nc) < 0 || slurp(wstem, &bw, &nw) < 0) {
fprintf(stderr, "w6l_ww FAIL: cannot read output for %s\n", tool);
rc = -1;
} else if (nc == 0) {
fprintf(stderr, "w6l_ww FAIL: empty output for %s\n", tool);
rc = -1;
} else if (nc != nw || memcmp(bc, bw, nc) != 0) {
fprintf(stderr, "w6l_ww FAIL: %s — C %zu vs ww %zu bytes\n",
tool, nc, nw);
rc = -1;
}
free(bc); free(bw);
snprintf(cmd, sizeof cmd, "rm -rf %s %s %s.sepwork %s.sepwork",
cstem, wstem, cstem, wstem);
if (system(cmd)) {}
cleanup:
/* `ww build` retains these exact caller-owned scratch trees; a
* never-created pid+id-keyed path is harmless to remove, so one
* funnel covers every exit. */
int cleanfail = 0;
if (unlink(cstem) != 0 && errno != ENOENT) {
perror(cstem);
cleanfail = 1;
}
if (unlink(wstem) != 0 && errno != ENOENT) {
perror(wstem);
cleanfail = 1;
}
snprintf(cmd, sizeof cmd, "rm -rf %s.sepwork %s.sepwork",
cstem, wstem);
if (runwait(cmd) != 0)
cleanfail = 1;
if (cleanfail)
fprintf(stderr, "w6l_ww: cleanup failed for %s\n", tool);
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -130,7 +154,7 @@ main(void)
int fail = 0;
int n = 0;
for (int i = 0; tools[i]; i++) {
if (build_and_diff(bin, cwd, tools[i]) != 0) fail++;
if (build_and_diff(bin, cwd, tools[i], i) != 0) fail++;
n++;
}
if (fail) {

View File

@@ -14,6 +14,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -48,11 +49,14 @@ slurp_eq(const char *a, const char *b)
FILE *fb = fopen(b, "rb");
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
int rc = 0;
int any = 0;
for (;;) {
int ca = fgetc(fa);
int cb = fgetc(fb);
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
/* a produced executable can never be legitimately empty. */
if (ca == EOF) { if (!any) rc = -1; break; }
any = 1;
}
fclose(fa); fclose(fb);
return rc;
@@ -92,29 +96,35 @@ diff_one(const char *bin, const char *cwd, const char *label,
snprintf(dw, sizeof dw, "/tmp/ww_d_%d_w", getpid());
char cmd[256];
int rc = -1;
snprintf(cmd, sizeof cmd, "rm -rf %s %s && mkdir -p %s %s", dc, dw, dc, dw);
if (runwait(cmd) != 0) return -1;
if (runwait(cmd) != 0) goto cleanup;
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;
goto cleanup;
}
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;
goto cleanup;
}
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) {
if (slurp_eq(co, wo) != 0)
fprintf(stderr, "ww_ww FAIL: %s — driver outputs differ\n", label);
rc = -1;
}
else
rc = 0;
cleanup:
/* `ww build` retains the caller-owned .sepwork tree inside the
* workdir; rm -rf of the exact pid-keyed pair covers it, and is
* safe even after a partial mkdir. */
snprintf(cmd, sizeof cmd, "rm -rf %s %s", dc, dw);
runwait(cmd);
if (runwait(cmd) != 0 && rc == 0)
rc = -1;
(void)cwd;
return rc;
}
@@ -132,12 +142,21 @@ run_exit_code(const char *bin, const char *driver, int code)
if (!f) return -1;
fprintf(f, "package main;\nexport fn main() i32 = {\n\treturn %d;\n};\n",
code);
fclose(f);
if (fclose(f) != 0) {
perror(src);
if (unlink(src) != 0 && errno != ENOENT)
perror(src);
return -1;
}
char cmd[256];
snprintf(cmd, sizeof cmd, "%s/%s run %s 2>/dev/null", bin, driver, src);
int rc = runwait(cmd);
unlink(src);
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
if (rc == code)
rc = -1;
}
return rc;
}
@@ -152,14 +171,28 @@ 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;
if (!f) {
perror(src);
return -1;
}
wwtest_fputs("export fn main() i32 = {\n\treturn 1\n", f); /* no ; no } */
fclose(f);
if (fclose(f) != 0) {
perror(src);
if (unlink(src) != 0 && errno != ENOENT)
perror(src);
return -1;
}
char cmd[256];
snprintf(cmd, sizeof cmd, "%s/%s run %s 2>/dev/null", bin, driver, src);
int rc = runwait(cmd);
unlink(src);
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
/* cleanup failure maps onto the -1 sentinel the caller rejects;
* a real rc==0 (driver failed to fail) keeps its diagnostic. */
if (rc != 0)
rc = -1;
}
return rc;
}
@@ -173,13 +206,21 @@ 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);
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
return 1;
}
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; }
if (!f) {
perror(src);
if (runwait(rmcmd) != 0)
fprintf(stderr, "ww_ww: cleanup of %s failed\n", tmpdir);
return 1;
}
wwtest_fputs("import nosuchpkg;\nexport fn main() i32 = { return 0; };\n", f);
fclose(f);
@@ -200,12 +241,16 @@ run_missing_pkg(const char *bin, const char *driver)
buf[n] = '\0';
found = strstr(buf, "cannot find package nosuchpkg") != NULL;
}
runwait(rmcmd);
int cleanfail = runwait(rmcmd) != 0;
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;
}
if (cleanfail) {
fprintf(stderr, "ww_ww: cleanup of %s failed\n", tmpdir);
return 1;
}
return 0;
}
@@ -232,7 +277,12 @@ main(void)
"\tos.write(1, \"hi\\n\".ptr, 3u64);\n"
"\treturn 0;\n"
"};\n", f);
fclose(f);
if (fclose(f) != 0) {
perror(hello_src);
if (unlink(hello_src) != 0 && errno != ENOENT)
perror(hello_src);
return 1;
}
}
struct {
@@ -260,13 +310,17 @@ main(void)
int fail = 0;
int n = 0;
int cleanfail = 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);
if (unlink(hello_src) != 0 && errno != ENOENT) {
perror(hello_src);
cleanfail = 1;
}
/* #16: `ww_ww run` must propagate the child program's real exit code.
* Table-driven; the non-zero rows fail under the old collapse-to-1.
@@ -296,7 +350,9 @@ main(void)
* 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) {
/* <= 0 rejects the -1 staging/cleanup sentinel: equal sentinels must
* not pass as build-fail parity when neither driver ever ran. */
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++;
@@ -307,7 +363,7 @@ main(void)
rcfail += run_missing_pkg(bin, "ww");
rcfail += run_missing_pkg(bin, "ww_ww");
if (fail || rcfail) {
if (fail || rcfail || cleanfail) {
fprintf(stderr, "ww_ww: %d/%d diff(s) failed, %d exit-code "
"row(s) failed\n", fail, n, rcfail);
return 1;

View File

@@ -15,6 +15,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <dirent.h>
@@ -65,6 +66,9 @@ static int
diff_one(const char *bin, const char *label, const char *src)
{
char ws[64], cs[64], cmd[2048];
char *bw = NULL, *bc = NULL;
size_t nw = 0, nc = 0;
int rc = -1;
snprintf(ws, sizeof ws, "/tmp/wwc6_%d_w.s", getpid());
snprintf(cs, sizeof cs, "/tmp/wwc6_%d_c.s", getpid());
@@ -72,28 +76,37 @@ diff_one(const char *bin, const char *label, const char *src)
bin, src, ws);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6c_ww FAIL: wwdump_ww -c errored on %s\n", label);
unlink(ws);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "timeout 180 %s/w6c_ww -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6c_ww FAIL: w6c_ww errored on %s\n", label);
unlink(ws); unlink(cs);
return -1;
goto cleanup;
}
char *bw = NULL, *bc = NULL;
size_t nw = 0, nc = 0;
int rc = 0;
if (slurp(ws, &bw, &nw) < 0 || slurp(cs, &bc, &nc) < 0) {
fprintf(stderr, "w6c_ww FAIL: cannot read .s for %s\n", label);
rc = -1;
} else if (nw == 0) {
fprintf(stderr, "w6c_ww FAIL: empty .s for %s\n", label);
} else if (nw != nc || memcmp(bw, bc, nw) != 0) {
fprintf(stderr, "w6c_ww FAIL: %s — wwdump_ww %zu vs w6c_ww %zu bytes\n",
label, nw, nc);
rc = -1;
}
} else
rc = 0;
cleanup:
free(bw); free(bc);
unlink(ws); unlink(cs);
/* cs may not exist when w6c_ww never ran or died before -o. */
int cleanfail = 0;
if (unlink(ws) != 0 && errno != ENOENT) {
perror(ws);
cleanfail = 1;
}
if (unlink(cs) != 0 && errno != ENOENT) {
perror(cs);
cleanfail = 1;
}
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -103,7 +116,15 @@ write_file(const char *path, const char *content)
FILE *f = fopen(path, "wb");
if (!f) return -1;
wwtest_fputs(content, f);
fclose(f);
/* wwtest_fputs is void; ferror is the only short-write witness. */
int bad = ferror(f);
if (fclose(f) != 0) bad = 1;
if (bad) {
perror(path);
if (unlink(path) != 0 && errno != ENOENT)
perror(path);
return -1;
}
return 0;
}
@@ -282,7 +303,10 @@ main(void)
snprintf(src, sizeof src, "/tmp/wwc6_%d_%d.ww", getpid(), i);
if (write_file(src, progs[i].src) != 0) { fail++; n++; continue; }
if (diff_one(bin, progs[i].label, src) != 0) fail++;
unlink(src);
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
fail++;
}
n++;
}
@@ -321,7 +345,14 @@ main(void)
bad[i].label);
fail++;
}
unlink(src); unlink(s);
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
fail++;
}
if (unlink(s) != 0 && errno != ENOENT) {
perror(s);
fail++;
}
n++;
}
@@ -371,7 +402,11 @@ main(void)
for (int s = 0; s < 2; s++) {
snprintf(cmd, sizeof cmd, "rm -rf %s %s.sepwork %s.cache",
stg[s].prog, stg[s].prog, stg[s].prog);
runwait(cmd);
if (runwait(cmd) != 0) {
fprintf(stderr, "Tier-C: cleanup %s failed\n",
stg[s].prog);
fail++;
}
}
}

View File

@@ -18,6 +18,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
@@ -53,12 +54,16 @@ slurp_eq(const char *a, const char *b)
return -1;
}
int rc = 0;
size_t nb = 0;
for (;;) {
int ca = fgetc(fa);
int cb = fgetc(fb);
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
nb++;
}
/* two 0-byte artifacts must not compare green (pkgcache 0-byte poison). */
if (rc == 0 && nb == 0) rc = -1;
fclose(fa); fclose(fb);
return rc;
}
@@ -93,10 +98,10 @@ spawn_build(const char *bin, const char *cwd, struct buildjob *j)
{
snprintf(j->workdir, sizeof j->workdir, "/tmp/wwsr_%d_%s",
getpid(), j->tool);
char setup[256];
snprintf(setup, sizeof setup, "rm -rf %s && mkdir -p %s",
j->workdir, j->workdir);
if (runwait(setup) != 0) return -1;
if (mkdir(j->workdir, 0755) != 0) {
perror(j->workdir);
return -1;
}
char cmd[4096];
if (j->inc_local && j->inc_local[0]) {
@@ -115,7 +120,14 @@ spawn_build(const char *bin, const char *cwd, struct buildjob *j)
}
pid_t p = fork();
if (p < 0) return -1;
if (p < 0) {
char clean[128];
snprintf(clean, sizeof clean, "rm -rf %s", j->workdir);
if (runwait(clean) != 0)
fprintf(stderr, "self-rebuild: cleanup %s failed\n",
j->workdir);
return -1;
}
if (p == 0) {
int rc = system(cmd);
if (rc == -1) _exit(1);
@@ -162,7 +174,11 @@ collect_build(const char *bin, struct buildjob *j)
char cleanup[128];
snprintf(cleanup, sizeof cleanup, "rm -rf %s", j->workdir);
runwait(cleanup);
int cleanfail = runwait(cleanup) != 0;
if (cleanfail)
fprintf(stderr, "self-rebuild: cleanup %s failed\n", j->workdir);
if (cleanfail && rc == 0)
rc = -1;
return rc;
}

View File

@@ -10,6 +10,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
@@ -67,22 +68,23 @@ main(void)
* private /tmp stem (NOT next to the tracked example source), then
* link <stem>.sepwork/__root.o — the self-contained single-package
* object the flip emits in place of the old next-to-source .o. */
char cmd[4096], mbstem[256], mbobj[320];
char cmd[4096], mbstem[256], mbobj[320], co[64], wo[64];
snprintf(mbstem, sizeof mbstem, "/tmp/wwld_%d_mb", getpid());
snprintf(mbobj, sizeof mbobj, "%s.sepwork/__root.o", mbstem);
snprintf(co, sizeof co, "/tmp/wwld_%d_c", getpid());
snprintf(wo, sizeof wo, "/tmp/wwld_%d_w", getpid());
int rc = 0;
snprintf(cmd, sizeof cmd,
"cd %s/examples/mandelbrot && %s/ww build "
"-o %s mandelbrot.ww -L /usr/lib -l c >/dev/null 2>&1",
cwd, bin, mbstem);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: cannot build mandelbrot.o via C driver\n");
return 1;
rc = 1;
goto cleanup;
}
char co[64], wo[64];
snprintf(co, sizeof co, "/tmp/wwld_%d_c", getpid());
snprintf(wo, sizeof wo, "/tmp/wwld_%d_w", getpid());
/* C-side w6l with the dynamic flags. */
snprintf(cmd, sizeof cmd,
"%s/w6l -o %s %s -L /usr/lib "
@@ -90,8 +92,8 @@ main(void)
bin, co, mbobj, cwd);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: C w6l errored\n");
unlink(co);
return 1;
rc = 1;
goto cleanup;
}
/* ww-side w6l with the same flags. */
@@ -101,16 +103,18 @@ main(void)
bin, wo, mbobj, cwd);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: ww w6l errored\n");
unlink(co); unlink(wo);
return 1;
rc = 1;
goto cleanup;
}
char *bc = NULL, *bw = NULL;
size_t nc = 0, nw = 0;
int rc = 0;
if (slurp(co, &bc, &nc) < 0 || slurp(wo, &bw, &nw) < 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: cannot read outputs\n");
rc = 1;
} else if (nc == 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: empty outputs\n");
rc = 1;
} else if (nc != nw || memcmp(bc, bw, nc) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: C %zu vs ww %zu bytes\n",
nc, nw);
@@ -121,9 +125,24 @@ main(void)
"%zu bytes)\n", nc);
}
free(bc); free(bw);
unlink(co); unlink(wo);
/* #93: the sep scratch + tmpdir-owned outputs. */
snprintf(cmd, sizeof cmd, "rm -rf %s.sepwork %s", mbstem, mbstem);
if (system(cmd)) {}
cleanup:
/* #93: `ww build` retains this exact caller-owned scratch tree. */
snprintf(cmd, sizeof cmd, "rm -rf %s.sepwork", mbstem);
int cleanfail = runwait(cmd) != 0;
if (unlink(co) != 0 && errno != ENOENT) {
perror(co);
cleanfail = 1;
}
if (unlink(wo) != 0 && errno != ENOENT) {
perror(wo);
cleanfail = 1;
}
if (unlink(mbstem) != 0 && errno != ENOENT) {
perror(mbstem);
cleanfail = 1;
}
if (cleanfail && rc == 0)
rc = 1;
return rc;
}