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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user