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);
|
size_t tlen = strlen(target);
|
||||||
if (strcmp(target, "...") == 0 ||
|
if (strcmp(target, "...") == 0 ||
|
||||||
(tlen >= 4 && strcmp(target + tlen - 4, "/...") == 0)) {
|
(tlen >= 4 && strcmp(target + tlen - 4, "/...") == 0)) {
|
||||||
if (outstem[0]) {
|
if (emit_asm) {
|
||||||
fprintf(stderr,
|
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;
|
return 2;
|
||||||
}
|
}
|
||||||
if (workdir[0]) {
|
if (workdir[0]) {
|
||||||
@@ -1725,9 +1732,14 @@ do_test(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (is_dir) {
|
if (is_dir) {
|
||||||
if (outstem[0]) {
|
if (emit_asm) {
|
||||||
fprintf(stderr,
|
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;
|
return 2;
|
||||||
}
|
}
|
||||||
if (workdir[0]) {
|
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);
|
fprintf(stderr, "ww test: %s is neither file nor directory\n", target);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (outstem[0]) {
|
if (emit_asm) {
|
||||||
fprintf(stderr, "ww test: -c/-S/-o need a single test file\n");
|
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;
|
return 2;
|
||||||
}
|
}
|
||||||
if (workdir[0]) {
|
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:
|
sequential loop exactly. Measured on the 31-package `lib/...` walk:
|
||||||
7.0s sequential, 2.4s at `-j 4`. With `-c`, it publishes each exact
|
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>.test` binary and adjacent `<package>.test.sepwork` tree in the
|
||||||
package directory; those become caller-owned artifacts. Without `-c`, it
|
package directory; those become caller-owned artifacts. `-c -o <name>`
|
||||||
removes the temporary binary and scratch with its workspace. The language
|
names that artifact instead of the fixed stem, for exactly one package:
|
||||||
runtime owns individual `@test` functions.
|
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
|
Separate compilation is the only driver build path; no compatibility mode
|
||||||
switch remains.
|
switch remains.
|
||||||
@@ -320,11 +325,8 @@ timeout policy in this architecture.
|
|||||||
|
|
||||||
## Open driver work
|
## Open driver work
|
||||||
|
|
||||||
Carried over from the dissolved project plan; each is deliberate scope, not
|
None; the package-level `-o` contract (the last carried bullet) landed as
|
||||||
drift:
|
`-c -o <name>` for exactly one package.
|
||||||
|
|
||||||
- A package-level `-o` contract (single-file `ww test -o` exists; directory
|
|
||||||
packages publish fixed `<package>.test` stems under `-c`).
|
|
||||||
|
|
||||||
## Validation policy
|
## Validation policy
|
||||||
|
|
||||||
|
|||||||
@@ -506,13 +506,17 @@ fn pkgcombined(g: *pkggroup, srcs: []pkgsource) bool = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn pkgsetpaths(g: *pkggroup, root: str, index: i32,
|
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);
|
let num: str = strconv.i32tos(index, strconv.base.DEC);
|
||||||
g.root = strings.concat(root, "/group-", num);
|
g.root = strings.concat(root, "/group-", num);
|
||||||
if (!pkgmakedir(g.root)) { return false; };
|
if (!pkgmakedir(g.root)) { return false; };
|
||||||
g.combined = strings.concat(g.root, "/package.ww");
|
g.combined = strings.concat(g.root, "/package.ww");
|
||||||
if (compileonly) {
|
if (compileonly) {
|
||||||
|
if (outname.len != 0) {
|
||||||
|
g.bin = outname;
|
||||||
|
} else {
|
||||||
g.bin = strings.concat(g.dir, "/", g.pkg, ".test");
|
g.bin = strings.concat(g.dir, "/", g.pkg, ".test");
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
g.bin = strings.concat(g.root, "/package.test");
|
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 filters: []str = alloc([], (args.len + 1): u64)!;
|
||||||
let includes: []str = alloc([], (args.len + 1): u64)!;
|
let includes: []str = alloc([], (args.len + 1): u64)!;
|
||||||
let timeoutarg: str = "";
|
let timeoutarg: str = "";
|
||||||
|
let outname: str = "";
|
||||||
let builder: str = pkgdefaultbuilder();
|
let builder: str = pkgdefaultbuilder();
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < args.len) {
|
for (i < args.len) {
|
||||||
@@ -688,6 +693,17 @@ export fn packagecommand(args: []str) int = {
|
|||||||
i += 1;
|
i += 1;
|
||||||
continue;
|
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 (strings.hasprefix(a, "-timeout-ms=")) {
|
||||||
if (timeoutarg.len != 0 || a.len == 12
|
if (timeoutarg.len != 0 || a.len == 12
|
||||||
|| pkgparsedec(a[12:a.len], 3600000i64) <= 0i64) {
|
|| pkgparsedec(a[12:a.len], 3600000i64) <= 0i64) {
|
||||||
@@ -735,6 +751,12 @@ export fn packagecommand(args: []str) int = {
|
|||||||
pkgusage();
|
pkgusage();
|
||||||
return 2;
|
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
|
// Go's ./... form: a trailing "..." path element walks the tree
|
||||||
// rooted at the prefix instead of one explicit directory.
|
// 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; };
|
if (groups.len == 0) { return 0; };
|
||||||
pkgsortgroups(groups);
|
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 borrowed: str = temp.dir();
|
||||||
let tmproot: str = strings.dup(borrowed);
|
let tmproot: str = strings.dup(borrowed);
|
||||||
@@ -894,7 +923,8 @@ export fn packagecommand(args: []str) int = {
|
|||||||
for (emitted < groups.len) {
|
for (emitted < groups.len) {
|
||||||
for (!stopped && launched < groups.len && active < jobs) {
|
for (!stopped && launched < groups.len && active < jobs) {
|
||||||
let g: *pkggroup = &groups[launched];
|
let g: *pkggroup = &groups[launched];
|
||||||
if (!pkgsetpaths(g, tmproot, launched, compileonly)) {
|
if (!pkgsetpaths(g, tmproot, launched, compileonly,
|
||||||
|
outname)) {
|
||||||
g.fail = PKGFAILSETUP;
|
g.fail = PKGFAILSETUP;
|
||||||
g.state = PKGDONE;
|
g.state = PKGDONE;
|
||||||
stopped = true;
|
stopped = true;
|
||||||
|
|||||||
@@ -2684,8 +2684,14 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
&& target[tlen - 1u64] == '.';
|
&& target[tlen - 1u64] == '.';
|
||||||
};
|
};
|
||||||
if (istree) {
|
if (istree) {
|
||||||
if (outstem != nil) {
|
if (emitasm != 0) {
|
||||||
cerr("ww test: -c/-S/-o need a single test file\n");
|
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;
|
return 2;
|
||||||
};
|
};
|
||||||
if (workdir != nil) {
|
if (workdir != nil) {
|
||||||
@@ -2728,8 +2734,12 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
emitasm, outstem, workdir, patarg);
|
emitasm, outstem, workdir, patarg);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (outstem != nil) {
|
if (emitasm != 0) {
|
||||||
cerr("ww test: -c/-S/-o need a single test file\n");
|
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;
|
return 2;
|
||||||
};
|
};
|
||||||
if (workdir != nil) {
|
if (workdir != nil) {
|
||||||
|
|||||||
@@ -569,7 +569,8 @@ fn packagepath(relative: str) str = {
|
|||||||
expectexit(&outc, 2);
|
expectexit(&outc, 2);
|
||||||
expectexit(&outw, 2);
|
expectexit(&outw, 2);
|
||||||
assert(same(outc.stderr, outw.stderr));
|
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 wdc: []str = [driver("ww"), "test", "-w", root, spec];
|
||||||
let wdw: []str = [driver("ww_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"));
|
"escaped_writer_does_not_hold_runner ... ok\n"));
|
||||||
clean(root);
|
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