From 27a501fc315d976269d5b50d9358b4e9a7ee0b30 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Thu, 13 Aug 2026 00:54:12 +0900 Subject: [PATCH] test: prove command-global package action reuse --- test/package/package_test.ww | 573 ++++++++++++++++++++++++++--------- 1 file changed, 426 insertions(+), 147 deletions(-) diff --git a/test/package/package_test.ww b/test/package/package_test.ww index 1d2cc090..5595216f 100644 --- a/test/package/package_test.ww +++ b/test/package/package_test.ww @@ -644,9 +644,9 @@ fn workescape(s: str) str = { assert(has(outc.stdout, strings.concat(" -> ", root, "/route_test.test\n"))); let cwhite: str = readfile(strings.concat(root, - "/route.test.sepwork/__ww-test-000-same.s")); + "/route.test.sepwork/route-internal-test.s")); let cexternal: str = readfile(strings.concat(root, - "/route.test.sepwork/__ww-test-001-external.s")); + "/route.test.sepwork/route_test-external-test.s")); // The explicit package `-c` outputs are caller-owned artifacts. Release // the shared C-stage tree before asking the WW driver to acquire the same // stem; the driver never deletes a pre-existing `.sepwork` path. @@ -659,9 +659,9 @@ fn workescape(s: str) str = { assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(same(cwhite, readfile(strings.concat(root, - "/route.test.sepwork/__ww-test-000-same.s")))); + "/route.test.sepwork/route-internal-test.s")))); assert(same(cexternal, readfile(strings.concat(root, - "/route.test.sepwork/__ww-test-001-external.s")))); + "/route.test.sepwork/route_test-external-test.s")))); clean(root); }; @@ -786,9 +786,9 @@ fn workescape(s: str) str = { let referencesameunit: str = ""; let referenceexternalunit: str = ""; let referenceproductionunit: str = ""; - let packageactions: []str = ["__ww-test-000-same", - "__ww-test-000-main", "__ww-test-001-external", - "__ww-test-001-main"]; + let packageactions: []str = ["pkg-internal-test", + "pkg-internal-test-main", "pkg_test-external-test", + "pkg_test-external-test-main"]; let referenceactionexports: []str = ["", "", "", ""]; let referenceactionarchives: []str = ["", "", "", ""]; let i: i32 = 0; @@ -807,15 +807,17 @@ fn workescape(s: str) str = { let buildstdout: str = strings.dup(out.stdout); let buildstderr: str = strings.dup(out.stderr); let sameunit: str = readfile(strings.concat(sharedwork, - "__ww-test-000-same.unit.ww")); + "pkg-internal-test.unit.ww")); let externalunit: str = readfile(strings.concat(sharedwork, - "__ww-test-001-external.unit.ww")); + "pkg_test-external-test.unit.ww")); let externalproduction: str = readfile(strings.concat(sharedwork, - "__ww-test-001-external-production.unit.ww")); + "pkg.unit.ww")); let samemainunit: str = readfile(strings.concat(sharedwork, - "__ww-test-000-main.unit.ww")); + "pkg-internal-test-main.unit.ww")); let externalmainunit: str = readfile(strings.concat(sharedwork, - "__ww-test-001-main.unit.ww")); + "pkg_test-external-test-main.unit.ww")); + assert(!os.exists(strings.concat(sharedwork, + "__ww-test-001-external-production.unit.ww"))); assert(!os.exists(strings.concat(externalbin, ".sepwork"))); assert(!has(sameunit, "//ww:module ")); assert(os.exists(strings.concat(sharedwork, @@ -834,21 +836,20 @@ fn workescape(s: str) str = { assert(!has(externalproduction, "SAME_TEST_SOURCE")); assert(!has(externalproduction, "EXTERNAL_TEST_SOURCE")); assert(same(samemainunit, strings.concat( - "//ww:module-reset __wwtestmain.000.main\n", + "//ww:module-reset __wwtestmain.pkg.internal.main\n", "package main;\nimport pkg;\nimport test;\n"))); assert(same(externalmainunit, strings.concat( - "//ww:module-reset __wwtestmain.001.main\n", + "//ww:module-reset __wwtestmain.pkg_test.external.main\n", "package main;\nimport pkg_test;\nimport test;\n"))); assert(!has(samemainunit, "PACKAGE_PRODUCTION")); assert(!has(samemainunit, "SAME_TEST_SOURCE")); assert(!has(externalmainunit, "EXTERNAL_TEST_SOURCE")); assert(!has(readfile(strings.concat(sharedwork, "api.unit.ww")), "TEST_DEPENDENCY_MUST_NOT_COMPILE")); - let artifacts: []str = ["implementation", "api", - "__ww-test-001-external-production", - "__same", "__external", "test", "__ww-test-000-same", - "__ww-test-000-main", "__ww-test-001-external", - "__ww-test-001-main"]; + let artifacts: []str = ["implementation", "api", "pkg", + "__same", "__external", "test", "pkg-internal-test", + "pkg-internal-test-main", "pkg_test-external-test", + "pkg_test-external-test-main"]; let ai: i32 = 0; for (ai < artifacts.len) { assert(os.exists(strings.concat(sharedwork, artifacts[ai], ".wwi"))); @@ -877,62 +878,61 @@ fn workescape(s: str) str = { assert(occurrences(ctrace, "implementation.unit.ww") == 1); assert(occurrences(ctrace, "api.unit.ww") == 1); assert(occurrences(ctrace, - "__ww-test-001-external-production.unit.ww") == 1); + "/pkg.unit.ww") == 1); assert(occurrences(ctrace, "/__same.unit.ww") == 1); assert(occurrences(ctrace, "/__external.unit.ww") == 1); assert(occurrences(ctrace, "/test.unit.ww") == 1); assert(occurrences(ctrace, - "__ww-test-000-same.unit.ww") == 1); + "pkg-internal-test.unit.ww") == 1); assert(occurrences(ctrace, - "__ww-test-001-external.unit.ww") == 1); + "pkg_test-external-test.unit.ww") == 1); assert(occurrences(ctrace, - "__ww-test-000-main.unit.ww") == 1); + "pkg-internal-test-main.unit.ww") == 1); assert(occurrences(ctrace, - "__ww-test-001-main.unit.ww") == 1); + "pkg_test-external-test-main.unit.ww") == 1); assert(occurrences(ctrace, "-T --entry --test-support-module") == 2); assert(occurrences(ctrace, "--test-package") == 2); let samecompile: str = linecontaining(ctrace, - "__ww-test-000-same.unit.ww"); + "pkg-internal-test.unit.ww"); let externalcompile: str = linecontaining(ctrace, - "__ww-test-001-external.unit.ww"); + "pkg_test-external-test.unit.ww"); let productioncompile: str = linecontaining(ctrace, - "__ww-test-001-external-production.unit.ww"); + "/pkg.unit.ww"); assert(same(samecompile, strings.concat( "--test-package -c --import __same ", sharedwork, "__same.wwi --import api ", sharedwork, - "api.wwi -I ", sharedwork, "__ww-test-000-same.wwi -o ", - sharedwork, "__ww-test-000-same.s ", sharedwork, - "__ww-test-000-same.unit.ww"))); + "api.wwi -I ", sharedwork, "pkg-internal-test.wwi -o ", + sharedwork, "pkg-internal-test.s ", sharedwork, + "pkg-internal-test.unit.ww"))); assert(same(externalcompile, strings.concat( "--test-package -c --import __external ", sharedwork, "__external.wwi --import pkg ", sharedwork, - "__ww-test-001-external-production.wwi -I ", sharedwork, - "__ww-test-001-external.wwi -o ", sharedwork, - "__ww-test-001-external.s ", sharedwork, - "__ww-test-001-external.unit.ww"))); + "pkg.wwi -I ", sharedwork, + "pkg_test-external-test.wwi -o ", sharedwork, + "pkg_test-external-test.s ", sharedwork, + "pkg_test-external-test.unit.ww"))); let samemaincompile: str = linecontaining(ctrace, - "__ww-test-000-main.unit.ww"); + "pkg-internal-test-main.unit.ww"); let externalmaincompile: str = linecontaining(ctrace, - "__ww-test-001-main.unit.ww"); + "pkg_test-external-test-main.unit.ww"); assert(same(samemaincompile, strings.concat( "-T --entry --test-support-module test -c --import pkg ", - sharedwork, "__ww-test-000-same.wwi --import test ", + sharedwork, "pkg-internal-test.wwi --import test ", sharedwork, "test.wwi -I ", sharedwork, - "__ww-test-000-main.wwi -o ", sharedwork, - "__ww-test-000-main.s ", sharedwork, - "__ww-test-000-main.unit.ww"))); + "pkg-internal-test-main.wwi -o ", sharedwork, + "pkg-internal-test-main.s ", sharedwork, + "pkg-internal-test-main.unit.ww"))); assert(same(externalmaincompile, strings.concat( "-T --entry --test-support-module test -c --import pkg_test ", - sharedwork, "__ww-test-001-external.wwi --import test ", + sharedwork, "pkg_test-external-test.wwi --import test ", sharedwork, "test.wwi -I ", sharedwork, - "__ww-test-001-main.wwi -o ", sharedwork, - "__ww-test-001-main.s ", sharedwork, - "__ww-test-001-main.unit.ww"))); + "pkg_test-external-test-main.wwi -o ", sharedwork, + "pkg_test-external-test-main.s ", sharedwork, + "pkg_test-external-test-main.unit.ww"))); assert(same(productioncompile, strings.concat( "-c --import api ", sharedwork, "api.wwi -I ", sharedwork, - "__ww-test-001-external-production.wwi -o ", sharedwork, - "__ww-test-001-external-production.s ", sharedwork, - "__ww-test-001-external-production.unit.ww"))); + "pkg.wwi -o ", sharedwork, + "pkg.s ", sharedwork, "pkg.unit.ww"))); let ltrace: str = readfile(linkertrace); assert(occurrences(ltrace, "\n") == 2); let samelink: str = linecontaining(ltrace, @@ -944,19 +944,19 @@ fn workescape(s: str) str = { strings.concat(sharedwork, "implementation.a"))); assert(has(samelink, strings.concat(sharedwork, "__same.a"))); assert(has(samelink, strings.concat(sharedwork, - "__ww-test-000-main.a"))); + "pkg-internal-test-main.a"))); assert(has(samelink, strings.concat(sharedwork, - "__ww-test-000-same.a"))); + "pkg-internal-test.a"))); assert(!has(samelink, strings.concat(sharedwork, - "__ww-test-001-external-production.a"))); + "pkg.a"))); assert(!has(samelink, strings.concat(sharedwork, "__external.a"))); assert(has(externallink, strings.concat(sharedwork, - "__ww-test-001-external-production.a"))); + "pkg.a"))); assert(has(externallink, strings.concat(sharedwork, - "__ww-test-001-main.a"))); + "pkg_test-external-test-main.a"))); assert(has(externallink, strings.concat(sharedwork, - "__ww-test-001-external.a"))); + "pkg_test-external-test.a"))); assert(!has(samelink, ".wwi")); assert(!has(externallink, ".wwi")); assert(has(externallink, strings.concat(sharedwork, "api.a"))); @@ -1000,8 +1000,8 @@ fn workescape(s: str) str = { }; i += 1; }; - // The illegal-in-import root keys stay disjoint from the valid packages - // literally named __same and __external, including warm reuse. + // Canonical variant keys stay disjoint from valid packages literally named + // __same and __external, including warm reuse. let warmroot: str = strings.concat(root, "/warm"); assert(os.mkdir(warmroot, 448i32) == 0); let warmav: []str = [driver("ww"), "test", "-w", warmroot, @@ -1017,20 +1017,87 @@ fn workescape(s: str) str = { assert(has(readfile(strings.concat(warmwork, "__same.unit.ww")), "package __same;")); assert(has(readfile(strings.concat(warmwork, - "__ww-test-000-same.unit.ww")), "SAME_TEST_SOURCE")); + "pkg-internal-test.unit.ww")), "SAME_TEST_SOURCE")); assert(has(readfile(strings.concat(warmwork, "__external.unit.ww")), "package __external;")); assert(has(readfile(strings.concat(warmwork, - "__ww-test-001-external.unit.ww")), "EXTERNAL_TEST_SOURCE")); + "pkg_test-external-test.unit.ww")), "EXTERNAL_TEST_SOURCE")); assert(has(readfile(strings.concat(warmwork, - "__ww-test-001-external-production.unit.ww")), + "pkg.unit.ww")), "PACKAGE_PRODUCTION_A")); + assert(!os.exists(strings.concat(warmwork, + "__ww-test-001-external-production.unit.ww"))); + // Equivalent hidden product order is presentation-only. The driver sorts + // and interns before compilation, so reversing the descriptors reuses the + // same persistent actions and leaves every artifact byte unchanged. + let orderwork: str = strings.concat(root, "/order-work"); + assert(os.mkdir(orderwork, 448i32) == 0); + let orderav: []str = [driver("ww"), "test", "-c", "-w", orderwork, + "-I", root, + "--ww-package-test", "external", "pkg_test", pkg, + externalbin, strings.concat(root, "/order-external.status"), + "--ww-package-test", "same", "pkg", pkg, + samebin, strings.concat(root, "/order-same.status"), pkg]; + runcommandenv(root, "variant-order-reversed", orderav, traceenv, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let ordertrace: str = readfile(compilertrace); + let coldcounts: []i32 = [occurrences(ordertrace, "/pkg.unit.new"), + occurrences(ordertrace, "/pkg-internal-test.unit.new"), + occurrences(ordertrace, "/pkg_test-external-test.unit.new")]; + assert(coldcounts[0] == 2 && coldcounts[1] == 2 && coldcounts[2] == 2); + let orderworkdir: str = strings.concat(orderwork, "/"); + let orderactions: []str = ["pkg", "pkg-internal-test", + "pkg_test-external-test", "pkg-internal-test-main", + "pkg_test-external-test-main"]; + let oi: i32 = 0; + for (oi < orderactions.len) { + let suffixes: []str = [".unit.ww", ".wwi", ".a"]; + let xi: i32 = 0; + for (xi < suffixes.len) { + assert(same(readfile(strings.concat(warmwork, orderactions[oi], + suffixes[xi])), readfile(strings.concat(orderworkdir, + orderactions[oi], suffixes[xi])))); + xi += 1; + }; + oi += 1; + }; + let orderwwwork: str = strings.concat(root, "/order-ww-work"); + let orderwwsame: str = strings.concat(root, "/order-ww-same.test"); + let orderwwexternal: str = strings.concat(root, "/order-ww-external.test"); + assert(os.mkdir(orderwwwork, 448i32) == 0); + let orderwwav: []str = [driver("ww_ww"), "test", "-c", "-w", + orderwwwork, "-I", root, + "--ww-package-test", "external", "pkg_test", pkg, + orderwwexternal, strings.concat(root, "/order-ww-external.status"), + "--ww-package-test", "same", "pkg", pkg, + orderwwsame, strings.concat(root, "/order-ww-same.status"), pkg]; + runcommand(root, "variant-order-reversed-ww", orderwwav, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + oi = 0; + for (oi < orderactions.len) { + let suffixes: []str = [".unit.ww", ".wwi", ".a"]; + let xi: i32 = 0; + for (xi < suffixes.len) { + assert(same(readfile(strings.concat(warmwork, orderactions[oi], + suffixes[xi])), readfile(strings.concat(orderwwwork, "/", + orderactions[oi], suffixes[xi])))); + xi += 1; + }; + oi += 1; + }; runcommandenv(root, "variant-warm-reuse", warmav, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(warmstdout, out.stdout)); assert(same(warmstderr, out.stderr)); - assert(same(warmtrace, readfile(compilertrace))); + let reusedtrace: str = readfile(compilertrace); + assert(occurrences(reusedtrace, "/pkg.unit.new") == coldcounts[0]); + assert(occurrences(reusedtrace, "/pkg-internal-test.unit.new") + == coldcounts[1]); + assert(occurrences(reusedtrace, "/pkg_test-external-test.unit.new") + == coldcounts[2]); let aliasunit: str = strings.concat(root, "/alias.unit.ww"); writefile(aliasunit, strings.concat( "//ww:module-reset __wwtest\n", @@ -1068,13 +1135,14 @@ fn workescape(s: str) str = { let suite: str = strings.concat(root, "/suite"); let alpha: str = strings.concat(suite, "/alpha"); let beta: str = strings.concat(suite, "/beta"); + let gamma: str = strings.concat(suite, "/gamma"); let alphasame: str = strings.concat(alpha, "/_alpha_same"); let alphaexternal: str = strings.concat(alpha, "/_alpha_external"); let betasame: str = strings.concat(beta, "/_beta_same"); let betaexternal: str = strings.concat(beta, "/_beta_external"); let alphashadow: str = strings.concat(root, "/_alpha_same"); let betashadow: str = strings.concat(root, "/_beta_same"); - let dirs: []str = [leaf, common, suite, alpha, beta, alphasame, + let dirs: []str = [leaf, common, suite, alpha, beta, gamma, alphasame, alphaexternal, betasame, betaexternal, alphashadow, betashadow]; let di: i32 = 0; for (di < dirs.len) { @@ -1126,11 +1194,17 @@ fn workescape(s: str) str = { "@test fn alpha_external_runs() void = {\n", " assert(alpha.value() + alpha.exported()", " + _alpha_external.value() == 42);\n};\n")); + // A no-test directory is an ordinary production product. Beta also imports + // it, proving that root/dependency discovery shares the same action. + writefile(strings.concat(gamma, "/gamma.ww"), strings.concat( + "package gamma;\nimport common;\n", + "// MULTIDIR_GAMMA\n", + "export fn value() i32 = { return common.value(); };\n")); writefile(strings.concat(beta, "/beta.ww"), strings.concat( - "package beta;\nimport common;\nimport alpha;\n", + "package beta;\nimport gamma;\nimport alpha;\n", "// MULTIDIR_BETA\n", "fn secret() i32 = { return 2; };\n", - "export fn value() i32 = { return common.value(); };\n", + "export fn value() i32 = { return gamma.value(); };\n", "export fn exported() i32 = { return alpha.exported() + 1; };\n")); writefile(strings.concat(beta, "/same_test.ww"), strings.concat( "package beta;\nimport _beta_same;\n", @@ -1228,21 +1302,23 @@ fn workescape(s: str) str = { let bins: []str = [strings.concat(alpha, "/alpha.test"), strings.concat(alpha, "/alpha_test.test"), strings.concat(beta, "/beta.test"), - strings.concat(beta, "/beta_test.test")]; - let rootkeys: []str = ["__ww-test-000-same", - "__ww-test-001-external", "__ww-test-002-same", - "__ww-test-003-external"]; - let mainkeys: []str = ["__ww-test-000-main", "__ww-test-001-main", - "__ww-test-002-main", "__ww-test-003-main"]; - let alphaartifact: str = "__ww-test-001-external-production"; - let betaartifact: str = "__ww-test-003-external-production"; + strings.concat(beta, "/beta_test.test"), + strings.concat(gamma, "/gamma.test")]; + let rootkeys: []str = ["alpha-internal-test", + "alpha_test-external-test", "beta-internal-test", + "beta_test-external-test", "gamma"]; + let mainkeys: []str = ["alpha-internal-test-main", + "alpha_test-external-test-main", "beta-internal-test-main", + "beta_test-external-test-main", "gamma-main"]; + let alphaartifact: str = "alpha"; + let betaartifact: str = "beta"; let expectedtests: []str = ["alpha_same_runs ... ok\n", "alpha_external_runs ... ok\n", "beta_same_runs ... ok\n", "beta_external_runs ... ok\n"]; let sharedworkroot: str = strings.concat(bins[0], ".sepwork"); let sharedwork: str = strings.concat(sharedworkroot, "/"); - let referencebins: []str = ["", "", "", ""]; - let referenceroots: []str = ["", "", "", ""]; + let referencebins: []str = ["", "", "", "", ""]; + let referenceroots: []str = ["", "", "", "", ""]; let referenceout: str = ""; let referenceerr: str = ""; let referencecommonwwi: str = ""; @@ -1277,6 +1353,8 @@ fn workescape(s: str) str = { alphaartifact, ".unit.ww")); let betaunit: str = readfile(strings.concat(sharedwork, betaartifact, ".unit.ww")); + let gammaunit: str = readfile(strings.concat(sharedwork, + "gamma.unit.ww")); assert(has(commonunit, "//ww:module-reset common\n")); assert(!has(commonunit, "//ww:module ")); assert(!has(alphaunit, "//ww:module ")); @@ -1284,12 +1362,16 @@ fn workescape(s: str) str = { assert(has(alphaunit, "MULTIDIR_ALPHA_Z")); assert(!has(betaunit, "//ww:module ")); assert(has(betaunit, "MULTIDIR_BETA")); + assert(!has(gammaunit, "//ww:module ")); + assert(has(gammaunit, "MULTIDIR_GAMMA")); + assert(!has(gammaunit, "MULTIDIR_COMMON")); assert(!has(commonunit, "DEPENDENCY_TEST_FILE_MUST_NOT_COMPILE")); let roots: []str = [readfile(strings.concat(sharedwork, rootkeys[0], ".unit.ww")), readfile(strings.concat(sharedwork, rootkeys[1], ".unit.ww")), readfile(strings.concat(sharedwork, rootkeys[2], ".unit.ww")), readfile(strings.concat(sharedwork, - rootkeys[3], ".unit.ww"))]; + rootkeys[3], ".unit.ww")), readfile(strings.concat(sharedwork, + rootkeys[4], ".unit.ww"))]; assert(!has(roots[0], "//ww:module ")); assert(has(roots[0], "MULTIDIR_ALPHA_A")); assert(has(roots[0], "MULTIDIR_ALPHA_Z")); @@ -1304,13 +1386,15 @@ fn workescape(s: str) str = { assert(!has(roots[3], "//ww:module ")); assert(has(roots[3], "MULTIDIR_BETA_EXTERNAL")); assert(!has(roots[3], "MULTIDIR_BETA_SAME")); + assert(has(roots[4], "MULTIDIR_GAMMA")); + assert(!has(roots[4], "MULTIDIR_COMMON")); let products: []str = ["leaf", "common", alphaartifact, betaartifact, - "_alpha_same", "_alpha_external", "_beta_same", - "_beta_external", "test", "__ww-test-000-same", - "__ww-test-001-external", "__ww-test-002-same", - "__ww-test-003-external", "__ww-test-000-main", - "__ww-test-001-main", "__ww-test-002-main", - "__ww-test-003-main"]; + "gamma", "_alpha_same", "_alpha_external", "_beta_same", + "_beta_external", "test", "alpha-internal-test", + "alpha_test-external-test", "beta-internal-test", + "beta_test-external-test", "alpha-internal-test-main", + "alpha_test-external-test-main", "beta-internal-test-main", + "beta_test-external-test-main", "gamma-main"]; let pi: i32 = 0; for (pi < products.len) { assert(os.exists(strings.concat(sharedwork, products[pi], @@ -1336,12 +1420,17 @@ fn workescape(s: str) str = { rootkeys[ri]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); - assert(has(out.stdout, expectedtests[ri])); + if (ri < expectedtests.len) { + assert(has(out.stdout, expectedtests[ri])); + } else { + assert(out.stdout.len == 0 && out.stderr.len == 0); + }; ri += 1; }; if (si == 0) { let ctrace: str = readfile(compilertrace); let once: []str = ["leaf", "common", alphaartifact, betaartifact, + "gamma", "_alpha_same", "_alpha_external", "_beta_same", "_beta_external", "test"]; pi = 0; @@ -1350,10 +1439,12 @@ fn workescape(s: str) str = { ".unit.ww")) == 1); pi += 1; }; - // alpha is both a selected external-test production package and a - // direct dependency of beta. Those identities must intern to the one - // owning production action above, never a second alpha.unit.ww. - assert(occurrences(ctrace, "/alpha.unit.ww") == 0); + // alpha is both external-test production and a direct dependency of + // beta. The canonical production action above is compiled once and no + // role-qualified production action exists. + assert(occurrences(ctrace, "/alpha.unit.ww") == 1); + assert(occurrences(ctrace, "/gamma.unit.ww") == 1); + assert(occurrences(ctrace, "external-production") == 0); ri = 0; for (ri < rootkeys.len) { assert(occurrences(ctrace, strings.concat(rootkeys[ri], @@ -1371,7 +1462,7 @@ fn workescape(s: str) str = { < pos(ctrace, strings.concat("/", betaartifact, ".unit.ww"))); let ltrace: str = readfile(linkertrace); - assert(occurrences(ltrace, "\n") == 4); + assert(occurrences(ltrace, "\n") == 5); ri = 0; for (ri < bins.len) { let link: str = linecontaining(ltrace, @@ -1382,7 +1473,23 @@ fn workescape(s: str) str = { assert(has(link, strings.concat(sharedwork, "common.a"))); assert(has(link, strings.concat(sharedwork, "leaf.a"))); assert(has(link, strings.concat(sharedwork, "test.a"))); - if (ri < 2) { + if (ri >= 2) { + assert(has(link, strings.concat(sharedwork, "gamma.a"))); + } else { + assert(!has(link, strings.concat(sharedwork, "gamma.a"))); + }; + if (ri == 4) { + assert(!has(link, strings.concat(sharedwork, + alphaartifact, ".a"))); + assert(!has(link, strings.concat(sharedwork, + betaartifact, ".a"))); + assert(!has(link, strings.concat(sharedwork, "_alpha_same.a"))); + assert(!has(link, strings.concat(sharedwork, + "_alpha_external.a"))); + assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); + assert(!has(link, strings.concat(sharedwork, + "_beta_external.a"))); + } else { if (ri < 2) { assert(!has(link, strings.concat(sharedwork, betaartifact, ".a"))); assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); @@ -1419,7 +1526,7 @@ fn workescape(s: str) str = { assert(has(link, strings.concat(sharedwork, "_beta_external.a"))); assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); - }; }; }; + }; }; }; }; ri += 1; }; referenceout = buildstdout; @@ -1455,6 +1562,7 @@ fn workescape(s: str) str = { if (si == 2) { let wwtrace: str = readfile(wwcompilertrace); let once: []str = ["leaf", "common", alphaartifact, betaartifact, + "gamma", "_alpha_same", "_alpha_external", "_beta_same", "_beta_external", "test"]; pi = 0; @@ -1463,7 +1571,9 @@ fn workescape(s: str) str = { ".unit.ww")) == 1); pi += 1; }; - assert(occurrences(wwtrace, "/alpha.unit.ww") == 0); + assert(occurrences(wwtrace, "/alpha.unit.ww") == 1); + assert(occurrences(wwtrace, "/gamma.unit.ww") == 1); + assert(occurrences(wwtrace, "external-production") == 0); ri = 0; for (ri < rootkeys.len) { assert(occurrences(wwtrace, strings.concat(rootkeys[ri], @@ -1472,7 +1582,7 @@ fn workescape(s: str) str = { ".unit.ww")) == 1); ri += 1; }; - assert(occurrences(readfile(wwlinkertrace), "\n") == 4); + assert(occurrences(readfile(wwlinkertrace), "\n") == 5); }; if (si + 1 < stages.len) { clean(sharedworkroot); @@ -1510,9 +1620,9 @@ fn workescape(s: str) str = { let warmkey: str = strings.concat("/d_", workescape(suite), "_p_alpha/"); let warmwork: str = strings.concat(warmroot, warmkey); assert(os.exists(strings.concat(warmwork, - "__ww-test-000-same.unit.ww"))); + "alpha-internal-test.unit.ww"))); assert(os.exists(strings.concat(warmwork, - "__ww-test-003-external.unit.ww"))); + "beta_test-external-test.unit.ww"))); let warmww: []str = [wwtracedriver, "test", "-j", "4", "-w", warmwwroot, "-I", suite, "-I", root, strings.concat(suite, "/...")]; @@ -1534,8 +1644,81 @@ fn workescape(s: str) str = { assert(same(readfile(strings.concat(warmwork, "common.a")), readfile(strings.concat(warmwwwork, "common.a")))); assert(same(readfile(strings.concat(warmwork, - "__ww-test-000-same.unit.ww")), readfile(strings.concat(warmwwwork, - "__ww-test-000-same.unit.ww")))); + "alpha-internal-test.unit.ww")), readfile(strings.concat(warmwwwork, + "alpha-internal-test.unit.ww")))); + // Change one shared dependency's public export in place. Each stage must + // rebuild that canonical action and its one direct importer exactly once; + // common's unchanged regenerated export stops propagation to gamma/alpha. + let oldleafwwi: str = readfile(strings.concat(warmwork, "leaf.wwi")); + let oldcommonwwi: str = readfile(strings.concat(warmwork, "common.wwi")); + let oldcommona: str = readfile(strings.concat(warmwork, "common.a")); + let oldgammaa: str = readfile(strings.concat(warmwork, "gamma.a")); + assert(os.remove(strings.concat(leaf, "/leaf.ww")) == 0); + writefile(strings.concat(leaf, "/leaf.ww"), strings.concat( + "package leaf;\n// MULTIDIR_LEAF\n", + "export fn value() i32 = { return 40; };\n", + "export fn newly_visible() i32 = { return 2; };\n")); + let cbeforechange: str = strings.dup(readfile(compilertrace)); + let wbeforechange: str = strings.dup(readfile(wwcompilertrace)); + runcommandenv(root, "multidir-warm-c-export-change", warmparallel, + traceenv, (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(same(warmstdout, out.stdout)); + assert(same(warmstderr, out.stderr)); + let cafterchange: str = strings.dup(readfile(compilertrace)); + assert(occurrences(cafterchange, "/leaf.unit.new") + == occurrences(cbeforechange, "/leaf.unit.new") + 1); + assert(occurrences(cafterchange, "/common.unit.new") + == occurrences(cbeforechange, "/common.unit.new") + 1); + assert(occurrences(cafterchange, "/gamma.unit.new") + == occurrences(cbeforechange, "/gamma.unit.new")); + assert(occurrences(cafterchange, "/alpha-internal-test.unit.new") + == occurrences(cbeforechange, "/alpha-internal-test.unit.new")); + assert(!same(oldleafwwi, readfile(strings.concat(warmwork, "leaf.wwi")))); + assert(same(oldcommonwwi, readfile(strings.concat(warmwork, "common.wwi")))); + assert(same(oldcommona, readfile(strings.concat(warmwork, "common.a")))); + assert(same(oldgammaa, readfile(strings.concat(warmwork, "gamma.a")))); + let changedleafunit: str = readfile(strings.concat(warmwork, + "leaf.unit.ww")); + let changedleafwwi: str = readfile(strings.concat(warmwork, "leaf.wwi")); + let changedleafa: str = readfile(strings.concat(warmwork, "leaf.a")); + let changedcommonunit: str = readfile(strings.concat(warmwork, + "common.unit.ww")); + runcommandenv(root, "multidir-warm-ww-export-change", warmww, + wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(same(warmstdout, out.stdout)); + assert(same(warmstderr, out.stderr)); + let wafterchange: str = strings.dup(readfile(wwcompilertrace)); + assert(occurrences(wafterchange, "/leaf.unit.new") + == occurrences(wbeforechange, "/leaf.unit.new") + 1); + assert(occurrences(wafterchange, "/common.unit.new") + == occurrences(wbeforechange, "/common.unit.new") + 1); + assert(occurrences(wafterchange, "/gamma.unit.new") + == occurrences(wbeforechange, "/gamma.unit.new")); + assert(occurrences(wafterchange, "/alpha-internal-test.unit.new") + == occurrences(wbeforechange, "/alpha-internal-test.unit.new")); + assert(same(changedleafunit, + readfile(strings.concat(warmwwwork, "leaf.unit.ww")))); + assert(same(changedleafwwi, + readfile(strings.concat(warmwwwork, "leaf.wwi")))); + assert(same(changedleafa, + readfile(strings.concat(warmwwwork, "leaf.a")))); + assert(same(changedcommonunit, + readfile(strings.concat(warmwwwork, "common.unit.ww")))); + assert(same(readfile(strings.concat(warmwork, "common.wwi")), + readfile(strings.concat(warmwwwork, "common.wwi")))); + assert(same(readfile(strings.concat(warmwork, "common.a")), + readfile(strings.concat(warmwwwork, "common.a")))); + // A second unchanged request in either stage performs no compilation. + runcommandenv(root, "multidir-warm-c-export-reuse", warmparallel, + traceenv, (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(same(cafterchange, readfile(compilertrace))); + runcommandenv(root, "multidir-warm-ww-export-reuse", warmww, + wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(same(wafterchange, readfile(wwcompilertrace))); clean(wwtracebin); clean(root); }; @@ -1583,16 +1766,16 @@ fn workescape(s: str) str = { runcommand(root, strings.concat("context-conflict-", stages[i]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); - assert(has(out.stderr, - "ww: package _leaf resolves to more than one location\n")); + assert(has(out.stderr, strings.concat( + "ww: package _leaf resolves to directories ", aleaf, + " and ", bleaf, "\n"))); assert(has(out.stderr, strings.concat("FAIL ", a, " [a] (build exit 1)\n"))); assert(has(out.stderr, strings.concat("FAIL ", b, " [b] (build exit 1)\n"))); - assert(!has(out.stderr, strings.concat("FAIL ", good, " ["))); - assert(has(out.stdout, "unrelated_runs ... ok\n")); - assert(has(out.stdout, strings.concat("ok ", good, - " [good, same-package]\n"))); + assert(has(out.stderr, strings.concat("FAIL ", good, + " [good] (build exit 1)\n"))); + assert(!has(out.stdout, "unrelated_runs")); diagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); if (i == 0) { referenceout = strings.dup(out.stdout); } else { assert(same(referenceout, out.stdout)); }; @@ -1622,8 +1805,8 @@ fn workescape(s: str) str = { "@test fn named_external_runs() void = {", " assert(test.value() == 42); };\n")); writefile(strings.concat(consumer, "/consumer.ww"), strings.concat( - "package consumer;\nimport test;\n", - "// MIXED_EXPLICIT_TOOLCHAIN_TEST_IMPORT\n", + "package consumer;\n", + "// MIXED_GENERATED_TOOLCHAIN_TEST_SUPPORT\n", "export fn value() i32 = { return 42; };\n")); writefile(strings.concat(consumer, "/same_test.ww"), strings.concat( "package consumer;\n@test fn consumer_same_runs() void = {", @@ -1656,8 +1839,8 @@ fn workescape(s: str) str = { strings.concat(named, "/test_test.test"), strings.concat(consumer, "/consumer.test"), strings.concat(consumer, "/consumer_test.test")]; - let keys: []str = ["__ww-test-000-same", "__ww-test-001-external", - "__ww-test-002-same", "__ww-test-003-external"]; + let keys: []str = ["test-internal-test", "test_test-external-test", + "consumer-internal-test", "consumer_test-external-test"]; let expected: []str = ["named_same_runs ... ok\n", "named_external_runs ... ok\n", "consumer_same_runs ... ok\n", "consumer_external_runs ... ok\n"]; @@ -1678,17 +1861,13 @@ fn workescape(s: str) str = { (120i64 * (time.second: i64)): time.duration, &out); }; expectexit(&out, 0); - let namedprod: str = readfile(strings.concat(work, - "__ww-test-001-external-production.unit.ww")); - let normaltest: str = readfile(strings.concat(work, "test.unit.ww")); + let namedprod: str = readfile(strings.concat(work, "test.unit.ww")); let support: str = readfile(strings.concat(work, "__wwtest.unit.ww")); assert(has(namedprod, "MIXED_USER_TEST_PRODUCTION")); - assert(!has(normaltest, "MIXED_USER_TEST_PRODUCTION")); - assert(has(normaltest, "//ww:module-reset test\n")); + assert(has(namedprod, "//ww:module-reset test\n")); assert(has(support, "//ww:module-reset __wwtest\n")); let suffixes: []str = [".unit.ww", ".wwi", ".a"]; - let actions: []str = ["__ww-test-001-external-production", - "test", "__wwtest"]; + let actions: []str = ["test", "__wwtest"]; let ai: i32 = 0; for (ai < actions.len) { let xi: i32 = 0; @@ -1725,18 +1904,12 @@ fn workescape(s: str) str = { strings.concat("-o ", bins[3], " ")); assert(has(namedsame, strings.concat(work, "__wwtest.a"))); assert(!has(namedsame, strings.concat(work, "test.a"))); - assert(has(namedexternal, strings.concat(work, - "__ww-test-001-external-production.a"))); + assert(has(namedexternal, strings.concat(work, "test.a"))); assert(has(namedexternal, strings.concat(work, "__wwtest.a"))); - assert(!has(namedexternal, strings.concat(work, "test.a"))); - assert(has(consumersame, strings.concat(work, "test.a"))); assert(has(consumersame, strings.concat(work, "__wwtest.a"))); - assert(!has(consumersame, strings.concat(work, - "__ww-test-001-external-production.a"))); - assert(has(consumerexternal, strings.concat(work, "test.a"))); + assert(!has(consumersame, strings.concat(work, "test.a"))); assert(has(consumerexternal, strings.concat(work, "__wwtest.a"))); - assert(!has(consumerexternal, strings.concat(work, - "__ww-test-001-external-production.a"))); + assert(!has(consumerexternal, strings.concat(work, "test.a"))); }; if (si == 0) { clean(workroot); @@ -1817,7 +1990,7 @@ fn workescape(s: str) str = { let cstderr: str = strings.dup(out.stderr); let cbin: str = readfile(bin); let rootunit: str = readfile(strings.concat(work, - "__ww-test-000-same.unit.ww")); + "target-internal-test.unit.ww")); assert(has(rootunit, "//ww:module-reset target\npackage target;")); assert(!has(rootunit, "//ww:module ")); let linkargs: str = readfile(trace); @@ -2036,17 +2209,17 @@ fn workescape(s: str) str = { expectexit(&out, 1); let firsttrace: str = readfile(trace); assert(occurrences(firsttrace, - "/__ww-test-000-same.unit.new") == 1); + "/badexternal-internal-test.unit.new") == 1); assert(occurrences(firsttrace, - "/__ww-test-001-external.unit.new") == 1); + "/badexternal_test-external-test.unit.new") == 1); runcommandenv(root, "isolated-warm-reuse", warmav, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let secondtrace: str = readfile(trace); assert(occurrences(secondtrace, - "/__ww-test-000-same.unit.new") == 1); + "/badexternal-internal-test.unit.new") == 1); assert(occurrences(secondtrace, - "/__ww-test-001-external.unit.new") == 2); + "/badexternal_test-external-test.unit.new") == 2); let wwtracebin: str = strings.concat(repo(), "/out/partial-trace-", workescape(root)); @@ -2086,17 +2259,17 @@ fn workescape(s: str) str = { expectexit(&out, 1); let wwfirst: str = readfile(wwtrace); assert(occurrences(wwfirst, - "/__ww-test-000-same.unit.new") == 1); + "/badexternal-internal-test.unit.new") == 1); assert(occurrences(wwfirst, - "/__ww-test-001-external.unit.new") == 1); + "/badexternal_test-external-test.unit.new") == 1); runcommandenv(root, "isolated-warm-ww-reuse", wwwarmav, wwenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let wwsecond: str = readfile(wwtrace); assert(occurrences(wwsecond, - "/__ww-test-000-same.unit.new") == 1); + "/badexternal-internal-test.unit.new") == 1); assert(occurrences(wwsecond, - "/__ww-test-001-external.unit.new") == 2); + "/badexternal_test-external-test.unit.new") == 2); clean(wwtracebin); clean(root); }; @@ -2140,9 +2313,9 @@ fn workescape(s: str) str = { (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let rootunit: str = readfile(strings.concat(work, - "__ww-test-000-external.unit.ww")); + "test_test-external-test.unit.ww")); let produnit: str = readfile(strings.concat(work, - "__ww-test-000-external-production.unit.ww")); + "test.unit.ww")); let supportunit: str = readfile(strings.concat(work, "__wwtest.unit.ww")); assert(!has(rootunit, "//ww:module ")); @@ -2154,9 +2327,11 @@ fn workescape(s: str) str = { assert(has(supportunit, "//ww:module-reset __wwtest\n")); assert(has(supportunit, "package test;\n")); assert(os.exists(strings.concat(work, - "__ww-test-000-external-production.wwi"))); + "test.wwi"))); assert(os.exists(strings.concat(work, - "__ww-test-000-external-production.a"))); + "test.a"))); + assert(!os.exists(strings.concat(work, + "__ww-test-000-external-production.unit.ww"))); assert(os.exists(strings.concat(work, "__wwtest.wwi"))); assert(os.exists(strings.concat(work, "__wwtest.a"))); let runav: []str = [bin]; @@ -2285,9 +2460,9 @@ fn workescape(s: 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", "__root"]; + "ww_root_parity_left_7f3", "ww_root_parity_right_7f3", "main"]; let unitartifacts: []str = ["ww_root_parity_base_7f3", - "ww_root_parity_left_7f3", "ww_root_parity_right_7f3", "__root"]; + "ww_root_parity_left_7f3", "ww_root_parity_right_7f3", "main"]; 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"), @@ -2425,23 +2600,28 @@ fn workescape(s: str) str = { "ww_root_parity_left_7f3.wwi><--import>", "<", work, "ww_root_parity_right_7f3.wwi><-I><", work, - "__root.wwi><-o><", work, - "__root.s><", work, "__root.unit.ww>"); + "main.wwi><-o><", work, + "main.s><", work, "main.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, "__root.unit.ww"), rootline)); + assert(same(linecontaining(ctrace, "main.unit.ww"), rootline)); assert(!has(rootline, "ww_root_parity_base_7f3.wwi")); assert(occurrences(atrace, "\n") == 4); - assert(has(atrace, strings.concat("BEGIN<-o><", work, - "ww_root_parity_left_7f3.o><", work, - "ww_root_parity_left_7f3.s>"))); + ai = 0; + for (ai < artifacts.len) { + let assemblerline: str = strings.concat("BEGIN<-o><", work, + artifacts[ai], ".o><", work, artifacts[ai], ".s>"); + assert(same(linecontaining(atrace, strings.concat(work, + artifacts[ai], ".o")), assemblerline)); + ai += 1; + }; assert(occurrences(ltrace, "\n") == 1); assert(has(ltrace, strings.concat("BEGIN<-o><", bin, "><", work, - "__root.a>"))); + "main.a>"))); ai = 0; for (ai < artifacts.len) { assert(occurrences(ltrace, strings.concat("<", work, artifacts[ai], @@ -2522,6 +2702,11 @@ fn workescape(s: str) str = { let privatepkg: str = strings.concat(root, "/privatepkg"); let exportroot: str = strings.concat(root, "/exportroot"); let exportdep: str = strings.concat(root, "/dupexport"); + let identityroot: str = strings.concat(root, "/identityroot"); + let identityactual: str = strings.concat(root, "/identityactual"); + let identityalias: str = strings.concat(root, "/identityalias"); + let dupleft: str = strings.concat(root, "/dupleft"); + let dupright: str = strings.concat(root, "/dupright"); assert(os.mkdir(missing, 448i32) == 0); assert(os.mkdir(cycle, 448i32) == 0); assert(os.mkdir(left, 448i32) == 0); @@ -2530,6 +2715,10 @@ fn workescape(s: str) str = { assert(os.mkdir(privatepkg, 448i32) == 0); assert(os.mkdir(exportroot, 448i32) == 0); assert(os.mkdir(exportdep, 448i32) == 0); + assert(os.mkdir(identityroot, 448i32) == 0); + assert(os.mkdir(identityactual, 448i32) == 0); + assert(os.mkdir(dupleft, 448i32) == 0); + assert(os.mkdir(dupright, 448i32) == 0); writefile(strings.concat(missing, "/missing.ww"), "package missing;\nfn local() void = { };\n"); writefile(strings.concat(missing, "/missing_test.ww"), strings.concat( @@ -2563,6 +2752,25 @@ fn workescape(s: str) str = { "export fn value() i32 = { return dupexport.value(); };\n")); writefile(strings.concat(exportdep, "/dupexport.ww"), "package dupexport;\nexport fn value() i32 = { return 1; };\n"); + writefile(strings.concat(identityactual, "/identityactual.ww"), + "package identityactual;\nexport fn value() i32 = { return 1; };\n"); + let linkout: commandout; + let linkav: []str = ["/bin/ln", "-s", identityactual, identityalias]; + runcommand(root, "identity-alias", linkav, time.second, &linkout); + expectexit(&linkout, 0); + writefile(strings.concat(identityroot, "/identityroot.ww"), strings.concat( + "package identityroot;\nimport identityactual;\n", + "import identityalias;\nexport fn value() i32 = { return 1; };\n")); + writefile(strings.concat(dupleft, "/dupproduct.ww"), + "package dupproduct;\nexport fn value() i32 = { return 1; };\n"); + writefile(strings.concat(dupleft, "/external_test.ww"), strings.concat( + "package dupproduct_test;\nimport dupproduct;\n", + "@test fn left() void = { assert(dupproduct.value() == 1); };\n")); + writefile(strings.concat(dupright, "/dupproduct.ww"), + "package dupproduct;\nexport fn value() i32 = { return 2; };\n"); + writefile(strings.concat(dupright, "/external_test.ww"), strings.concat( + "package dupproduct_test;\nimport dupproduct;\n", + "@test fn right() void = { assert(dupproduct.value() == 2); };\n")); rejectpackagestable(root, "missing", missing, "cannot find package nowhere"); rejectpackagestable(root, "cycle", cycle, "ww: dependency cycle:"); @@ -2572,21 +2780,92 @@ fn workescape(s: str) str = { "package 'privatepkg' has no exported declaration 'hidden'"); let exportdiagnostics: []str = ["", ""]; let stages: []str = ["ww", "ww_ww"]; + let tooltrace: str = strings.concat(root, "/identity-tool.trace"); + let compilerwrapper: str = strings.concat(root, "/identity-w6c.sh"); + let linkerwrapper: str = strings.concat(root, "/identity-w6l.sh"); + writefile(tooltrace, ""); + writeexecutable(compilerwrapper, strings.concat( + "#!/bin/sh\nprintf 'compile\\n' >> \"$WW_IDENTITY_TRACE\"\n", + "exec \"$WW_IDENTITY_W6C\" \"$@\"\n")); + writeexecutable(linkerwrapper, strings.concat( + "#!/bin/sh\nprintf 'link\\n' >> \"$WW_IDENTITY_TRACE\"\n", + "exec \"$WW_IDENTITY_W6L\" \"$@\"\n")); + let baseenv: []str = os.getenvs(); + let traceenv: []str = alloc([], (baseenv.len + 5): u64)!; + let ei: i32 = 0; + for (ei < baseenv.len) { + if (!strings.hasprefix(baseenv[ei], "WW_W6C=") + && !strings.hasprefix(baseenv[ei], "WW_W6L=") + && !strings.hasprefix(baseenv[ei], "WW_IDENTITY_TRACE=") + && !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6C=") + && !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6L=")) { + append(traceenv, baseenv[ei]); + }; + ei += 1; + }; + append(traceenv, strings.concat("WW_W6C=", compilerwrapper)); + append(traceenv, strings.concat("WW_W6L=", linkerwrapper)); + append(traceenv, strings.concat("WW_IDENTITY_TRACE=", tooltrace)); + append(traceenv, strings.concat("WW_IDENTITY_W6C=", driver("w6c"))); + append(traceenv, strings.concat("WW_IDENTITY_W6L=", driver("w6l"))); let out: commandout; let i: i32 = 0; for (i < stages.len) { let av: []str = [driver(stages[i]), "build", "-p", "-I", root, "-o", strings.concat(root, "/bad-export-", stages[i], ".a"), exportroot]; - runcommand(root, strings.concat("duplicate-export-module-", stages[i]), - av, (60i64 * (time.second: i64)): time.duration, &out); + runcommandenv(root, + strings.concat("duplicate-export-module-", stages[i]), + av, traceenv, (60i64 * (time.second: i64)): time.duration, &out); assert(out.termination == exec.termination.EXIT && out.code != 0); - assert(has(out.stderr, - "product closure contains multiple packages named dupexport")); + assert(has(out.stderr, strings.concat( + "ww: package dupexport resolves to directories ", exportdep, + " and ", exportroot, "\n"))); exportdiagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); i += 1; }; assert(same(exportdiagnostics[0], exportdiagnostics[1])); + let identitydiagnostics: []str = ["", ""]; + i = 0; + for (i < stages.len) { + let av: []str = [driver(stages[i]), "build", "-p", "-I", root, + "-o", strings.concat(root, "/bad-identity-", stages[i], ".a"), + identityroot]; + runcommandenv(root, strings.concat("duplicate-directory-identity-", + stages[i]), av, traceenv, + (60i64 * (time.second: i64)): time.duration, &out); + assert(out.termination == exec.termination.EXIT && out.code != 0); + assert(has(out.stderr, strings.concat("ww: package directory ", + identityactual, " has import identities identityactual and ", + "identityalias\n"))); + identitydiagnostics[i] = strings.dup(out.stderr); + i += 1; + }; + assert(same(identitydiagnostics[0], identitydiagnostics[1])); + let productdiagnostics: []str = ["", ""]; + i = 0; + for (i < stages.len) { + let av: []str = [driver(stages[i]), "test", "-c", "-I", root, + "--ww-package-test", "external", "dupproduct_test", dupright, + strings.concat(root, "/dup-right-", stages[i], ".test"), + strings.concat(root, "/dup-right-", stages[i], ".status"), + "--ww-package-test", "external", "dupproduct_test", dupleft, + strings.concat(root, "/dup-left-", stages[i], ".test"), + strings.concat(root, "/dup-left-", stages[i], ".status"), root]; + runcommandenv(root, strings.concat("duplicate-product-identity-", + stages[i]), av, traceenv, + (60i64 * (time.second: i64)): time.duration, &out); + assert(out.termination == exec.termination.EXIT && out.code != 0); + assert(has(out.stderr, strings.concat( + "ww: package dupproduct resolves to directories ", dupleft, + " and ", dupright, "\n"))); + productdiagnostics[i] = strings.dup(out.stderr); + i += 1; + }; + assert(same(productdiagnostics[0], productdiagnostics[1])); + // Both identity directions reject while the command-global package + // universe is still unambiguous, before compiler or linker invocation. + assert(readfile(tooltrace).len == 0); clean(root); }; @@ -2820,8 +3099,8 @@ fn workescape(s: str) str = { let key: str = strings.concat(work, "/d_", workescape(tree), "_p_", workescape("b_c")); assert(os.exists(key)); - assert(os.exists(strings.concat(key, "/__ww-test-000-same.unit.ww"))); - assert(os.exists(strings.concat(key, "/__ww-test-001-same.unit.ww"))); + assert(os.exists(strings.concat(key, "/b_c-internal-test.unit.ww"))); + assert(os.exists(strings.concat(key, "/c-internal-test.unit.ww"))); runcommand(root, "workkey-warm", av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0);