ww: build intermediates follow -o output stem, not the source dir (test-perf T3a)

build_one/buildone gain an explicit objstem: 'ww build -o X' derives
main.{combined.ww,s,o} beside X; 'ww run' uses its per-pid /tmp stem;
default no--o stays next-to-source (load-bearing for the make regen of
tracked combined.ww and the freshness gate). Realizes the redirect TODO
in 995_self_rebuild.c. Kills the concurrent-test torn-read race on
selfhost/cmd/<tool>/main.* (the 991 transient). 993/995/949 pass -o
into their per-pid workdirs; 949's '-o /dev/null' relied on the old
driver ignoring -o (audited: the only live driver case). Atomic
combined write (os.rename) deferred to its own commit - lib/os lacks
rename and that addition is an import-floor regen.
This commit is contained in:
2026-06-10 17:02:28 +09:00
parent 9b99d0883f
commit 44c47c3ea0
6 changed files with 178 additions and 60 deletions

View File

@@ -35,21 +35,26 @@ static int
run_miss(const char *bin)
{
int pid = getpid();
char src[64], errf[64], cmd[2048], line[4096];
char src[64], errf[64], outb[64], comb[80], cmd[2048], line[4096];
snprintf(src, sizeof src, "/tmp/mp949_miss_%d.ww", pid);
snprintf(errf, sizeof errf, "/tmp/mp949_miss_%d.err", pid);
/* -o a writable /tmp stem (not /dev/null): post-T3 the intermediate
* .combined.ww/.s/.o follow -o, so /dev/null would yield an
* uncreatable /dev/null.combined.ww and mask the missing-pkg fatal. */
snprintf(outb, sizeof outb, "/tmp/mp949_miss_%d.out", pid);
snprintf(comb, sizeof comb, "/tmp/mp949_miss_%d.out.combined.ww", pid);
FILE *f = fopen(src, "w");
if (!f) { fprintf(stderr, "949 FAIL: stage miss\n"); return 1; }
fputs("import nosuchpkg;\n"
"export fn main() i32 = { return 0; };\n", f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s/ww build %s -o /dev/null 2>%s",
bin, src, errf);
snprintf(cmd, sizeof cmd, "%s/ww build %s -o %s 2>%s",
bin, src, outb, errf);
int rc = runwait(cmd);
if (rc == 0) {
fprintf(stderr, "949 FAIL: ww accepted a missing import\n");
unlink(src); unlink(errf);
unlink(src); unlink(errf); unlink(outb); unlink(comb);
return 1;
}
int found = 0;
@@ -61,7 +66,7 @@ run_miss(const char *bin)
}
fclose(f);
}
unlink(src); unlink(errf);
unlink(src); unlink(errf); unlink(outb); unlink(comb);
if (!found) {
fprintf(stderr, "949 FAIL: no 'cannot find package nosuchpkg' "
"on stderr\n");

View File

@@ -62,17 +62,20 @@ slurp_eq(const char *a, const char *b)
* success. */
static int
build_via(const char *bin, const char *driver, const char *src,
const char *workdir, const char *incs)
const char *workdir, const char *incs, const char *out_basename)
{
char cmd[4096];
/* -o <workdir>/<out> 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 -I %s %s 2>/dev/null",
workdir, bin, driver, incs, src);
"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 %s 2>/dev/null",
workdir, bin, driver, src);
"cd %s && timeout 180 %s/%s build -o %s/%s %s 2>/dev/null",
workdir, bin, driver, workdir, out_basename, src);
}
return runwait(cmd);
}
@@ -90,11 +93,11 @@ diff_one(const char *bin, const char *cwd, const char *label,
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) != 0) {
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) != 0) {
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;
}

View File

@@ -82,10 +82,12 @@ struct buildjob {
/* Fork a child that runs the `ww_ww build` for this tool. The child
* inherits no concurrent siblings — system() spawns a fresh /bin/sh -c.
* stderr lands in <workdir>/build.err so concurrent builds don't merge
* their diagnostics. The driver leaves intermediates next to every
* source it traverses (selfhost/cmd/<tool>/main.s, etc.) — task #15 in
* the queue will let us redirect via `-o <outdir>`; until then, test/run
* serializes 990/995 after the parallel phase. */
* their diagnostics. `-o <workdir>/main` (T3) makes the driver write its
* intermediates (main.{combined.ww,s,o}) beside the output in <workdir>
* instead of next to every traversed source — so concurrent driver
* builds no longer share the next-to-source fixed paths, and this gate
* may run in the parallel group. The rebuilt binary still lands at
* <workdir>/main where collect_build diffs it. */
static int
spawn_build(const char *bin, const char *cwd, struct buildjob *j)
{
@@ -99,17 +101,17 @@ spawn_build(const char *bin, const char *cwd, struct buildjob *j)
char cmd[4096];
if (j->inc_local && j->inc_local[0]) {
snprintf(cmd, sizeof cmd,
"cd %s && timeout 180 %s/ww_ww build -I %s/%s "
"cd %s && timeout 180 %s/ww_ww build -o %s/main -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>%s/build.err",
j->workdir, bin, cwd, j->inc_local,
j->workdir, bin, j->workdir, cwd, j->inc_local,
cwd, cwd, cwd, cwd, cwd, j->src_rel, j->workdir);
} else {
snprintf(cmd, sizeof cmd,
"cd %s && timeout 180 %s/ww_ww build "
"cd %s && timeout 180 %s/ww_ww build -o %s/main "
"-I %s/lib/ww -I %s/lib/ww/lex -I %s/lib/ww/parse "
"-I %s/selfhost/cmd/wcc %s/%s >/dev/null 2>%s/build.err",
j->workdir, bin, cwd, cwd, cwd, cwd, cwd, j->src_rel, j->workdir);
j->workdir, bin, j->workdir, cwd, cwd, cwd, cwd, cwd, j->src_rel, j->workdir);
}
pid_t p = fork();