ww: package-level -c -o names the single package's test artifact

The last Open-driver-work bullet. `-c -o <name>` replaces the fixed
<package>.test stem for exactly one package; the coordinator rejects a
multi-package fan-out ("cannot use -o with multiple packages", Go's
`go test -o` rule) and -o without -c is rejected at the driver
("needs -c for a package target" — a plain run executes from the temp
root, so a caller-owned name has nothing to name). Both driver stages
byte-identical wording; -S keeps its single-file-only reject.

package_test gains the contract row (naming, fixed-stem absence,
artifact runs, both rejects, both drivers); the tree-mode -o row's
pinned wording follows the contract.
This commit is contained in:
2026-08-08 17:30:36 +09:00
parent cf90c50c38
commit 192ddd4c66
5 changed files with 150 additions and 22 deletions

View File

@@ -506,13 +506,17 @@ fn pkgcombined(g: *pkggroup, srcs: []pkgsource) bool = {
};
fn pkgsetpaths(g: *pkggroup, root: str, index: i32,
compileonly: bool) bool = {
compileonly: bool, outname: str) bool = {
let num: str = strconv.i32tos(index, strconv.base.DEC);
g.root = strings.concat(root, "/group-", num);
if (!pkgmakedir(g.root)) { return false; };
g.combined = strings.concat(g.root, "/package.ww");
if (compileonly) {
g.bin = strings.concat(g.dir, "/", g.pkg, ".test");
if (outname.len != 0) {
g.bin = outname;
} else {
g.bin = strings.concat(g.dir, "/", g.pkg, ".test");
};
} else {
g.bin = strings.concat(g.root, "/package.test");
};
@@ -669,6 +673,7 @@ export fn packagecommand(args: []str) int = {
let filters: []str = alloc([], (args.len + 1): u64)!;
let includes: []str = alloc([], (args.len + 1): u64)!;
let timeoutarg: str = "";
let outname: str = "";
let builder: str = pkgdefaultbuilder();
let i: i32 = 0;
for (i < args.len) {
@@ -688,6 +693,17 @@ export fn packagecommand(args: []str) int = {
i += 1;
continue;
};
if (strings.compare(a, "-o") == 0) {
if (i + 1 >= args.len) { pkgusage(); return 2; };
outname = args[i + 1];
i += 2;
continue;
};
if (strings.hasprefix(a, "-o") && a.len > 2) {
outname = a[2:a.len];
i += 1;
continue;
};
if (strings.hasprefix(a, "-timeout-ms=")) {
if (timeoutarg.len != 0 || a.len == 12
|| pkgparsedec(a[12:a.len], 3600000i64) <= 0i64) {
@@ -735,6 +751,12 @@ export fn packagecommand(args: []str) int = {
pkgusage();
return 2;
};
// -o names the -c artifact; a plain run always executes from the
// temp root, so a caller-owned name has nothing to name.
if (outname.len != 0 && !compileonly) {
pkgputln(os.STDERR_FILENO, "wwtest package: -o needs -c");
return 2;
};
// Go's ./... form: a trailing "..." path element walks the tree
// rooted at the prefix instead of one explicit directory.
@@ -870,6 +892,13 @@ export fn packagecommand(args: []str) int = {
};
if (groups.len == 0) { return 0; };
pkgsortgroups(groups);
// One caller-owned name cannot fan out (Go: `go test -o` with
// multiple packages is an error).
if (outname.len != 0 && groups.len > 1) {
pkgputln(os.STDERR_FILENO,
"wwtest package: cannot use -o with multiple packages");
return 2;
};
let borrowed: str = temp.dir();
let tmproot: str = strings.dup(borrowed);
@@ -894,7 +923,8 @@ export fn packagecommand(args: []str) int = {
for (emitted < groups.len) {
for (!stopped && launched < groups.len && active < jobs) {
let g: *pkggroup = &groups[launched];
if (!pkgsetpaths(g, tmproot, launched, compileonly)) {
if (!pkgsetpaths(g, tmproot, launched, compileonly,
outname)) {
g.fail = PKGFAILSETUP;
g.state = PKGDONE;
stopped = true;