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

@@ -2609,7 +2609,8 @@ fn localidentity(dir: str, leaf: str) str = {
let base: str = strings.concat(source, "/ww_root_parity_base_7f3");
let left: str = strings.concat(source, "/ww_root_parity_left_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");
assert(os.mkdir(source, 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 work: str = strings.concat(workroot, "/");
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",
"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(
"//ww:module-reset ww_root_parity_base_7f3\n", basea,
"\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"),
strings.concat("//ww:module-reset ww_root_parity_right_7f3\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 referenceexports: []str = ["", "", "", ""];
let referencearchives: []str = ["", "", "", ""];
@@ -2807,6 +2811,11 @@ fn localidentity(dir: str, leaf: str) str = {
".wwi"));
let archive: str = readfile(strings.concat(work, artifacts[ai],
".a"));
if (ai == 3) {
assert(strings.hasprefix(exportf, strings.concat("package ",
rootidentity, ";\n")));
assert(!strings.hasprefix(exportf, "package main;\n"));
};
if (si == 0) {
referenceexports[ai] = strings.dup(exportf);
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_right_7f3><", work,
"ww_root_parity_right_7f3.wwi><-I><", work,
"main.wwi><-o><", work,
"main.s><", work, "main.unit.ww>");
rootidentity, ".wwi><-o><", work,
rootidentity, ".s><", work, rootidentity, ".unit.ww>");
assert(same(linecontaining(ctrace,
"ww_root_parity_base_7f3.unit.ww"), baseline));
assert(same(linecontaining(ctrace,
"ww_root_parity_left_7f3.unit.ww"), leftline));
assert(same(linecontaining(ctrace,
"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"));
// 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);
ai = 0;
for (ai < artifacts.len) {
@@ -2864,7 +2885,7 @@ fn localidentity(dir: str, leaf: str) str = {
};
assert(occurrences(ltrace, "\n") == 1);
assert(has(ltrace, strings.concat("BEGIN<-o><", bin, "><", work,
"main.a>")));
rootidentity, ".a>")));
ai = 0;
for (ai < artifacts.len) {
assert(occurrences(ltrace, strings.concat("<", work, artifacts[ai],
@@ -2935,6 +2956,138 @@ fn localidentity(dir: str, leaf: str) str = {
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 = {
let root: str = fresh();
let missing: str = strings.concat(root, "/missing");