ww test: retain directory test binaries like Go
This commit is contained in:
@@ -24,6 +24,8 @@ type pkgfolder = struct {
|
||||
type pkggroup = struct {
|
||||
dir: str,
|
||||
pkg: str,
|
||||
testname: str,
|
||||
publish: str,
|
||||
prodpkg: str,
|
||||
samepkg: str,
|
||||
externalpkg: str,
|
||||
@@ -467,7 +469,7 @@ fn pkgusage() void = {
|
||||
pkgput(os.STDERR_FILENO,
|
||||
" *_test.ww is the sole test-source form; @test elsewhere is rejected\n");
|
||||
pkgput(os.STDERR_FILENO,
|
||||
" -c retains the compiled package binaries; -j N runs up to N build or test processes at once\n");
|
||||
" -c retains without running; -o retains and still runs unless -c is present\n");
|
||||
pkgput(os.STDERR_FILENO,
|
||||
" -w DIR is one persistent semantic-action store shared by the selected packages\n");
|
||||
};
|
||||
@@ -616,6 +618,28 @@ fn pkgbase(path: str) str = {
|
||||
return path;
|
||||
};
|
||||
|
||||
// Directory test binaries are presentation artifacts. Their Go-like visible
|
||||
// basename comes from the selected canonical import spelling, never from the
|
||||
// declared package name or any test variant. A physical root is the fallback
|
||||
// when the request has no explicit logical identity.
|
||||
fn pkgimportbase(path: str) str = {
|
||||
let i: i32 = path.len - 1;
|
||||
for (i >= 0) {
|
||||
if (path[i] == '.') {
|
||||
let r: str;
|
||||
r.ptr = path.ptr + ((i + 1): u64);
|
||||
r.len = path.len - i - 1;
|
||||
return r;
|
||||
};
|
||||
i -= 1;
|
||||
};
|
||||
return path;
|
||||
};
|
||||
|
||||
fn pkgisabs(path: str) bool = {
|
||||
return path.len != 0 && path[0] == '/';
|
||||
};
|
||||
|
||||
fn pkgmodeis(m: os.mode, want: os.mode) bool = {
|
||||
return (((m: u32) & 61440u32) == (want: u32));
|
||||
};
|
||||
@@ -1319,7 +1343,7 @@ fn pkgemitfile(path: str, fd: i32) bool = {
|
||||
|
||||
fn pkgsetplanpaths(p: *pkgplan, groups: []pkggroup, root: str, index: i32,
|
||||
compileonly: bool, buildonly: bool, outname: str, outputdir: bool,
|
||||
workroot: str) bool = {
|
||||
createdir: str, workroot: str) bool = {
|
||||
let num: str = strconv.i32tos(index, strconv.base.DEC);
|
||||
if (!pkgstring(&p.root, root, "/plan-", num)) { return false; };
|
||||
if (!pkgmakedir(p.root)) {
|
||||
@@ -1330,8 +1354,7 @@ fn pkgsetplanpaths(p: *pkgplan, groups: []pkggroup, root: str, index: i32,
|
||||
// spelling or traversal prefix must never select another cache container.
|
||||
// The private driver creates it only after graph preflight succeeds.
|
||||
p.workdir = workroot;
|
||||
p.outputdir = "";
|
||||
if (outputdir) { p.outputdir = outname; };
|
||||
p.outputdir = createdir;
|
||||
if (!pkgstring(&p.buildout, p.root, "/build.stdout")
|
||||
|| !pkgstring(&p.builderr, p.root, "/build.stderr")) { return false; };
|
||||
let i: i32 = p.start;
|
||||
@@ -1349,12 +1372,6 @@ fn pkgsetplanpaths(p: *pkgplan, groups: []pkggroup, root: str, index: i32,
|
||||
if (!pkgjoinpath(outname, pkgbase(g.dir), &g.bin)) { return false; };
|
||||
} else if (outname.len != 0 && !outputdir) { g.bin = outname; }
|
||||
else if (!pkgstring(&g.bin, g.root, "/package.build")) { return false; };
|
||||
} else if (compileonly) {
|
||||
if (outname.len != 0) {
|
||||
g.bin = outname;
|
||||
} else {
|
||||
if (!pkgstring(&g.bin, g.dir, "/", g.pkg, ".test")) { return false; };
|
||||
};
|
||||
} else {
|
||||
if (!pkgstring(&g.bin, g.root, "/package.test")) { return false; };
|
||||
};
|
||||
@@ -1400,11 +1417,11 @@ fn pkgstartbuild(p: *pkgplan, groups: []pkggroup, builder: str, includes: []str,
|
||||
libdirs: []str, libs: []str, h: *exec.process) bool = {
|
||||
let nproducts: i32 = p.end - p.start;
|
||||
let capacity: i32 = 16;
|
||||
if (nproducts < 0 || nproducts > (PKG_COUNT_MAX - capacity) / 9) {
|
||||
if (nproducts < 0 || nproducts > (PKG_COUNT_MAX - capacity) / 10) {
|
||||
pkgputln(os.STDERR_FILENO, "wwtest package: package graph is too large");
|
||||
return false;
|
||||
};
|
||||
capacity += nproducts * 9;
|
||||
capacity += nproducts * 10;
|
||||
if (includes.len > (PKG_COUNT_MAX - capacity) / 2) {
|
||||
pkgputln(os.STDERR_FILENO, "wwtest package: package graph is too large");
|
||||
return false;
|
||||
@@ -1459,6 +1476,8 @@ fn pkgstartbuild(p: *pkgplan, groups: []pkggroup, builder: str, includes: []str,
|
||||
else { append(ba, "-"); };
|
||||
append(ba, g.dir);
|
||||
append(ba, g.bin);
|
||||
if (g.publish.len != 0) { append(ba, g.publish); }
|
||||
else { append(ba, "-"); };
|
||||
append(ba, g.buildok);
|
||||
i += 1;
|
||||
};
|
||||
@@ -1572,10 +1591,9 @@ fn pkgemitgroup(g: *pkggroup, compileonly: bool) bool = {
|
||||
return true;
|
||||
};
|
||||
if (compileonly) {
|
||||
pkgput(os.STDOUT_FILENO, "built ");
|
||||
pkglabel(g);
|
||||
pkgput(os.STDOUT_FILENO, " -> ");
|
||||
pkgputln(os.STDOUT_FILENO, g.bin);
|
||||
// Go's compile-only print action is a nop after the retained binary
|
||||
// install completes. Build and publication failures still diagnose on
|
||||
// stderr through the plan result above.
|
||||
return true;
|
||||
};
|
||||
if (g.runstartfailed) { return false; };
|
||||
@@ -1835,14 +1853,9 @@ export fn packagecommand(args: []str) int = {
|
||||
pkgusage();
|
||||
return 2;
|
||||
};
|
||||
// A non-compile run leaves no caller-owned artifact for -o to name.
|
||||
if (outname.len != 0 && !compileonly) {
|
||||
pkgputln(os.STDERR_FILENO, "wwtest package: -o needs -c");
|
||||
return 2;
|
||||
};
|
||||
// -c publishes caller-owned sepwork artifacts; mixing that
|
||||
// contract with a persistent workdir is unwired — reject rather
|
||||
// than guess which tree the caller owns.
|
||||
// -c suppresses execution and requests a caller-visible binary. Keep it
|
||||
// separate from -w until compile-only persistent-action ownership is wired;
|
||||
// -o without -c already permits a retained copy beside a persistent store.
|
||||
if (workroot.len != 0 && compileonly && !buildonly) {
|
||||
pkgputln(os.STDERR_FILENO,
|
||||
"wwtest package: -w conflicts with -c");
|
||||
@@ -2034,6 +2047,8 @@ export fn packagecommand(args: []str) int = {
|
||||
let g: pkggroup;
|
||||
g.dir = f.path;
|
||||
g.pkg = f.prodpkg;
|
||||
g.testname = "";
|
||||
g.publish = "";
|
||||
g.prodpkg = f.prodpkg;
|
||||
g.samepkg = "";
|
||||
g.externalpkg = "";
|
||||
@@ -2157,6 +2172,8 @@ export fn packagecommand(args: []str) int = {
|
||||
g.dir = f.path;
|
||||
g.pkg = family;
|
||||
if (g.pkg.len == 0) { g.pkg = srcs[f.start].pkg; };
|
||||
g.testname = "";
|
||||
g.publish = "";
|
||||
g.prodpkg = f.prodpkg;
|
||||
g.samepkg = samepkg;
|
||||
g.externalpkg = externalpkg;
|
||||
@@ -2240,9 +2257,89 @@ export fn packagecommand(args: []str) int = {
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
// A non-directory caller-owned name cannot fan out. A directory output
|
||||
// publishes each selected command under its canonical directory basename.
|
||||
if (outname.len != 0 && !outputdir && groups.len > 1) {
|
||||
// Go's test binary is always linked into request-private storage. -c and
|
||||
// -o independently request a caller-visible executable copy; only -c
|
||||
// suppresses execution. Visible names are import-leaf metadata and never
|
||||
// action, package, variant, symbol, or persistence identity.
|
||||
let testretain: bool = !buildonly && (compileonly || explicitout);
|
||||
let testnull: bool = !buildonly && explicitout
|
||||
&& strings.compare(outname, "/dev/null") == 0;
|
||||
let testoutdir: bool = !buildonly && explicitout && !testnull
|
||||
&& pkgoutputdir(outname);
|
||||
let invocationdir: str = "";
|
||||
if (testretain) {
|
||||
let cwdoom: bool = false;
|
||||
if (!pkgcanonicaldir(".", &invocationdir, &cwdoom)) {
|
||||
if (!cwdoom) {
|
||||
pkgputln(os.STDERR_FILENO,
|
||||
"wwtest package: cannot determine invocation directory");
|
||||
};
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
i = 0;
|
||||
for (i < groups.len) {
|
||||
let leaf: str = pkgbase(groups[i].dir);
|
||||
if (roots.len == 1 && !anyrecurse && requestidentity.len != 0) {
|
||||
leaf = pkgimportbase(requestidentity);
|
||||
};
|
||||
if (!pkgstring(&groups[i].testname, leaf, ".test")) { return 1; };
|
||||
groups[i].publish = "";
|
||||
if (testretain && !groups[i].notests && !testnull) {
|
||||
if (!explicitout) {
|
||||
if (!pkgjoinpath(invocationdir, groups[i].testname,
|
||||
&groups[i].publish)) { return 1; };
|
||||
} else if (testoutdir) {
|
||||
let targetdir: str = outname;
|
||||
if (!pkgisabs(outname)
|
||||
&& !pkgjoinpath(invocationdir, outname, &targetdir)) {
|
||||
return 1;
|
||||
};
|
||||
if (!pkgjoinpath(targetdir, groups[i].testname,
|
||||
&groups[i].publish)) { return 1; };
|
||||
} else if (pkgisabs(outname)) {
|
||||
groups[i].publish = outname;
|
||||
} else if (!pkgjoinpath(invocationdir, outname,
|
||||
&groups[i].publish)) { return 1; };
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
if (!buildonly && explicitout && groups.len > 1
|
||||
&& !testnull && !testoutdir) {
|
||||
pkgputln(os.STDERR_FILENO,
|
||||
"ww test: with multiple packages, -o must refer to a directory or /dev/null");
|
||||
return 1;
|
||||
};
|
||||
if (!buildonly && groups.len > 1 && testretain && !testnull) {
|
||||
i = 0;
|
||||
for (i < groups.len) {
|
||||
let j: i32 = 0;
|
||||
for (j < i) {
|
||||
if (strings.compare(groups[j].testname,
|
||||
groups[i].testname) == 0) {
|
||||
pkgput(os.STDERR_FILENO,
|
||||
"ww test: cannot write test binary ");
|
||||
pkgput(os.STDERR_FILENO, groups[i].testname);
|
||||
pkgputln(os.STDERR_FILENO,
|
||||
" for multiple packages:");
|
||||
let k: i32 = 0;
|
||||
for (k < groups.len) {
|
||||
if (strings.compare(groups[k].testname,
|
||||
groups[i].testname) == 0) {
|
||||
pkgputln(os.STDERR_FILENO, groups[k].dir);
|
||||
};
|
||||
k += 1;
|
||||
};
|
||||
return 1;
|
||||
};
|
||||
j += 1;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
// A non-directory caller-owned build name cannot fan out. A directory
|
||||
// build output publishes each selected command under its directory leaf.
|
||||
if (buildonly && outname.len != 0 && !outputdir && groups.len > 1) {
|
||||
pkgputln(os.STDERR_FILENO,
|
||||
"wwtest package: cannot use -o with multiple packages");
|
||||
return 2;
|
||||
@@ -2277,13 +2374,27 @@ export fn packagecommand(args: []str) int = {
|
||||
&& strings.compare(groups[0].pkg, "main") != 0;
|
||||
plan.emitasm = emitasm;
|
||||
append(plans, plan);
|
||||
let createdir: str = "";
|
||||
if (buildonly && outputdir) {
|
||||
createdir = outname;
|
||||
} else if (!buildonly && testretain && !testnull) {
|
||||
i = 0;
|
||||
for (i < groups.len) {
|
||||
if (groups[i].publish.len != 0) {
|
||||
createdir = pkgdirname(groups[i].publish);
|
||||
break;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
|
||||
let tmproot: str = temp.dir();
|
||||
let failed: i32 = 0;
|
||||
i = 0;
|
||||
for (i < plans.len) {
|
||||
if (!pkgsetplanpaths(&plans[i], groups, tmproot, i,
|
||||
compileonly, buildonly, outname, outputdir, workroot)) {
|
||||
compileonly, buildonly, outname, outputdir, createdir,
|
||||
workroot)) {
|
||||
if (!pkgremoveall(tmproot)) {
|
||||
pkgput(os.STDERR_FILENO,
|
||||
"wwtest package: cleanup failed; retained ");
|
||||
|
||||
Reference in New Issue
Block a user