ww test: retain directory test binaries like Go

This commit is contained in:
2026-08-20 20:13:08 +09:00
parent f28843f2f5
commit db5782eff9
10 changed files with 1014 additions and 213 deletions

View File

@@ -1508,6 +1508,7 @@ type sepproduct = struct {
internalpackage: *u8,
externalpackage: *u8,
status: *u8,
publish: *u8,
artifact: *u8,
variant: i32,
directoryproduct: bool,
@@ -1520,6 +1521,7 @@ type sepproduct = struct {
pxtest: i32,
support: i32,
stageout: *u8,
stagepublish: *u8,
stageiface: *u8,
stagestatus: *u8,
};
@@ -5914,6 +5916,32 @@ fn copyfilestage(src: *u8, dst: *u8) i32 = {
return 0;
};
// BuildInstallFunc's linked-executable mode is 0777 filtered by umask. The
// retained stage owns a distinct inode but exactly the temporary runnable's
// bytes; the request transaction publishes them together below.
fn copyexecutablestage(src: *u8, dst: *u8) i32 = {
let in: i32 = os.open(pathstr(src), os.flag.RDONLY, 0i32);
if (in < 0) { return -1; };
let out: i32 = os.open(pathstr(dst),
os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 511i32);
if (out < 0) { os.close(in); return -1; };
let buf: [65536]u8;
let bad: bool = false;
for (!bad) {
let n: i64 = os.read(in, &buf[0], 65536u64);
if (n < 0) { bad = true; break; };
if (n == 0) { break; };
match (os.writeall(out, &buf[0], n: u64)) {
case let wrote: i64 => { if (wrote != n) { bad = true; }; };
case let e: os.oserror => bad = true;
};
};
if (os.close(in) != 0) { bad = true; };
if (os.close(out) != 0) { bad = true; };
if (bad) { os.remove(pathstr(dst)); return -1; };
return 0;
};
fn sepproductstagepath(dst: *u8) *u8 = {
let path: *u8 = sepappendlit(dst, ".new");
if (path != nil && cstrlen(path) + 1u64 > os.PATH_MAX: u64) {
@@ -5945,12 +5973,16 @@ fn sepproductpathsoverlap(a: *u8, b: *u8) bool = {
};
fn sepvalidateproductpathpair(a: *sepproduct, b: *sepproduct) i32 = {
let ap: []*u8 = [nil, nil, a.stagestatus, a.stageout, a.stageiface];
let bp: []*u8 = [nil, nil, b.stagestatus, b.stageout, b.stageiface];
let ap: []*u8 = [nil, nil, nil, a.stagestatus, a.stageout,
a.stagepublish, a.stageiface];
let bp: []*u8 = [nil, nil, nil, b.stagestatus, b.stageout,
b.stagepublish, b.stageiface];
if (a.stagestatus != nil) { ap[0] = a.status; };
if (a.stageout != nil) { ap[1] = a.out; };
if (a.stagepublish != nil) { ap[2] = a.publish; };
if (b.stagestatus != nil) { bp[0] = b.status; };
if (b.stageout != nil) { bp[1] = b.out; };
if (b.stagepublish != nil) { bp[2] = b.publish; };
let i: i32 = 0;
for (i < ap.len) {
if (ap[i] != nil) {
@@ -6037,6 +6069,11 @@ fn sepvalidaterequeststaging(g: *sepgraph, scratch: *u8, warm: bool,
products[i].stagestatus, products[i].status);
if (products[i].stagestatus == nil) { return -1; };
};
if (products[i].publish != nil && !products[i].notests) {
products[i].stagepublish = sepprepareproductstage(
products[i].stagepublish, products[i].publish);
if (products[i].stagepublish == nil) { return -1; };
};
if (emitasm == 0) {
let ownsoutput: bool = false;
if (rootpackage) { ownsoutput = publishpackage != 0; }
@@ -6413,7 +6450,8 @@ fn sepdiscardrequeststaging(g: *sepgraph, scratch: *u8, warm: bool,
let producti: i32 = 0;
for (producti < nproducts) {
let paths: []*u8 = [products[producti].stageout,
products[producti].stageiface, products[producti].stagestatus];
products[producti].stagepublish, products[producti].stageiface,
products[producti].stagestatus];
let si: i32 = 0;
for (si < paths.len) {
if (paths[si] != nil) {
@@ -6564,8 +6602,8 @@ fn sepfinishfail(entries: *[]septxnentry, n: i32) i32 = {
fn sepfreeproductstaging(products: *sepproduct, nproducts: i32) void = {
let i: i32 = 0;
for (i < nproducts) {
let paths: []*u8 = [products[i].stageout, products[i].stageiface,
products[i].stagestatus];
let paths: []*u8 = [products[i].stageout, products[i].stagepublish,
products[i].stageiface, products[i].stagestatus];
let k: i32 = 0;
for (k < paths.len) {
if (paths[k] != nil) {
@@ -6574,6 +6612,7 @@ fn sepfreeproductstaging(products: *sepproduct, nproducts: i32) void = {
k += 1;
};
products[i].stageout = nil;
products[i].stagepublish = nil;
products[i].stageiface = nil;
products[i].stagestatus = nil;
i += 1;
@@ -6779,6 +6818,14 @@ fn sepfinishrequest(selfdir: *u8, l6: *u8, c6: *u8, a6: *u8,
g.pkg[root].failed = true;
return sepfinishfail(&entries, ntxn);
};
if (products[producti].publish != nil
&& (products[producti].stagepublish == nil
|| copyexecutablestage(products[producti].stageout,
products[producti].stagepublish) != 0)) {
cerrpath("ww: cannot stage test binary ",
products[producti].publish, "\n");
return sepfinishfail(&entries, ntxn);
};
if (sepstageproductstatus(&products[producti]) != 0) {
cerr("ww: cannot stage package-test product\n");
return sepfinishfail(&entries, ntxn);
@@ -6860,6 +6907,11 @@ fn sepfinishrequest(selfdir: *u8, l6: *u8, c6: *u8, a6: *u8,
products[producti].out)) {
return sepfinishfail(&entries, ntxn);
};
if (products[producti].stagepublish != nil
&& !septxnadd(&entries, &ntxn, products[producti].stagepublish,
products[producti].publish)) {
return sepfinishfail(&entries, ntxn);
};
if (products[producti].stageiface != nil) {
let outiface: *u8 = sepappendlit(products[producti].out, ".wwi");
if (outiface == nil
@@ -7091,6 +7143,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
products[producti].ptest = -1;
products[producti].pxtest = -1;
products[producti].stageout = nil;
products[producti].stagepublish = nil;
products[producti].stageiface = nil;
products[producti].stagestatus = nil;
producti += 1;
@@ -7443,6 +7496,10 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
&& validatecommandoutputpath(products[producti].out) < 0) {
return 1;
};
if (products[producti].publish != nil
&& validatecommandoutputpath(products[producti].publish) < 0) {
return 1;
};
if (products[producti].status != nil
&& validatecommandoutputpath(products[producti].status) < 0) {
return 1;
@@ -7563,10 +7620,17 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
let createdwork: sepcreateddirs;
createdoutput.n = 0;
createdwork.n = 0;
let outputmode: i32 = 448;
if (istest != 0) { outputmode = 511; };
if (createoutputdir != nil
&& sepmkdirsrecord(createoutputdir, 448, &createdoutput) != 0) {
cerrpath("ww: cannot create build output directory ",
createoutputdir, "\n");
&& sepmkdirsrecord(createoutputdir, outputmode, &createdoutput) != 0) {
if (istest != 0) {
cerrpath("ww: cannot create test output directory ",
createoutputdir, "\n");
} else {
cerrpath("ww: cannot create build output directory ",
createoutputdir, "\n");
};
return 1;
};
if (warm && !workdirexists) {
@@ -8095,6 +8159,7 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
product.internalpackage = nil;
product.externalpackage = nil;
product.status = nil;
product.publish = nil;
product.artifact = nil;
if (entryisdir == 0) {
product.artifact = "__root\0".ptr;
@@ -8910,8 +8975,8 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
continue;
};
if (cstreqlit(p, "--ww-package-test")) {
if (i + 8 >= argc) {
cerr("ww test: --ww-package-test needs kind, package, production, internal, external, directory, output, and status\n");
if (i + 9 >= argc) {
cerr("ww test: --ww-package-test needs kind, package, production, internal, external, directory, output, publication, and status\n");
return 2;
};
let kind: *u8 = argv[i + 1];
@@ -8921,7 +8986,8 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
let external: *u8 = argv[i + 5];
let dir: *u8 = argv[i + 6];
let output: *u8 = argv[i + 7];
let status: *u8 = argv[i + 8];
let publish: *u8 = argv[i + 8];
let status: *u8 = argv[i + 9];
let pn: u64 = cstrlen(name);
let buildproduct: bool = cstreqlit(kind, "build");
let testproduct: bool = cstreqlit(kind, "test");
@@ -8931,6 +8997,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
if ((!buildproduct && !testproduct)
|| pn == 0u64
|| dir[0u64] == 0u8 || output[0u64] == 0u8
|| publish[0u64] == 0u8
|| status[0u64] == 0u8
|| (hasproduction && !cstreq(production, name))
|| (hasinternal && !cstreq(internal, name))
@@ -8938,9 +9005,12 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|| !strings.hasprefix(pathstr(external), pathstr(name))
|| !cstrendswithlit(external, "_test")))
|| (buildproduct && (!hasproduction
|| hasinternal || hasexternal))
|| hasinternal || hasexternal
|| !cstreqlit(publish, "-")))
|| (testproduct && !hasproduction
&& !hasinternal && !hasexternal)) {
&& !hasinternal && !hasexternal)
|| (testproduct && !hasinternal && !hasexternal
&& !cstreqlit(publish, "-"))) {
cerr("ww test: invalid --ww-package-test product\n");
return 2;
};
@@ -8956,6 +9026,8 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
product.externalpackage = nil;
if (hasexternal) { product.externalpackage = external; };
product.status = status;
product.publish = nil;
if (!cstreqlit(publish, "-")) { product.publish = publish; };
product.artifact = nil;
product.variant = SEP_VARIANT_TEST_MAIN;
if (buildproduct) { product.variant = SEP_VARIANT_PRODUCTION; };
@@ -8974,7 +9046,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
if (!sepreserveproducts(&products,
products.len + 1)) { return 1; };
append(products, product);
i += 9;
i += 10;
continue;
};
if (p[1u64] == 73u8) { // '-I'
@@ -9133,7 +9205,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
cerr("ww test: invalid --ww-package-publish\n");
return 2;
};
if (packagecreateoutputdir != nil && !packagebuild) {
if (packagecreateoutputdir != nil && products.len == 0) {
cerr("ww test: invalid private directory creation\n");
return 2;
};
@@ -9220,12 +9292,8 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
cerr("ww test: -S needs a single test file\n");
return 2;
};
// -c -o forwards: the coordinator names the single
// package's artifact and rejects a multi-package fan-out.
if (outstem != nil && compileonly == 0) {
cerr("ww test: -o needs -c for a package target\n");
return 2;
};
// The coordinator independently wires -o retention and -c run
// suppression after loading the complete package set.
// -w forwards one caller-owned semantic-action store shared by
// the complete selected package universe.
return execpackagetests(selfdir, argv, argc, start,
@@ -9273,10 +9341,6 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
cerr("ww test: -S needs a single test file\n");
return 2;
};
if (outstem != nil && compileonly == 0) {
cerr("ww test: -o needs -c for a package target\n");
return 2;
};
if (products.len != 0) {
if (compileonly == 0) {
cerr("ww test: package-test products need -c\n");