ww: preserve command package identities
This commit is contained in:
8
Makefile
8
Makefile
@@ -226,6 +226,7 @@ $(BIN)/w6a_ww: selfhost/cmd/w6a/main.ww selfhost/cmd/w6a/opcodes.ww \
|
|||||||
$(LIB)/libwwrt.a | $(BIN)
|
$(LIB)/libwwrt.a | $(BIN)
|
||||||
@mkdir -p $(WWBUILD)/w6a_ww
|
@mkdir -p $(WWBUILD)/w6a_ww
|
||||||
@$(CURDIR)/$(BIN)/ww build -w $(WWBUILD)/w6a_ww \
|
@$(CURDIR)/$(BIN)/ww build -w $(WWBUILD)/w6a_ww \
|
||||||
|
-I $(CURDIR)/selfhost/cmd \
|
||||||
-o $(WWBUILD)/w6a_ww/main \
|
-o $(WWBUILD)/w6a_ww/main \
|
||||||
$(CURDIR)/selfhost/cmd/w6a
|
$(CURDIR)/selfhost/cmd/w6a
|
||||||
@mv $(WWBUILD)/w6a_ww/main $@
|
@mv $(WWBUILD)/w6a_ww/main $@
|
||||||
@@ -244,6 +245,7 @@ $(BIN)/w6l_ww: selfhost/cmd/w6l/main.ww selfhost/cmd/w6l/sym.ww \
|
|||||||
$(LIB)/libwwrt.a | $(BIN)
|
$(LIB)/libwwrt.a | $(BIN)
|
||||||
@mkdir -p $(WWBUILD)/w6l_ww
|
@mkdir -p $(WWBUILD)/w6l_ww
|
||||||
@$(CURDIR)/$(BIN)/ww build -w $(WWBUILD)/w6l_ww \
|
@$(CURDIR)/$(BIN)/ww build -w $(WWBUILD)/w6l_ww \
|
||||||
|
-I $(CURDIR)/selfhost/cmd \
|
||||||
-o $(WWBUILD)/w6l_ww/main \
|
-o $(WWBUILD)/w6l_ww/main \
|
||||||
$(CURDIR)/selfhost/cmd/w6l
|
$(CURDIR)/selfhost/cmd/w6l
|
||||||
@mv $(WWBUILD)/w6l_ww/main $@
|
@mv $(WWBUILD)/w6l_ww/main $@
|
||||||
@@ -961,9 +963,11 @@ nocc:
|
|||||||
-I $(CURDIR)/lib/ww \
|
-I $(CURDIR)/lib/ww \
|
||||||
-I $(CURDIR)/selfhost/cmd \
|
-I $(CURDIR)/selfhost/cmd \
|
||||||
-I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/w6c/main.ww && mv main w6c_ww1
|
-I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/w6c/main.ww && mv main w6c_ww1
|
||||||
@cd $(NOCC_BIN) && ./ww build -o main -I $(CURDIR)/lib \
|
@cd $(NOCC_BIN) && ./ww build -o main \
|
||||||
|
-I $(CURDIR)/selfhost/cmd -I $(CURDIR)/lib \
|
||||||
$(CURDIR)/selfhost/cmd/w6a && mv main w6a_ww1
|
$(CURDIR)/selfhost/cmd/w6a && mv main w6a_ww1
|
||||||
@cd $(NOCC_BIN) && ./ww build -o main -I $(CURDIR)/lib \
|
@cd $(NOCC_BIN) && ./ww build -o main \
|
||||||
|
-I $(CURDIR)/selfhost/cmd -I $(CURDIR)/lib \
|
||||||
$(CURDIR)/selfhost/cmd/w6l && mv main w6l_ww1
|
$(CURDIR)/selfhost/cmd/w6l && mv main w6l_ww1
|
||||||
@cd $(NOCC_BIN) && ./ww build -o main -I $(CURDIR)/selfhost/cmd/wcc \
|
@cd $(NOCC_BIN) && ./ww build -o main -I $(CURDIR)/selfhost/cmd/wcc \
|
||||||
-I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/ww/main.ww && mv main ww_ww1
|
-I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/ww/main.ww && mv main ww_ww1
|
||||||
|
|||||||
@@ -31,13 +31,14 @@ struct importin {
|
|||||||
|
|
||||||
static Node *
|
static Node *
|
||||||
parseinput(Arena *a, const char *file, char *buf, u64 len,
|
parseinput(Arena *a, const char *file, char *buf, u64 len,
|
||||||
const char *mod, const char *testsupport, int *bad)
|
const char *mod, const char *testsupport, int commandpackage, int *bad)
|
||||||
{
|
{
|
||||||
Lex l;
|
Lex l;
|
||||||
Parser p;
|
Parser p;
|
||||||
lexinit(&l, a, file, buf, len);
|
lexinit(&l, a, file, buf, len);
|
||||||
parserinit(&p, a, &l);
|
parserinit(&p, a, &l);
|
||||||
p.testmodule = testsupport;
|
p.testmodule = testsupport;
|
||||||
|
p.commandpackage = commandpackage;
|
||||||
if (mod != NULL) {
|
if (mod != NULL) {
|
||||||
p.pathmod = mod;
|
p.pathmod = mod;
|
||||||
p.curmod = mod;
|
p.curmod = mod;
|
||||||
@@ -66,6 +67,7 @@ main(int argc, char **argv)
|
|||||||
const char *testsupport = NULL;
|
const char *testsupport = NULL;
|
||||||
int testmode = 0;
|
int testmode = 0;
|
||||||
int testpackage = 0;
|
int testpackage = 0;
|
||||||
|
int commandpackage = 0;
|
||||||
int entrymode = 0;
|
int entrymode = 0;
|
||||||
int sepmode = 0; /* -c: #22 M3 separate-compile / primary-
|
int sepmode = 0; /* -c: #22 M3 separate-compile / primary-
|
||||||
* only codegen (emit imported==0 decls
|
* only codegen (emit imported==0 decls
|
||||||
@@ -86,6 +88,8 @@ main(int argc, char **argv)
|
|||||||
testmode = 1;
|
testmode = 1;
|
||||||
} else if (strcmp(a, "--test-package") == 0) {
|
} else if (strcmp(a, "--test-package") == 0) {
|
||||||
testpackage = 1;
|
testpackage = 1;
|
||||||
|
} else if (strcmp(a, "--command-package") == 0) {
|
||||||
|
commandpackage = 1;
|
||||||
} else if (strcmp(a, "--entry") == 0) {
|
} else if (strcmp(a, "--entry") == 0) {
|
||||||
entrymode = 1;
|
entrymode = 1;
|
||||||
} else if (strcmp(a, "--test-support-module") == 0) {
|
} else if (strcmp(a, "--test-support-module") == 0) {
|
||||||
@@ -115,7 +119,7 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (src == NULL) {
|
if (src == NULL) {
|
||||||
fputs("usage: w6c [-T|--test-package] [--entry] [-c] [-I out.wwi] "
|
fputs("usage: w6c [-T|--test-package] [--command-package] [--entry] [-c] [-I out.wwi] "
|
||||||
"[--import path dep.wwi]... [-o out.s] file.ww\n", stderr);
|
"[--import path dep.wwi]... [-o out.s] file.ww\n", stderr);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
@@ -123,8 +127,8 @@ main(int argc, char **argv)
|
|||||||
fputs("w6c: --import requires -c\n", stderr);
|
fputs("w6c: --import requires -c\n", stderr);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if ((entrymode || testpackage) && !sepmode) {
|
if ((entrymode || testpackage || commandpackage) && !sepmode) {
|
||||||
fputs("w6c: --entry and --test-package require -c\n", stderr);
|
fputs("w6c: --entry, --test-package, and --command-package require -c\n", stderr);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (testmode && testpackage) {
|
if (testmode && testpackage) {
|
||||||
@@ -162,7 +166,7 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
int bad = 0;
|
int bad = 0;
|
||||||
Node *f = parseinput(a, imports[i].file, imports[i].buf,
|
Node *f = parseinput(a, imports[i].file, imports[i].buf,
|
||||||
imports[i].len, imports[i].path, testsupport, &bad);
|
imports[i].len, imports[i].path, testsupport, 0, &bad);
|
||||||
if (bad) return 1;
|
if (bad) return 1;
|
||||||
appendnodes(&head, &tail, f->list);
|
appendnodes(&head, &tail, f->list);
|
||||||
}
|
}
|
||||||
@@ -174,7 +178,11 @@ main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int bad = 0;
|
int bad = 0;
|
||||||
Node *file = parseinput(a, src, buf, len, NULL, testsupport, &bad);
|
/* --entry classifies an ordinary selected command. The separate
|
||||||
|
* --command-package marker carries the same parser-only fact for command
|
||||||
|
* test variants whose code generation must not expose bare main. */
|
||||||
|
Node *file = parseinput(a, src, buf, len, NULL, testsupport,
|
||||||
|
commandpackage || entrymode, &bad);
|
||||||
if (bad) return 1;
|
if (bad) return 1;
|
||||||
if (head != NULL) {
|
if (head != NULL) {
|
||||||
tail->next = file->list;
|
tail->next = file->list;
|
||||||
|
|||||||
@@ -1575,7 +1575,12 @@ parsefile(Parser *p)
|
|||||||
&& strcmp(p->testmodule, "__wwtest") == 0
|
&& strcmp(p->testmodule, "__wwtest") == 0
|
||||||
&& strcmp(active, "__wwtest") == 0
|
&& strcmp(active, "__wwtest") == 0
|
||||||
&& strcmp(name, "test") == 0;
|
&& strcmp(name, "test") == 0;
|
||||||
if (strcmp(name, last) != 0 && !testsupport) {
|
int commandpackage = p->commandpackage
|
||||||
|
&& p->pathmod == NULL && p->resetmod != NULL
|
||||||
|
&& (strcmp(name, "main") == 0
|
||||||
|
|| strcmp(name, "main_test") == 0);
|
||||||
|
if (strcmp(name, last) != 0 && !testsupport
|
||||||
|
&& !commandpackage) {
|
||||||
errorf(p->cur.pos,
|
errorf(p->cur.pos,
|
||||||
"package %s does not match import path %s",
|
"package %s does not match import path %s",
|
||||||
name, active);
|
name, active);
|
||||||
|
|||||||
@@ -381,6 +381,8 @@ struct Parser {
|
|||||||
* component) instead of overwriting curmod. */
|
* component) instead of overwriting curmod. */
|
||||||
const char *testmodule; /* hidden package-driver alias for toolchain
|
const char *testmodule; /* hidden package-driver alias for toolchain
|
||||||
* `package test`; NULL outside that compile */
|
* `package test`; NULL outside that compile */
|
||||||
|
int commandpackage; /* selected command family: package main/main_test
|
||||||
|
* validates kind without replacing resetmod identity */
|
||||||
};
|
};
|
||||||
|
|
||||||
void parserinit(Parser*, Arena*, Lex*);
|
void parserinit(Parser*, Arena*, Lex*);
|
||||||
|
|||||||
@@ -638,6 +638,37 @@ sep_import_base_valid(const char *path)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Go command packages retain their canonical import identity while declaring
|
||||||
|
* package main. An external command-test variant declares main_test. These
|
||||||
|
* declarations classify package kind but never participate in interning. */
|
||||||
|
static int
|
||||||
|
sep_command_declared_name(const struct seppkg *p)
|
||||||
|
{
|
||||||
|
return p->variant == SEP_VARIANT_EXTERNAL
|
||||||
|
? strcmp(p->name, "main_test") == 0
|
||||||
|
: strcmp(p->name, "main") == 0;
|
||||||
|
}
|
||||||
|
static int
|
||||||
|
sep_forbidden_command_import(const struct sepgraph *g, int importer, int dep)
|
||||||
|
{
|
||||||
|
const struct seppkg *from = &g->pkg[importer];
|
||||||
|
const struct seppkg *to = &g->pkg[dep];
|
||||||
|
if (strcmp(to->name, "main") != 0 || to->role != SEP_ROLE_NORMAL)
|
||||||
|
return 0;
|
||||||
|
/* An external test's exact import of its colocated command production is
|
||||||
|
* test-variant wiring, not a general source-importable command edge. */
|
||||||
|
return !(from->variant == SEP_VARIANT_EXTERNAL
|
||||||
|
&& sep_command_declared_name(from)
|
||||||
|
&& strcmp(from->canon, to->canon) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sep_command_compiler_marker(const struct sepgraph *g, int pi)
|
||||||
|
{
|
||||||
|
return sep_command_declared_name(&g->pkg[pi])
|
||||||
|
&& !g->pkg[pi].link_entry;
|
||||||
|
}
|
||||||
|
|
||||||
/* Bind the canonical ordinary import identity of one provisional directory
|
/* Bind the canonical ordinary import identity of one provisional directory
|
||||||
* action. The action's compiler path is derived from that base and its semantic
|
* action. The action's compiler path is derived from that base and its semantic
|
||||||
* variant; neither requested-root state nor artifact naming participates. */
|
* variant; neither requested-root state nor artifact naming participates. */
|
||||||
@@ -1223,7 +1254,9 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
|||||||
}
|
}
|
||||||
int self = strcmp(canon, g->pkg[pi].canon) == 0;
|
int self = strcmp(canon, g->pkg[pi].canon) == 0;
|
||||||
free(canon);
|
free(canon);
|
||||||
if (self && sep_external_production_name(&g->pkg[pi], name, 1))
|
if (self
|
||||||
|
&& (sep_external_production_name(&g->pkg[pi], name, 1)
|
||||||
|
|| sep_command_declared_name(&g->pkg[pi])))
|
||||||
external_production = 1;
|
external_production = 1;
|
||||||
if (self && !external_production) {
|
if (self && !external_production) {
|
||||||
const char *owner = g->pkg[pi].path[0]
|
const char *owner = g->pkg[pi].path[0]
|
||||||
@@ -1409,7 +1442,8 @@ sep_load_pkg(struct sepgraph *g, int pi, int context)
|
|||||||
&& !g->pkg[pi].test_support) {
|
&& !g->pkg[pi].test_support) {
|
||||||
const char *dot = strrchr(g->pkg[pi].path, '.');
|
const char *dot = strrchr(g->pkg[pi].path, '.');
|
||||||
const char *leaf = dot ? dot + 1 : g->pkg[pi].path;
|
const char *leaf = dot ? dot + 1 : g->pkg[pi].path;
|
||||||
if (strcmp(g->pkg[pi].name, leaf) != 0) {
|
if (strcmp(g->pkg[pi].name, leaf) != 0
|
||||||
|
&& !sep_command_declared_name(&g->pkg[pi])) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"ww: package %s does not match import path %s\n",
|
"ww: package %s does not match import path %s\n",
|
||||||
g->pkg[pi].name, g->pkg[pi].path);
|
g->pkg[pi].name, g->pkg[pi].path);
|
||||||
@@ -1461,11 +1495,20 @@ sep_load_pkg(struct sepgraph *g, int pi, int context)
|
|||||||
/* Mark before recursion so a source cycle terminates here; topo emits the
|
/* Mark before recursion so a source cycle terminates here; topo emits the
|
||||||
* stable cycle diagnostic after all direct bindings are known. */
|
* stable cycle diagnostic after all direct bindings are known. */
|
||||||
g->pkg[pi].context_state[context] = 2;
|
g->pkg[pi].context_state[context] = 2;
|
||||||
for (int k = 0; k < g->pkg[pi].ndeps; k++)
|
for (int k = 0; k < g->pkg[pi].ndeps; k++) {
|
||||||
if (sep_load_pkg(g, g->pkg[pi].deps[k], context) < 0) {
|
int dep = g->pkg[pi].deps[k];
|
||||||
|
if (sep_load_pkg(g, dep, context) < 0) {
|
||||||
g->pkg[pi].failed = 1;
|
g->pkg[pi].failed = 1;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (dep != pi && sep_forbidden_command_import(g, pi, dep)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ww: package %s is a program, not an importable package\n",
|
||||||
|
g->pkg[dep].path[0] ? g->pkg[dep].path : g->pkg[dep].canon);
|
||||||
|
g->pkg[pi].failed = 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1677,7 +1720,8 @@ sep_finalize_directory_identities(struct sepgraph *g)
|
|||||||
int support_alias = p->role == SEP_ROLE_TEST_SUPPORT
|
int support_alias = p->role == SEP_ROLE_TEST_SUPPORT
|
||||||
&& strcmp(p->path, SEP_TEST_SUPPORT_MODULE) == 0
|
&& strcmp(p->path, SEP_TEST_SUPPORT_MODULE) == 0
|
||||||
&& strcmp(p->name, "test") == 0;
|
&& strcmp(p->name, "test") == 0;
|
||||||
if (!support_alias && strcmp(p->name, leaf) != 0) {
|
if (!support_alias && strcmp(p->name, leaf) != 0
|
||||||
|
&& !sep_command_declared_name(p)) {
|
||||||
fprintf(stderr, "ww: package %s does not match import path %s\n",
|
fprintf(stderr, "ww: package %s does not match import path %s\n",
|
||||||
p->name, p->path);
|
p->name, p->path);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -2437,6 +2481,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
|||||||
if (g->pkg[pi].variant == SEP_VARIANT_SAME_TEST
|
if (g->pkg[pi].variant == SEP_VARIANT_SAME_TEST
|
||||||
|| g->pkg[pi].variant == SEP_VARIANT_EXTERNAL)
|
|| g->pkg[pi].variant == SEP_VARIANT_EXTERNAL)
|
||||||
cargv[cpos++] = "--test-package";
|
cargv[cpos++] = "--test-package";
|
||||||
|
if (sep_command_compiler_marker(g, pi))
|
||||||
|
cargv[cpos++] = "--command-package";
|
||||||
if (g->pkg[pi].link_entry)
|
if (g->pkg[pi].link_entry)
|
||||||
cargv[cpos++] = "--entry";
|
cargv[cpos++] = "--entry";
|
||||||
if (g->pkg[pi].test_support) {
|
if (g->pkg[pi].test_support) {
|
||||||
|
|||||||
@@ -2805,9 +2805,14 @@ compilation.
|
|||||||
|
|
||||||
A directory package consists of its immediate regular non-symlink `.ww` files,
|
A directory package consists of its immediate regular non-symlink `.ww` files,
|
||||||
excluding `*_test.ww`, in byte-sorted filename order. Every selected file must
|
excluding `*_test.ww`, in byte-sorted filename order. Every selected file must
|
||||||
declare the same package. An imported directory's declared package must equal
|
declare the same package. An ordinary importable directory's declaration must
|
||||||
the final component of its import path; two logical identities for one physical
|
equal the final component of its import path. A selected command directory
|
||||||
directory are rejected rather than compiled twice.
|
instead declares `package main` to validate command kind while retaining its
|
||||||
|
complete canonical import identity. A source import of a command package from a
|
||||||
|
different directory is rejected; the one same-directory exception is an
|
||||||
|
external `main_test` variant's canonical import of its production action. Two
|
||||||
|
logical identities for one physical directory are rejected rather than compiled
|
||||||
|
twice.
|
||||||
|
|
||||||
Packages compile serially in dependency-first postorder. The compiler emits the
|
Packages compile serially in dependency-first postorder. The compiler emits the
|
||||||
existing deterministic `.wwi` interface for every directory-package action,
|
existing deterministic `.wwi` interface for every directory-package action,
|
||||||
@@ -2839,13 +2844,18 @@ boundary is live in production Cstage and WWstage compilers and drivers.
|
|||||||
An ordinary executable directory root is one normal package action. Its
|
An ordinary executable directory root is one normal package action. Its
|
||||||
finalized canonical import identity tags its owner-only unit; it receives only
|
finalized canonical import identity tags its owner-only unit; it receives only
|
||||||
direct exports, emits `.wwi`, `.o`, and a deterministic `.a`, and is compiled
|
direct exports, emits `.wwi`, `.o`, and a deterministic `.a`, and is compiled
|
||||||
exactly once. The declared package name only validates the last component of
|
exactly once. The declared package name validates the last component of that
|
||||||
that identity. The narrow compiler `--entry` flag controls only bare `main`
|
identity for ordinary importable packages. A selected command root may declare
|
||||||
codegen and is independent of export production. The linker receives that root
|
`package main`; `main` validates its executable role and never replaces or
|
||||||
archive first, then the complete reachable package-archive closure and runtime
|
truncates an identity such as `cmd.tool`. The narrow compiler `--entry` flag
|
||||||
archive; it never receives `.wwi`. The linkers seed `main` before archive
|
both classifies that primary unit as a command and controls bare `main` codegen;
|
||||||
selection, so the existing WWAR member protocol needs no special root object or
|
the package driver uses the parser-only `--command-package` marker for command
|
||||||
format change.
|
test variants that must remain ordinary archive code. Neither flag changes
|
||||||
|
export identity, and imported interfaces remain under strict leaf validation.
|
||||||
|
The linker receives that root archive first, then the complete reachable
|
||||||
|
package-archive closure and runtime archive; it never receives `.wwi`. The
|
||||||
|
linkers seed `main` before archive selection, so the existing WWAR member
|
||||||
|
protocol needs no special root object or format change.
|
||||||
|
|
||||||
`ww build -p -o lib.a DIR` explicitly requests a non-main package product: it
|
`ww build -p -o lib.a DIR` explicitly requests a non-main package product: it
|
||||||
emits a deterministic archive at `lib.a` and its compiler interface at
|
emits a deterministic archive at `lib.a` and its compiler interface at
|
||||||
@@ -2941,7 +2951,11 @@ invocation, each still-unbound directory is finalized by this exact algorithm:
|
|||||||
|
|
||||||
The selected full identity's final component is then validated against the
|
The selected full identity's final component is then validated against the
|
||||||
ordinary declared package name (or against the ordinary leaf obtained by
|
ordinary declared package name (or against the ordinary leaf obtained by
|
||||||
removing `_test` for the external variant). There is no fallback from an empty
|
removing `_test` for the external variant). The one command-kind rule is that a
|
||||||
|
selected command family declares `main`/`main_test` while keeping the finalized
|
||||||
|
ordinary identity unchanged. A source import of that command from another
|
||||||
|
directory rejects as a program before tools; a colocated external command test
|
||||||
|
may reuse the canonical production action. There is no fallback from an empty
|
||||||
import path to a declaration name. Relative, absolute, and symlink spellings
|
import path to a declaration name. Relative, absolute, and symlink spellings
|
||||||
converge through the canonical directory; two unrelated local directories with
|
converge through the canonical directory; two unrelated local directories with
|
||||||
the same declaration therefore remain distinct. One bound import path mapping
|
the same declaration therefore remain distinct. One bound import path mapping
|
||||||
@@ -3292,7 +3306,14 @@ and `b/foo` directories declaring the same `package foo` publish distinct
|
|||||||
coexist in one command under distinct reversible local identities. Equivalent
|
coexist in one command under distinct reversible local identities. Equivalent
|
||||||
recursive spellings reuse the same persistent request directory without new
|
recursive spellings reuse the same persistent request directory without new
|
||||||
compilation, while source imports of the reserved local namespace reject before
|
compilation, while source imports of the reserved local namespace reject before
|
||||||
tool invocation.
|
tool invocation. The exact-argv command root declares `package main` but keeps
|
||||||
|
its non-`main` canonical identity in the unit, export, archive, compiler argv,
|
||||||
|
and linker argv; the bootstrap self-rebuild independently proves the same rule
|
||||||
|
for the real `w6a` and `w6l` command directories. A focused command-test row
|
||||||
|
proves distinct production, internal, external, and generated-main actions,
|
||||||
|
colocated external-production reuse, parser-only command markers, stage-equal
|
||||||
|
unit/export/archive bytes, both test binaries' runtime behavior, and pre-tool
|
||||||
|
rejection when another directory tries to import the command.
|
||||||
|
|
||||||
The exact-argv regression uses the real diamond
|
The exact-argv regression uses the real diamond
|
||||||
`base -> {left,right} -> root`. It proves one compile per node; no input for
|
`base -> {left,right} -> root`. It proves one compile per node; no input for
|
||||||
@@ -3350,6 +3371,10 @@ The pinned official Go 1.26.5 tag (commit
|
|||||||
and the command-global package cache returns the existing package pointer for
|
and the command-global package cache returns the existing package pointer for
|
||||||
a later root or import of the resolved identity
|
a later root or import of the resolved identity
|
||||||
([lines 757–775](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L757-L775)).
|
([lines 757–775](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L757-L775)).
|
||||||
|
Go keeps a command-line `Name == "main"` package as the selected command and
|
||||||
|
rejects an import from a different directory, while permitting the
|
||||||
|
same-directory test-loader edge
|
||||||
|
([lines 799–805](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L799-L805)).
|
||||||
The package's parsed import list becomes its direct package dependencies,
|
The package's parsed import list becomes its direct package dependencies,
|
||||||
rather than a transitive flattening
|
rather than a transitive flattening
|
||||||
([lines 433–440](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L433-L440),
|
([lines 433–440](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L433-L440),
|
||||||
@@ -3358,6 +3383,9 @@ The pinned official Go 1.26.5 tag (commit
|
|||||||
pointer ([`action.go`, lines 202–206](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L202-L206))
|
pointer ([`action.go`, lines 202–206](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L202-L206))
|
||||||
and returns the already-interned action for that key
|
and returns the already-interned action for that key
|
||||||
([lines 437–447](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L447)).
|
([lines 437–447](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L447)).
|
||||||
|
A selected package whose independent `Name` is `main` receives a link action
|
||||||
|
([lines 450–455](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L450-L455))
|
||||||
|
while retaining its ordinary compiled archive action.
|
||||||
A compile action depends on only `p.Internal.Imports`
|
A compile action depends on only `p.Internal.Imports`
|
||||||
([lines 628–658](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L658));
|
([lines 628–658](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L658));
|
||||||
an executable link asks for that same cached root compile action
|
an executable link asks for that same cached root compile action
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ export type parser = struct {
|
|||||||
// Hidden package-driver alias for the toolchain `package test` source.
|
// Hidden package-driver alias for the toolchain `package test` source.
|
||||||
// Empty outside that one separate-compilation edge.
|
// Empty outside that one separate-compilation edge.
|
||||||
testmodule: str,
|
testmodule: str,
|
||||||
|
// Selected command-family validation: main/main_test classifies the
|
||||||
|
// package without replacing the resetmod canonical identity.
|
||||||
|
commandpackage: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn refill(p: *parser) void = {
|
fn refill(p: *parser) void = {
|
||||||
@@ -71,6 +74,7 @@ export fn parserinit(p: *parser, l: *lex) void = {
|
|||||||
p.pathmod = "";
|
p.pathmod = "";
|
||||||
p.resetmod = "";
|
p.resetmod = "";
|
||||||
p.testmodule = "";
|
p.testmodule = "";
|
||||||
|
p.commandpackage = false;
|
||||||
refill(p);
|
refill(p);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -619,7 +623,12 @@ export fn parsefile(p: *parser) *node = {
|
|||||||
p.testmodule, "__wwtest") == 0
|
p.testmodule, "__wwtest") == 0
|
||||||
&& strings.compare(active, "__wwtest") == 0
|
&& strings.compare(active, "__wwtest") == 0
|
||||||
&& strings.compare(name, "test") == 0;
|
&& strings.compare(name, "test") == 0;
|
||||||
if (strings.compare(name, last) != 0 && !testsupport) {
|
let commandpackage: bool = p.commandpackage
|
||||||
|
&& p.pathmod.len == 0 && p.resetmod.len != 0
|
||||||
|
&& (strings.compare(name, "main") == 0
|
||||||
|
|| strings.compare(name, "main_test") == 0);
|
||||||
|
if (strings.compare(name, last) != 0 && !testsupport
|
||||||
|
&& !commandpackage) {
|
||||||
errmsg(p, "package does not match import path");
|
errmsg(p, "package does not match import path");
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
let testsupport: *u8 = nil;
|
let testsupport: *u8 = nil;
|
||||||
let testmode: i32 = 0i32; // #15: `-T` test-mode
|
let testmode: i32 = 0i32; // #15: `-T` test-mode
|
||||||
let testpackage: i32 = 0i32;
|
let testpackage: i32 = 0i32;
|
||||||
|
let commandpackage: i32 = 0i32;
|
||||||
let entrymode: i32 = 0i32;
|
let entrymode: i32 = 0i32;
|
||||||
let sepmode: i32 = 0i32; // -c: #22 M3 separate-compile / primary-
|
let sepmode: i32 = 0i32; // -c: #22 M3 separate-compile / primary-
|
||||||
// only codegen (emit imported==0 decls
|
// only codegen (emit imported==0 decls
|
||||||
@@ -100,6 +101,8 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
testmode = 1i32;
|
testmode = 1i32;
|
||||||
} else { if (cstreq(a, "--test-package")) {
|
} else { if (cstreq(a, "--test-package")) {
|
||||||
testpackage = 1i32;
|
testpackage = 1i32;
|
||||||
|
} else { if (cstreq(a, "--command-package")) {
|
||||||
|
commandpackage = 1i32;
|
||||||
} else { if (cstreq(a, "--entry")) {
|
} else { if (cstreq(a, "--entry")) {
|
||||||
entrymode = 1i32;
|
entrymode = 1i32;
|
||||||
} else { if (cstreq(a, "--test-support-module")) {
|
} else { if (cstreq(a, "--test-support-module")) {
|
||||||
@@ -133,12 +136,12 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
src = a;
|
src = a;
|
||||||
}; }; }; }; }; }; }; }; };
|
}; }; }; }; }; }; }; }; }; };
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (src == nil) {
|
if (src == nil) {
|
||||||
let m: str = "usage: w6c_ww [-T|--test-package] [--entry] [-c] [-I out.wwi] [--import path dep.wwi]... [-o out.s] file.ww\n";
|
let m: str = "usage: w6c_ww [-T|--test-package] [--command-package] [--entry] [-c] [-I out.wwi] [--import path dep.wwi]... [-o out.s] file.ww\n";
|
||||||
os.write(2, m.ptr, m.len: u64);
|
os.write(2, m.ptr, m.len: u64);
|
||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
@@ -147,8 +150,9 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
os.write(2, m.ptr, m.len: u64);
|
os.write(2, m.ptr, m.len: u64);
|
||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
if ((entrymode != 0 || testpackage != 0) && sepmode == 0) {
|
if ((entrymode != 0 || testpackage != 0 || commandpackage != 0)
|
||||||
let m: str = "w6c: --entry and --test-package require -c\n";
|
&& sepmode == 0) {
|
||||||
|
let m: str = "w6c: --entry, --test-package, and --command-package require -c\n";
|
||||||
os.write(2, m.ptr, m.len: u64);
|
os.write(2, m.ptr, m.len: u64);
|
||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
@@ -233,6 +237,9 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
let ps: parser;
|
let ps: parser;
|
||||||
parserinit(&ps, &l);
|
parserinit(&ps, &l);
|
||||||
if (testsupport != nil) { ps.testmodule = pathstr(testsupport); };
|
if (testsupport != nil) { ps.testmodule = pathstr(testsupport); };
|
||||||
|
// Entry compilation classifies an ordinary command package. The explicit
|
||||||
|
// marker carries only that parser fact for non-entry command test variants.
|
||||||
|
ps.commandpackage = commandpackage != 0 || entrymode != 0;
|
||||||
let f: *node = parsefile(&ps);
|
let f: *node = parsefile(&ps);
|
||||||
// Gate cgen on parse-stage errors. Mirrors cmd/w6c/main.c's
|
// Gate cgen on parse-stage errors. Mirrors cmd/w6c/main.c's
|
||||||
// `if (l.errs || p.errs) return 1;` — broken AST otherwise reaches
|
// `if (l.errs || p.errs) return 1;` — broken AST otherwise reaches
|
||||||
|
|||||||
@@ -899,6 +899,33 @@ fn sepimportbasevalid(base: *u8) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Go-style command packages keep their canonical import identity while
|
||||||
|
// declaring main; an external command-test variant declares main_test. These
|
||||||
|
// declarations classify package kind but never enter action identity.
|
||||||
|
fn sepcommanddeclaredname(p: *seppkg) bool = {
|
||||||
|
if (p.name == nil) { return false; };
|
||||||
|
if (p.variant == SEP_VARIANT_EXTERNAL) {
|
||||||
|
return cstreqlit(p.name, "main_test");
|
||||||
|
};
|
||||||
|
return cstreqlit(p.name, "main");
|
||||||
|
};
|
||||||
|
|
||||||
|
fn sepforbiddencommandimport(g: *sepgraph, importer: i32, dep: i32) bool = {
|
||||||
|
let from: *seppkg = &g.pkg[importer];
|
||||||
|
let to: *seppkg = &g.pkg[dep];
|
||||||
|
if (to.name == nil || !cstreqlit(to.name, "main")
|
||||||
|
|| to.role != SEP_ROLE_NORMAL) { return false; };
|
||||||
|
// An external test's exact colocated production edge is variant wiring,
|
||||||
|
// not a general source-importable command-package alias.
|
||||||
|
return !(from.variant == SEP_VARIANT_EXTERNAL
|
||||||
|
&& sepcommanddeclaredname(from)
|
||||||
|
&& cstreq(from.canon, to.canon));
|
||||||
|
};
|
||||||
|
|
||||||
|
fn sepcommandcompilermarker(g: *sepgraph, pi: i32) bool = {
|
||||||
|
return sepcommanddeclaredname(&g.pkg[pi]) && !g.pkg[pi].linkentry;
|
||||||
|
};
|
||||||
|
|
||||||
// Bind a provisional directory action to its canonical ordinary import
|
// Bind a provisional directory action to its canonical ordinary import
|
||||||
// identity. The compiler path is derived from that base and the semantic
|
// identity. The compiler path is derived from that base and the semantic
|
||||||
// variant; root/product/artifact state never participates.
|
// variant; root/product/artifact state never participates.
|
||||||
@@ -1458,7 +1485,8 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
|
|||||||
sepbindadd(bindings, 'D': u8, u.usepath, ipath);
|
sepbindadd(bindings, 'D': u8, u.usepath, ipath);
|
||||||
let self: bool = os.samefile(pathstr(ipath),
|
let self: bool = os.samefile(pathstr(ipath),
|
||||||
pathstr(g.pkg[pi].entry));
|
pathstr(g.pkg[pi].entry));
|
||||||
if (self && sepexternalname(&g.pkg[pi], idp, idn, true)) {
|
if (self && (sepexternalname(&g.pkg[pi], idp, idn, true)
|
||||||
|
|| sepcommanddeclaredname(&g.pkg[pi]))) {
|
||||||
externalproduction = true;
|
externalproduction = true;
|
||||||
};
|
};
|
||||||
if (self && !externalproduction) {
|
if (self && !externalproduction) {
|
||||||
@@ -1719,7 +1747,8 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
|
|||||||
if (g.pkg[pi].path[j] == '.') { leaf = g.pkg[pi].path + j + 1u64; };
|
if (g.pkg[pi].path[j] == '.') { leaf = g.pkg[pi].path + j + 1u64; };
|
||||||
j += 1u64;
|
j += 1u64;
|
||||||
};
|
};
|
||||||
if (!cstreq(g.pkg[pi].name, leaf)) {
|
if (!cstreq(g.pkg[pi].name, leaf)
|
||||||
|
&& !sepcommanddeclaredname(&g.pkg[pi])) {
|
||||||
cerr("ww: package ");
|
cerr("ww: package ");
|
||||||
cerr(pathstr(g.pkg[pi].name));
|
cerr(pathstr(g.pkg[pi].name));
|
||||||
cerr(" does not match import path ");
|
cerr(" does not match import path ");
|
||||||
@@ -1765,7 +1794,17 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
|
|||||||
g.pkg[pi].contextstate[context] = 2u8;
|
g.pkg[pi].contextstate[context] = 2u8;
|
||||||
let k: i32 = 0;
|
let k: i32 = 0;
|
||||||
for (k < g.pkg[pi].ndeps) {
|
for (k < g.pkg[pi].ndeps) {
|
||||||
if (seploadpkg(g, g.pkg[pi].deps[k], context) < 0) {
|
let dep: i32 = g.pkg[pi].deps[k];
|
||||||
|
if (seploadpkg(g, dep, context) < 0) {
|
||||||
|
g.pkg[pi].failed = true;
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
if (dep != pi && sepforbiddencommandimport(g, pi, dep)) {
|
||||||
|
cerr("ww: package ");
|
||||||
|
if (g.pkg[dep].path[0u64] != 0u8) {
|
||||||
|
cerr(pathstr(g.pkg[dep].path));
|
||||||
|
} else { cerr(pathstr(g.pkg[dep].canon)); };
|
||||||
|
cerr(" is a program, not an importable package\n");
|
||||||
g.pkg[pi].failed = true;
|
g.pkg[pi].failed = true;
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
@@ -1997,7 +2036,8 @@ fn sepfinalizedirectoryidentities(g: *sepgraph) i32 = {
|
|||||||
let supportalias: bool = p.role == SEP_ROLE_TEST_SUPPORT
|
let supportalias: bool = p.role == SEP_ROLE_TEST_SUPPORT
|
||||||
&& cstreqlit(p.path, SEP_TEST_SUPPORT_MODULE)
|
&& cstreqlit(p.path, SEP_TEST_SUPPORT_MODULE)
|
||||||
&& cstreqlit(p.name, "test");
|
&& cstreqlit(p.name, "test");
|
||||||
if (!supportalias && !cstreq(p.name, leaf)) {
|
if (!supportalias && !cstreq(p.name, leaf)
|
||||||
|
&& !sepcommanddeclaredname(p)) {
|
||||||
cerr("ww: package "); cerr(pathstr(p.name));
|
cerr("ww: package "); cerr(pathstr(p.name));
|
||||||
cerr(" does not match import path "); cerr(pathstr(p.path));
|
cerr(" does not match import path "); cerr(pathstr(p.path));
|
||||||
cerr("\n");
|
cerr("\n");
|
||||||
@@ -2969,12 +3009,14 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
let gent: bool = g.pkg[pi].generatedmain || rawtest;
|
let gent: bool = g.pkg[pi].generatedmain || rawtest;
|
||||||
let testpkg: bool = g.pkg[pi].variant == SEP_VARIANT_SAME_TEST
|
let testpkg: bool = g.pkg[pi].variant == SEP_VARIANT_SAME_TEST
|
||||||
|| g.pkg[pi].variant == SEP_VARIANT_EXTERNAL;
|
|| g.pkg[pi].variant == SEP_VARIANT_EXTERNAL;
|
||||||
|
let commandpkg: bool = sepcommandcompilermarker(g, pi);
|
||||||
let entry: bool = g.pkg[pi].linkentry;
|
let entry: bool = g.pkg[pi].linkentry;
|
||||||
let supportpkg: bool = g.pkg[pi].testsupport;
|
let supportpkg: bool = g.pkg[pi].testsupport;
|
||||||
let alen: u64 = 8u64;
|
let alen: u64 = 8u64;
|
||||||
if (gent) { alen += 4u64; }
|
if (gent) { alen += 4u64; }
|
||||||
else {
|
else {
|
||||||
if (testpkg) { alen += 1u64; };
|
if (testpkg) { alen += 1u64; };
|
||||||
|
if (commandpkg) { alen += 1u64; };
|
||||||
if (entry) { alen += 1u64; };
|
if (entry) { alen += 1u64; };
|
||||||
if (supportpkg) { alen += 2u64; };
|
if (supportpkg) { alen += 2u64; };
|
||||||
};
|
};
|
||||||
@@ -2988,6 +3030,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
append(argv, testsupportmodule);
|
append(argv, testsupportmodule);
|
||||||
} else {
|
} else {
|
||||||
if (testpkg) { append(argv, "--test-package"); };
|
if (testpkg) { append(argv, "--test-package"); };
|
||||||
|
if (commandpkg) { append(argv, "--command-package"); };
|
||||||
if (entry) { append(argv, "--entry"); };
|
if (entry) { append(argv, "--entry"); };
|
||||||
if (supportpkg) {
|
if (supportpkg) {
|
||||||
append(argv, "--test-support-module");
|
append(argv, "--test-support-module");
|
||||||
|
|||||||
@@ -2609,7 +2609,8 @@ fn localidentity(dir: str, leaf: str) str = {
|
|||||||
let base: str = strings.concat(source, "/ww_root_parity_base_7f3");
|
let base: str = strings.concat(source, "/ww_root_parity_base_7f3");
|
||||||
let left: str = strings.concat(source, "/ww_root_parity_left_7f3");
|
let left: str = strings.concat(source, "/ww_root_parity_left_7f3");
|
||||||
let right: str = strings.concat(source, "/ww_root_parity_right_7f3");
|
let right: str = strings.concat(source, "/ww_root_parity_right_7f3");
|
||||||
let target: str = strings.concat(source, "/main");
|
let rootidentity: str = "ww_root_parity_target_7f3";
|
||||||
|
let target: str = strings.concat(source, "/", rootidentity);
|
||||||
let tools: str = strings.concat(root, "/tool wrappers");
|
let tools: str = strings.concat(root, "/tool wrappers");
|
||||||
assert(os.mkdir(source, 448i32) == 0);
|
assert(os.mkdir(source, 448i32) == 0);
|
||||||
assert(os.mkdir(runtime, 448i32) == 0);
|
assert(os.mkdir(runtime, 448i32) == 0);
|
||||||
@@ -2703,9 +2704,11 @@ fn localidentity(dir: str, leaf: str) str = {
|
|||||||
let workroot: str = strings.concat(bin, ".sepwork");
|
let workroot: str = strings.concat(bin, ".sepwork");
|
||||||
let work: str = strings.concat(workroot, "/");
|
let work: str = strings.concat(workroot, "/");
|
||||||
let artifacts: []str = ["ww_root_parity_base_7f3",
|
let artifacts: []str = ["ww_root_parity_base_7f3",
|
||||||
"ww_root_parity_left_7f3", "ww_root_parity_right_7f3", "main"];
|
"ww_root_parity_left_7f3", "ww_root_parity_right_7f3",
|
||||||
|
rootidentity];
|
||||||
let unitartifacts: []str = ["ww_root_parity_base_7f3",
|
let unitartifacts: []str = ["ww_root_parity_base_7f3",
|
||||||
"ww_root_parity_left_7f3", "ww_root_parity_right_7f3", "main"];
|
"ww_root_parity_left_7f3", "ww_root_parity_right_7f3",
|
||||||
|
rootidentity];
|
||||||
let wantunits: []str = [strings.concat(
|
let wantunits: []str = [strings.concat(
|
||||||
"//ww:module-reset ww_root_parity_base_7f3\n", basea,
|
"//ww:module-reset ww_root_parity_base_7f3\n", basea,
|
||||||
"\n//ww:module-reset ww_root_parity_base_7f3\n", basez, "\n"),
|
"\n//ww:module-reset ww_root_parity_base_7f3\n", basez, "\n"),
|
||||||
@@ -2713,7 +2716,8 @@ fn localidentity(dir: str, leaf: str) str = {
|
|||||||
leftbody, "\n"),
|
leftbody, "\n"),
|
||||||
strings.concat("//ww:module-reset ww_root_parity_right_7f3\n",
|
strings.concat("//ww:module-reset ww_root_parity_right_7f3\n",
|
||||||
rightbody, "\n"),
|
rightbody, "\n"),
|
||||||
strings.concat("//ww:module-reset main\n", rootbody, "\n")];
|
strings.concat("//ww:module-reset ", rootidentity, "\n", rootbody,
|
||||||
|
"\n")];
|
||||||
let referenceunits: []str = ["", "", "", ""];
|
let referenceunits: []str = ["", "", "", ""];
|
||||||
let referenceexports: []str = ["", "", "", ""];
|
let referenceexports: []str = ["", "", "", ""];
|
||||||
let referencearchives: []str = ["", "", "", ""];
|
let referencearchives: []str = ["", "", "", ""];
|
||||||
@@ -2807,6 +2811,11 @@ fn localidentity(dir: str, leaf: str) str = {
|
|||||||
".wwi"));
|
".wwi"));
|
||||||
let archive: str = readfile(strings.concat(work, artifacts[ai],
|
let archive: str = readfile(strings.concat(work, artifacts[ai],
|
||||||
".a"));
|
".a"));
|
||||||
|
if (ai == 3) {
|
||||||
|
assert(strings.hasprefix(exportf, strings.concat("package ",
|
||||||
|
rootidentity, ";\n")));
|
||||||
|
assert(!strings.hasprefix(exportf, "package main;\n"));
|
||||||
|
};
|
||||||
if (si == 0) {
|
if (si == 0) {
|
||||||
referenceexports[ai] = strings.dup(exportf);
|
referenceexports[ai] = strings.dup(exportf);
|
||||||
referencearchives[ai] = strings.dup(archive);
|
referencearchives[ai] = strings.dup(archive);
|
||||||
@@ -2843,16 +2852,28 @@ fn localidentity(dir: str, leaf: str) str = {
|
|||||||
"ww_root_parity_left_7f3.wwi><--import>",
|
"ww_root_parity_left_7f3.wwi><--import>",
|
||||||
"<ww_root_parity_right_7f3><", work,
|
"<ww_root_parity_right_7f3><", work,
|
||||||
"ww_root_parity_right_7f3.wwi><-I><", work,
|
"ww_root_parity_right_7f3.wwi><-I><", work,
|
||||||
"main.wwi><-o><", work,
|
rootidentity, ".wwi><-o><", work,
|
||||||
"main.s><", work, "main.unit.ww>");
|
rootidentity, ".s><", work, rootidentity, ".unit.ww>");
|
||||||
assert(same(linecontaining(ctrace,
|
assert(same(linecontaining(ctrace,
|
||||||
"ww_root_parity_base_7f3.unit.ww"), baseline));
|
"ww_root_parity_base_7f3.unit.ww"), baseline));
|
||||||
assert(same(linecontaining(ctrace,
|
assert(same(linecontaining(ctrace,
|
||||||
"ww_root_parity_left_7f3.unit.ww"), leftline));
|
"ww_root_parity_left_7f3.unit.ww"), leftline));
|
||||||
assert(same(linecontaining(ctrace,
|
assert(same(linecontaining(ctrace,
|
||||||
"ww_root_parity_right_7f3.unit.ww"), rightline));
|
"ww_root_parity_right_7f3.unit.ww"), rightline));
|
||||||
assert(same(linecontaining(ctrace, "main.unit.ww"), rootline));
|
assert(same(linecontaining(ctrace, strings.concat(rootidentity,
|
||||||
|
".unit.ww")), rootline));
|
||||||
assert(!has(rootline, "ww_root_parity_base_7f3.wwi"));
|
assert(!has(rootline, "ww_root_parity_base_7f3.wwi"));
|
||||||
|
// The primary command declaration is accepted only when the compiler
|
||||||
|
// receives command classification from --entry. Imported interfaces and
|
||||||
|
// ordinary package compiles retain strict identity-leaf validation.
|
||||||
|
let rejectwwi: str = strings.concat(root, "/reject-", tags[si], ".wwi");
|
||||||
|
let rejectasm: str = strings.concat(root, "/reject-", tags[si], ".s");
|
||||||
|
let rejectav: []str = [driver(compilers[si]), "-c", "-I", rejectwwi,
|
||||||
|
"-o", rejectasm, strings.concat(work, rootidentity, ".unit.ww")];
|
||||||
|
runcommand(root, strings.concat("command-without-entry-", tags[si]),
|
||||||
|
rejectav, (60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(has(out.stderr, "does not match import path"));
|
||||||
assert(occurrences(atrace, "\n") == 4);
|
assert(occurrences(atrace, "\n") == 4);
|
||||||
ai = 0;
|
ai = 0;
|
||||||
for (ai < artifacts.len) {
|
for (ai < artifacts.len) {
|
||||||
@@ -2864,7 +2885,7 @@ fn localidentity(dir: str, leaf: str) str = {
|
|||||||
};
|
};
|
||||||
assert(occurrences(ltrace, "\n") == 1);
|
assert(occurrences(ltrace, "\n") == 1);
|
||||||
assert(has(ltrace, strings.concat("BEGIN<-o><", bin, "><", work,
|
assert(has(ltrace, strings.concat("BEGIN<-o><", bin, "><", work,
|
||||||
"main.a>")));
|
rootidentity, ".a>")));
|
||||||
ai = 0;
|
ai = 0;
|
||||||
for (ai < artifacts.len) {
|
for (ai < artifacts.len) {
|
||||||
assert(occurrences(ltrace, strings.concat("<", work, artifacts[ai],
|
assert(occurrences(ltrace, strings.concat("<", work, artifacts[ai],
|
||||||
@@ -2935,6 +2956,138 @@ fn localidentity(dir: str, leaf: str) str = {
|
|||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@test fn command_package_test_variants_keep_canonical_identity() void = {
|
||||||
|
let root: str = fresh();
|
||||||
|
let source: str = strings.concat(root, "/source");
|
||||||
|
let target: str = strings.concat(source, "/cmdtool");
|
||||||
|
let importer: str = strings.concat(source, "/importer");
|
||||||
|
let wrapper: str = strings.concat(root, "/command-w6c.sh");
|
||||||
|
assert(os.mkdir(source, 448i32) == 0);
|
||||||
|
assert(os.mkdir(target, 448i32) == 0);
|
||||||
|
assert(os.mkdir(importer, 448i32) == 0);
|
||||||
|
writefile(strings.concat(target, "/main.ww"), strings.concat(
|
||||||
|
"package main;\n",
|
||||||
|
"export fn value() i32 = { return 41; };\n",
|
||||||
|
"fn main() i32 = { return 0; };\n"));
|
||||||
|
writefile(strings.concat(target, "/internal_test.ww"), strings.concat(
|
||||||
|
"package main;\n",
|
||||||
|
"@test fn command_internal() void = { assert(value() == 41); };\n"));
|
||||||
|
writefile(strings.concat(target, "/external_test.ww"), strings.concat(
|
||||||
|
"package main_test;\n",
|
||||||
|
"import cmdtool;\n",
|
||||||
|
"@test fn command_external() void = { ",
|
||||||
|
"assert(cmdtool.value() == 41); };\n"));
|
||||||
|
writefile(strings.concat(importer, "/importer.ww"), strings.concat(
|
||||||
|
"package importer;\n",
|
||||||
|
"import cmdtool;\n",
|
||||||
|
"export fn value() i32 = { return cmdtool.value(); };\n"));
|
||||||
|
writeexecutable(wrapper, strings.concat(
|
||||||
|
"#!/bin/sh\n",
|
||||||
|
"printf 'BEGIN' >> \"$WW_COMMAND_COMPILER_TRACE\"\n",
|
||||||
|
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||||||
|
"\"$WW_COMMAND_COMPILER_TRACE\"; done\n",
|
||||||
|
"printf '\\n' >> \"$WW_COMMAND_COMPILER_TRACE\"\n",
|
||||||
|
"exec \"$WW_COMMAND_REAL_COMPILER\" \"$@\"\n"));
|
||||||
|
|
||||||
|
let stages: []str = ["ww", "ww_ww"];
|
||||||
|
let compilers: []str = ["w6c", "w6c_ww"];
|
||||||
|
let artifacts: []str = ["cmdtool", "cmdtool-internal-test",
|
||||||
|
"cmdtool_test-external-test"];
|
||||||
|
let suffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||||||
|
let reference: []str = ["", "", "", "", "", "", "", "", ""];
|
||||||
|
let samebin: str = strings.concat(target, "/main.test");
|
||||||
|
let externalbin: str = strings.concat(target, "/main_test.test");
|
||||||
|
let workroot: str = strings.concat(samebin, ".sepwork");
|
||||||
|
let work: str = strings.concat(workroot, "/");
|
||||||
|
let baseenv: []str = os.getenvs();
|
||||||
|
let si: i32 = 0;
|
||||||
|
for (si < stages.len) {
|
||||||
|
let trace: str = strings.concat(root, "/", stages[si], ".trace");
|
||||||
|
writefile(trace, "");
|
||||||
|
let env: []str = alloc([], (baseenv.len + 3): u64)!;
|
||||||
|
let ei: i32 = 0;
|
||||||
|
for (ei < baseenv.len) {
|
||||||
|
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_COMMAND_COMPILER_TRACE=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_COMMAND_REAL_COMPILER=")) {
|
||||||
|
append(env, baseenv[ei]);
|
||||||
|
};
|
||||||
|
ei += 1;
|
||||||
|
};
|
||||||
|
append(env, strings.concat("WW_W6C=", wrapper));
|
||||||
|
append(env, strings.concat("WW_COMMAND_COMPILER_TRACE=", trace));
|
||||||
|
append(env, strings.concat("WW_COMMAND_REAL_COMPILER=",
|
||||||
|
driver(compilers[si])));
|
||||||
|
let av: []str = [driver(stages[si]), "test", "-c", "-I", source,
|
||||||
|
target];
|
||||||
|
let out: commandout;
|
||||||
|
runcommandenv(root, strings.concat("command-variants-", stages[si]), av,
|
||||||
|
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
|
||||||
|
let ctrace: str = readfile(trace);
|
||||||
|
let ai: i32 = 0;
|
||||||
|
for (ai < artifacts.len) {
|
||||||
|
let line: str = linecontaining(ctrace,
|
||||||
|
strings.concat(artifacts[ai], ".unit.ww"));
|
||||||
|
assert(has(line, "<--command-package>"));
|
||||||
|
let sj: i32 = 0;
|
||||||
|
for (sj < suffixes.len) {
|
||||||
|
let body: str = readfile(strings.concat(work, artifacts[ai],
|
||||||
|
suffixes[sj]));
|
||||||
|
let ri: i32 = ai * suffixes.len + sj;
|
||||||
|
if (si == 0) { reference[ri] = strings.dup(body); }
|
||||||
|
else { assert(same(reference[ri], body)); };
|
||||||
|
sj += 1;
|
||||||
|
};
|
||||||
|
ai += 1;
|
||||||
|
};
|
||||||
|
assert(has(readfile(strings.concat(work, "cmdtool.unit.ww")),
|
||||||
|
"//ww:module-reset cmdtool\n"));
|
||||||
|
assert(has(readfile(strings.concat(work,
|
||||||
|
"cmdtool-internal-test.unit.ww")),
|
||||||
|
"//ww:module-reset cmdtool\n"));
|
||||||
|
assert(has(readfile(strings.concat(work,
|
||||||
|
"cmdtool_test-external-test.unit.ww")),
|
||||||
|
"//ww:module-reset cmdtool_test\n"));
|
||||||
|
|
||||||
|
let runav: []str = [samebin, "-package=main"];
|
||||||
|
runcommand(root, strings.concat("command-internal-run-", stages[si]),
|
||||||
|
runav, (60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(has(out.stdout, "command_internal ... ok\n"));
|
||||||
|
let externalav: []str = [externalbin, "-package=main_test"];
|
||||||
|
runcommand(root, strings.concat("command-external-run-", stages[si]),
|
||||||
|
externalav, (60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(has(out.stdout, "command_external ... ok\n"));
|
||||||
|
|
||||||
|
// The same command action is not generally source-importable. It is
|
||||||
|
// classified before leaf validation so this Go-like diagnostic is stable,
|
||||||
|
// and graph failure occurs before any compiler invocation.
|
||||||
|
clean(trace);
|
||||||
|
writefile(trace, "");
|
||||||
|
let importout: str = strings.concat(root, "/importer-", stages[si], ".a");
|
||||||
|
let importav: []str = [driver(stages[si]), "build", "-p", "-I", source,
|
||||||
|
"-o", importout, importer];
|
||||||
|
runcommandenv(root, strings.concat("command-import-", stages[si]),
|
||||||
|
importav, env, (60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(has(out.stderr,
|
||||||
|
"package cmdtool is a program, not an importable package\n"));
|
||||||
|
assert(readfile(trace).len == 0);
|
||||||
|
|
||||||
|
clean(workroot);
|
||||||
|
clean(samebin);
|
||||||
|
clean(externalbin);
|
||||||
|
si += 1;
|
||||||
|
};
|
||||||
|
clean(root);
|
||||||
|
};
|
||||||
|
|
||||||
@test fn package_graph_diagnostics_are_stable() void = {
|
@test fn package_graph_diagnostics_are_stable() void = {
|
||||||
let root: str = fresh();
|
let root: str = fresh();
|
||||||
let missing: str = strings.concat(root, "/missing");
|
let missing: str = strings.concat(root, "/missing");
|
||||||
|
|||||||
Reference in New Issue
Block a user