ww: preserve command package identities

This commit is contained in:
2026-08-13 03:37:08 +09:00
parent 10d24a3237
commit 4377634bb9
10 changed files with 348 additions and 43 deletions

View File

@@ -638,6 +638,37 @@ sep_import_base_valid(const char *path)
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
* action. The action's compiler path is derived from that base and its semantic
* 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;
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;
if (self && !external_production) {
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) {
const char *dot = strrchr(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,
"ww: package %s does not match import path %s\n",
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
* stable cycle diagnostic after all direct bindings are known. */
g->pkg[pi].context_state[context] = 2;
for (int k = 0; k < g->pkg[pi].ndeps; k++)
if (sep_load_pkg(g, g->pkg[pi].deps[k], context) < 0) {
for (int k = 0; k < g->pkg[pi].ndeps; k++) {
int dep = g->pkg[pi].deps[k];
if (sep_load_pkg(g, dep, context) < 0) {
g->pkg[pi].failed = 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;
}
@@ -1677,7 +1720,8 @@ sep_finalize_directory_identities(struct sepgraph *g)
int support_alias = p->role == SEP_ROLE_TEST_SUPPORT
&& strcmp(p->path, SEP_TEST_SUPPORT_MODULE) == 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",
p->name, p->path);
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
|| g->pkg[pi].variant == SEP_VARIANT_EXTERNAL)
cargv[cpos++] = "--test-package";
if (sep_command_compiler_marker(g, pi))
cargv[cpos++] = "--command-package";
if (g->pkg[pi].link_entry)
cargv[cpos++] = "--entry";
if (g->pkg[pi].test_support) {