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

@@ -449,9 +449,8 @@ def SEP_VARIANT_SAME_TEST: i32 = 1;
def SEP_VARIANT_EXTERNAL: i32 = 2;
def SEP_VARIANT_TEST_MAIN: i32 = 3;
def SEP_ROLE_NORMAL: i32 = 0;
def SEP_ROLE_EXTERNAL_PRODUCTION: i32 = 1;
def SEP_ROLE_TEST_SUPPORT: i32 = 2;
def SEP_ROLE_GENERATED_MAIN: i32 = 3;
def SEP_ROLE_TEST_SUPPORT: i32 = 1;
def SEP_ROLE_GENERATED_MAIN: i32 = 2;
def SEP_TEST_SUPPORT_MODULE: str = "__wwtest";
def SEP_MAXPRODUCT: i32 = 256;
def SEP_MAXCONTEXT: i32 = 257;
@@ -759,9 +758,10 @@ type sepbind = 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
artifact: *u8, // non-importable product-root artifact key
artifact: *u8, // stable non-importable variant artifact key
name: *u8, // validated declared name; directory packages only
testpackage: *u8,
sources: **u8, // owned, byte-sorted selected paths; dirs only
@@ -769,7 +769,7 @@ type seppkg = struct {
isdir: i32,
variant: i32,
role: i32,
root: bool,
root: bool, // requested usage; never package-action identity
linkentry: bool,
generatedmain: bool,
failed: bool,
@@ -795,6 +795,7 @@ type sepgraph = struct {
context: []sepcontext,
ncontext: i32,
supportcontext: i32,
identityfailed: bool,
};
type sepproduct = struct {
@@ -809,6 +810,99 @@ type sepproduct = struct {
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,
isdir: i32, variant: i32, testpackage: *u8, role: i32,
artifact: *u8, root: bool) i32 = {
@@ -816,104 +910,71 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
cerr("ww: package path is too long (limit 255 bytes)\n");
return -1;
};
let incoming: *u8 = nil;
if (isdir != 0 && path[0u64] != 0u8) {
incoming = sepvariantpath(variant, path);
};
let i: i32 = 0;
for (i < g.n) {
let samelocation: bool = os.samefile(pathstr(g.pkg[i].entry),
pathstr(entry));
if (root || g.pkg[i].root) {
if (root && g.pkg[i].root) {
if (!samelocation) { i += 1; continue; };
if (variant != g.pkg[i].variant) { i += 1; continue; };
if (isdir != 0 && g.pkg[i].isdir != 0
&& !g.pkg[i].generatedmain) {
let supportalias: bool = role == SEP_ROLE_TEST_SUPPORT
|| 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
&& g.pkg[i].testpackage == nil;
&& g.pkg[i].testpackage == nil;
if (testpackage != nil && g.pkg[i].testpackage != nil) {
sametest = cstreq(testpackage, g.pkg[i].testpackage);
};
if (g.pkg[i].role == role && cstreq(g.pkg[i].path, path)
&& sametest) {
// One directory variant is one compile action across products.
return i;
if (variant != SEP_VARIANT_PRODUCTION && !sametest) {
cerr("ww: incompatible package-test roots ");
cerr(pathstr(entry)); cerr("\n");
return -1;
};
cerr("ww: incompatible package-test roots ");
cerr(pathstr(entry)); cerr("\n");
return -1;
};
// A cycle back to an ordinary production root reuses that root after
// loading assigns its declared identity. Test roots stay distinct.
if (samelocation
&& variant == SEP_VARIANT_PRODUCTION
&& g.pkg[i].variant == SEP_VARIANT_PRODUCTION
&& cstreq(g.pkg[i].path, path)) {
if (path[0u64] != 0u8
&& sepbindimportbase(g, i, path) < 0) { return -1; };
g.pkg[i].root = g.pkg[i].root || root;
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");
if (samelocation) {
if (supportalias) { i += 1; continue; };
if (g.pkg[i].importbase != nil && path[0u64] != 0u8
&& !cstreq(g.pkg[i].importbase, path)) {
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(" has incompatible variants\n");
return -1;
};
return i;
i += 1; continue;
};
if (samelocation) {
if (role == SEP_ROLE_TEST_SUPPORT
|| g.pkg[i].role == SEP_ROLE_TEST_SUPPORT) {
i += 1;
continue;
};
cerr("ww: package directory "); cerr(pathstr(entry));
cerr(" has identities ");
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;
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;
};
@@ -924,8 +985,9 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
let plen: u64 = cstrlen(path);
let elen: u64 = cstrlen(entry);
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].artifact = artifact;
g.pkg[g.n].artifact = nil;
g.pkg[g.n].name = nil;
g.pkg[g.n].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].ndeps = 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;
g.n += 1;
return r;
@@ -1056,6 +1139,7 @@ fn sepfname(g: *sepgraph, pi: i32, scratch: *u8, suffix: str) *u8 = {
fn sepvalidateartifactpaths(g: *sepgraph, scratch: *u8) i32 = {
let i: i32 = 0;
for (i < g.n) {
if (g.pkg[i].failed || !g.pkg[i].loaded) { i += 1; continue; };
let base: *u8 = g.pkg[i].artifact;
if (base == nil) {
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");
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;
};
return 0;
@@ -1287,33 +1386,13 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
cerr("' cannot import itself\n");
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 k: u64 = 0u64;
for (k < idn) { nm[k] = idp[k]; k += 1u64; };
nm[idn] = 0u8;
let di: i32 = -1;
if (externalproduction && !runtimeproduction) {
let art: *u8 = appendlit(g.pkg[pi].artifact,
"-production");
di = sepfindoraddrole(g, nm.ptr, ipath, 1,
SEP_ROLE_EXTERNAL_PRODUCTION, art);
} else {
di = sepfindoradd(g, nm.ptr, ipath, 1);
};
// External self-production is the ordinary canonical production
// action. Discovery role and product artifact never create another.
let di: i32 = sepfindoradd(g, nm.ptr, ipath, 1);
if (di < 0) { return -1; };
let seen: bool = false;
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),
pathstr(g.pkg[b].path)): i32;
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].artifact == nil && g.pkg[b].artifact == nil) { return 0; };
if (g.pkg[a].artifact == nil) { return -1; };
if (g.pkg[b].artifact == nil) { return 1; };
return strings.compare(pathstr(g.pkg[a].artifact),
pathstr(g.pkg[b].artifact)): i32;
return strings.compare(pathstr(g.pkg[a].entry),
pathstr(g.pkg[b].entry)): i32;
};
fn generatedmainpath(index: i32) *u8 = {
let buf: []u8 = alloc([], 64u64)!;
buf.len = 64;
fn generatedmainkind(variant: i32) str = {
if (variant == SEP_VARIANT_SAME_TEST) { return "internal"; };
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.");
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 = cstrinto(buf.ptr, off, pkgpath);
off = byteinto(buf.ptr, off, '.': u8);
off = strinto(buf.ptr, off, kind);
off = strinto(buf.ptr, off, ".main");
cstrseal(buf.ptr, off);
return buf.ptr;
@@ -1387,27 +1473,52 @@ fn generatedmainpath(index: i32) *u8 = {
// Materialize the generated test dispatcher as a normal package action. It
// 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,
support: i32) i32 = {
if (g.n >= SEP_MAXPKG) {
cerr("ww: too many packages\n");
return -1;
};
let variant: i32 = product.variantroot;
if (variant < 0 || variant >= g.n) { return -1; };
let p: *seppkg = &g.pkg[g.n];
p.path = generatedmainpath(ordinal);
let kind: str = generatedmainkind(g.pkg[variant].variant);
let mainpath: *u8 = generatedmainpath(g.pkg[variant].path, kind);
let pathi: i32 = 0;
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(pathstr(p.path)); cerr("\n");
cerr(pathstr(mainpath)); cerr("\n");
return -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.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.testpackage = nil;
p.sources = nil;
@@ -1549,14 +1660,6 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
g.pkg[pi].failed = true;
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;
for (si < g.pkg[pi].ndeps) {
let v: i32 = g.pkg[pi].deps[si];
@@ -1581,6 +1684,88 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
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 = {
if (p[0] == 0u8) { cerr("(root)"); } else { cerr(pathstr(p)); };
};
@@ -2230,6 +2415,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
context = contextslot,
ncontext = 0,
supportcontext = -1,
identityfailed = false,
})!;
if (graphout != nil) { *graphout = g; };
let rootpath: *u8 = "\0".ptr;
@@ -2249,9 +2435,13 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
products[producti].context = sepcontextfor(g, contextroot,
incs, toolsrcdir);
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,
entryisdir, products[producti].variant,
products[producti].testpackage,
selector,
SEP_ROLE_NORMAL, products[producti].artifact, true);
if (products[producti].root < 0) { return 1; };
products[producti].variantroot = products[producti].root;
@@ -2358,10 +2548,9 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
producti += 1;
continue;
};
if (products[producti].variant != SEP_VARIANT_PRODUCTION
&& (products[producti].testpackage == nil
|| !cstreq(g.pkg[root].name,
products[producti].testpackage))) {
if (products[producti].testpackage != nil
&& !cstreq(g.pkg[root].name,
products[producti].testpackage)) {
cerr("ww: package-test selector does not match loaded package\n");
g.pkg[root].failed = true;
};
@@ -2377,13 +2566,25 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
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],
producti, support);
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;
producti += 1;
};
@@ -2515,7 +2716,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
let rawtest: bool = (istest != 0) && g.pkg[pi].root
&& g.pkg[pi].isdir == 0;
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 supportpkg: bool = g.pkg[pi].testsupport;
let alen: u64 = 8u64;
@@ -2845,7 +3047,9 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
product.testpackage = testpackage;
product.status = 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.root = -1;
product.variantroot = -1;
@@ -2898,25 +3102,6 @@ fn cstrendswithlit(p: *u8, lit: str) bool = {
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 = {
let start: 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 pn: u64 = cstrlen(name);
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;
};
if ((!cstreqlit(kind, "same")
}; };
if ((!cstreqlit(kind, "production")
&& !cstreqlit(kind, "same")
&& !cstreqlit(kind, "external"))
|| pn == 0u64 || pn >= 256u64
|| 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");
return 2;
};
products[producti].artifact = productartifact(producti,
products[producti].variant);
// Artifact identity is finalized from canonical package identity.
products[producti].artifact = nil;
producti += 1;
};
if (products.len != 0 && packageopts) {