test: prove internal package visibility

This commit is contained in:
2026-08-13 15:38:03 +09:00
parent 9a5dbac84f
commit 088105413d
2 changed files with 756 additions and 0 deletions

View File

@@ -3855,6 +3855,139 @@ preloader, parallel action scheduler, or network behavior. Normal local WW
builds and tests remain offline, manifest-free, registry-free, database-free, builds and tests remain offline, manifest-free, registry-free, database-free,
CAS-free, and network-free. CAS-free, and network-free.
### 11.15 Implemented internal-package import visibility
A source import whose complete canonical import path contains a directory
component named exactly `internal` is now contextual. Locate the final such
component. Its parent directory is the ownership boundary, and the import is
legal only when the importing source package's canonical filesystem directory
is that boundary or a descendant at a real path-component boundary. Thus
`domain.client` may import `domain.internal.secret`, while `outsider` and
`domainx.client` may not. `internalx` has no special meaning. For
`domain.internal.outer.internal.deep`, the final `internal` wins and the owner
is `domain.internal.outer`, not `domain`.
The complete dotted import identity determines whether the rule applies and
how many components comprise the final `internal` plus its following suffix.
WW removes those components from that source edge's resolved lexical target
route, canonicalizes the resulting owner directory physically, and compares it
with the already canonical physical importer directory. Stripping precedes
physical canonicalization because a symlink at or below `internal` may point to
a target with a different name or depth; an action spelling interned by an
earlier edge is never reused for this contextual calculation. Cstage obtains
directory canonicalization through `realpath`; WWstage uses its equivalent
`chdir`/`getcwd` canonicalization. For explicit single-file compatibility
roots, Cstage uses `realpath` and WWstage's component walker uses
`lstat`/`readlink` plus the same canonical current-directory representation;
both preserve component and trailing-directory semantics across symlinks. The
equality-or-`/`-boundary comparison never uses a raw string prefix. This
division is intentional: an unrelated physical
ancestor literally named `internal` does not impose visibility on an import
whose canonical identity has no such component, while a symlink spelling at or
below an import-path `internal` cannot move the effective owner or importer.
Explicit single-file compatibility roots use their canonicalized containing
directory as the importer context. Directly selecting an internal directory as
a command root remains legal because selection is not a source import.
Visibility is an import-edge property, not part of canonical package or action
identity. Both drivers resolve the complete target, intern or reuse its one
canonical production action, and then perform the importer-context check before
accepting the source binding or dependency edge. Consequently an allowed
importer may load and compile the target normally, but that cached action cannot
authorize a later forbidden importer. Reversing requested products or visiting
the forbidden importer first produces the same result. No declared package
name, leaf, output name, product ordinal, storage locator, hash, or test variant
participates in the decision. Legal edges therefore retain the existing
owner-only byte-sorted unit, exact sorted/deduplicated direct `.wwi` compiler
inputs, deterministic archive, and complete archive-only linker closure.
Cstage represents the rule with the bounded
`sep_internal_parent_count`/`sep_internal_import_allowed` helpers in
`cmd/ww/main.c`. WWstage has the isomorphic
`sepinternalparentcount`/`sepinternalimportallowed` helpers in
`selfhost/cmd/ww/main.ww`. Neither adds a package field, fixed-size package
table, second action universe, or action-key input. A distinct
`SEP_LOAD_INTERNAL` result propagates through the iterative loader. On rejection
both stages emit the importing parser position followed by exactly:
```text
use of internal package <canonical-import-path> not allowed
```
The command returns immediately from graph loading, before directory-identity
finalization, unit composition, workdir owner validation, stale-voucher
invalidation, tool-identity recording, compiler, assembler, in-driver archive
production, linker, publication, or execution. A cold rejection therefore
commits no target/importer artifact or `.wwtool.*` state. A forbidden warm
request cannot use a previously committed target to bypass the check and leaves
the already committed target voucher and all tool records byte-unchanged.
Allocation or path canonicalization failure remains a deterministic
command-fatal pre-tool error; the visibility helper does not change stable
package indices or growth behavior.
Every selected source file reaches the same driver scan. The rule therefore
applies to ordinary production sources, same-package test sources in the
internal production-plus-test variant, external `_test` sources, and real
source imports inside the test-support package. Generated-main-to-variant,
generated-main-to-support, and coordinator product wiring remain synthetic
edges and are not retroactively treated as source imports. No coordinator
change is required: `cmd/wwtest` only dispatches, and
`internal/wwpackage/package.ww` only discovers and classifies source groups,
constructs the union request, and runs the driver; neither resolves a source
import or owns its importing position.
This follows only the pinned official Go 1.26.5 source at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- `loadImport` resolves and reuses the canonical package first, then explicitly
checks the rule on every import because the result depends on the importing
code, and attaches the importing position
([`pkg.go`, lines 787791](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L787-L791)).
- The rule is the tree rooted at the parent of the target's `internal`
directory
([`pkg.go`, lines 14631471](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1463-L1471)).
A package named directly on the command line is not an import
([lines 14981502](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1498-L1502)),
and the import-path boundary is located before filesystem containment
([lines 15051515](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1505-L1515)).
- Go's filesystem branch cleans the importer and owner, requires a
path-component-aware prefix, and retries with both paths symlink-expanded
([`pkg.go`, lines 15341546](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1534-L1546)).
Its rejection text is exactly the diagnostic above
([lines 15641571](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1564-L1571)),
and its exact-component search deliberately selects the final `internal`
element as the most restrictive rule
([lines 15741590](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1574-L1590)).
- Same-package and external test imports both pass through ordinary
`loadImport` with their own source positions before variant construction
([`test.go`, lines 102161](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L102-L161)).
Generated test-main dependencies are synthesized separately
([lines 307330](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L307-L330)).
- Go's downstream action cache keys canonical package actions independently
and consumes the already validated direct package imports, so visibility does
not belong in action identity
([`action.go`, lines 437455](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L455),
[lines 628657](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L657)).
The focused native observer `internal_package_import_visibility` generates its
lexical package/import tree under a temporary physical ancestor also named
`internal`, while deliberate symlink destinations live outside that ancestor,
so that unrelated-host-path case is exercised rather than documented only. It
builds and runs allowed descendant, nested-final, `internalx`, and symlinked
physical-owner cases; rejects outsider, sibling-prefix, nested-final, and
symlink-escape cases at exact source positions with empty tool traces; maps an
internal target symlinked to a differently shaped physical path back to its
lexical owner's canonical directory; builds an internal package directly; and
reverses requested allowed/forbidden product-descriptor order around one reused
target. It checks ordinary, internal-test, and external-test
source imports, generated-main transitive-export isolation, owner-only source
order, direct export inputs, archive-only link closure, runtime results, warm
no-op package production, rejection-state preservation, and the primary
production chain's unit, export, assembly, object, archive, binary, and exact
normalized tool arguments across independent cold Cstage and WWstage work
roots. Test variants additionally compare their unit/export/archive bytes and
exercise byte-equivalent runtime output in both stages.
## 12. Candidate architectures and hard-gate decision ## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins. Five candidates were developed as coherent systems, not as feature bins.

View File

@@ -423,6 +423,18 @@ fn mkdirall(path: str) void = {
}; };
}; };
fn normalizedtrace(trace: str, work: str, output: str) str = {
let first: str;
match (strings.replace(trace, work, "$WORK/")) {
case let value: str => first = value;
case nomem => abort("trace normalization failed");
};
match (strings.replace(first, output, "$OUTPUT")) {
case let value: str => return value;
case nomem => abort("trace normalization failed");
};
};
@test fn deterministic_routing_and_filters() void = { @test fn deterministic_routing_and_filters() void = {
let root: str = fresh(); let root: str = fresh();
let target: str = packagepath("routing"); let target: str = packagepath("routing");
@@ -3550,6 +3562,617 @@ fn mkdirall(path: str) void = {
clean(root); clean(root);
}; };
@test fn internal_package_import_visibility() void = {
let root: str = fresh();
let source: str = strings.concat(root, "/host/internal/source");
let runtime: str = strings.concat(root, "/runtime");
let tools: str = strings.concat(root, "/tools");
let domain: str = strings.concat(source, "/domain");
let leaf: str = strings.concat(domain, "/leaf");
let secret: str = strings.concat(domain, "/internal/secret");
let client: str = strings.concat(domain, "/client");
let outsider: str = strings.concat(source, "/outsider");
let prefixclient: str = strings.concat(source, "/domainx/client");
let deep: str = strings.concat(domain,
"/internal/outer/internal/deep");
let deepclient: str = strings.concat(domain, "/internal/outer/client");
let nestedoutsider: str = strings.concat(domain, "/nestedclient");
let internalx: str = strings.concat(domain, "/internalx/visible");
let internalxclient: str = strings.concat(source, "/internalxclient");
let testclient: str = strings.concat(domain, "/testclient");
let outsame: str = strings.concat(source, "/outsame");
let outexternal: str = strings.concat(source, "/outexternal");
let physical: str = strings.concat(root, "/physical/domain");
let linkedsecret: str = strings.concat(physical, "/internal/secret");
let linkedclient: str = strings.concat(physical, "/client");
let rawallowed: str = strings.concat(source, "/alias/rawclient.ww");
let rawalias: str = strings.concat(domain, "/rawalias.ww");
let rawescape: str = strings.concat(domain, "/rawescape.ww");
let aliasinternal: str = strings.concat(source, "/alias/internal");
let aliasclient: str = strings.concat(source, "/alias/client");
let aliasownedclient: str = strings.concat(source,
"/alias/owned-client");
let aliasoutsider: str = strings.concat(source, "/alias/outsider");
let shapedomain: str = strings.concat(source, "/shape/domain");
let shapeinternal: str = strings.concat(shapedomain, "/internal");
let shapeclient: str = strings.concat(shapedomain, "/client");
let shapeoutsider: str = strings.concat(source, "/shape/outsider");
let shapevault: str = strings.concat(root, "/vault");
mkdirall(leaf); mkdirall(secret); mkdirall(client); mkdirall(outsider);
mkdirall(prefixclient); mkdirall(deep); mkdirall(deepclient);
mkdirall(nestedoutsider); mkdirall(internalx); mkdirall(internalxclient);
mkdirall(testclient); mkdirall(outsame); mkdirall(outexternal);
mkdirall(linkedsecret); mkdirall(linkedclient); mkdirall(aliasinternal);
mkdirall(aliasownedclient); mkdirall(shapeinternal); mkdirall(shapeclient);
mkdirall(shapeoutsider); mkdirall(shapevault); mkdirall(runtime);
mkdirall(tools);
writefile(strings.concat(runtime, "/libwwrt.a"),
readfile(strings.concat(repo(), "/out/lib/libwwrt.a")));
let leafbody: str = strings.concat(
"package leaf;\n",
"export fn value() i32 = { return 20; };\n");
let secreta: str = strings.concat(
"package secret;\n",
"import domain.leaf;\n",
"export fn value() i32 = { return leaf.value() + 22; };\n");
let secretz: str = strings.concat(
"package secret;\n",
"fn private_value() i32 = { return 99; };\n");
let clientbody: str = strings.concat(
"package main;\n",
"import domain.internal.secret;\n",
"import domain.internal.secret;\n",
"fn main() i32 = { return secret.value(); };\n");
writefile(strings.concat(leaf, "/leaf.ww"), leafbody);
writefile(strings.concat(secret, "/a.ww"), secreta);
writefile(strings.concat(secret, "/z.ww"), secretz);
writefile(strings.concat(client, "/main.ww"), clientbody);
writefile(strings.concat(outsider, "/main.ww"), strings.concat(
"package main;\nimport domain.internal.secret;\n",
"fn main() i32 = { return secret.value(); };\n"));
writefile(strings.concat(prefixclient, "/main.ww"), strings.concat(
"package main;\nimport domain.internal.secret;\n",
"fn main() i32 = { return secret.value(); };\n"));
writefile(strings.concat(deep, "/deep.ww"), strings.concat(
"package deep;\n",
"export fn value() i32 = { return 7; };\n"));
writefile(strings.concat(deepclient, "/main.ww"), strings.concat(
"package main;\n",
"import domain.internal.outer.internal.deep;\n",
"fn main() i32 = { return deep.value(); };\n"));
writefile(strings.concat(nestedoutsider, "/main.ww"), strings.concat(
"package main;\n",
"import domain.internal.outer.internal.deep;\n",
"fn main() i32 = { return deep.value(); };\n"));
writefile(strings.concat(internalx, "/visible.ww"), strings.concat(
"package visible;\n",
"export fn value() i32 = { return 9; };\n"));
writefile(strings.concat(internalxclient, "/main.ww"), strings.concat(
"package main;\nimport domain.internalx.visible;\n",
"fn main() i32 = { return visible.value(); };\n"));
writefile(strings.concat(linkedsecret, "/secret.ww"), strings.concat(
"package secret;\n",
"export fn value() i32 = { return 31; };\n"));
writefile(strings.concat(linkedclient, "/main.ww"), strings.concat(
"package main;\nimport alias.internal.secret;\n",
"fn main() i32 = { return secret.value(); };\n"));
writefile(rawallowed, strings.concat(
"package main;\nimport alias.internal.secret;\n",
"fn main() i32 = { return secret.value(); };\n"));
assert(os.symlink(rawallowed, rawalias) == 0);
assert(os.symlink(strings.concat(outsider, "/main.ww"), rawescape) == 0);
assert(os.symlink(linkedsecret,
strings.concat(aliasinternal, "/secret")) == 0);
writefile(strings.concat(aliasownedclient, "/main.ww"), strings.concat(
"package main;\nimport alias.internal.secret;\n",
"fn main() i32 = { return secret.value(); };\n"));
assert(os.symlink(aliasownedclient, aliasclient) == 0);
assert(os.symlink(linkedclient, aliasoutsider) == 0);
writefile(strings.concat(shapevault, "/secret.ww"), strings.concat(
"package secret;\n",
"export fn value() i32 = { return 17; };\n"));
assert(os.symlink(shapevault,
strings.concat(shapeinternal, "/secret")) == 0);
writefile(strings.concat(shapeclient, "/main.ww"), strings.concat(
"package main;\nimport shape.domain.internal.secret;\n",
"fn main() i32 = { return secret.value(); };\n"));
writefile(strings.concat(shapeoutsider, "/main.ww"), strings.concat(
"package main;\nimport shape.domain.internal.secret;\n",
"fn main() i32 = { return secret.value(); };\n"));
writefile(strings.concat(testclient, "/testclient.ww"), strings.concat(
"package testclient;\n",
"export fn value() i32 = { return 1; };\n"));
writefile(strings.concat(testclient, "/same_test.ww"), strings.concat(
"package testclient;\nimport domain.internal.secret;\n",
"@test fn allowed_same() void = {\n",
" assert(secret.value() == 42);\n};\n"));
writefile(strings.concat(testclient, "/external_test.ww"), strings.concat(
"package testclient_test;\nimport domain.internal.secret;\n",
"import domain.testclient;\n",
"@test fn allowed_external() void = {\n",
" assert(secret.value() + testclient.value() == 43);\n};\n"));
writefile(strings.concat(outsame, "/outsame.ww"),
"package outsame;\nexport fn value() i32 = { return 1; };\n");
writefile(strings.concat(outsame, "/same_test.ww"), strings.concat(
"package outsame;\nimport domain.internal.secret;\n",
"@test fn forbidden_same() void = { assert(false); };\n"));
writefile(strings.concat(outexternal, "/outexternal.ww"),
"package outexternal;\nexport fn value() i32 = { return 1; };\n");
writefile(strings.concat(outexternal, "/external_test.ww"), strings.concat(
"package outexternal_test;\nimport domain.internal.secret;\n",
"@test fn forbidden_external() void = { assert(false); };\n"));
let compilerwrapper: str = strings.concat(tools, "/w6c.sh");
let assemblerwrapper: str = strings.concat(tools, "/w6a.sh");
let linkerwrapper: str = strings.concat(tools, "/w6l.sh");
writeexecutable(compilerwrapper, strings.concat(
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_INTERNAL_COMPILER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_INTERNAL_COMPILER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_INTERNAL_COMPILER_TRACE\"\n",
"exec \"$WW_INTERNAL_REAL_COMPILER\" \"$@\"\n"));
writeexecutable(assemblerwrapper, strings.concat(
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_INTERNAL_ASSEMBLER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_INTERNAL_ASSEMBLER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_INTERNAL_ASSEMBLER_TRACE\"\n",
"exec \"$WW_INTERNAL_REAL_ASSEMBLER\" \"$@\"\n"));
writeexecutable(linkerwrapper, strings.concat(
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_INTERNAL_LINKER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_INTERNAL_LINKER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_INTERNAL_LINKER_TRACE\"\n",
"exec \"$WW_INTERNAL_REAL_LINKER\" \"$@\"\n"));
let stages: []str = ["ww", "ww_ww"];
let compilers: []str = ["w6c", "w6c_ww"];
let assemblers: []str = ["w6a", "w6a_ww"];
let linkers: []str = ["w6l", "w6l_ww"];
let labels: []str = ["0", "1", "2", "3", "4"];
let works: []str = [strings.concat(root, "/c-work"),
strings.concat(root, "/ww-work")];
let bins: []str = [strings.concat(root, "/c-bin"),
strings.concat(root, "/ww-bin")];
let compilertraces: []str = [strings.concat(root, "/c-compiler"),
strings.concat(root, "/ww-compiler")];
let assemblertraces: []str = [strings.concat(root, "/c-assembler"),
strings.concat(root, "/ww-assembler")];
let linkertraces: []str = [strings.concat(root, "/c-linker"),
strings.concat(root, "/ww-linker")];
let artifacts: []str = ["domain.leaf", "domain.internal.secret",
"domain.client"];
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
let references: []str = alloc([], (artifacts.len * suffixes.len): u64)!;
let ri: i32 = 0;
for (ri < artifacts.len * suffixes.len) {
append(references, ""); ri += 1;
};
let referencebin: str = "";
let referencecompiler: str = "";
let referenceassembler: str = "";
let referencelinker: str = "";
let referencediagnostic: str = "";
let referencetestout: str = "";
let referencetesterr: str = "";
let referencetestartifacts: []str = alloc([], 12u64)!;
ri = 0;
for (ri < 12) { append(referencetestartifacts, ""); ri += 1; };
let allowedreferences: []str = ["", "", "", ""];
let testrejectreferences: []str = ["", ""];
let orderreference: str = "";
let baseenv: []str = os.getenvs();
let si: i32 = 0;
for (si < stages.len) {
assert(os.mkdir(works[si], 448i32) == 0);
writefile(compilertraces[si], ""); writefile(assemblertraces[si], "");
writefile(linkertraces[si], "");
let env: []str = alloc([], (baseenv.len + 11): u64)!;
let ei: i32 = 0;
for (ei < baseenv.len) {
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
&& !strings.hasprefix(baseenv[ei],
"WW_INTERNAL_COMPILER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_INTERNAL_ASSEMBLER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_INTERNAL_LINKER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_INTERNAL_REAL_COMPILER=")
&& !strings.hasprefix(baseenv[ei],
"WW_INTERNAL_REAL_ASSEMBLER=")
&& !strings.hasprefix(baseenv[ei],
"WW_INTERNAL_REAL_LINKER=")
&& !strings.hasprefix(baseenv[ei], "WW_LIB=")) {
append(env, baseenv[ei]);
};
ei += 1;
};
append(env, strings.concat("WW_W6C=", compilerwrapper));
append(env, strings.concat("WW_W6A=", assemblerwrapper));
append(env, strings.concat("WW_W6L=", linkerwrapper));
append(env, strings.concat("WW_INTERNAL_COMPILER_TRACE=",
compilertraces[si]));
append(env, strings.concat("WW_INTERNAL_ASSEMBLER_TRACE=",
assemblertraces[si]));
append(env, strings.concat("WW_INTERNAL_LINKER_TRACE=",
linkertraces[si]));
append(env, strings.concat("WW_INTERNAL_REAL_COMPILER=",
driver(compilers[si])));
append(env, strings.concat("WW_INTERNAL_REAL_ASSEMBLER=",
driver(assemblers[si])));
append(env, strings.concat("WW_INTERNAL_REAL_LINKER=",
driver(linkers[si])));
append(env, strings.concat("WW_LIB=", runtime));
let av: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", bins[si], client];
let out: commandout;
runcommandenv(root, strings.concat("internal-cold-", stages[si]), av,
env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
let wantsecret: str = strings.concat(
"//ww:module-reset domain.internal.secret\n", secreta,
"\n//ww:module-reset domain.internal.secret\n", secretz, "\n");
assert(same(readfile(strings.concat(works[si],
"/domain.internal.secret.unit.ww")), wantsecret));
let ai: i32 = 0;
for (ai < artifacts.len) {
let xi: i32 = 0;
for (xi < suffixes.len) {
let bytes: str = readfile(strings.concat(works[si], "/",
artifacts[ai], suffixes[xi]));
let index: i32 = ai * suffixes.len + xi;
if (si == 0) { references[index] = strings.dup(bytes); }
else { assert(same(references[index], bytes)); };
xi += 1;
};
ai += 1;
};
let ctrace: str = readfile(compilertraces[si]);
let atrace: str = readfile(assemblertraces[si]);
let ltrace: str = readfile(linkertraces[si]);
assert(occurrences(ctrace, "\n") == 3);
assert(occurrences(atrace, "\n") == 3);
assert(occurrences(ltrace, "\n") == 1);
let secretline: str = linecontaining(ctrace,
"domain.internal.secret.unit.new");
assert(occurrences(secretline, "domain.leaf.wwi") == 1);
assert(!has(secretline, "domain.client.wwi"));
let clientline: str = linecontaining(ctrace,
"domain.client.unit.new");
assert(occurrences(clientline,
"domain.internal.secret.wwi") == 1);
assert(!has(clientline, "domain.leaf.wwi"));
assert(occurrences(ltrace, strings.concat(works[si],
"/domain.client.a")) == 1);
assert(occurrences(ltrace, strings.concat(works[si],
"/domain.internal.secret.a")) == 1);
assert(occurrences(ltrace, strings.concat(works[si],
"/domain.leaf.a")) == 1);
assert(!has(ltrace, ".wwi"));
let normalizedcompiler: str = normalizedtrace(ctrace,
strings.concat(works[si], "/"), bins[si]);
let normalizedassembler: str = normalizedtrace(atrace,
strings.concat(works[si], "/"), bins[si]);
let normalizedlinker: str = normalizedtrace(ltrace,
strings.concat(works[si], "/"), bins[si]);
if (si == 0) {
referencecompiler = strings.dup(normalizedcompiler);
referenceassembler = strings.dup(normalizedassembler);
referencelinker = strings.dup(normalizedlinker);
referencebin = strings.dup(readfile(bins[si]));
} else {
assert(same(referencecompiler, normalizedcompiler));
assert(same(referenceassembler, normalizedassembler));
assert(same(referencelinker, normalizedlinker));
assert(same(referencebin, readfile(bins[si])));
};
let runav: []str = [bins[si]];
runcommand(root, strings.concat("internal-run-", stages[si]), runav,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
assert(out.stdout.len == 0 && out.stderr.len == 0);
let voucher: str = readfile(strings.concat(works[si],
"/domain.internal.secret.unit.ww"));
let toolww: str = readfile(strings.concat(works[si], "/.wwtool.ww"));
let toolc: str = readfile(strings.concat(works[si], "/.wwtool.w6c"));
let toola: str = readfile(strings.concat(works[si], "/.wwtool.w6a"));
let toolstamp: str = readfile(strings.concat(works[si],
"/.wwtool.stamp"));
rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
runcommandenv(root, strings.concat("internal-warm-", stages[si]), av,
env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(occurrences(readfile(linkertraces[si]), "\n") == 1);
assert(same(voucher, readfile(strings.concat(works[si],
"/domain.internal.secret.unit.ww"))));
rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let badout: str = strings.concat(root, "/warm-forbidden-", stages[si]);
let badav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", badout, outsider];
runcommandenv(root, strings.concat("internal-warm-forbidden-",
stages[si]), badav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let wantdiag: str = strings.concat(outsider,
"/main.ww:2:1: error: use of internal package ",
"domain.internal.secret not allowed\n");
assert(same(out.stderr, wantdiag));
if (si == 0) { referencediagnostic = strings.dup(out.stderr); }
else { assert(same(referencediagnostic, out.stderr)); };
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
assert(!os.exists(badout));
assert(!os.exists(strings.concat(works[si], "/outsider.unit.ww")));
assert(same(voucher, readfile(strings.concat(works[si],
"/domain.internal.secret.unit.ww"))));
assert(same(toolww, readfile(strings.concat(works[si], "/.wwtool.ww"))));
assert(same(toolc, readfile(strings.concat(works[si], "/.wwtool.w6c"))));
assert(same(toola, readfile(strings.concat(works[si], "/.wwtool.w6a"))));
assert(same(toolstamp, readfile(strings.concat(works[si],
"/.wwtool.stamp"))));
let coldreject: str = strings.concat(root, "/cold-reject-", stages[si]);
assert(os.mkdir(coldreject, 448i32) == 0);
rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let coldav: []str = [driver(stages[si]), "build", "-w", coldreject,
"-I", source, "-o", strings.concat(coldreject, ".bin"), outsider];
runcommandenv(root, strings.concat("internal-cold-forbidden-",
stages[si]), coldav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(same(out.stderr, wantdiag));
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
assert(!os.exists(strings.concat(coldreject, "/.wwtool.ww")));
assert(!os.exists(strings.concat(coldreject, "/.wwtool.w6c")));
assert(!os.exists(strings.concat(coldreject, "/.wwtool.w6a")));
assert(!os.exists(strings.concat(coldreject, "/.wwtool.stamp")));
assert(!os.exists(strings.concat(coldreject, ".bin")));
assert(!os.exists(strings.concat(coldreject,
"/domain.internal.secret.unit.ww")));
assert(!os.exists(strings.concat(coldreject,
"/domain.internal.secret.a")));
assert(!os.exists(strings.concat(coldreject, "/outsider.unit.ww")));
let allowedtargets: []str = [deepclient, internalxclient, aliasclient,
shapeclient];
let allowedcodes: []i32 = [7, 9, 31, 17];
let allowedi: i32 = 0;
for (allowedi < allowedtargets.len) {
let allowedout: str = strings.concat(root, "/allowed-", stages[si],
"-", labels[allowedi]);
let allowedav: []str = [driver(stages[si]), "build", "-I", source,
"-o", allowedout, allowedtargets[allowedi]];
runcommandenv(root, strings.concat("internal-allowed-", stages[si],
"-", labels[allowedi]), allowedav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let allowedrun: []str = [allowedout];
runcommand(root, strings.concat("internal-allowed-run-", stages[si],
"-", labels[allowedi]), allowedrun,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, allowedcodes[allowedi]);
if (si == 0) {
allowedreferences[allowedi] = strings.dup(readfile(allowedout));
} else {
assert(same(allowedreferences[allowedi], readfile(allowedout)));
};
allowedi += 1;
};
let rawout: str = strings.concat(root, "/raw-allowed-", stages[si]);
let rawav: []str = [driver(stages[si]), "build", "-I", source,
"-o", rawout, rawalias];
runcommandenv(root, strings.concat("internal-raw-allowed-", stages[si]),
rawav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let rawrun: []str = [rawout];
runcommand(root, strings.concat("internal-raw-run-", stages[si]), rawrun,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 31);
let directout: str = strings.concat(root, "/direct-", stages[si], ".a");
let directav: []str = [driver(stages[si]), "build", "-I", source,
"-o", directout, secret];
runcommandenv(root, strings.concat("internal-direct-", stages[si]),
directav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(same(references[1 * suffixes.len + 4], readfile(directout)));
assert(same(references[1 * suffixes.len + 1],
readfile(strings.concat(directout, ".wwi"))));
let rejecttargets: []str = [prefixclient, nestedoutsider, aliasoutsider,
rawescape, shapeoutsider];
let rejectimports: []str = ["domain.internal.secret",
"domain.internal.outer.internal.deep", "alias.internal.secret",
"domain.internal.secret", "shape.domain.internal.secret"];
let rejecti: i32 = 0;
for (rejecti < rejecttargets.len) {
let rejectwork: str = strings.concat(root, "/reject-", stages[si],
"-", labels[rejecti]);
assert(os.mkdir(rejectwork, 448i32) == 0);
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let rejectav: []str = [driver(stages[si]), "build", "-w",
rejectwork, "-I", source, "-o", strings.concat(rejectwork,
".bin"), rejecttargets[rejecti]];
runcommandenv(root, strings.concat("internal-reject-", stages[si],
"-", labels[rejecti]), rejectav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let rejectsource: str = strings.concat(rejecttargets[rejecti],
"/main.ww");
if (rejecti == 3) { rejectsource = rawescape; };
let rejectdiag: str = strings.concat(rejectsource,
":2:1: error: use of internal package ",
rejectimports[rejecti], " not allowed\n");
assert(same(out.stderr, rejectdiag));
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
assert(!os.exists(strings.concat(rejectwork, "/.wwtool.ww")));
assert(!os.exists(strings.concat(rejectwork, "/.wwtool.w6c")));
assert(!os.exists(strings.concat(rejectwork, "/.wwtool.w6a")));
assert(!os.exists(strings.concat(rejectwork, "/.wwtool.stamp")));
assert(!os.exists(strings.concat(rejectwork, ".bin")));
rejecti += 1;
};
let testworkroot: str = strings.concat(root, "/test-work-", stages[si]);
assert(os.mkdir(testworkroot, 448i32) == 0);
rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let testav: []str = [driver(stages[si]), "test", "-w", testworkroot,
"-I", source, testclient];
runcommandenv(root, strings.concat("internal-test-allowed-", stages[si]),
testav, env, (180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(has(out.stdout, "allowed_same ... ok\n"));
assert(has(out.stdout, "allowed_external ... ok\n"));
if (si == 0) {
referencetestout = strings.dup(out.stdout);
referencetesterr = strings.dup(out.stderr);
} else {
assert(same(referencetestout, out.stdout));
assert(same(referencetesterr, out.stderr));
};
let testwork: str = strings.concat(testworkroot, "/",
requestworkkey(testclient), "/");
let testactions: []str = ["domain.testclient-internal-test",
"domain.testclient_test-external-test",
"domain.testclient-internal-test-main",
"domain.testclient_test-external-test-main"];
ai = 0;
for (ai < testactions.len) {
let testsuffixes: []str = [".unit.ww", ".wwi", ".a"];
let xi: i32 = 0;
for (xi < testsuffixes.len) {
let bytes: str = readfile(strings.concat(testwork,
testactions[ai], testsuffixes[xi]));
let index: i32 = ai * testsuffixes.len + xi;
if (si == 0) {
referencetestartifacts[index] = strings.dup(bytes);
} else {
assert(same(referencetestartifacts[index], bytes));
};
xi += 1;
};
ai += 1;
};
let testcompiletrace: str = readfile(compilertraces[si]);
let sameline: str = linecontaining(testcompiletrace,
"domain.testclient-internal-test.unit");
let externalline: str = linecontaining(testcompiletrace,
"domain.testclient_test-external-test.unit");
assert(occurrences(sameline, "domain.internal.secret.wwi") == 1);
assert(occurrences(externalline,
"domain.internal.secret.wwi") == 1);
assert(!has(sameline, "domain.leaf.wwi"));
assert(!has(externalline, "domain.leaf.wwi"));
let samemainline: str = linecontaining(testcompiletrace,
"domain.testclient-internal-test-main.unit");
let externalmainline: str = linecontaining(testcompiletrace,
"domain.testclient_test-external-test-main.unit");
assert(!has(samemainline, "domain.internal.secret.wwi"));
assert(!has(externalmainline, "domain.internal.secret.wwi"));
assert(occurrences(testcompiletrace, "--test-package") == 2);
assert(occurrences(testcompiletrace,
"<-T><--entry><--test-support-module>") == 2);
assert(!has(readfile(linkertraces[si]), ".wwi"));
let testrejects: []str = [outsame, outexternal];
let testfiles: []str = ["/same_test.ww", "/external_test.ww"];
let tri: i32 = 0;
for (tri < testrejects.len) {
let tw: str = strings.concat(root, "/test-reject-", stages[si],
"-", labels[tri]);
assert(os.mkdir(tw, 448i32) == 0);
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let tav: []str = [driver(stages[si]), "test", "-w", tw,
"-I", source, testrejects[tri]];
runcommandenv(root, strings.concat("internal-test-reject-",
stages[si], "-", labels[tri]), tav, env,
(180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let testdiag: str = strings.concat(testrejects[tri], testfiles[tri],
":2:1: error: use of internal package ",
"domain.internal.secret not allowed\n");
assert(has(out.stderr, testdiag));
if (si == 0) {
testrejectreferences[tri] = strings.dup(out.stderr);
} else {
assert(same(testrejectreferences[tri], out.stderr));
};
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
tri += 1;
};
let orderwork: str = strings.concat(root, "/order-work-", stages[si]);
assert(os.mkdir(orderwork, 448i32) == 0);
let orderforward: []str = [driver(stages[si]), "test", "-c", "-w",
orderwork, "-I", source,
"--ww-package-test", "production", "main", client,
strings.concat(root, "/order-client-", stages[si]),
strings.concat(root, "/order-client-", stages[si], ".status"),
"--ww-package-test", "production", "main", outsider,
strings.concat(root, "/order-outsider-", stages[si]),
strings.concat(root, "/order-outsider-", stages[si], ".status"),
client];
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
orderwork, "-I", source,
"--ww-package-test", "production", "main", outsider,
strings.concat(root, "/order-outsider-", stages[si]),
strings.concat(root, "/order-outsider-", stages[si], ".status"),
"--ww-package-test", "production", "main", client,
strings.concat(root, "/order-client-", stages[si]),
strings.concat(root, "/order-client-", stages[si], ".status"),
client];
let orderrequests: [][]str = [orderforward, orderreverse];
let orderdiags: []str = ["", ""];
let oi: i32 = 0;
for (oi < 2) {
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
runcommandenv(root, strings.concat("internal-order-", stages[si],
"-", labels[oi]), orderrequests[oi], env,
(180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
orderdiags[oi] = strings.dup(out.stderr);
assert(same(out.stderr, wantdiag));
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
oi += 1;
};
assert(same(orderdiags[0], orderdiags[1]));
if (si == 0) { orderreference = strings.dup(orderdiags[0]); }
else { assert(same(orderreference, orderdiags[0])); };
si += 1;
};
clean(root);
};
@test fn exact_package_tool_argv_stage_parity() void = { @test fn exact_package_tool_argv_stage_parity() void = {
let root: str = fresh(); let root: str = fresh();
let source: str = strings.concat(root, "/source tree"); let source: str = strings.concat(root, "/source tree");