ww (selfhost): procrun propagates the real child exit code (fix #16)
The shared fork/exec/wait helper collapsed every non-zero child exit to 1, so `ww_ww run <prog>` lost the program's real exit status (return 42 -> exit 1). The C ww driver's do_run returns WEXITSTATUS(status) — the exact code. procrun now returns the real exit code instead of folding to 1; signal kill still returns 1 and fork/wait failure still returns -1, matching do_run. procrun is shared with the build-step callers (w6c/w6a/w6l), but they only test `!= 0` (success vs failure), so a real non-zero code is still `!= 0` — they are unaffected. dorun's final exec then reports the true program exit code. Driver behavior is not under byte-id (993 pins build_one output bytes, not wait-status handling); 993 extended with table-driven run exit-code rows (0/7/42) that fail under the old collapse-to-1.
This commit is contained in:
@@ -2986,8 +2986,11 @@ fn joinpathlit(dir: *u8, name: str) *u8 = {
|
|||||||
// ---- Subprocess plumbing ----------------------------------------------
|
// ---- Subprocess plumbing ----------------------------------------------
|
||||||
|
|
||||||
// procrun — fork, execve `path` with `argv` (NULL-terminated), wait.
|
// procrun — fork, execve `path` with `argv` (NULL-terminated), wait.
|
||||||
// Returns 0 on clean exit-0, 1 on any non-zero exit or signal kill,
|
// Returns the child's real exit code on clean exit, 1 on signal kill,
|
||||||
// -1 on fork/wait failure.
|
// -1 on fork/wait failure. Mirrors cmd/ww/main.c:do_run WEXITSTATUS:
|
||||||
|
// the build-step callers only test `!= 0`, so propagating the exact
|
||||||
|
// non-zero code leaves them unaffected while `dorun` reports the true
|
||||||
|
// program exit status (was collapsing every non-zero exit to 1; fix #16).
|
||||||
fn procrun(path: *u8, argv: **u8) i32 = {
|
fn procrun(path: *u8, argv: **u8) i32 = {
|
||||||
let pid: i32 = os.fork();
|
let pid: i32 = os.fork();
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
@@ -3009,8 +3012,7 @@ fn procrun(path: *u8, argv: **u8) i32 = {
|
|||||||
// next byte = exit code.
|
// next byte = exit code.
|
||||||
if ((status & 127i32) != 0) { return 1; };
|
if ((status & 127i32) != 0) { return 1; };
|
||||||
let code: i32 = (status >> 8i32) & 255i32;
|
let code: i32 = (status >> 8i32) & 255i32;
|
||||||
if (code != 0) { return 1; };
|
return code;
|
||||||
return 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- `use` resolution + source concatenation --------------------------
|
// ---- `use` resolution + source concatenation --------------------------
|
||||||
|
|||||||
@@ -152,8 +152,11 @@ fn joinpathlit(dir: *u8, name: str) *u8 = {
|
|||||||
// ---- Subprocess plumbing ----------------------------------------------
|
// ---- Subprocess plumbing ----------------------------------------------
|
||||||
|
|
||||||
// procrun — fork, execve `path` with `argv` (NULL-terminated), wait.
|
// procrun — fork, execve `path` with `argv` (NULL-terminated), wait.
|
||||||
// Returns 0 on clean exit-0, 1 on any non-zero exit or signal kill,
|
// Returns the child's real exit code on clean exit, 1 on signal kill,
|
||||||
// -1 on fork/wait failure.
|
// -1 on fork/wait failure. Mirrors cmd/ww/main.c:do_run WEXITSTATUS:
|
||||||
|
// the build-step callers only test `!= 0`, so propagating the exact
|
||||||
|
// non-zero code leaves them unaffected while `dorun` reports the true
|
||||||
|
// program exit status (was collapsing every non-zero exit to 1; fix #16).
|
||||||
fn procrun(path: *u8, argv: **u8) i32 = {
|
fn procrun(path: *u8, argv: **u8) i32 = {
|
||||||
let pid: i32 = os.fork();
|
let pid: i32 = os.fork();
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
@@ -175,8 +178,7 @@ fn procrun(path: *u8, argv: **u8) i32 = {
|
|||||||
// next byte = exit code.
|
// next byte = exit code.
|
||||||
if ((status & 127i32) != 0) { return 1; };
|
if ((status & 127i32) != 0) { return 1; };
|
||||||
let code: i32 = (status >> 8i32) & 255i32;
|
let code: i32 = (status >> 8i32) & 255i32;
|
||||||
if (code != 0) { return 1; };
|
return code;
|
||||||
return 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- `use` resolution + source concatenation --------------------------
|
// ---- `use` resolution + source concatenation --------------------------
|
||||||
|
|||||||
@@ -113,6 +113,27 @@ diff_one(const char *bin, const char *cwd, const char *label,
|
|||||||
return rc;
|
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, "export 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;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
@@ -171,11 +192,38 @@ main(void)
|
|||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
unlink(hello_src);
|
unlink(hello_src);
|
||||||
if (fail) {
|
|
||||||
fprintf(stderr, "ww_ww: %d/%d diff(s) failed\n", fail, n);
|
/* #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.
|
||||||
|
* 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 };
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fail || rcfail) {
|
||||||
|
fprintf(stderr, "ww_ww: %d/%d diff(s) failed, %d exit-code "
|
||||||
|
"row(s) failed\n", fail, n, rcfail);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
printf("ww_ww: byte-identical to C ww on %d corpus builds "
|
printf("ww_ww: byte-identical to C ww on %d corpus builds "
|
||||||
"(single-file + selfhost/wwdump multi-import)\n", n);
|
"(single-file + selfhost/wwdump multi-import); "
|
||||||
|
"run exit-code propagated on %d rows (#16)\n", n, nrc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user