cmd: compile packages from direct exports
This commit is contained in:
@@ -41,6 +41,17 @@ fn build(dir: str, driver: str, tag: str, target: str, out: str) i32 = {
|
||||
return code(dir, strings.concat("build_", tag), av);
|
||||
};
|
||||
|
||||
fn diagnosticbody(s: str) str = {
|
||||
let p: i32 = testenv.pos(s, ": error: ");
|
||||
// Driver-owned graph diagnostics have no source-location prefix. Compare
|
||||
// those in full; strip only compiler paths/locations from source errors.
|
||||
if (p < 0) { return s; };
|
||||
let begin: i32 = p + 9;
|
||||
let end: i32 = begin;
|
||||
for (end < s.len && s[end] != '\n') { end += 1; };
|
||||
return strings.sub(s, begin, end);
|
||||
};
|
||||
|
||||
fn rejectstable(dir: str, label: str, target: str, needle: str) void = {
|
||||
let drivers: []str = ["ww", "ww_ww", "ww", "ww_ww"];
|
||||
let tags: []str = ["c1", "w1", "c2", "w2"];
|
||||
@@ -74,6 +85,12 @@ fn rejectstable(dir: str, label: str, target: str, needle: str) void = {
|
||||
if (!testenv.same(errors[1], errors[3])) {
|
||||
fail(label, "WW diagnostic changed across repeated builds");
|
||||
};
|
||||
let cb: str = diagnosticbody(errors[0]);
|
||||
let wb: str = diagnosticbody(errors[1]);
|
||||
if (!testenv.same(cb, wb)) {
|
||||
fail(label, strings.concat("C/WW diagnostic bodies differ: '", cb,
|
||||
"' vs '", wb, "'"));
|
||||
};
|
||||
};
|
||||
|
||||
@test fn standalone_and_package_artifact() void = {
|
||||
@@ -136,6 +153,162 @@ fn rejectstable(dir: str, label: str, target: str, needle: str) void = {
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
@test fn self_contained_direct_exports() void = {
|
||||
let td: str = testenv.fresh();
|
||||
let dimensions: str = strings.concat(td, "/dimensions");
|
||||
let implementation: str = strings.concat(td, "/implementation");
|
||||
let api: str = strings.concat(td, "/api");
|
||||
let main: str = strings.concat(td, "/main");
|
||||
mkdir(dimensions); mkdir(implementation); mkdir(api); mkdir(main);
|
||||
let dimensionssrc: str = strings.concat(
|
||||
"package dimensions;\n",
|
||||
"export def WIDTH: i32 = 4;\n",
|
||||
"export def UNUSED_WIDTH: i32 = 8;\n");
|
||||
let implsrc: str = strings.concat(
|
||||
"package implementation;\n",
|
||||
"import dimensions;\n",
|
||||
"def PRIVATE_WIDTH: i32 = 3;\n",
|
||||
"export type Buffer = [PRIVATE_WIDTH]u8;\n",
|
||||
"export type PublicBuffer = [WIDTH]u8;\n",
|
||||
"export type CastBuffer = [(WIDTH: u64)]u8;\n",
|
||||
"export type Detail = struct { code: i64, };\n",
|
||||
"export type Member = struct { detail: Detail, payload: i64, };\n",
|
||||
"export type Unused = struct { noise: i32, };\n",
|
||||
"export def UNUSED: i32 = 99;\n",
|
||||
"type private_type = struct { secret: i32, };\n",
|
||||
"def private_value: i32 = 77;\n",
|
||||
"export fn construct(v: i32) Member = { return Member{detail=Detail{code=(v: i64) + 1i64}, payload=(v: i64) + 2i64}; };\n",
|
||||
"export fn unrelated() i32 = { return UNUSED; };\n",
|
||||
"fn hidden() i32 = { return private_value; };\n");
|
||||
let apisrc: str = strings.concat(
|
||||
"package api;\n",
|
||||
"import implementation;\n",
|
||||
"export type Member = struct { local: i32, };\n",
|
||||
"export fn produce(v: i32) implementation.Member = { return implementation.construct(v); };\n",
|
||||
"export fn score(m: implementation.Member) i32 = { return (m.detail.code + m.payload): i32; };\n",
|
||||
"export fn width_of(b: Buffer) i32 = { return len(b): i32; };\n",
|
||||
"export fn public_width_of(b: implementation.PublicBuffer) i32 = { return len(b): i32; };\n",
|
||||
"export fn cast_width_of(b: implementation.CastBuffer) i32 = { return len(b): i32; };\n");
|
||||
let mainsrc: str = strings.concat(
|
||||
"package main;\n",
|
||||
"import api;\n",
|
||||
"fn main() i32 = { let m = api.produce(19); let p: *i64 = alloc(api.score(m): i64)!; return (*p): i32; };\n");
|
||||
testenv.writefile(strings.concat(dimensions, "/dimensions.ww"), dimensionssrc);
|
||||
testenv.writefile(strings.concat(implementation, "/implementation.ww"),
|
||||
implsrc);
|
||||
testenv.writefile(strings.concat(api, "/api.ww"), apisrc);
|
||||
testenv.writefile(strings.concat(main, "/main.ww"), mainsrc);
|
||||
|
||||
let outputs: []str = [strings.concat(td, "/direct-c1"),
|
||||
strings.concat(td, "/direct-c2"), strings.concat(td, "/direct-w")];
|
||||
let drivers: []str = ["ww", "ww", "ww_ww"];
|
||||
let tags: []str = ["direct_c1", "direct_c2", "direct_w"];
|
||||
let i: i32 = 0;
|
||||
for (i < outputs.len) {
|
||||
if (build(td, drivers[i], tags[i], main, outputs[i]) != 0) {
|
||||
fail("self-contained", strings.concat(drivers[i], " build failed"));
|
||||
};
|
||||
let runav: []str = [outputs[i]];
|
||||
if (code(td, strings.concat("run_", tags[i]), runav) != 41) {
|
||||
fail("self-contained", "implementation archive was not linked");
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
if (!testenv.same(testenv.readfile(outputs[0]), testenv.readfile(outputs[1]))
|
||||
|| !testenv.same(testenv.readfile(outputs[0]), testenv.readfile(outputs[2]))) {
|
||||
fail("self-contained", "clean C/C/WW executable outputs differ");
|
||||
};
|
||||
|
||||
let cwork: str = strings.concat(outputs[0], ".sepwork/");
|
||||
let cwork2: str = strings.concat(outputs[1], ".sepwork/");
|
||||
let wwork: str = strings.concat(outputs[2], ".sepwork/");
|
||||
let apiiface: str = testenv.readfile(strings.concat(cwork, "api.wwi"));
|
||||
if (!testenv.same(apiiface, testenv.readfile(strings.concat(cwork2,
|
||||
"api.wwi"))) || !testenv.same(apiiface, testenv.readfile(strings.concat(
|
||||
wwork, "api.wwi")))) {
|
||||
fail("self-contained", "C/C/WW api exports differ");
|
||||
};
|
||||
if (!testenv.has(apiiface, "//ww:module implementation\n")
|
||||
|| !testenv.has(apiiface, "export type Buffer = [3]u8;")
|
||||
|| !testenv.has(apiiface, "export type PublicBuffer = [4]u8;")
|
||||
|| !testenv.has(apiiface, "export type CastBuffer = [4]u8;")
|
||||
|| !testenv.has(apiiface, "export type Detail = struct")
|
||||
|| testenv.occurrences(apiiface, "export type Member = struct") != 2) {
|
||||
fail("self-contained", "api export lacks the recursive public type facts");
|
||||
};
|
||||
if (testenv.has(apiiface, "//ww:module dimensions\n")
|
||||
|| testenv.has(apiiface, "WIDTH")
|
||||
|| testenv.has(apiiface, "export def UNUSED_WIDTH")
|
||||
|| testenv.has(apiiface, "PRIVATE_WIDTH")
|
||||
|| testenv.has(apiiface, "export type Unused")
|
||||
|| testenv.has(apiiface, "export def UNUSED")
|
||||
|| testenv.has(apiiface, "export fn construct")
|
||||
|| testenv.has(apiiface, "unrelated")
|
||||
|| testenv.has(apiiface, "private_type")
|
||||
|| testenv.has(apiiface, "private_value")
|
||||
|| testenv.has(apiiface, "hidden")) {
|
||||
fail("self-contained", "api export leaked unrelated or private declarations");
|
||||
};
|
||||
let expectedunit: str = strings.concat("//ww:module api\n", apiiface,
|
||||
"\n//ww:module-reset\n", mainsrc, "\n");
|
||||
if (!testenv.same(expectedunit, testenv.readfile(strings.concat(cwork,
|
||||
"__root.unit.ww"))) || !testenv.same(expectedunit,
|
||||
testenv.readfile(strings.concat(wwork, "__root.unit.ww")))) {
|
||||
fail("self-contained", "consumer compiler input was not direct-api-only");
|
||||
};
|
||||
if (!testenv.exists(strings.concat(cwork, "implementation.a"))
|
||||
|| !testenv.exists(strings.concat(cwork, "dimensions.a"))) {
|
||||
fail("self-contained", "reachable archives missing from link closure");
|
||||
};
|
||||
|
||||
let packages: []str = [strings.concat(td, "/api-c1.a"),
|
||||
strings.concat(td, "/api-c2.a"), strings.concat(td, "/api-w.a")];
|
||||
i = 0;
|
||||
for (i < packages.len) {
|
||||
let pav: []str = [testenv.driver(drivers[i]), "build", "-p", "-I", td,
|
||||
"-o", packages[i], api];
|
||||
if (code(td, strings.concat("package_", tags[i]), pav) != 0) {
|
||||
fail("self-contained", "ww build -p api failed");
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
if (!testenv.same(testenv.readfile(packages[0]), testenv.readfile(packages[1]))
|
||||
|| !testenv.same(testenv.readfile(packages[0]), testenv.readfile(packages[2]))
|
||||
|| !testenv.same(apiiface, testenv.readfile(strings.concat(packages[0],
|
||||
".wwi"))) || !testenv.same(apiiface, testenv.readfile(strings.concat(
|
||||
packages[1], ".wwi"))) || !testenv.same(apiiface,
|
||||
testenv.readfile(strings.concat(packages[2], ".wwi")))) {
|
||||
fail("self-contained", "deterministic package artifact/export mismatch");
|
||||
};
|
||||
|
||||
testenv.writefile(strings.concat(td, "/deep-qualifier.ww"), strings.concat(
|
||||
"package main;\nimport api;\n",
|
||||
"fn main() i32 = { let m: implementation.Member; return 0; };\n"));
|
||||
rejectstable(td, "deep-qualifier", strings.concat(td,
|
||||
"/deep-qualifier.ww"), "unknown type 'implementation.Member'");
|
||||
testenv.writefile(strings.concat(td, "/deep-value.ww"), strings.concat(
|
||||
"package main;\nimport api;\n",
|
||||
"fn main() i32 = { return construct(1); };\n"));
|
||||
rejectstable(td, "deep-value", strings.concat(td, "/deep-value.ww"),
|
||||
"undefined: construct");
|
||||
testenv.writefile(strings.concat(td, "/deep-const.ww"), strings.concat(
|
||||
"package main;\nimport api;\n",
|
||||
"fn main() i32 = { return WIDTH; };\n"));
|
||||
rejectstable(td, "deep-const", strings.concat(td, "/deep-const.ww"),
|
||||
"undefined: WIDTH");
|
||||
testenv.writefile(strings.concat(td, "/deep-type.ww"), strings.concat(
|
||||
"package main;\nimport api;\n",
|
||||
"fn main() i32 = { let d: Detail; return 0; };\n"));
|
||||
rejectstable(td, "deep-type", strings.concat(td, "/deep-type.ww"),
|
||||
"unknown type 'Detail'");
|
||||
testenv.writefile(strings.concat(td, "/private-direct.ww"), strings.concat(
|
||||
"package main;\nimport implementation;\n",
|
||||
"fn main() i32 = { return implementation.hidden(); };\n"));
|
||||
rejectstable(td, "private-direct", strings.concat(td,
|
||||
"/private-direct.ww"), "has no exported declaration 'hidden'");
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
fn writediamond(td: str, reverse: bool) str = {
|
||||
let shared: str = strings.concat(td, "/shared");
|
||||
let left: str = strings.concat(td, "/left");
|
||||
@@ -159,13 +332,13 @@ fn writediamond(td: str, reverse: bool) str = {
|
||||
};
|
||||
testenv.writefile(strings.concat(left, "/left.ww"), strings.concat(
|
||||
"package left;\nimport shared;\nimport shared;\n",
|
||||
"export fn value() i32 = { return shared.base() + 1; };\n"));
|
||||
"export fn make() shared.token = { return shared.token{value=shared.base() + 1}; };\n"));
|
||||
testenv.writefile(strings.concat(right, "/right.ww"), strings.concat(
|
||||
"package right;\nimport shared;\n",
|
||||
"export fn value() i32 = { return shared.base() + 2; };\n"));
|
||||
"export fn value(t: shared.token) i32 = { return t.value + shared.base() + 2; };\n"));
|
||||
testenv.writefile(strings.concat(main, "/main.ww"), strings.concat(
|
||||
"package main;\nimport right;\nimport left;\n",
|
||||
"fn main() i32 = { return left.value() + right.value(); };\n"));
|
||||
"fn main() i32 = { return right.value(left.make()); };\n"));
|
||||
return main;
|
||||
};
|
||||
|
||||
@@ -209,9 +382,17 @@ fn writediamond(td: str, reverse: bool) str = {
|
||||
};
|
||||
let leftiface: str = testenv.readfile(strings.concat(scratch,
|
||||
"left.wwi"));
|
||||
let rightiface: str = testenv.readfile(strings.concat(scratch,
|
||||
"right.wwi"));
|
||||
if (testenv.occurrences(leftiface, "import shared;") != 1) {
|
||||
fail("diamond", "duplicate import escaped into export data");
|
||||
};
|
||||
if (testenv.occurrences(leftiface, "export type token") != 1
|
||||
|| testenv.occurrences(rightiface, "export type token") != 1
|
||||
|| testenv.occurrences(unit, "export type token") != 2
|
||||
|| testenv.occurrences(unit, "//ww:module shared\n") != 2) {
|
||||
fail("diamond", "origin fact closure did not merge deterministically");
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
let shuffledout: str = strings.concat(td, "/diamond-c-shuffled");
|
||||
|
||||
Reference in New Issue
Block a user