ww: intern canonical directory package actions

This commit is contained in:
2026-08-13 00:54:08 +09:00
parent 2dd6b8a250
commit f8ec59999d
3 changed files with 757 additions and 374 deletions

View File

@@ -324,10 +324,10 @@ source_has_test_decl(const char *path)
#define SEP_VARIANT_EXTERNAL 2 #define SEP_VARIANT_EXTERNAL 2
#define SEP_VARIANT_TEST_MAIN 3 #define SEP_VARIANT_TEST_MAIN 3
#define SEP_ROLE_NORMAL 0 #define SEP_ROLE_NORMAL 0
#define SEP_ROLE_EXTERNAL_PRODUCTION 1 #define SEP_ROLE_TEST_SUPPORT 1
#define SEP_ROLE_TEST_SUPPORT 2 #define SEP_ROLE_GENERATED_MAIN 2
#define SEP_ROLE_GENERATED_MAIN 3
#define SEP_TEST_SUPPORT_MODULE "__wwtest" #define SEP_TEST_SUPPORT_MODULE "__wwtest"
#define SEP_IMPORT_PATH_MAX 256
#define SEP_MAXPRODUCT 256 #define SEP_MAXPRODUCT 256
#define SEP_MAXCONTEXT (SEP_MAXPRODUCT + 1) #define SEP_MAXCONTEXT (SEP_MAXPRODUCT + 1)
@@ -510,18 +510,19 @@ enumerate_dir_ww(const char *dirpath, int variant, const char *test_package,
#define SEP_MAXPKG 256 #define SEP_MAXPKG 256
struct seppkg { struct seppkg {
char path[256]; /* canonical dotted package identity */ char path[512]; /* compiler/import identity; derived from import_base */
char import_base[SEP_IMPORT_PATH_MAX]; /* canonical directory import identity */
char entry[1024]; /* resolved package dir (or file, for a file root) */ char entry[1024]; /* resolved package dir (or file, for a file root) */
char canon[1024]; /* canonical location; never package identity */ char canon[1024]; /* canonical location; never package identity */
char artifact[64]; /* non-importable product-root artifact key */ char artifact[512]; /* stable non-importable variant artifact key */
char name[256]; /* validated declared name; directory packages only */ char name[256]; /* validated declared name; directory packages only */
char test_package[256]; /* selected test package; root variants only */ char test_package[256]; /* selected test package; root variants only */
char **sources; /* owned, byte-sorted selected paths; dirs only */ char **sources; /* owned, byte-sorted selected paths; dirs only */
int nsources; int nsources;
int is_dir; int is_dir;
int variant; /* SEP_VARIANT_*; dependencies are production */ int variant; /* SEP_VARIANT_*; dependencies are production */
int role; /* normal, external-production, or test support */ int role; /* normal, reserved test support, or generated main */
int root; /* requested package action (possibly a test variant) */ int root; /* requested usage; never package-action identity */
int link_entry; /* package supplies the executable's bare main */ int link_entry; /* package supplies the executable's bare main */
int generated_main; /* compiler-owned generated test-main package */ int generated_main; /* compiler-owned generated test-main package */
int failed; /* discovery/compile failure reaches this action */ int failed; /* discovery/compile failure reaches this action */
@@ -547,6 +548,7 @@ struct sepgraph {
struct sepcontext context[SEP_MAXCONTEXT]; struct sepcontext context[SEP_MAXCONTEXT];
int ncontext; int ncontext;
int support_context; int support_context;
int identity_failed; /* command-global canonical identity collision */
}; };
struct sepproduct { struct sepproduct {
@@ -570,14 +572,116 @@ struct seplinkflags {
int nlibs; int nlibs;
}; };
static int
sep_directory_variant(int variant)
{
return variant == SEP_VARIANT_PRODUCTION
|| variant == SEP_VARIANT_SAME_TEST
|| variant == SEP_VARIANT_EXTERNAL;
}
static int
sep_variant_path(int variant, const char *base, char *out, size_t outsz)
{
int n;
if (variant == SEP_VARIANT_EXTERNAL)
n = snprintf(out, outsz, "%s_test", base);
else
n = snprintf(out, outsz, "%s", base);
return n >= 0 && (size_t)n < outsz ? 0 : -1;
}
static void
sep_diag_path_locations(const char *path, const char *a, const char *b)
{
if (strcmp(a, b) > 0) { const char *t = a; a = b; b = t; }
fprintf(stderr, "ww: package %s resolves to directories %s and %s\n",
path, a, b);
}
static void
sep_diag_directory_identities(const char *entry, const char *a, const char *b)
{
if (strcmp(a, b) > 0) { const char *t = a; a = b; b = t; }
fprintf(stderr, "ww: package directory %s has import identities %s and %s\n",
entry, a, b);
}
/* 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. */
static int
sep_bind_import_base(struct sepgraph *g, int pi, const char *base)
{
struct seppkg *p = &g->pkg[pi];
if (base == NULL || base[0] == '\0'
|| strlen(base) >= SEP_IMPORT_PATH_MAX) {
fprintf(stderr, "ww: package path is too long (limit %d bytes)\n",
SEP_IMPORT_PATH_MAX - 1);
return -1;
}
if (p->import_base[0] != '\0') {
if (strcmp(p->import_base, base) == 0) return 0;
g->identity_failed = 1;
sep_diag_directory_identities(p->entry, p->import_base, base);
return -1;
}
char path[sizeof p->path];
if (sep_variant_path(p->variant, base, path, sizeof path) < 0) {
fprintf(stderr, "ww: package variant path is too long\n");
return -1;
}
for (int i = 0; i < g->n; i++) {
if (i == pi || !g->pkg[i].is_dir || g->pkg[i].generated_main)
continue;
int same_location = strcmp(g->pkg[i].canon, p->canon) == 0;
int support_alias = p->role == SEP_ROLE_TEST_SUPPORT
|| g->pkg[i].role == SEP_ROLE_TEST_SUPPORT;
if (!support_alias && !same_location
&& g->pkg[i].import_base[0] != '\0'
&& strcmp(g->pkg[i].import_base, base) == 0) {
g->identity_failed = 1;
sep_diag_path_locations(base, g->pkg[i].entry, p->entry);
return -1;
}
if (same_location && !support_alias
&& g->pkg[i].import_base[0] != '\0'
&& strcmp(g->pkg[i].import_base, base) != 0) {
g->identity_failed = 1;
sep_diag_directory_identities(p->entry,
g->pkg[i].import_base, base);
return -1;
}
if (g->pkg[i].path[0] != '\0'
&& strcmp(g->pkg[i].path, path) == 0) {
if (!same_location) {
g->identity_failed = 1;
sep_diag_path_locations(path, g->pkg[i].entry, p->entry);
return -1;
}
if (support_alias
|| g->pkg[i].variant == p->variant) {
g->identity_failed = 1;
fprintf(stderr,
"ww: package action identity collision for %s in %s\n",
path, p->entry);
return -1;
}
}
}
snprintf(p->import_base, sizeof p->import_base, "%s", base);
snprintf(p->path, sizeof p->path, "%s", path);
return 0;
}
static int static int
sep_find_or_add_variant(struct sepgraph *g, const char *path, sep_find_or_add_variant(struct sepgraph *g, const char *path,
const char *entry, int is_dir, int variant, const char *test_package, const char *entry, int is_dir, int variant, const char *test_package,
int role, const char *artifact, int root) int role, const char *artifact, int root)
{ {
if (strlen(path) >= sizeof g->pkg[0].path) { if (path != NULL && strlen(path) >= SEP_IMPORT_PATH_MAX) {
fprintf(stderr, "ww: package path is too long (limit %zu bytes)\n", fprintf(stderr, "ww: package path is too long (limit %d bytes)\n",
sizeof g->pkg[0].path - 1); SEP_IMPORT_PATH_MAX - 1);
return -1; return -1;
} }
char *canon = realpath(entry, NULL); char *canon = realpath(entry, NULL);
@@ -590,104 +694,83 @@ sep_find_or_add_variant(struct sepgraph *g, const char *path,
free(canon); free(canon);
return -1; return -1;
} }
for (int i = 0; i < g->n; i++) { const char *base = path ? path : "";
int same_location = strcmp(g->pkg[i].canon, canon) == 0; char incoming_path[sizeof g->pkg[0].path];
if (root || g->pkg[i].root) { incoming_path[0] = '\0';
if (root && g->pkg[i].root) { if (is_dir && base[0] != '\0'
if (!same_location) continue; && sep_variant_path(variant, base, incoming_path,
if (variant != g->pkg[i].variant) continue; sizeof incoming_path) < 0) {
if (g->pkg[i].role == role fprintf(stderr, "ww: package variant path is too long\n");
&& strcmp(g->pkg[i].path, path) == 0
&& strcmp(g->pkg[i].test_package,
test_package ? test_package : "") == 0) {
/* One canonical directory variant is one compile action,
* even when more than one product requests it. */
free(canon); free(canon);
return i; return -1;
} }
for (int i = 0; i < g->n; i++) {
struct seppkg *q = &g->pkg[i];
int same_location = strcmp(q->canon, canon) == 0;
if (is_dir && q->is_dir && !q->generated_main) {
int support_alias = role == SEP_ROLE_TEST_SUPPORT
|| q->role == SEP_ROLE_TEST_SUPPORT;
if (!support_alias && !same_location && base[0] != '\0'
&& q->import_base[0] != '\0'
&& strcmp(base, q->import_base) == 0) {
g->identity_failed = 1;
sep_diag_path_locations(base, q->entry, entry);
free(canon);
return -1;
}
if (incoming_path[0] != '\0' && q->path[0] != '\0'
&& strcmp(incoming_path, q->path) == 0
&& !same_location) {
g->identity_failed = 1;
sep_diag_path_locations(incoming_path, q->entry, entry);
free(canon);
return -1;
}
if (same_location && q->variant == variant && q->role == role) {
if (variant != SEP_VARIANT_PRODUCTION
&& strcmp(q->test_package,
test_package ? test_package : "") != 0) {
fprintf(stderr, fprintf(stderr,
"ww: incompatible package-test roots %s\n", entry); "ww: incompatible package-test roots %s\n", entry);
free(canon); free(canon);
return -1; return -1;
} }
/* A cycle back to an ordinary production root reuses that root if (base[0] != '\0'
* after loading has assigned its declared package identity. Test && sep_bind_import_base(g, i, base) < 0) {
* roots remain non-importable and distinct from production. */
if (same_location
&& variant == SEP_VARIANT_PRODUCTION
&& g->pkg[i].variant == SEP_VARIANT_PRODUCTION
&& strcmp(g->pkg[i].path, path) == 0) {
free(canon);
return i;
}
if (!same_location) continue;
int root_variant = root ? variant : g->pkg[i].variant;
int production_variant = root ? g->pkg[i].variant : variant;
if (is_dir && g->pkg[i].is_dir
&& root_variant != SEP_VARIANT_PRODUCTION
&& production_variant == SEP_VARIANT_PRODUCTION)
continue;
fprintf(stderr,
"ww: package directory %s has incompatible root and production variants\n",
entry);
free(canon);
return -1;
}
int same_path = strcmp(g->pkg[i].path, path) == 0;
int same_action = same_path && same_location
&& g->pkg[i].variant == variant && g->pkg[i].role == role
&& (role != SEP_ROLE_EXTERNAL_PRODUCTION
|| strcmp(g->pkg[i].artifact,
artifact ? artifact : "") == 0);
if (same_action) {
free(canon);
return i;
}
/* External tests still consume the directory's one canonical
* production action. The role only disambiguates genuinely distinct
* physical packages that share a source qualifier in this request. */
if (same_path && same_location
&& g->pkg[i].variant == SEP_VARIANT_PRODUCTION
&& variant == SEP_VARIANT_PRODUCTION
&& ((role == SEP_ROLE_EXTERNAL_PRODUCTION
&& g->pkg[i].role == SEP_ROLE_NORMAL)
|| (role == SEP_ROLE_NORMAL
&& g->pkg[i].role == SEP_ROLE_EXTERNAL_PRODUCTION))) {
free(canon);
return i;
}
/* Differing physical packages with one source qualifier may need an
* owning-product external action in the command-global universe. A
* per-product closure check below still forbids linking both. */
if (same_path && (role == SEP_ROLE_EXTERNAL_PRODUCTION
|| g->pkg[i].role == SEP_ROLE_EXTERNAL_PRODUCTION))
continue;
if (same_path) {
if (!same_location || g->pkg[i].variant != variant
|| g->pkg[i].role != role) {
fprintf(stderr,
"ww: package %s resolves to more than one location\n",
path[0] ? path : "(root)");
free(canon); free(canon);
return -1; return -1;
} }
q->root = q->root || root;
free(canon); free(canon);
return i; return i;
} }
if (same_location) { if (same_location) {
/* A reserved compiler-generated test-support action is a if (support_alias) continue;
* deliberately distinct qualifier for the same toolchain sources. if (q->import_base[0] != '\0' && base[0] != '\0'
* No ordinary source import can create this role. */ && strcmp(q->import_base, base) != 0) {
if (role == SEP_ROLE_TEST_SUPPORT g->identity_failed = 1;
|| g->pkg[i].role == SEP_ROLE_TEST_SUPPORT) sep_diag_directory_identities(entry,
continue; q->import_base, base);
fprintf(stderr,
"ww: package directory %s has identities %s and %s\n",
entry, g->pkg[i].path[0] ? g->pkg[i].path : "(root)",
path[0] ? path : "(root)");
free(canon); free(canon);
return -1; return -1;
} }
if (sep_directory_variant(q->variant)
&& sep_directory_variant(variant))
continue;
fprintf(stderr,
"ww: package directory %s has incompatible variants\n",
entry);
free(canon);
return -1;
}
continue;
}
if (same_location && strcmp(q->path, base) == 0
&& q->variant == variant && q->role == role) {
q->root = q->root || root;
free(canon);
return i;
}
} }
if (g->n >= SEP_MAXPKG) { if (g->n >= SEP_MAXPKG) {
fprintf(stderr, "ww: too many packages (limit %d)\n", fprintf(stderr, "ww: too many packages (limit %d)\n",
@@ -695,39 +778,43 @@ sep_find_or_add_variant(struct sepgraph *g, const char *path,
free(canon); free(canon);
return -1; return -1;
} }
struct seppkg *p = &g->pkg[g->n]; int ni = g->n++;
snprintf(p->path, sizeof p->path, "%s", path); struct seppkg *p = &g->pkg[ni];
memset(p, 0, sizeof *p);
snprintf(p->entry, sizeof p->entry, "%s", entry); snprintf(p->entry, sizeof p->entry, "%s", entry);
snprintf(p->canon, sizeof p->canon, "%s", canon); snprintf(p->canon, sizeof p->canon, "%s", canon);
p->artifact[0] = '\0';
if (artifact != NULL)
snprintf(p->artifact, sizeof p->artifact, "%s", artifact);
free(canon); free(canon);
p->is_dir = is_dir; p->is_dir = is_dir;
p->variant = variant; p->variant = variant;
p->role = role; p->role = role;
p->root = root; p->root = root;
p->link_entry = 0;
p->generated_main = 0;
p->failed = 0;
p->test_support = 0;
p->loaded = 0;
p->export_changed = 0;
p->emit_context = -1; p->emit_context = -1;
memset(p->context_state, 0, sizeof p->context_state);
p->bindings.paths = NULL;
p->bindings.n = 0;
p->bindings.cap = 0;
p->name[0] = '\0';
p->test_package[0] = '\0';
if (test_package != NULL) if (test_package != NULL)
snprintf(p->test_package, sizeof p->test_package, "%s", snprintf(p->test_package, sizeof p->test_package, "%s",
test_package); test_package);
p->sources = NULL; if (is_dir) {
p->nsources = 0; const char *inherited = base;
p->ndeps = 0; if (inherited[0] == '\0')
p->color = 0; for (int i = 0; i < ni; i++)
return g->n++; if (g->pkg[i].is_dir && !g->pkg[i].generated_main
&& g->pkg[i].role != SEP_ROLE_TEST_SUPPORT
&& role != SEP_ROLE_TEST_SUPPORT
&& strcmp(g->pkg[i].canon, p->canon) == 0
&& g->pkg[i].import_base[0] != '\0') {
inherited = g->pkg[i].import_base;
break;
}
if (inherited[0] != '\0'
&& sep_bind_import_base(g, ni, inherited) < 0) {
g->n--;
return -1;
}
} else {
snprintf(p->path, sizeof p->path, "%s", base);
if (artifact != NULL)
snprintf(p->artifact, sizeof p->artifact, "%s", artifact);
}
return ni;
} }
static int static int
@@ -823,6 +910,7 @@ static int
sep_validate_artifact_paths(const struct sepgraph *g, const char *scratch) sep_validate_artifact_paths(const struct sepgraph *g, const char *scratch)
{ {
for (int i = 0; i < g->n; i++) { for (int i = 0; i < g->n; i++) {
if (g->pkg[i].failed || !g->pkg[i].loaded) continue;
const char *base = g->pkg[i].artifact[0] != '\0' const char *base = g->pkg[i].artifact[0] != '\0'
? g->pkg[i].artifact ? g->pkg[i].artifact
: (g->pkg[i].path[0] != '\0' ? g->pkg[i].path : "__root"); : (g->pkg[i].path[0] != '\0' ? g->pkg[i].path : "__root");
@@ -832,6 +920,19 @@ sep_validate_artifact_paths(const struct sepgraph *g, const char *scratch)
fprintf(stderr, "ww: package artifact path is too long\n"); fprintf(stderr, "ww: package artifact path is too long\n");
return -1; return -1;
} }
for (int j = i + 1; j < g->n; j++) {
if (g->pkg[j].failed || !g->pkg[j].loaded) continue;
const char *other = g->pkg[j].artifact[0] != '\0'
? g->pkg[j].artifact
: (g->pkg[j].path[0] != '\0'
? g->pkg[j].path : "__root");
if (strcmp(base, other) == 0) {
fprintf(stderr,
"ww: package actions share artifact identity %s\n",
base);
return -1;
}
}
} }
return 0; return 0;
} }
@@ -1082,15 +1183,6 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
break; break;
} }
int self = strcmp(canon, g->pkg[pi].canon) == 0; int self = strcmp(canon, g->pkg[pi].canon) == 0;
int runtime_production = 0;
if (external_production)
for (int gi = 0; gi < g->n; gi++)
if (g->pkg[gi].test_support
&& strcmp(g->pkg[gi].path, name) == 0
&& strcmp(g->pkg[gi].canon, canon) == 0) {
runtime_production = 1;
break;
}
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))
external_production = 1; external_production = 1;
@@ -1102,20 +1194,10 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
rc = -1; rc = -1;
break; break;
} }
int di; /* The external package's self-production import is the ordinary
if (external_production && !runtime_production) { * canonical production action for this directory. Discovery role and
char artifact[64]; * the external product's artifact name never create another action. */
int an = snprintf(artifact, sizeof artifact, "%s-production", int di = sep_find_or_add(g, name, ipath, 1);
g->pkg[pi].artifact);
if (an < 0 || (size_t)an >= sizeof artifact) {
rc = -1;
break;
}
di = sep_find_or_add_role(g, name, ipath, 1,
SEP_ROLE_EXTERNAL_PRODUCTION, artifact);
} else {
di = sep_find_or_add(g, name, ipath, 1);
}
if (di < 0) { rc = -1; break; } if (di < 0) { rc = -1; break; }
int seen = 0; int seen = 0;
for (int k = 0; k < g->pkg[pi].ndeps; k++) for (int k = 0; k < g->pkg[pi].ndeps; k++)
@@ -1146,50 +1228,75 @@ sep_dep_cmp(const struct sepgraph *g, int a, int b)
{ {
int r = strcmp(g->pkg[a].path, g->pkg[b].path); int r = strcmp(g->pkg[a].path, g->pkg[b].path);
if (r != 0) return r; if (r != 0) return r;
if (g->pkg[a].variant != g->pkg[b].variant)
return g->pkg[a].variant - g->pkg[b].variant;
if (g->pkg[a].role != g->pkg[b].role) if (g->pkg[a].role != g->pkg[b].role)
return g->pkg[a].role - g->pkg[b].role; return g->pkg[a].role - g->pkg[b].role;
return strcmp(g->pkg[a].artifact, g->pkg[b].artifact); return strcmp(g->pkg[a].canon, g->pkg[b].canon);
} }
/* Add the compiler-owned test main as a real package action. Its semantic /* Add the compiler-owned test main as a real package action. Its identity is a
* identity and artifact key are distinct from every directory variant; its * pure function of the selected variant (and its one command-global support
* only dependencies are the selected test variant and dispatcher support. */ * edge), never a product ordinal. Equivalent products therefore reuse it. */
static int static int
sep_add_generated_main(struct sepgraph *g, struct sepproduct *product, sep_add_generated_main(struct sepgraph *g, struct sepproduct *product,
int ordinal, int support) int ordinal, int support)
{ {
if (g->n >= SEP_MAXPKG) { (void)ordinal;
fprintf(stderr, "ww: too many packages (limit %d)\n", SEP_MAXPKG);
return -1;
}
int variant = product->variant_root; int variant = product->variant_root;
if (variant < 0 || variant >= g->n) return -1; if (variant < 0 || variant >= g->n) return -1;
char variantcanon[sizeof g->pkg[0].canon]; const char *kind = "production";
char variantentry[sizeof g->pkg[0].entry]; if (g->pkg[variant].variant == SEP_VARIANT_SAME_TEST)
snprintf(variantcanon, sizeof variantcanon, "%s", g->pkg[variant].canon); kind = "internal";
snprintf(variantentry, sizeof variantentry, "%s", g->pkg[variant].entry); else if (g->pkg[variant].variant == SEP_VARIANT_EXTERNAL)
struct seppkg *p = &g->pkg[g->n]; kind = "external";
memset(p, 0, sizeof *p); char path[sizeof g->pkg[0].path];
int pn = snprintf(p->path, sizeof p->path, char artifact[sizeof g->pkg[0].artifact];
"__wwtestmain.%03d.main", ordinal); char canon[sizeof g->pkg[0].canon];
int an = snprintf(p->artifact, sizeof p->artifact, char entry[sizeof g->pkg[0].entry];
"__ww-test-%03d-main", ordinal); snprintf(entry, sizeof entry, "%s", g->pkg[variant].entry);
int cn = snprintf(p->canon, sizeof p->canon, "%s#test-main-%03d", const char *variant_artifact = g->pkg[variant].artifact[0]
variantcanon, ordinal); ? g->pkg[variant].artifact : g->pkg[variant].path;
if (pn < 0 || (size_t)pn >= sizeof p->path int pn = snprintf(path, sizeof path, "__wwtestmain.%s.%s.main",
|| an < 0 || (size_t)an >= sizeof p->artifact g->pkg[variant].path, kind);
|| cn < 0 || (size_t)cn >= sizeof p->canon) { int an = snprintf(artifact, sizeof artifact, "%s-main", variant_artifact);
int cn = snprintf(canon, sizeof canon, "%s#%s-test-main",
g->pkg[variant].canon, kind);
if (pn < 0 || (size_t)pn >= sizeof path
|| an < 0 || (size_t)an >= sizeof artifact
|| cn < 0 || (size_t)cn >= sizeof canon) {
fprintf(stderr, "ww: generated test-main identity is too long\n"); fprintf(stderr, "ww: generated test-main identity is too long\n");
return -1; return -1;
} }
for (int i = 0; i < g->n; i++) { for (int i = 0; i < g->n; i++) {
if (strcmp(g->pkg[i].path, p->path) != 0) continue; if (strcmp(g->pkg[i].path, path) != 0) continue;
if (g->pkg[i].generated_main) {
int wants_support = support >= 0 && support != variant;
int has_variant = 0, has_support = 0;
for (int k = 0; k < g->pkg[i].ndeps; k++) {
if (g->pkg[i].deps[k] == variant) has_variant = 1;
if (wants_support && g->pkg[i].deps[k] == support)
has_support = 1;
}
if (has_variant && has_support == wants_support
&& g->pkg[i].ndeps == 1 + wants_support)
return i;
}
fprintf(stderr, fprintf(stderr,
"ww: generated test-main package identity collides with source import %s\n", "ww: generated test-main package identity collides with source import %s\n",
p->path); path);
return -1; return -1;
} }
snprintf(p->entry, sizeof p->entry, "%s", variantentry); if (g->n >= SEP_MAXPKG) {
fprintf(stderr, "ww: too many packages (limit %d)\n", SEP_MAXPKG);
return -1;
}
struct seppkg *p = &g->pkg[g->n];
memset(p, 0, sizeof *p);
snprintf(p->path, sizeof p->path, "%s", path);
snprintf(p->artifact, sizeof p->artifact, "%s", artifact);
snprintf(p->canon, sizeof p->canon, "%s", canon);
snprintf(p->entry, sizeof p->entry, "%s", entry);
snprintf(p->name, sizeof p->name, "main"); snprintf(p->name, sizeof p->name, "main");
p->variant = SEP_VARIANT_TEST_MAIN; p->variant = SEP_VARIANT_TEST_MAIN;
p->role = SEP_ROLE_GENERATED_MAIN; p->role = SEP_ROLE_GENERATED_MAIN;
@@ -1303,15 +1410,6 @@ sep_load_pkg(struct sepgraph *g, int pi, int context)
g->pkg[pi].failed = 1; g->pkg[pi].failed = 1;
return -1; return -1;
} }
/* Give a directory root its declared package identity before recursively
* loading dependencies. Explicit raw single-file compiler fixtures retain
* their historical anonymous multi-package boundary. */
if (g->pkg[pi].root && g->pkg[pi].is_dir
&& g->pkg[pi].path[0] == '\0'
&& g->pkg[pi].name[0] != '\0') {
size_t n = strlen(g->pkg[pi].name);
memcpy(g->pkg[pi].path, g->pkg[pi].name, n + 1);
}
for (int i = 1; i < g->pkg[pi].ndeps; i++) { for (int i = 1; i < g->pkg[pi].ndeps; i++) {
int v = g->pkg[pi].deps[i]; int v = g->pkg[pi].deps[i];
int j = i; int j = i;
@@ -1332,6 +1430,85 @@ sep_load_pkg(struct sepgraph *g, int pi, int context)
return 0; return 0;
} }
/* Finalize every provisional directory root after source discovery but before
* generated-main construction or compilation. A source import binding wins;
* otherwise a manifest-free literal root uses its validated declared package
* name (external p_test roots bind the ordinary base p). */
static int
sep_finalize_directory_identities(struct sepgraph *g)
{
for (int pi = 0; pi < g->n; pi++) {
struct seppkg *p = &g->pkg[pi];
if (!p->is_dir || p->generated_main || p->failed || !p->loaded
|| p->import_base[0] != '\0')
continue;
const char *base = NULL;
for (int i = 0; i < g->n; i++) {
if (i == pi || !g->pkg[i].is_dir || g->pkg[i].generated_main
|| g->pkg[i].role == SEP_ROLE_TEST_SUPPORT
|| p->role == SEP_ROLE_TEST_SUPPORT
|| strcmp(g->pkg[i].canon, p->canon) != 0
|| g->pkg[i].import_base[0] == '\0')
continue;
base = g->pkg[i].import_base;
break;
}
char fallback[SEP_IMPORT_PATH_MAX];
if (base == NULL) {
if (p->name[0] == '\0') {
fprintf(stderr,
"ww: package directory %s has no canonical import identity\n",
p->entry);
return -1;
}
if (p->variant == SEP_VARIANT_EXTERNAL) {
size_t n = strlen(p->name);
if (n <= 5 || strcmp(p->name + n - 5, "_test") != 0) {
fprintf(stderr,
"ww: package-test selector does not name an external package\n");
return -1;
}
memcpy(fallback, p->name, n - 5);
fallback[n - 5] = '\0';
} else {
snprintf(fallback, sizeof fallback, "%s", p->name);
}
base = fallback;
}
if (sep_bind_import_base(g, pi, base) < 0) return -1;
}
for (int pi = 0; pi < g->n; pi++) {
struct seppkg *p = &g->pkg[pi];
if (!p->is_dir || p->generated_main || p->failed || !p->loaded)
continue;
const char *dot = strrchr(p->path, '.');
const char *leaf = dot ? dot + 1 : p->path;
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) {
fprintf(stderr, "ww: package %s does not match import path %s\n",
p->name, p->path);
return -1;
}
p->artifact[0] = '\0';
int n = 0;
char action_path[sizeof p->path];
snprintf(action_path, sizeof action_path, "%s", p->path);
if (p->variant == SEP_VARIANT_SAME_TEST)
n = snprintf(p->artifact, sizeof p->artifact,
"%s-internal-test", action_path);
else if (p->variant == SEP_VARIANT_EXTERNAL)
n = snprintf(p->artifact, sizeof p->artifact,
"%s-external-test", action_path);
if (n < 0 || (size_t)n >= sizeof p->artifact) {
fprintf(stderr, "ww: package variant artifact identity is too long\n");
return -1;
}
}
return 0;
}
/* DFS post-order over the dep DAG → reverse-topo (deps before importer), /* DFS post-order over the dep DAG → reverse-topo (deps before importer),
* cite Hare gather (deps.ha:123). Tri-color: a gray back-edge is a loud * cite Hare gather (deps.ha:123). Tri-color: a gray back-edge is a loud
* dep-cycle reject naming the chain (Hare deps.ha:243); `stack[0..depth)` * dep-cycle reject naming the chain (Hare deps.ha:243); `stack[0..depth)`
@@ -1364,10 +1541,9 @@ sep_topo_visit(struct sepgraph *g, int pi, int *order, int *no,
return 0; return 0;
} }
/* Special external-production actions may share a compiler module qualifier /* Internal test variants replace their colocated production action in the
* with an unrelated normal action in the command-global universe, but never * corresponding test link closure. Canonical package identity has already
* in one product. A single link closure must remain an unambiguous package * made every remaining compiler qualifier globally unambiguous. */
* namespace. */
static int static int
sep_internal_replaces_production(const struct sepgraph *g, int a, int b) sep_internal_replaces_production(const struct sepgraph *g, int a, int b)
{ {
@@ -1822,8 +1998,10 @@ build_one_sep_impl(const char *src, int entry_is_dir,
products[i].context = sep_context_for(g, contextroot, products[i].context = sep_context_for(g, contextroot,
extra_includes, toolsrcdir); extra_includes, toolsrcdir);
if (products[i].context < 0) return 1; if (products[i].context < 0) return 1;
const char *selector = products[i].variant == SEP_VARIANT_PRODUCTION
? NULL : products[i].test_package;
products[i].root = sep_find_or_add_variant(g, rootpath, entry, products[i].root = sep_find_or_add_variant(g, rootpath, entry,
entry_is_dir, products[i].variant, products[i].test_package, entry_is_dir, products[i].variant, selector,
SEP_ROLE_NORMAL, products[i].artifact, 1); SEP_ROLE_NORMAL, products[i].artifact, 1);
if (products[i].root < 0) return 1; if (products[i].root < 0) return 1;
products[i].variant_root = products[i].root; products[i].variant_root = products[i].root;
@@ -1919,10 +2097,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
g->pkg[root].failed = 1; g->pkg[root].failed = 1;
continue; continue;
} }
if (products[i].variant != SEP_VARIANT_PRODUCTION if (products[i].test_package != NULL
&& (products[i].test_package == NULL && strcmp(g->pkg[root].name, products[i].test_package) != 0) {
|| strcmp(g->pkg[root].name,
products[i].test_package) != 0)) {
fprintf(stderr, fprintf(stderr,
"ww: package-test selector does not match loaded package\n"); "ww: package-test selector does not match loaded package\n");
g->pkg[root].failed = 1; g->pkg[root].failed = 1;
@@ -1935,12 +2111,22 @@ build_one_sep_impl(const char *src, int entry_is_dir,
if (support >= 0 && support != variant if (support >= 0 && support != variant
&& sep_load_pkg(g, support, products[i].context) < 0) && sep_load_pkg(g, support, products[i].context) < 0)
g->pkg[variant].failed = 1; g->pkg[variant].failed = 1;
}
}
if (g->identity_failed) return 1;
if (sep_finalize_directory_identities(g) < 0) return 1;
if (is_test && entry_is_dir) {
for (int i = 0; i < nproducts; i++) {
int variant = products[i].variant_root;
int support = support_for[i];
if (g->pkg[variant].failed
|| (support >= 0 && g->pkg[support].failed)) {
products[i].root = variant;
continue;
}
int mainpkg = sep_add_generated_main(g, &products[i], i, int mainpkg = sep_add_generated_main(g, &products[i], i,
support); support);
if (mainpkg < 0) return 1; if (mainpkg < 0) return 1;
if (g->pkg[variant].failed
|| (support >= 0 && g->pkg[support].failed))
g->pkg[mainpkg].failed = 1;
products[i].root = mainpkg; products[i].root = mainpkg;
} }
} }
@@ -2058,7 +2244,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
cargv[cpos++] = "--test-support-module"; cargv[cpos++] = "--test-support-module";
cargv[cpos++] = (char *)test_support_module; cargv[cpos++] = (char *)test_support_module;
} else { } else {
if (is_test && g->pkg[pi].root) if (g->pkg[pi].variant == SEP_VARIANT_SAME_TEST
|| g->pkg[pi].variant == SEP_VARIANT_EXTERNAL)
cargv[cpos++] = "--test-package"; cargv[cpos++] = "--test-package";
if (g->pkg[pi].link_entry) if (g->pkg[pi].link_entry)
cargv[cpos++] = "--entry"; cargv[cpos++] = "--entry";
@@ -2297,7 +2484,7 @@ build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
.root = -1, .root = -1,
.variant_root = -1, .variant_root = -1,
}; };
if (!package_only) if (!package_only && !entry_is_dir)
snprintf(product.artifact, sizeof product.artifact, "__root"); snprintf(product.artifact, sizeof product.artifact, "__root");
int r = build_one_sep_impl(src, entry_is_dir, root_identity, out, objstem, int r = build_one_sep_impl(src, entry_is_dir, root_identity, out, objstem,
extra_includes, linkflags, package_only, is_test, extra_includes, linkflags, package_only, is_test,
@@ -2714,9 +2901,13 @@ do_test(int argc, char **argv)
const char *output = argv[++i]; const char *output = argv[++i];
const char *status = argv[++i]; const char *status = argv[++i];
size_t pn = strlen(name); size_t pn = strlen(name);
int variant = strcmp(kind, "same") == 0 int variant = SEP_VARIANT_EXTERNAL;
? SEP_VARIANT_SAME_TEST : SEP_VARIANT_EXTERNAL; if (strcmp(kind, "production") == 0)
if ((strcmp(kind, "same") != 0 variant = SEP_VARIANT_PRODUCTION;
else if (strcmp(kind, "same") == 0)
variant = SEP_VARIANT_SAME_TEST;
if ((strcmp(kind, "production") != 0
&& strcmp(kind, "same") != 0
&& strcmp(kind, "external") != 0) && strcmp(kind, "external") != 0)
|| pn == 0 || pn >= sizeof ((struct seppkg *)0)->name || pn == 0 || pn >= sizeof ((struct seppkg *)0)->name
|| dir[0] == '\0' || output[0] == '\0' || dir[0] == '\0' || output[0] == '\0'
@@ -2806,10 +2997,9 @@ do_test(int argc, char **argv)
"ww test: duplicate --ww-package-test variant for directory\n"); "ww test: duplicate --ww-package-test variant for directory\n");
return 2; return 2;
} }
snprintf(products[i].artifact, sizeof products[i].artifact, /* Artifact identity is derived from canonical package identity after
"__ww-test-%03d-%s", i, * discovery; product position is deliberately not an action key. */
products[i].variant == SEP_VARIANT_SAME_TEST products[i].artifact[0] = '\0';
? "same" : "external");
} }
if (nproducts != 0 && packageopts) { if (nproducts != 0 && packageopts) {
fprintf(stderr, fprintf(stderr,

View File

@@ -25,6 +25,7 @@ type pkggroup = struct {
dir: str, dir: str,
pkg: str, pkg: str,
external: bool, external: bool,
production: bool,
plan: i32, plan: i32,
root: str, root: str,
bin: str, bin: str,
@@ -608,8 +609,10 @@ fn pkgstartbuild(p: *pkgplan, groups: []pkggroup, builder: str, includes: []str,
for (i < p.end) { for (i < p.end) {
let g: *pkggroup = &groups[i]; let g: *pkggroup = &groups[i];
append(ba, "--ww-package-test"); append(ba, "--ww-package-test");
if (g.external) { append(ba, "external"); } if (g.production) { append(ba, "production"); }
else { if (g.external) { append(ba, "external"); }
else { append(ba, "same"); }; else { append(ba, "same"); };
};
append(ba, g.pkg); append(ba, g.pkg);
append(ba, g.dir); append(ba, g.dir);
append(ba, g.bin); append(ba, g.bin);
@@ -953,6 +956,7 @@ export fn packagecommand(args: []str) int = {
g.pkg = srcs[j].pkg; g.pkg = srcs[j].pkg;
g.external = f.prodpkg.len != 0 g.external = f.prodpkg.len != 0
&& strings.compare(f.prodpkg, g.pkg) != 0; && strings.compare(f.prodpkg, g.pkg) != 0;
g.production = false;
append(groups, g); append(groups, g);
}; };
}; };
@@ -964,6 +968,7 @@ export fn packagecommand(args: []str) int = {
g.pkg = f.prodpkg; g.pkg = f.prodpkg;
if (g.pkg.len == 0) { g.pkg = srcs[f.start].pkg; }; if (g.pkg.len == 0) { g.pkg = srcs[f.start].pkg; };
g.external = false; g.external = false;
g.production = true;
append(groups, g); append(groups, g);
}; };
i += 1; i += 1;

View File

@@ -449,9 +449,8 @@ def SEP_VARIANT_SAME_TEST: i32 = 1;
def SEP_VARIANT_EXTERNAL: i32 = 2; def SEP_VARIANT_EXTERNAL: i32 = 2;
def SEP_VARIANT_TEST_MAIN: i32 = 3; def SEP_VARIANT_TEST_MAIN: i32 = 3;
def SEP_ROLE_NORMAL: i32 = 0; def SEP_ROLE_NORMAL: i32 = 0;
def SEP_ROLE_EXTERNAL_PRODUCTION: i32 = 1; def SEP_ROLE_TEST_SUPPORT: i32 = 1;
def SEP_ROLE_TEST_SUPPORT: i32 = 2; def SEP_ROLE_GENERATED_MAIN: i32 = 2;
def SEP_ROLE_GENERATED_MAIN: i32 = 3;
def SEP_TEST_SUPPORT_MODULE: str = "__wwtest"; def SEP_TEST_SUPPORT_MODULE: str = "__wwtest";
def SEP_MAXPRODUCT: i32 = 256; def SEP_MAXPRODUCT: i32 = 256;
def SEP_MAXCONTEXT: i32 = 257; def SEP_MAXCONTEXT: i32 = 257;
@@ -759,9 +758,10 @@ type sepbind = struct {
}; };
type seppkg = struct { type seppkg = struct {
path: *u8, // canonical dotted package identity, NUL-term path: *u8, // compiler/import identity derived from importbase
importbase: *u8, // canonical ordinary directory import identity
entry: *u8, // resolved package dir (or file, file root), NUL-term entry: *u8, // resolved package dir (or file, file root), NUL-term
artifact: *u8, // non-importable product-root artifact key artifact: *u8, // stable non-importable variant artifact key
name: *u8, // validated declared name; directory packages only name: *u8, // validated declared name; directory packages only
testpackage: *u8, testpackage: *u8,
sources: **u8, // owned, byte-sorted selected paths; dirs only sources: **u8, // owned, byte-sorted selected paths; dirs only
@@ -769,7 +769,7 @@ type seppkg = struct {
isdir: i32, isdir: i32,
variant: i32, variant: i32,
role: i32, role: i32,
root: bool, root: bool, // requested usage; never package-action identity
linkentry: bool, linkentry: bool,
generatedmain: bool, generatedmain: bool,
failed: bool, failed: bool,
@@ -795,6 +795,7 @@ type sepgraph = struct {
context: []sepcontext, context: []sepcontext,
ncontext: i32, ncontext: i32,
supportcontext: i32, supportcontext: i32,
identityfailed: bool,
}; };
type sepproduct = struct { type sepproduct = struct {
@@ -809,6 +810,99 @@ type sepproduct = struct {
variantroot: i32, variantroot: i32,
}; };
fn sepdirectoryvariant(variant: i32) bool = {
return variant == SEP_VARIANT_PRODUCTION
|| variant == SEP_VARIANT_SAME_TEST
|| variant == SEP_VARIANT_EXTERNAL;
};
fn sepvariantpath(variant: i32, base: *u8) *u8 = {
if (variant == SEP_VARIANT_EXTERNAL) { return appendlit(base, "_test"); };
return arenadupcstr(base, cstrlen(base));
};
fn sepdiagpathlocations(path: *u8, a: *u8, b: *u8) void = {
let first: *u8 = a;
let second: *u8 = b;
if (strings.compare(pathstr(first), pathstr(second)) > 0) {
first = b; second = a;
};
cerr("ww: package "); cerr(pathstr(path));
cerr(" resolves to directories "); cerr(pathstr(first));
cerr(" and "); cerr(pathstr(second)); cerr("\n");
};
fn sepdiagdiridentities(entry: *u8, a: *u8, b: *u8) void = {
let first: *u8 = a;
let second: *u8 = b;
if (strings.compare(pathstr(first), pathstr(second)) > 0) {
first = b; second = a;
};
cerr("ww: package directory "); cerr(pathstr(entry));
cerr(" has import identities "); cerr(pathstr(first));
cerr(" and "); cerr(pathstr(second)); cerr("\n");
};
// Bind a provisional directory action to its canonical ordinary import
// identity. The compiler path is derived from that base and the semantic
// variant; root/product/artifact state never participates.
fn sepbindimportbase(g: *sepgraph, pi: i32, base: *u8) i32 = {
if (base == nil || base[0u64] == 0u8 || cstrlen(base) >= 256u64) {
cerr("ww: package path is too long (limit 255 bytes)\n");
return -1;
};
let p: *seppkg = &g.pkg[pi];
if (p.importbase != nil) {
if (cstreq(p.importbase, base)) { return 0; };
g.identityfailed = true;
sepdiagdiridentities(p.entry, p.importbase, base);
return -1;
};
let candidate: *u8 = sepvariantpath(p.variant, base);
let i: i32 = 0;
for (i < g.n) {
if (i != pi && g.pkg[i].isdir != 0 && !g.pkg[i].generatedmain) {
let samelocation: bool = os.samefile(pathstr(g.pkg[i].entry),
pathstr(p.entry));
let supportalias: bool = p.role == SEP_ROLE_TEST_SUPPORT
|| g.pkg[i].role == SEP_ROLE_TEST_SUPPORT;
if (!supportalias && !samelocation
&& g.pkg[i].importbase != nil
&& cstreq(g.pkg[i].importbase, base)) {
g.identityfailed = true;
sepdiagpathlocations(base, g.pkg[i].entry, p.entry);
return -1;
};
if (samelocation && !supportalias
&& g.pkg[i].importbase != nil
&& !cstreq(g.pkg[i].importbase, base)) {
g.identityfailed = true;
sepdiagdiridentities(p.entry, g.pkg[i].importbase, base);
return -1;
};
if (g.pkg[i].path != nil && g.pkg[i].path[0u64] != 0u8
&& cstreq(g.pkg[i].path, candidate)) {
if (!samelocation) {
g.identityfailed = true;
sepdiagpathlocations(candidate, g.pkg[i].entry, p.entry);
return -1;
};
if (supportalias || g.pkg[i].variant == p.variant) {
g.identityfailed = true;
cerr("ww: package action identity collision for ");
cerr(pathstr(candidate)); cerr(" in ");
cerr(pathstr(p.entry)); cerr("\n");
return -1;
};
};
};
i += 1;
};
p.importbase = arenadupcstr(base, cstrlen(base));
p.path = candidate;
return 0;
};
fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8, fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
isdir: i32, variant: i32, testpackage: *u8, role: i32, isdir: i32, variant: i32, testpackage: *u8, role: i32,
artifact: *u8, root: bool) i32 = { artifact: *u8, root: bool) i32 = {
@@ -816,105 +910,72 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
cerr("ww: package path is too long (limit 255 bytes)\n"); cerr("ww: package path is too long (limit 255 bytes)\n");
return -1; return -1;
}; };
let incoming: *u8 = nil;
if (isdir != 0 && path[0u64] != 0u8) {
incoming = sepvariantpath(variant, path);
};
let i: i32 = 0; let i: i32 = 0;
for (i < g.n) { for (i < g.n) {
let samelocation: bool = os.samefile(pathstr(g.pkg[i].entry), let samelocation: bool = os.samefile(pathstr(g.pkg[i].entry),
pathstr(entry)); pathstr(entry));
if (root || g.pkg[i].root) { if (isdir != 0 && g.pkg[i].isdir != 0
if (root && g.pkg[i].root) { && !g.pkg[i].generatedmain) {
if (!samelocation) { i += 1; continue; }; let supportalias: bool = role == SEP_ROLE_TEST_SUPPORT
if (variant != g.pkg[i].variant) { i += 1; continue; }; || g.pkg[i].role == SEP_ROLE_TEST_SUPPORT;
if (!supportalias && !samelocation && path[0u64] != 0u8
&& g.pkg[i].importbase != nil
&& cstreq(path, g.pkg[i].importbase)) {
g.identityfailed = true;
sepdiagpathlocations(path, g.pkg[i].entry, entry);
return -1;
};
if (incoming != nil && g.pkg[i].path != nil
&& g.pkg[i].path[0u64] != 0u8
&& cstreq(incoming, g.pkg[i].path) && !samelocation) {
g.identityfailed = true;
sepdiagpathlocations(incoming, g.pkg[i].entry, entry);
return -1;
};
if (samelocation && g.pkg[i].variant == variant
&& g.pkg[i].role == role) {
let sametest: bool = testpackage == nil let sametest: bool = testpackage == nil
&& g.pkg[i].testpackage == nil; && g.pkg[i].testpackage == nil;
if (testpackage != nil && g.pkg[i].testpackage != nil) { if (testpackage != nil && g.pkg[i].testpackage != nil) {
sametest = cstreq(testpackage, g.pkg[i].testpackage); sametest = cstreq(testpackage, g.pkg[i].testpackage);
}; };
if (g.pkg[i].role == role && cstreq(g.pkg[i].path, path) if (variant != SEP_VARIANT_PRODUCTION && !sametest) {
&& sametest) {
// One directory variant is one compile action across products.
return i;
};
cerr("ww: incompatible package-test roots "); cerr("ww: incompatible package-test roots ");
cerr(pathstr(entry)); cerr("\n"); cerr(pathstr(entry)); cerr("\n");
return -1; return -1;
}; };
// A cycle back to an ordinary production root reuses that root after if (path[0u64] != 0u8
// loading assigns its declared identity. Test roots stay distinct. && sepbindimportbase(g, i, path) < 0) { return -1; };
if (samelocation g.pkg[i].root = g.pkg[i].root || root;
&& variant == SEP_VARIANT_PRODUCTION
&& g.pkg[i].variant == SEP_VARIANT_PRODUCTION
&& cstreq(g.pkg[i].path, path)) {
return i;
};
if (!samelocation) { i += 1; continue; };
let rootvariant: i32 = variant;
let productionvariant: i32 = g.pkg[i].variant;
if (!root) {
rootvariant = g.pkg[i].variant;
productionvariant = variant;
};
if (isdir != 0 && g.pkg[i].isdir != 0
&& rootvariant != SEP_VARIANT_PRODUCTION
&& productionvariant == SEP_VARIANT_PRODUCTION) {
i += 1;
continue;
};
cerr("ww: package directory "); cerr(pathstr(entry));
cerr(" has incompatible root and production variants\n");
return -1;
};
let samepath: bool = cstreq(g.pkg[i].path, path);
let sameartifact: bool = true;
if (role == SEP_ROLE_EXTERNAL_PRODUCTION) {
sameartifact = g.pkg[i].artifact != nil && artifact != nil
&& cstreq(g.pkg[i].artifact, artifact);
};
let sameaction: bool = samepath && samelocation
&& g.pkg[i].variant == variant && g.pkg[i].role == role
&& sameartifact;
if (sameaction) { return i; };
// An external test consumes the one canonical production action for
// its directory. The role only separates physically different
// packages that happen to use the same source qualifier.
if (samepath && samelocation
&& g.pkg[i].variant == SEP_VARIANT_PRODUCTION
&& variant == SEP_VARIANT_PRODUCTION
&& ((role == SEP_ROLE_EXTERNAL_PRODUCTION
&& g.pkg[i].role == SEP_ROLE_NORMAL)
|| (role == SEP_ROLE_NORMAL
&& g.pkg[i].role == SEP_ROLE_EXTERNAL_PRODUCTION))) {
return i;
};
if (samepath && (role == SEP_ROLE_EXTERNAL_PRODUCTION
|| g.pkg[i].role == SEP_ROLE_EXTERNAL_PRODUCTION)) {
i += 1;
continue;
};
if (samepath) {
if (!samelocation || g.pkg[i].variant != variant
|| g.pkg[i].role != role) {
cerr("ww: package "); cerr(pathstr(path));
cerr(" resolves to more than one location\n");
return -1;
};
return i; return i;
}; };
if (samelocation) { if (samelocation) {
if (role == SEP_ROLE_TEST_SUPPORT if (supportalias) { i += 1; continue; };
|| g.pkg[i].role == SEP_ROLE_TEST_SUPPORT) { if (g.pkg[i].importbase != nil && path[0u64] != 0u8
i += 1; && !cstreq(g.pkg[i].importbase, path)) {
continue; g.identityfailed = true;
sepdiagdiridentities(entry, g.pkg[i].importbase, path);
return -1;
};
if (sepdirectoryvariant(g.pkg[i].variant)
&& sepdirectoryvariant(variant)) {
i += 1; continue;
}; };
cerr("ww: package directory "); cerr(pathstr(entry)); cerr("ww: package directory "); cerr(pathstr(entry));
cerr(" has identities "); cerr(" has incompatible variants\n");
if (g.pkg[i].path[0u64] == 0u8) { cerr("(root)"); }
else { cerr(pathstr(g.pkg[i].path)); };
cerr(" and ");
if (path[0u64] == 0u8) { cerr("(root)"); }
else { cerr(pathstr(path)); };
cerr("\n");
return -1; return -1;
}; };
i += 1; continue;
};
if (samelocation && cstreq(g.pkg[i].path, path)
&& g.pkg[i].variant == variant && g.pkg[i].role == role) {
g.pkg[i].root = g.pkg[i].root || root;
return i;
};
i += 1; i += 1;
}; };
if (g.n >= SEP_MAXPKG) { if (g.n >= SEP_MAXPKG) {
@@ -924,8 +985,9 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
let plen: u64 = cstrlen(path); let plen: u64 = cstrlen(path);
let elen: u64 = cstrlen(entry); let elen: u64 = cstrlen(entry);
g.pkg[g.n].path = arenadupcstr(path, plen); g.pkg[g.n].path = arenadupcstr(path, plen);
g.pkg[g.n].importbase = nil;
g.pkg[g.n].entry = arenadupcstr(entry, elen); g.pkg[g.n].entry = arenadupcstr(entry, elen);
g.pkg[g.n].artifact = artifact; g.pkg[g.n].artifact = nil;
g.pkg[g.n].name = nil; g.pkg[g.n].name = nil;
g.pkg[g.n].testpackage = nil; g.pkg[g.n].testpackage = nil;
if (testpackage != nil) { if (testpackage != nil) {
@@ -955,6 +1017,27 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
g.pkg[g.n].deps = dslot; g.pkg[g.n].deps = dslot;
g.pkg[g.n].ndeps = 0; g.pkg[g.n].ndeps = 0;
g.pkg[g.n].color = 0; g.pkg[g.n].color = 0;
if (isdir != 0) {
let inherited: *u8 = path;
if (inherited[0u64] == 0u8) {
i = 0;
for (i < g.n) {
if (g.pkg[i].isdir != 0 && !g.pkg[i].generatedmain
&& g.pkg[i].role != SEP_ROLE_TEST_SUPPORT
&& role != SEP_ROLE_TEST_SUPPORT
&& os.samefile(pathstr(g.pkg[i].entry), pathstr(entry))
&& g.pkg[i].importbase != nil) {
inherited = g.pkg[i].importbase;
break;
};
i += 1;
};
};
if (inherited[0u64] != 0u8
&& sepbindimportbase(g, g.n, inherited) < 0) { return -1; };
} else {
g.pkg[g.n].artifact = artifact;
};
let r: i32 = g.n; let r: i32 = g.n;
g.n += 1; g.n += 1;
return r; return r;
@@ -1056,6 +1139,7 @@ fn sepfname(g: *sepgraph, pi: i32, scratch: *u8, suffix: str) *u8 = {
fn sepvalidateartifactpaths(g: *sepgraph, scratch: *u8) i32 = { fn sepvalidateartifactpaths(g: *sepgraph, scratch: *u8) i32 = {
let i: i32 = 0; let i: i32 = 0;
for (i < g.n) { for (i < g.n) {
if (g.pkg[i].failed || !g.pkg[i].loaded) { i += 1; continue; };
let base: *u8 = g.pkg[i].artifact; let base: *u8 = g.pkg[i].artifact;
if (base == nil) { if (base == nil) {
base = g.pkg[i].path; base = g.pkg[i].path;
@@ -1067,6 +1151,21 @@ fn sepvalidateartifactpaths(g: *sepgraph, scratch: *u8) i32 = {
cerr("ww: package artifact path is too long\n"); cerr("ww: package artifact path is too long\n");
return -1; return -1;
}; };
let j: i32 = i + 1;
for (j < g.n) {
if (g.pkg[j].failed || !g.pkg[j].loaded) { j += 1; continue; };
let other: *u8 = g.pkg[j].artifact;
if (other == nil) {
other = g.pkg[j].path;
if (other[0u64] == 0u8) { other = "__root\0".ptr; };
};
if (cstreq(base, other)) {
cerr("ww: package actions share artifact identity ");
cerr(pathstr(base)); cerr("\n");
return -1;
};
j += 1;
};
i += 1; i += 1;
}; };
return 0; return 0;
@@ -1287,33 +1386,13 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
cerr("' cannot import itself\n"); cerr("' cannot import itself\n");
return -1; return -1;
}; };
let runtimeproduction: bool = false;
if (externalproduction) {
let gi: i32 = 0;
for (gi < g.n) {
if (g.pkg[gi].testsupport
&& syntax.streq(pathstr(g.pkg[gi].path),
u.usepath)
&& os.samefile(pathstr(g.pkg[gi].entry),
pathstr(ipath))) {
runtimeproduction = true;
};
gi += 1;
};
};
let nm: []u8 = alloc([], idn + 1u64)!; let nm: []u8 = alloc([], idn + 1u64)!;
let k: u64 = 0u64; let k: u64 = 0u64;
for (k < idn) { nm[k] = idp[k]; k += 1u64; }; for (k < idn) { nm[k] = idp[k]; k += 1u64; };
nm[idn] = 0u8; nm[idn] = 0u8;
let di: i32 = -1; // External self-production is the ordinary canonical production
if (externalproduction && !runtimeproduction) { // action. Discovery role and product artifact never create another.
let art: *u8 = appendlit(g.pkg[pi].artifact, let di: i32 = sepfindoradd(g, nm.ptr, ipath, 1);
"-production");
di = sepfindoraddrole(g, nm.ptr, ipath, 1,
SEP_ROLE_EXTERNAL_PRODUCTION, art);
} else {
di = sepfindoradd(g, nm.ptr, ipath, 1);
};
if (di < 0) { return -1; }; if (di < 0) { return -1; };
let seen: bool = false; let seen: bool = false;
let m: i32 = 0; let m: i32 = 0;
@@ -1364,22 +1443,29 @@ fn sepdepcmp(g: *sepgraph, a: i32, b: i32) i32 = {
let r: i32 = strings.compare(pathstr(g.pkg[a].path), let r: i32 = strings.compare(pathstr(g.pkg[a].path),
pathstr(g.pkg[b].path)): i32; pathstr(g.pkg[b].path)): i32;
if (r != 0) { return r; }; if (r != 0) { return r; };
if (g.pkg[a].variant < g.pkg[b].variant) { return -1; };
if (g.pkg[a].variant > g.pkg[b].variant) { return 1; };
if (g.pkg[a].role < g.pkg[b].role) { return -1; }; if (g.pkg[a].role < g.pkg[b].role) { return -1; };
if (g.pkg[a].role > g.pkg[b].role) { return 1; }; if (g.pkg[a].role > g.pkg[b].role) { return 1; };
if (g.pkg[a].artifact == nil && g.pkg[b].artifact == nil) { return 0; }; return strings.compare(pathstr(g.pkg[a].entry),
if (g.pkg[a].artifact == nil) { return -1; }; pathstr(g.pkg[b].entry)): i32;
if (g.pkg[b].artifact == nil) { return 1; };
return strings.compare(pathstr(g.pkg[a].artifact),
pathstr(g.pkg[b].artifact)): i32;
}; };
fn generatedmainpath(index: i32) *u8 = { fn generatedmainkind(variant: i32) str = {
let buf: []u8 = alloc([], 64u64)!; if (variant == SEP_VARIANT_SAME_TEST) { return "internal"; };
buf.len = 64; if (variant == SEP_VARIANT_EXTERNAL) { return "external"; };
return "production";
};
fn generatedmainpath(pkgpath: *u8, kind: str) *u8 = {
let need: u64 = "__wwtestmain.".len: u64 + cstrlen(pkgpath)
+ 1u64 + kind.len: u64 + ".main".len: u64 + 1u64;
let buf: []u8 = alloc([], need)!;
buf.len = need: i32;
let off: u64 = strinto(buf.ptr, 0u64, "__wwtestmain."); let off: u64 = strinto(buf.ptr, 0u64, "__wwtestmain.");
buf[off] = (((index / 100) % 10) + 48): u8; off += 1u64; off = cstrinto(buf.ptr, off, pkgpath);
buf[off] = (((index / 10) % 10) + 48): u8; off += 1u64; off = byteinto(buf.ptr, off, '.': u8);
buf[off] = ((index % 10) + 48): u8; off += 1u64; off = strinto(buf.ptr, off, kind);
off = strinto(buf.ptr, off, ".main"); off = strinto(buf.ptr, off, ".main");
cstrseal(buf.ptr, off); cstrseal(buf.ptr, off);
return buf.ptr; return buf.ptr;
@@ -1387,27 +1473,52 @@ fn generatedmainpath(index: i32) *u8 = {
// Materialize the generated test dispatcher as a normal package action. It // Materialize the generated test dispatcher as a normal package action. It
// owns a generated source unit and imports exactly its test variant/support. // owns a generated source unit and imports exactly its test variant/support.
// Its identity is variant-derived, never product-ordinal-derived.
fn sepaddgeneratedmain(g: *sepgraph, product: *sepproduct, ordinal: i32, fn sepaddgeneratedmain(g: *sepgraph, product: *sepproduct, ordinal: i32,
support: i32) i32 = { support: i32) i32 = {
if (g.n >= SEP_MAXPKG) {
cerr("ww: too many packages\n");
return -1;
};
let variant: i32 = product.variantroot; let variant: i32 = product.variantroot;
if (variant < 0 || variant >= g.n) { return -1; }; if (variant < 0 || variant >= g.n) { return -1; };
let p: *seppkg = &g.pkg[g.n]; let kind: str = generatedmainkind(g.pkg[variant].variant);
p.path = generatedmainpath(ordinal); let mainpath: *u8 = generatedmainpath(g.pkg[variant].path, kind);
let pathi: i32 = 0; let pathi: i32 = 0;
for (pathi < g.n) { for (pathi < g.n) {
if (cstreq(g.pkg[pathi].path, p.path)) { if (cstreq(g.pkg[pathi].path, mainpath)) {
if (g.pkg[pathi].generatedmain) {
let wantssupport: bool = support >= 0 && support != variant;
let wanteddeps: i32 = 1;
if (wantssupport) { wanteddeps = 2; };
let hasvariant: bool = false;
let hassupport: bool = false;
let dk: i32 = 0;
for (dk < g.pkg[pathi].ndeps) {
if (g.pkg[pathi].deps[dk] == variant) { hasvariant = true; };
if (wantssupport && g.pkg[pathi].deps[dk] == support) {
hassupport = true;
};
dk += 1;
};
if (hasvariant && hassupport == wantssupport
&& g.pkg[pathi].ndeps == wanteddeps) {
return pathi;
};
};
cerr("ww: generated test-main package identity collides with source import "); cerr("ww: generated test-main package identity collides with source import ");
cerr(pathstr(p.path)); cerr("\n"); cerr(pathstr(mainpath)); cerr("\n");
return -1; return -1;
}; };
pathi += 1; pathi += 1;
}; };
if (g.n >= SEP_MAXPKG) {
cerr("ww: too many packages\n");
return -1;
};
let p: *seppkg = &g.pkg[g.n];
p.path = mainpath;
p.importbase = nil;
p.entry = g.pkg[variant].entry; p.entry = g.pkg[variant].entry;
p.artifact = productartifact(ordinal, SEP_VARIANT_TEST_MAIN); let variantartifact: *u8 = g.pkg[variant].artifact;
if (variantartifact == nil) { variantartifact = g.pkg[variant].path; };
p.artifact = appendlit(variantartifact, "-main");
p.name = arenadupcstr("main\0".ptr, 4u64); p.name = arenadupcstr("main\0".ptr, 4u64);
p.testpackage = nil; p.testpackage = nil;
p.sources = nil; p.sources = nil;
@@ -1549,14 +1660,6 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
g.pkg[pi].failed = true; g.pkg[pi].failed = true;
return rc; return rc;
}; };
// Directory roots acquire their canonical package identity. Explicit raw
// single-file compiler fixtures retain their anonymous multi-package reset.
if (g.pkg[pi].root && g.pkg[pi].isdir != 0
&& g.pkg[pi].path[0u64] == 0u8
&& g.pkg[pi].name != nil) {
g.pkg[pi].path = arenadupcstr(g.pkg[pi].name,
cstrlen(g.pkg[pi].name));
};
let si: i32 = 1; let si: i32 = 1;
for (si < g.pkg[pi].ndeps) { for (si < g.pkg[pi].ndeps) {
let v: i32 = g.pkg[pi].deps[si]; let v: i32 = g.pkg[pi].deps[si];
@@ -1581,6 +1684,88 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
return 0; return 0;
}; };
// Finalize provisional directory identities after source discovery and before
// generated-main construction or compilation. Source-import bindings win;
// otherwise a literal manifest-free root falls back to its validated package
// name (external p_test roots bind the ordinary base p).
fn sepfinalizedirectoryidentities(g: *sepgraph) i32 = {
let pi: i32 = 0;
for (pi < g.n) {
let p: *seppkg = &g.pkg[pi];
if (p.isdir != 0 && !p.generatedmain && !p.failed && p.loaded
&& p.importbase == nil) {
let base: *u8 = nil;
let i: i32 = 0;
for (i < g.n) {
if (i != pi && g.pkg[i].isdir != 0
&& !g.pkg[i].generatedmain
&& g.pkg[i].role != SEP_ROLE_TEST_SUPPORT
&& p.role != SEP_ROLE_TEST_SUPPORT
&& os.samefile(pathstr(g.pkg[i].entry), pathstr(p.entry))
&& g.pkg[i].importbase != nil) {
base = g.pkg[i].importbase;
break;
};
i += 1;
};
if (base == nil) {
if (p.name == nil || p.name[0u64] == 0u8) {
cerr("ww: package directory "); cerr(pathstr(p.entry));
cerr(" has no canonical import identity\n");
return -1;
};
if (p.variant == SEP_VARIANT_EXTERNAL) {
let n: u64 = cstrlen(p.name);
if (n <= 5u64 || !cstrendswithlit(p.name, "_test")) {
cerr("ww: package-test selector does not name an external package\n");
return -1;
};
let fallback: []u8 = alloc([], n - 5u64 + 1u64)!;
fallback.len = (n - 5u64 + 1u64): i32;
let k: u64 = 0u64;
for (k < n - 5u64) { fallback[k] = p.name[k]; k += 1u64; };
fallback[n - 5u64] = 0u8;
base = fallback.ptr;
} else {
base = p.name;
};
};
if (sepbindimportbase(g, pi, base) < 0) { return -1; };
};
pi += 1;
};
pi = 0;
for (pi < g.n) {
let p: *seppkg = &g.pkg[pi];
if (p.isdir != 0 && !p.generatedmain && !p.failed && p.loaded) {
let plen: u64 = cstrlen(p.path);
let leaf: *u8 = p.path;
let j: u64 = 0u64;
for (j < plen) {
if (p.path[j] == '.') { leaf = p.path + j + 1u64; };
j += 1u64;
};
let supportalias: bool = p.role == SEP_ROLE_TEST_SUPPORT
&& cstreqlit(p.path, SEP_TEST_SUPPORT_MODULE)
&& cstreqlit(p.name, "test");
if (!supportalias && !cstreq(p.name, leaf)) {
cerr("ww: package "); cerr(pathstr(p.name));
cerr(" does not match import path "); cerr(pathstr(p.path));
cerr("\n");
return -1;
};
p.artifact = nil;
if (p.variant == SEP_VARIANT_SAME_TEST) {
p.artifact = appendlit(p.path, "-internal-test");
} else { if (p.variant == SEP_VARIANT_EXTERNAL) {
p.artifact = appendlit(p.path, "-external-test");
}; };
};
pi += 1;
};
return 0;
};
fn sepcyclenode(p: *u8) void = { fn sepcyclenode(p: *u8) void = {
if (p[0] == 0u8) { cerr("(root)"); } else { cerr(pathstr(p)); }; if (p[0] == 0u8) { cerr("(root)"); } else { cerr(pathstr(p)); };
}; };
@@ -2230,6 +2415,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
context = contextslot, context = contextslot,
ncontext = 0, ncontext = 0,
supportcontext = -1, supportcontext = -1,
identityfailed = false,
})!; })!;
if (graphout != nil) { *graphout = g; }; if (graphout != nil) { *graphout = g; };
let rootpath: *u8 = "\0".ptr; let rootpath: *u8 = "\0".ptr;
@@ -2249,9 +2435,13 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
products[producti].context = sepcontextfor(g, contextroot, products[producti].context = sepcontextfor(g, contextroot,
incs, toolsrcdir); incs, toolsrcdir);
if (products[producti].context < 0) { return 1; }; if (products[producti].context < 0) { return 1; };
let selector: *u8 = products[producti].testpackage;
if (products[producti].variant == SEP_VARIANT_PRODUCTION) {
selector = nil;
};
products[producti].root = sepfindoraddvariant(g, rootpath, entry, products[producti].root = sepfindoraddvariant(g, rootpath, entry,
entryisdir, products[producti].variant, entryisdir, products[producti].variant,
products[producti].testpackage, selector,
SEP_ROLE_NORMAL, products[producti].artifact, true); SEP_ROLE_NORMAL, products[producti].artifact, true);
if (products[producti].root < 0) { return 1; }; if (products[producti].root < 0) { return 1; };
products[producti].variantroot = products[producti].root; products[producti].variantroot = products[producti].root;
@@ -2358,10 +2548,9 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
producti += 1; producti += 1;
continue; continue;
}; };
if (products[producti].variant != SEP_VARIANT_PRODUCTION if (products[producti].testpackage != nil
&& (products[producti].testpackage == nil && !cstreq(g.pkg[root].name,
|| !cstreq(g.pkg[root].name, products[producti].testpackage)) {
products[producti].testpackage))) {
cerr("ww: package-test selector does not match loaded package\n"); cerr("ww: package-test selector does not match loaded package\n");
g.pkg[root].failed = true; g.pkg[root].failed = true;
}; };
@@ -2377,13 +2566,25 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
g.pkg[variant].failed = true; g.pkg[variant].failed = true;
}; };
}; };
producti += 1;
};
};
if (g.identityfailed) { return 1; };
if (sepfinalizedirectoryidentities(g) < 0) { return 1; };
if (istest != 0 && entryisdir != 0) {
producti = 0;
for (producti < nproducts) {
let variant: i32 = products[producti].variantroot;
let support: i32 = supportfor[producti];
if (g.pkg[variant].failed
|| (support >= 0 && g.pkg[support].failed)) {
products[producti].root = variant;
producti += 1;
continue;
};
let mainpkg: i32 = sepaddgeneratedmain(g, &products[producti], let mainpkg: i32 = sepaddgeneratedmain(g, &products[producti],
producti, support); producti, support);
if (mainpkg < 0) { return 1; }; if (mainpkg < 0) { return 1; };
if (g.pkg[variant].failed
|| (support >= 0 && g.pkg[support].failed)) {
g.pkg[mainpkg].failed = true;
};
products[producti].root = mainpkg; products[producti].root = mainpkg;
producti += 1; producti += 1;
}; };
@@ -2515,7 +2716,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
let rawtest: bool = (istest != 0) && g.pkg[pi].root let rawtest: bool = (istest != 0) && g.pkg[pi].root
&& g.pkg[pi].isdir == 0; && g.pkg[pi].isdir == 0;
let gent: bool = g.pkg[pi].generatedmain || rawtest; let gent: bool = g.pkg[pi].generatedmain || rawtest;
let testpkg: bool = g.pkg[pi].root && (istest != 0) && !gent; let testpkg: bool = g.pkg[pi].variant == SEP_VARIANT_SAME_TEST
|| g.pkg[pi].variant == SEP_VARIANT_EXTERNAL;
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;
@@ -2845,7 +3047,9 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
product.testpackage = testpackage; product.testpackage = testpackage;
product.status = nil; product.status = nil;
product.artifact = nil; product.artifact = nil;
if (packageonly == 0) { product.artifact = "__root\0".ptr; }; if (packageonly == 0 && entryisdir == 0) {
product.artifact = "__root\0".ptr;
};
product.variant = rootvariant; product.variant = rootvariant;
product.root = -1; product.root = -1;
product.variantroot = -1; product.variantroot = -1;
@@ -2898,25 +3102,6 @@ fn cstrendswithlit(p: *u8, lit: str) bool = {
return strings.hassuffix(pathstr(p), lit); return strings.hassuffix(pathstr(p), lit);
}; };
fn productartifact(index: i32, variant: i32) *u8 = {
let buf: []u8 = alloc([], 64u64)!;
buf.len = 64;
let off: u64 = strinto(buf.ptr, 0u64, "__ww-test-");
buf[off] = (((index / 100) % 10) + 48): u8; off += 1u64;
buf[off] = (((index / 10) % 10) + 48): u8; off += 1u64;
buf[off] = ((index % 10) + 48): u8; off += 1u64;
off = byteinto(buf.ptr, off, '-': u8);
if (variant == SEP_VARIANT_SAME_TEST) {
off = strinto(buf.ptr, off, "same");
} else { if (variant == SEP_VARIANT_TEST_MAIN) {
off = strinto(buf.ptr, off, "main");
} else {
off = strinto(buf.ptr, off, "external");
}; };
cstrseal(buf.ptr, off);
return buf.ptr;
};
fn basenameoff(p: *u8, plen: u64) u64 = { fn basenameoff(p: *u8, plen: u64) u64 = {
let start: u64 = 0u64; let start: u64 = 0u64;
let i: u64 = 0u64; let i: u64 = 0u64;
@@ -3578,10 +3763,13 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
let status: *u8 = argv[i + 5]; let status: *u8 = argv[i + 5];
let pn: u64 = cstrlen(name); let pn: u64 = cstrlen(name);
let variant: i32 = SEP_VARIANT_EXTERNAL; let variant: i32 = SEP_VARIANT_EXTERNAL;
if (cstreqlit(kind, "same")) { if (cstreqlit(kind, "production")) {
variant = SEP_VARIANT_PRODUCTION;
} else { if (cstreqlit(kind, "same")) {
variant = SEP_VARIANT_SAME_TEST; variant = SEP_VARIANT_SAME_TEST;
}; }; };
if ((!cstreqlit(kind, "same") if ((!cstreqlit(kind, "production")
&& !cstreqlit(kind, "same")
&& !cstreqlit(kind, "external")) && !cstreqlit(kind, "external"))
|| pn == 0u64 || pn >= 256u64 || pn == 0u64 || pn >= 256u64
|| dir[0u64] == 0u8 || output[0u64] == 0u8 || dir[0u64] == 0u8 || output[0u64] == 0u8
@@ -3709,8 +3897,8 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
cerr("ww test: duplicate --ww-package-test variant for directory\n"); cerr("ww test: duplicate --ww-package-test variant for directory\n");
return 2; return 2;
}; };
products[producti].artifact = productartifact(producti, // Artifact identity is finalized from canonical package identity.
products[producti].variant); products[producti].artifact = nil;
producti += 1; producti += 1;
}; };
if (products.len != 0 && packageopts) { if (products.len != 0 && packageopts) {