wcc,ww: @test under separate compilation (M4 E1, #79)
Make `ww test --sep` work the Hare +test way: the -T synth test-main emits a qualified test.run, and the test package is injected as an ordinary separately-compiled dependency instead of splicing lib/test source into a flat unit. Additive — combined stays the default and 910/997 are untouched (their migration is M4 E2). - compiler synth (both stages): the -T main emits N_DOT test.run plus a synthetic N_USE "test"; cmd/wcc/check.c + selfhost/cmd/wcc/check.ww. - driver (both stages): build_one_sep gains is_test, injects the test package as a root dep, and passes -T to the root; do_test --sep routes a single-file test through the sep producer; cmd/ww/main.c + selfhost/cmd/ww/main.ww. - 989_septest_run gate: ww test --sep on both stages, run-exit + cs==ww byte-id of the sep .s, non-vacuous. The synth's test.run is left ty_err by the checker in both regimes (lib/test's run is scope-keyed under "" not "test"; cgen emits the correct CALL via run's //ww:module test directive) — wwstage tolerates it like cstage (rule-10). The genuine fix, module-keying run under sep so the call type-resolves, is #80. w6c_ww/wwdump_ww/ww_ww move (their embedded source changed); w6a_ww/w6l_ww and the combined codegen output are unchanged.
This commit is contained in:
@@ -3175,16 +3175,40 @@ check_file(Checker *c, Node *file)
|
||||
scope_define_in_module(c->cur, tab->str, NULL, SK_VAR,
|
||||
tt, tab);
|
||||
|
||||
/* return run(__wwtests); */
|
||||
/* return test.run(__wwtests);
|
||||
* QUALIFIED, not bare `run`: under the sep producer
|
||||
* lib/test is a real imported package, so the call must
|
||||
* carry the `test` module qualifier (N_DOT base ident
|
||||
* `test`, member `run`). The combined path tags the
|
||||
* auto-bundled lib/test `//ww:module test` too, so the
|
||||
* qualified form resolves+mangles identically there
|
||||
* (test.run either way) — byte-neutral. The base ident
|
||||
* resolves through a synthetic N_USE injected below. */
|
||||
Node *arg = newnode(c->a, N_IDENT, fp);
|
||||
arg->str = "__wwtests";
|
||||
Node *call = newnode(c->a, N_CALL, fp);
|
||||
call->lhs = newnode(c->a, N_IDENT, fp);
|
||||
call->lhs->str = "run";
|
||||
Node *dot = newnode(c->a, N_DOT, fp);
|
||||
dot->lhs = newnode(c->a, N_IDENT, fp);
|
||||
dot->lhs->str = "test";
|
||||
dot->str = "run";
|
||||
call->lhs = dot;
|
||||
call->list = arg;
|
||||
Node *ret = newnode(c->a, N_RETURN, fp);
|
||||
ret->lhs = call;
|
||||
body->list = ret;
|
||||
/* synth `use test;` so the N_DOT base ident binds as a
|
||||
* module qualifier (SK_USE) and use_path maps `test` to
|
||||
* its import path. Mirror the typedecl-pass N_USE install
|
||||
* (check.c:2892). Appended to file->list below; emits no
|
||||
* asm (cgen keys off module-tagged decls). */
|
||||
Node *usenode = newnode(c->a, N_USE, fp);
|
||||
usenode->str = "test";
|
||||
usenode->strlen = 4;
|
||||
usenode->usepath = "test";
|
||||
scope_define(c->cur, "test", SK_USE, NULL, usenode);
|
||||
Node *ul = file->list;
|
||||
if (ul == NULL) file->list = usenode;
|
||||
else { while (ul->next) ul = ul->next; ul->next = usenode; }
|
||||
}
|
||||
|
||||
Node *m = newnode(c->a, N_FNDECL, fp);
|
||||
|
||||
@@ -1132,7 +1132,7 @@ cache_store(struct sepgraph *g, int pi, const char *manifest,
|
||||
static int
|
||||
build_one_sep(const char *src, int entry_is_dir, const char *out,
|
||||
const char *objstem, const char *extra_includes, const char *extra_libs,
|
||||
const char *extra_libdirs)
|
||||
const char *extra_libdirs, int is_test)
|
||||
{
|
||||
const char *c6 = toolpath("WW_W6C", "w6c");
|
||||
const char *a6 = toolpath("WW_W6A", "w6a");
|
||||
@@ -1197,7 +1197,27 @@ build_one_sep(const char *src, int entry_is_dir, const char *out,
|
||||
struct sepgraph *g = calloc(1, sizeof *g);
|
||||
if (g == NULL) return 1;
|
||||
int root = sep_find_or_add(g, "", src, entry_is_dir);
|
||||
if (root < 0 || sep_scan_pkg(g, root, srcdir) < 0) { free(g); return 1; }
|
||||
if (root < 0) { free(g); return 1; }
|
||||
/* #79 (-T): lib/test is the synth main's `test.run` callee but @test
|
||||
* files never `import test;`. Inject it as a direct dep of the root so
|
||||
* sep_scan_pkg pulls test + its transitive deps; the producer adds -T
|
||||
* to the root and `test.run` links against test's `.a`. Mirrors the
|
||||
* combined path's auto-bundle (build_one is_test) and the sep_scan_file
|
||||
* dedup-guarded dep append. */
|
||||
if (is_test) {
|
||||
char tpath[1024];
|
||||
int tdir = 0;
|
||||
if (locate_import(srcdir, "test", tpath, sizeof tpath, &tdir)) {
|
||||
int ti = sep_find_or_add(g, "test", tpath, tdir);
|
||||
if (ti < 0) { free(g); return 1; }
|
||||
int seen = 0;
|
||||
for (int k = 0; k < g->pkg[root].ndeps; k++)
|
||||
if (g->pkg[root].deps[k] == ti) { seen = 1; break; }
|
||||
if (!seen && g->pkg[root].ndeps < SEP_MAXPKG)
|
||||
g->pkg[root].deps[g->pkg[root].ndeps++] = ti;
|
||||
}
|
||||
}
|
||||
if (sep_scan_pkg(g, root, srcdir) < 0) { free(g); return 1; }
|
||||
for (int i = 0; i < g->n; i++) g->pkg[i].color = 0;
|
||||
int *order = calloc((size_t)g->n, sizeof *order);
|
||||
int *stack = calloc((size_t)g->n, sizeof *stack);
|
||||
@@ -1233,8 +1253,11 @@ build_one_sep(const char *src, int entry_is_dir, const char *out,
|
||||
* export-check rejects. Skip -I for the root; its `.wwi`
|
||||
* is never consumed. */
|
||||
if (pi == root)
|
||||
snprintf(cmd, sizeof cmd, "%s -c -o %s %s",
|
||||
c6, asmf, unitf);
|
||||
/* #79: the root carries -T under `ww test --sep`
|
||||
* so w6c synthesizes the test main. Deps never
|
||||
* get -T. */
|
||||
snprintf(cmd, sizeof cmd, "%s %s-c -o %s %s",
|
||||
c6, is_test ? "-T " : "", asmf, unitf);
|
||||
else
|
||||
snprintf(cmd, sizeof cmd, "%s -c -I %s -o %s %s",
|
||||
c6, wwi, asmf, unitf);
|
||||
@@ -1542,7 +1565,7 @@ do_build(int argc, char **argv)
|
||||
}
|
||||
if (want_sep)
|
||||
return build_one_sep(resolved, is_dir, out, objstem, incs,
|
||||
libs, libdirs);
|
||||
libs, libdirs, 0);
|
||||
return build_one(resolved, is_dir, out, objstem, incs, libs, libdirs, 0);
|
||||
}
|
||||
|
||||
@@ -1606,12 +1629,21 @@ do_test(int argc, char **argv)
|
||||
* wwstage twin (selfhost/cmd/ww/main.ww dotest). */
|
||||
int compileonly = 0;
|
||||
char outstem[1024] = {0};
|
||||
/* #79 E1: --sep routes a single-file/module test through the
|
||||
* separate-compilation producer (build_one_sep, is_test=1) instead of
|
||||
* the amalgamator. Additive (combined stays default); dir-mode --sep is
|
||||
* deferred to E2. */
|
||||
int use_sep = 0;
|
||||
/* #17: an optional second positional after the target is a fnmatch
|
||||
* name-filter pattern, forwarded to the test binary as argv[1]. Only
|
||||
* meaningful for a single test file/module — rejected in dir mode. */
|
||||
const char *pattern = NULL;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
if (strcmp(argv[i], "--sep") == 0) {
|
||||
use_sep = 1;
|
||||
continue;
|
||||
}
|
||||
if (argv[i][1] == 'I') {
|
||||
const char *dir;
|
||||
if (argv[i][2]) {
|
||||
@@ -1664,8 +1696,12 @@ do_test(int argc, char **argv)
|
||||
const char *outp;
|
||||
if (outstem[0]) outp = outstem;
|
||||
else { snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d", getpid()); outp = tmp; }
|
||||
if (build_one(resolved, is_dir, outp, outstem[0] ? outstem : NULL,
|
||||
incs, "", "", 1) != 0) return 1;
|
||||
int br = use_sep
|
||||
? build_one_sep(resolved, is_dir, outp,
|
||||
outstem[0] ? outstem : NULL, incs, "", "", 1)
|
||||
: build_one(resolved, is_dir, outp,
|
||||
outstem[0] ? outstem : NULL, incs, "", "", 1);
|
||||
if (br != 0) return 1;
|
||||
if (compileonly) return 0;
|
||||
int rc = run_test_bin(outp, pattern);
|
||||
if (!outstem[0]) unlink(outp);
|
||||
@@ -1677,8 +1713,12 @@ do_test(int argc, char **argv)
|
||||
const char *outp;
|
||||
if (outstem[0]) outp = outstem;
|
||||
else { snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d", getpid()); outp = tmp; }
|
||||
if (build_one(target, 0, outp, outstem[0] ? outstem : NULL,
|
||||
incs, "", "", 1) != 0) return 1;
|
||||
int br = use_sep
|
||||
? build_one_sep(target, 0, outp, outstem[0] ? outstem : NULL,
|
||||
incs, "", "", 1)
|
||||
: build_one(target, 0, outp, outstem[0] ? outstem : NULL,
|
||||
incs, "", "", 1);
|
||||
if (br != 0) return 1;
|
||||
if (compileonly) return 0;
|
||||
int rc = run_test_bin(outp, pattern);
|
||||
if (!outstem[0]) unlink(outp);
|
||||
@@ -1692,6 +1732,13 @@ do_test(int argc, char **argv)
|
||||
fprintf(stderr, "ww test: -c/-o need a single test file\n");
|
||||
return 2;
|
||||
}
|
||||
/* #79 E1: dir-mode --sep is deferred to E2 — single-file/module is
|
||||
* enough to prove the sep test path. Loud-reject rather than silently
|
||||
* fall back to the combined per-file build. */
|
||||
if (use_sep) {
|
||||
fprintf(stderr, "ww test: --sep needs a single test file\n");
|
||||
return 2;
|
||||
}
|
||||
/* #17: a name-filter pattern is per-binary; directory mode builds one
|
||||
* binary per *_test.ww, so a single pattern can't sensibly route. */
|
||||
if (pattern) {
|
||||
|
||||
Reference in New Issue
Block a user