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:
@@ -1696,9 +1696,16 @@ do_test(int argc, char **argv)
|
||||
size_t tlen = strlen(target);
|
||||
if (strcmp(target, "...") == 0 ||
|
||||
(tlen >= 4 && strcmp(target + tlen - 4, "/...") == 0)) {
|
||||
if (outstem[0]) {
|
||||
if (emit_asm) {
|
||||
fprintf(stderr,
|
||||
"ww test: -c/-S/-o need a single test file\n");
|
||||
"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[0] && !compileonly) {
|
||||
fprintf(stderr,
|
||||
"ww test: -o needs -c for a package target\n");
|
||||
return 2;
|
||||
}
|
||||
if (workdir[0]) {
|
||||
@@ -1725,9 +1732,14 @@ do_test(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
if (is_dir) {
|
||||
if (outstem[0]) {
|
||||
if (emit_asm) {
|
||||
fprintf(stderr,
|
||||
"ww test: -c/-S/-o need a single test file\n");
|
||||
"ww test: -S needs a single test file\n");
|
||||
return 2;
|
||||
}
|
||||
if (outstem[0] && !compileonly) {
|
||||
fprintf(stderr,
|
||||
"ww test: -o needs -c for a package target\n");
|
||||
return 2;
|
||||
}
|
||||
if (workdir[0]) {
|
||||
@@ -1862,8 +1874,12 @@ do_test(int argc, char **argv)
|
||||
fprintf(stderr, "ww test: %s is neither file nor directory\n", target);
|
||||
return 1;
|
||||
}
|
||||
if (outstem[0]) {
|
||||
fprintf(stderr, "ww test: -c/-S/-o need a single test file\n");
|
||||
if (emit_asm) {
|
||||
fprintf(stderr, "ww test: -S needs a single test file\n");
|
||||
return 2;
|
||||
}
|
||||
if (outstem[0] && !compileonly) {
|
||||
fprintf(stderr, "ww test: -o needs -c for a package target\n");
|
||||
return 2;
|
||||
}
|
||||
if (workdir[0]) {
|
||||
|
||||
@@ -207,9 +207,14 @@ at every `-j` level, and `-j 1` — the default — matches the former
|
||||
sequential loop exactly. Measured on the 31-package `lib/...` walk:
|
||||
7.0s sequential, 2.4s at `-j 4`. With `-c`, it publishes each exact
|
||||
`<package>.test` binary and adjacent `<package>.test.sepwork` tree in the
|
||||
package directory; those become caller-owned artifacts. Without `-c`, it
|
||||
removes the temporary binary and scratch with its workspace. The language
|
||||
runtime owns individual `@test` functions.
|
||||
package directory; those become caller-owned artifacts. `-c -o <name>`
|
||||
names that artifact instead of the fixed 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") because a plain
|
||||
run always executes from the temp root. Without `-c`, it removes the
|
||||
temporary binary and scratch with its workspace. The language runtime
|
||||
owns individual `@test` functions.
|
||||
|
||||
Separate compilation is the only driver build path; no compatibility mode
|
||||
switch remains.
|
||||
@@ -320,11 +325,8 @@ timeout policy in this architecture.
|
||||
|
||||
## Open driver work
|
||||
|
||||
Carried over from the dissolved project plan; each is deliberate scope, not
|
||||
drift:
|
||||
|
||||
- A package-level `-o` contract (single-file `ww test -o` exists; directory
|
||||
packages publish fixed `<package>.test` stems under `-c`).
|
||||
None; the package-level `-o` contract (the last carried bullet) landed as
|
||||
`-c -o <name>` for exactly one package.
|
||||
|
||||
## Validation policy
|
||||
|
||||
|
||||
@@ -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) {
|
||||
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;
|
||||
|
||||
@@ -2684,8 +2684,14 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
&& target[tlen - 1u64] == '.';
|
||||
};
|
||||
if (istree) {
|
||||
if (outstem != nil) {
|
||||
cerr("ww test: -c/-S/-o need a single test file\n");
|
||||
if (emitasm != 0) {
|
||||
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;
|
||||
};
|
||||
if (workdir != nil) {
|
||||
@@ -2728,8 +2734,12 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
emitasm, outstem, workdir, patarg);
|
||||
};
|
||||
|
||||
if (outstem != nil) {
|
||||
cerr("ww test: -c/-S/-o need a single test file\n");
|
||||
if (emitasm != 0) {
|
||||
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 (workdir != nil) {
|
||||
|
||||
@@ -569,7 +569,8 @@ fn packagepath(relative: str) str = {
|
||||
expectexit(&outc, 2);
|
||||
expectexit(&outw, 2);
|
||||
assert(same(outc.stderr, outw.stderr));
|
||||
assert(has(outc.stderr, "ww test: -c/-S/-o need a single test file\n"));
|
||||
assert(has(outc.stderr,
|
||||
"ww test: -o needs -c for a package target\n"));
|
||||
|
||||
let wdc: []str = [driver("ww"), "test", "-w", root, spec];
|
||||
let wdw: []str = [driver("ww_ww"), "test", "-w", root, spec];
|
||||
@@ -775,3 +776,72 @@ fn runtimepath(relative: str) str = {
|
||||
"escaped_writer_does_not_hold_runner ... ok\n"));
|
||||
clean(root);
|
||||
};
|
||||
|
||||
// The package-level -o contract: -c -o names the single package's
|
||||
// artifact in place of the fixed <package>.test stem; -o without -c
|
||||
// has nothing to name (runs execute from the temp root); one name
|
||||
// cannot fan out over multiple packages (Go's `go test -o` rule).
|
||||
@test fn compile_artifact_naming() void = {
|
||||
let root: str = fresh();
|
||||
let pdir: str = strings.concat(root, "/pkg");
|
||||
assert(os.mkdir(pdir, 493i32) == 0);
|
||||
writefile(strings.concat(pdir, "/pkg.ww"),
|
||||
"package pkg;\nexport fn v() i32 = { return 7; };\n");
|
||||
writefile(strings.concat(pdir, "/pkg_test.ww"), strings.concat(
|
||||
"package pkg_test;\nimport pkg;\n",
|
||||
"@test fn seven() void = { assert(pkg.v() == 7); };\n"));
|
||||
let adir: str = strings.concat(root, "/a");
|
||||
assert(os.mkdir(adir, 493i32) == 0);
|
||||
// '_'-prefixed: the tree walk skips it, so the artifacts and
|
||||
// their .sepwork trees never pollute the multi-package discovery.
|
||||
let outdir: str = strings.concat(root, "/_out");
|
||||
assert(os.mkdir(outdir, 493i32) == 0);
|
||||
writefile(strings.concat(adir, "/a.ww"),
|
||||
"package a;\nexport fn v() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(adir, "/a_test.ww"), strings.concat(
|
||||
"package a_test;\nimport a;\n",
|
||||
"@test fn one() void = { assert(a.v() == 1); };\n"));
|
||||
let out: commandout;
|
||||
let drvs: []str = ["ww", "ww_ww"];
|
||||
let i: i32 = 0;
|
||||
for (i < 2) {
|
||||
let named: str = strings.concat(outdir, "/out_", drvs[i],
|
||||
".bin");
|
||||
let coav: []str = [driver(drvs[i]), "test", "-c", "-o",
|
||||
named, pdir];
|
||||
runcommand(root, strings.concat("nameco_", drvs[i]), coav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
let fi: os.filestat;
|
||||
match (os.stat(&fi, named)) {
|
||||
case void => void;
|
||||
case let e: os.oserror => abort("-c -o artifact missing");
|
||||
};
|
||||
match (os.stat(&fi, strings.concat(pdir, "/pkg.test"))) {
|
||||
case void => abort("-c -o still published the fixed stem");
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
let runav: []str = [named];
|
||||
runcommand(root, strings.concat("namerun_", drvs[i]), runav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(has(out.stdout, "seven ... ok"));
|
||||
|
||||
let nocav: []str = [driver(drvs[i]), "test", "-o", named,
|
||||
pdir];
|
||||
runcommand(root, strings.concat("noc_", drvs[i]), nocav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 2);
|
||||
assert(has(out.stderr, "-o needs -c for a package target"));
|
||||
|
||||
let mulav: []str = [driver(drvs[i]), "test", "-c", "-o",
|
||||
named, strings.concat(root, "/...")];
|
||||
runcommand(root, strings.concat("multi_", drvs[i]), mulav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 2);
|
||||
assert(has(out.stderr,
|
||||
"cannot use -o with multiple packages"));
|
||||
i += 1;
|
||||
};
|
||||
clean(root);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user