ww test: retain directory test binaries like Go
This commit is contained in:
105
cmd/ww/main.c
105
cmd/ww/main.c
@@ -13,6 +13,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
@@ -1300,6 +1301,7 @@ struct sepproduct {
|
||||
const char *internal_package;
|
||||
const char *external_package;
|
||||
const char *status;
|
||||
const char *publish; /* optional retained test executable */
|
||||
const char *artifact;
|
||||
int variant;
|
||||
int directory_product;
|
||||
@@ -1312,6 +1314,7 @@ struct sepproduct {
|
||||
int pxtest;
|
||||
int support; /* direct generated-main support action, or -1 */
|
||||
char *stage_out; /* request-private linked/published output */
|
||||
char *stage_publish; /* request-private retained executable copy */
|
||||
char *stage_iface; /* request-private published package interface */
|
||||
char *stage_status; /* request-private completion marker */
|
||||
};
|
||||
@@ -4932,6 +4935,34 @@ copy_file_stage(const char *src, const char *dst)
|
||||
return bad ? -1 : 0;
|
||||
}
|
||||
|
||||
/* Go's BuildInstallFunc installs linked test binaries with 0777 filtered by
|
||||
* the caller's umask. Keep the retained copy byte-identical to the temporary
|
||||
* runnable while giving the new staging inode that executable mode. */
|
||||
static int
|
||||
copy_executable_stage(const char *src, const char *dst)
|
||||
{
|
||||
FILE *in = fopen(src, "rb");
|
||||
if (in == NULL) return -1;
|
||||
int fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0777);
|
||||
if (fd < 0) { fclose(in); return -1; }
|
||||
FILE *out = fdopen(fd, "wb");
|
||||
if (out == NULL) { close(fd); fclose(in); (void)unlink(dst); return -1; }
|
||||
unsigned char buf[65536];
|
||||
int bad = 0;
|
||||
for (;;) {
|
||||
size_t n = fread(buf, 1, sizeof buf, in);
|
||||
if (n != 0 && fwrite(buf, 1, n, out) != n) bad = 1;
|
||||
if (bad || n < sizeof buf) {
|
||||
if (ferror(in)) bad = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fclose(in) != 0) bad = 1;
|
||||
if (fclose(out) != 0) bad = 1;
|
||||
if (bad) (void)unlink(dst);
|
||||
return bad ? -1 : 0;
|
||||
}
|
||||
|
||||
struct septxnentry {
|
||||
char *stage;
|
||||
char *dst;
|
||||
@@ -5117,9 +5148,11 @@ sep_free_product_staging(struct sepproduct *products, int nproducts)
|
||||
for (int i = 0; i < nproducts; i++) {
|
||||
free(products[i].stage_status);
|
||||
free(products[i].stage_iface);
|
||||
free(products[i].stage_publish);
|
||||
free(products[i].stage_out);
|
||||
products[i].stage_status = NULL;
|
||||
products[i].stage_iface = NULL;
|
||||
products[i].stage_publish = NULL;
|
||||
products[i].stage_out = NULL;
|
||||
}
|
||||
}
|
||||
@@ -5158,11 +5191,13 @@ sep_validate_product_path_pair(const struct sepproduct *a,
|
||||
const char *ap[] = {
|
||||
a->stage_status != NULL ? a->status : NULL,
|
||||
a->stage_out != NULL ? a->out : NULL,
|
||||
a->stage_status, a->stage_out, a->stage_iface };
|
||||
a->stage_publish != NULL ? a->publish : NULL,
|
||||
a->stage_status, a->stage_out, a->stage_publish, a->stage_iface };
|
||||
const char *bp[] = {
|
||||
b->stage_status != NULL ? b->status : NULL,
|
||||
b->stage_out != NULL ? b->out : NULL,
|
||||
b->stage_status, b->stage_out, b->stage_iface };
|
||||
b->stage_publish != NULL ? b->publish : NULL,
|
||||
b->stage_status, b->stage_out, b->stage_publish, b->stage_iface };
|
||||
for (size_t i = 0; i < nelem(ap); i++) {
|
||||
if (ap[i] == NULL) continue;
|
||||
for (size_t j = 0; j < nelem(bp); j++) {
|
||||
@@ -5221,6 +5256,10 @@ sep_validate_request_staging(struct sepgraph *g, const char *scratch, int warm,
|
||||
&& sep_prepare_product_stage(&products[i].stage_status,
|
||||
products[i].status) < 0)
|
||||
return -1;
|
||||
if (products[i].publish != NULL && !products[i].no_tests
|
||||
&& sep_prepare_product_stage(&products[i].stage_publish,
|
||||
products[i].publish) < 0)
|
||||
return -1;
|
||||
if (emit_asm) continue;
|
||||
int owns_output = root_package ? publish_package
|
||||
: is_test ? !products[i].no_tests
|
||||
@@ -5336,7 +5375,8 @@ sep_discard_request_staging(struct sepgraph *g, const char *scratch, int warm,
|
||||
}
|
||||
for (int i = 0; i < nproducts; i++) {
|
||||
const char *path[] = { products[i].stage_out,
|
||||
products[i].stage_iface, products[i].stage_status };
|
||||
products[i].stage_publish, products[i].stage_iface,
|
||||
products[i].stage_status };
|
||||
for (size_t j = 0; j < nelem(path); j++)
|
||||
if (path[j] != NULL
|
||||
&& unlink(path[j]) != 0 && errno != ENOENT)
|
||||
@@ -5564,6 +5604,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
products[i].ptest = -1;
|
||||
products[i].pxtest = -1;
|
||||
products[i].stage_out = NULL;
|
||||
products[i].stage_publish = NULL;
|
||||
products[i].stage_iface = NULL;
|
||||
products[i].stage_status = NULL;
|
||||
}
|
||||
@@ -5876,6 +5917,9 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
if (!g->pkg[root].failed && sep_root_is_command(&g->pkg[root])
|
||||
&& validate_command_output_path(products[i].out) < 0)
|
||||
return 1;
|
||||
if (products[i].publish != NULL
|
||||
&& validate_command_output_path(products[i].publish) < 0)
|
||||
return 1;
|
||||
if (products[i].status != NULL
|
||||
&& validate_command_output_path(products[i].status) < 0)
|
||||
return 1;
|
||||
@@ -5980,8 +6024,11 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
struct sep_created_dirs created_output = {0};
|
||||
struct sep_created_dirs created_work = {0};
|
||||
if (create_output_dir != NULL && create_output_dir[0] != '\0'
|
||||
&& sep_mkdirs(create_output_dir, 0700, &created_output) != 0) {
|
||||
fprintf(stderr, "ww: cannot create build output directory %s\n",
|
||||
&& sep_mkdirs(create_output_dir, is_test ? 0777 : 0700,
|
||||
&created_output) != 0) {
|
||||
fprintf(stderr, is_test
|
||||
? "ww: cannot create test output directory %s\n"
|
||||
: "ww: cannot create build output directory %s\n",
|
||||
create_output_dir);
|
||||
free(order);
|
||||
return 1;
|
||||
@@ -6527,6 +6574,14 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
g->pkg[root].failed = 1;
|
||||
goto request_fail;
|
||||
}
|
||||
if (products[i].publish != NULL
|
||||
&& (products[i].stage_publish == NULL
|
||||
|| copy_executable_stage(products[i].stage_out,
|
||||
products[i].stage_publish) != 0)) {
|
||||
fprintf(stderr, "ww: cannot stage test binary %s\n",
|
||||
products[i].publish);
|
||||
goto request_fail;
|
||||
}
|
||||
if (sep_stage_product_status(&products[i]) != 0) {
|
||||
fprintf(stderr, "ww: cannot stage package-test product\n");
|
||||
goto request_fail;
|
||||
@@ -6594,6 +6649,10 @@ prepare_transaction:
|
||||
&& sep_txn_add(&tx, products[i].stage_out,
|
||||
products[i].out) < 0)
|
||||
goto request_fail;
|
||||
if (products[i].stage_publish != NULL
|
||||
&& sep_txn_add(&tx, products[i].stage_publish,
|
||||
products[i].publish) < 0)
|
||||
goto request_fail;
|
||||
if (products[i].stage_iface != NULL) {
|
||||
char outiface[SEP_ARTIFACT_MAX];
|
||||
int on = snprintf(outiface, sizeof outiface, "%s.wwi",
|
||||
@@ -6655,6 +6714,7 @@ build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
|
||||
.identity = root_identity,
|
||||
.test_package = test_package,
|
||||
.status = NULL,
|
||||
.publish = NULL,
|
||||
.artifact = NULL,
|
||||
.variant = root_variant,
|
||||
.root = -1,
|
||||
@@ -7203,9 +7263,9 @@ do_test(int argc, char **argv)
|
||||
}
|
||||
package_create_output_dir = argv[++i];
|
||||
} else if (strcmp(argv[i], "--ww-package-test") == 0) {
|
||||
if (i + 8 >= argc) {
|
||||
if (i + 9 >= argc) {
|
||||
fprintf(stderr,
|
||||
"ww test: --ww-package-test needs kind, package, production, internal, external, directory, output, and status\n");
|
||||
"ww test: --ww-package-test needs kind, package, production, internal, external, directory, output, publication, and status\n");
|
||||
return 2;
|
||||
}
|
||||
const char *kind = argv[++i];
|
||||
@@ -7215,6 +7275,7 @@ do_test(int argc, char **argv)
|
||||
const char *external = argv[++i];
|
||||
const char *dir = argv[++i];
|
||||
const char *output = argv[++i];
|
||||
const char *publish = argv[++i];
|
||||
const char *status = argv[++i];
|
||||
size_t pn = strlen(name);
|
||||
int build_product = strcmp(kind, "build") == 0;
|
||||
@@ -7225,6 +7286,7 @@ do_test(int argc, char **argv)
|
||||
if ((!build_product && !test_product)
|
||||
|| pn == 0
|
||||
|| dir[0] == '\0' || output[0] == '\0'
|
||||
|| publish[0] == '\0'
|
||||
|| status[0] == '\0'
|
||||
|| (has_production && strcmp(production, name) != 0)
|
||||
|| (has_internal && strcmp(internal, name) != 0)
|
||||
@@ -7232,9 +7294,12 @@ do_test(int argc, char **argv)
|
||||
|| strncmp(external, name, pn) != 0
|
||||
|| strcmp(external + pn, "_test") != 0))
|
||||
|| (build_product && (!has_production
|
||||
|| has_internal || has_external))
|
||||
|| has_internal || has_external
|
||||
|| strcmp(publish, "-") != 0))
|
||||
|| (test_product && !has_production
|
||||
&& !has_internal && !has_external)) {
|
||||
&& !has_internal && !has_external)
|
||||
|| (test_product && !has_internal && !has_external
|
||||
&& strcmp(publish, "-") != 0)) {
|
||||
fprintf(stderr,
|
||||
"ww test: invalid --ww-package-test product\n");
|
||||
return 2;
|
||||
@@ -7256,6 +7321,8 @@ do_test(int argc, char **argv)
|
||||
products[nproducts].external_package = has_external
|
||||
? external : NULL;
|
||||
products[nproducts].status = status;
|
||||
products[nproducts].publish = strcmp(publish, "-") == 0
|
||||
? NULL : publish;
|
||||
products[nproducts].artifact = NULL;
|
||||
products[nproducts].variant = build_product
|
||||
? SEP_VARIANT_PRODUCTION : SEP_VARIANT_TEST_MAIN;
|
||||
@@ -7389,7 +7456,7 @@ do_test(int argc, char **argv)
|
||||
fprintf(stderr, "ww test: invalid --ww-package-publish\n");
|
||||
return 2;
|
||||
}
|
||||
if (package_create_output_dir != NULL && !package_build) {
|
||||
if (package_create_output_dir != NULL && nproducts == 0) {
|
||||
fprintf(stderr, "ww test: invalid private directory creation\n");
|
||||
return 2;
|
||||
}
|
||||
@@ -7460,13 +7527,8 @@ do_test(int argc, char **argv)
|
||||
"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;
|
||||
}
|
||||
/* The coordinator independently wires -o retention and -c run
|
||||
* suppression after it has loaded the complete package set. */
|
||||
/* -w forwards one caller-owned semantic-action store shared by
|
||||
* the complete selected package universe. */
|
||||
return exec_package_command(argc, argv, src, NULL, NULL, 0, 0);
|
||||
@@ -7488,11 +7550,6 @@ do_test(int argc, char **argv)
|
||||
"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 (nproducts != 0) {
|
||||
if (!compileonly) {
|
||||
fprintf(stderr,
|
||||
@@ -7656,10 +7713,6 @@ do_test(int argc, char **argv)
|
||||
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 (nproducts != 0) {
|
||||
if (!compileonly) {
|
||||
fprintf(stderr,
|
||||
|
||||
@@ -5474,11 +5474,13 @@ as same/external rather than blindly stripping every suffix. The private driver
|
||||
descriptor is an ordered directory record:
|
||||
|
||||
```
|
||||
--ww-package-test KIND FAMILY PRODUCTION INTERNAL EXTERNAL DIR OUTPUT STATUS
|
||||
--ww-package-test KIND FAMILY PRODUCTION INTERNAL EXTERNAL DIR OUTPUT PUBLICATION STATUS
|
||||
```
|
||||
|
||||
Missing action selectors are `-`. One descriptor owns at most one output and
|
||||
one status. Canonically duplicate products and pairwise output/status/staging
|
||||
Missing action selectors and absent publication are `-`. `OUTPUT` is the
|
||||
request-private runnable, while optional `PUBLICATION` is its caller-visible
|
||||
retained copy. One descriptor owns at most one output, publication, and status.
|
||||
Canonically duplicate products and pairwise output/publication/status/staging
|
||||
collisions reject before producer execution. Declared names and output stems do
|
||||
not identify products or actions.
|
||||
|
||||
@@ -6158,6 +6160,154 @@ outer stderr to be empty, direct/raw stderr to remain separate, artifacts and
|
||||
binaries to remain byte-identical, and every temporary or staged path to be
|
||||
cleaned.
|
||||
|
||||
### 11.27 Implemented Go-like directory test-binary retention
|
||||
|
||||
Directory-package `ww test` now separates the request-private executable that
|
||||
the coordinator may run from the optional caller-visible executable it retains.
|
||||
`-c` means retain without running; `-o` means retain at the requested location
|
||||
and still run unless `-c` is also present. Output naming, directory fan-out,
|
||||
duplicate-name preflight, exact null-device discard, executable mode, and
|
||||
no-test behavior follow the applicable Go 1.26.5 contract.
|
||||
|
||||
#### Pinned Go evidence and direct pre-fix measurements
|
||||
|
||||
The authority is official Go 1.26.5 at commit
|
||||
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||
|
||||
- `CmdTest.Long` directly states that `-c` writes `pkg.test` in the current
|
||||
directory and does not run it, while `-o` saves a copy and still runs unless
|
||||
`-c` is present; a trailing slash or existing directory receives
|
||||
`pkg.test`
|
||||
([`cmd/go/internal/test/test.go`, lines 150–168](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L150-L168)).
|
||||
- `testNeedBinary` makes nonempty `-o` an independent retention request
|
||||
([`test.go`, lines 631–646](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L631-L646)).
|
||||
`runTest` recognizes an existing directory or trailing separator, rejects a
|
||||
multi-package non-directory output, and preflights every selected package
|
||||
for duplicate test-binary names before builder execution, except when the
|
||||
output is the null device
|
||||
([`test.go`, lines 771–804](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L771-L804)).
|
||||
- `builderTest` takes the ordinary production-only branch when no test files
|
||||
exist, creating no test link or retained binary
|
||||
([`test.go`, lines 1133–1169](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1169)).
|
||||
A real test first links into its action object directory; `-c` or binary
|
||||
retention adds an install action, only `-c` selects the no-op print action,
|
||||
and the non-`-c` run action depends on the original build action rather than
|
||||
the installed copy
|
||||
([`test.go`, lines 1200–1313](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1200-L1313)).
|
||||
- `testBinaryName` explicitly uses the final import-path element rather than
|
||||
the declared package name; its command-line-files exception uses the source
|
||||
package name
|
||||
([`test.go`, lines 2287–2300](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L2287-L2300),
|
||||
[`cmd/go/internal/load/pkg.go`, lines 1727–1769](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1727-L1769)).
|
||||
- `BuildInstallFunc` creates parents and installs a linked executable with mode
|
||||
`0777` filtered by the process umask
|
||||
([`cmd/go/internal/work/exec.go`, lines 1904–2000](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/exec.go#L1904-L2000),
|
||||
[`cmd/go/internal/work/shell.go`, lines 119–220 and 283–301](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/shell.go#L119-L220)).
|
||||
On the pinned Unix target, only exact `/dev/null` is the null spelling
|
||||
([`cmd/go/internal/base/path.go`, lines 81–92](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/base/path.go#L81-L92)).
|
||||
- Official `test_compile_multi_pkg.txt` requires missing nested output
|
||||
directory creation, default current-directory output, rejection of a
|
||||
non-directory multi-output and duplicate names, `/dev/null` acceptance, and
|
||||
`-o DIR` retention while tests still run
|
||||
([lines 3–38](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_compile_multi_pkg.txt#L3-L38)).
|
||||
|
||||
The separation between saved and executed paths is a conclusion derived from
|
||||
the pinned action dependencies: the run consumes the temporary link action,
|
||||
not the install action. Duplicate names are likewise a materialization
|
||||
collision, not package identity. Go's sources do not assert that a set of
|
||||
retained binaries is installed as one rollback transaction; WW keeps its
|
||||
existing stronger request-wide transaction while matching the observable
|
||||
accepted, rejected, and preserved outputs. No installed host Go behavior was
|
||||
used as authority.
|
||||
|
||||
Before this slice, direct native measurements of both Cstage and WWstage showed
|
||||
that single-package `-c -o FILE` retained and did not run, but `-o FILE`
|
||||
without `-c` exited 2 with `-o needs -c for a package target`; multi-package
|
||||
`-c -o FILE` exited 2 with the older unconditional fan-out rejection. Default
|
||||
multi-package `-c` scattered `<declared-package>.test` binaries into their
|
||||
source directories. Those measurements used the public driver route and
|
||||
observed exits, diagnostics, files, executable behavior, and stage-equal bytes;
|
||||
they were not conclusions drawn from WW source.
|
||||
|
||||
#### Coordinator policy and identity boundaries
|
||||
|
||||
`internal/wwpackage.packagecommand` is the sole owner of public output policy.
|
||||
It resolves the invocation directory, computes each visible
|
||||
`<import-leaf>.test` name, recognizes output-directory and `/dev/null` forms,
|
||||
rejects non-directory fan-out and duplicate names, omits publication for
|
||||
no-test products, and schedules execution according to `-c`. A contextual
|
||||
dotted request uses its exact final component; a local path request uses its
|
||||
directory leaf as the manifest-free presentation equivalent. Neither becomes
|
||||
declared-name or physical-directory identity.
|
||||
|
||||
Every actual test product still links to `package.test` below its private plan
|
||||
root. The private descriptor carries that `OUTPUT` plus an optional absolute
|
||||
`PUBLICATION`. The Cstage and WWstage drivers implement only this symmetric
|
||||
mechanism; they do not independently decide names or CLI policy. The
|
||||
coordinator always executes `OUTPUT`, so `-o` cannot alter executable argv,
|
||||
cwd, environment, null stdin, combined output, filters, action topology, or
|
||||
test outcome.
|
||||
|
||||
Visible basename, publication path, private runnable path, declared family,
|
||||
physical source directory, production/internal/external/recompiled/support/main
|
||||
variants, symbols, `.wwi`, archives, action identity, and persistence keys
|
||||
remain distinct. A duplicate basename rejects only the requested
|
||||
materialization. It never merges, renames, folds, or rekeys either canonical
|
||||
package. Compiler inputs, exported interfaces, generated main, archive order,
|
||||
and linked bytes are otherwise unchanged.
|
||||
|
||||
#### Publication, execution, failure, and cleanup
|
||||
|
||||
Without explicit `-o`, `-c` retains each binary in the invocation directory.
|
||||
An existing directory or a path ending in `/` receives one visible name per
|
||||
selected package; missing parents are created with `0777` subject to umask. A
|
||||
non-directory destination accepts exactly one selected package. Exact
|
||||
`/dev/null` suppresses retained copies, permits duplicate visible names, and
|
||||
does not suppress execution unless `-c` is also present. A no-test package
|
||||
performs ordinary production validation, reports `[no tests]`, and creates no
|
||||
binary or otherwise-unused output directory. Successful compile-only products
|
||||
are silent, matching Go's no-op print action.
|
||||
|
||||
After linking, the driver copies the private runnable bytes to a distinct
|
||||
`.new` inode opened with executable mode `0777` subject to umask. Temporary
|
||||
runnable, retained copy, statuses, changed persistent actions, tool records,
|
||||
and stamp then enter the existing one-request transaction. All producers and
|
||||
linkers complete before installation. Any load, compile, assemble, archive,
|
||||
link, stage, or install failure preserves old retained binaries and persistent
|
||||
bytes, discards all stages, removes cold scratch, and rolls back only output
|
||||
prefixes created by that request. Occupied or dangling `.new` paths reject
|
||||
before tools and are never overwritten.
|
||||
|
||||
Execution begins only after publication commits. Assertion failure, signal,
|
||||
timeout, or child-setup failure therefore leaves an explicitly retained binary
|
||||
while retaining the established stdout/stderr result routing and sibling
|
||||
isolation. Parallel products stage independent runnable/copy pairs; the shared
|
||||
transaction prevents partial sibling publication and canonical result emission
|
||||
order remains unchanged. Direct invocation of a retained binary continues to
|
||||
inherit caller cwd, environment, and separate standard descriptors.
|
||||
|
||||
`-o` can accompany `-w`: unchanged semantic actions are reused, changed source
|
||||
invalidates the applicable test actions, the always-run link refreshes the
|
||||
private runnable and retained copy, and the test still runs. This is build
|
||||
reuse, not a result cache. `-c` retains its established incompatibility with
|
||||
`-w`; this slice does not invent persistent compile-only ownership.
|
||||
|
||||
No persisted byte schema changed. Build workdir format remains `18`, test
|
||||
workdir format remains `19`, and semantic storage remains `3`.
|
||||
|
||||
The focused native owners are `compile_artifact_naming` and
|
||||
`test_binary_publication_transaction` in `test/package/package_test.ww`. Their
|
||||
Cstage/WWstage matrix covers single/default/directory/nested/multi/null output;
|
||||
declared-name versus import-leaf naming; executable mode and direct execution;
|
||||
temporary argv versus retained path; no-test omission; duplicate and
|
||||
non-directory rejection; occupied stages; serial and parallel sibling
|
||||
publication; injected late-link rollback over old files and newly created
|
||||
parents; runtime-failure retention; persistent cold/warm/invalidation behavior;
|
||||
diagnostic equality; retained binary byte identity; and absence of `.new`
|
||||
residue. Existing package tests continue to own all action/test variants,
|
||||
graph identity, output ordering, cwd/environment/stdin, timeout, and broader
|
||||
transaction behavior.
|
||||
|
||||
## 12. Candidate architectures and hard-gate decision
|
||||
|
||||
Five candidates were developed as coherent systems, not as feature bins.
|
||||
|
||||
21
docs/spec.md
21
docs/spec.md
@@ -645,6 +645,27 @@ mixed directories are accepted from their selected test files. A directory
|
||||
with no selected test file validates ordinary production but creates no test
|
||||
support, generated main, link, binary, result, or process.
|
||||
|
||||
Every test-bearing directory product links one request-private runnable.
|
||||
`-c` retains an executable copy and suppresses its execution; `-o` retains a
|
||||
copy at the named destination and still executes unless `-c` is present. With
|
||||
no explicit output, `-c` writes `<import-leaf>.test` in the invocation
|
||||
directory. An output ending in `/` or naming an existing directory receives
|
||||
that basename and may have missing parent directories created. One
|
||||
non-directory output may name only one package. Multiple packages whose
|
||||
visible import leaves would produce the same test-binary name reject before
|
||||
tools or output creation; exact `/dev/null` is the discard exception. Declared
|
||||
package names, test variants, source filenames, physical directories, output
|
||||
paths, and retained binary names remain presentation or loader metadata and do
|
||||
not become canonical package or action identity.
|
||||
|
||||
The retained file is an executable, byte-identical copy of the private
|
||||
runnable. It joins package artifacts and statuses in the request-wide atomic
|
||||
publication transaction. Build, link, stage, or install failure preserves old
|
||||
destinations and removes temporary stages and invocation-created output
|
||||
prefixes. Test execution starts only after that transaction commits, so a
|
||||
runtime failure leaves an explicitly retained binary. A no-test product
|
||||
publishes no binary and does not create a directory solely for one.
|
||||
|
||||
When `ww test` executes a directory-owned product, the child process working
|
||||
directory is that product's canonical absolute physical package source
|
||||
directory. Its per-run environment has exactly one effective uppercase `PWD`,
|
||||
|
||||
@@ -216,18 +216,28 @@ test binaries concurrently under `os.exec` start/poll supervision (no threads);
|
||||
emission stays strictly in group order, so the byte stream is identical at
|
||||
every `-j` level, and `-j 1` — the default — matches the former sequential
|
||||
run 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` directory binary; the first output owns the one
|
||||
shared cold sepwork containing the command-global action universe. Those become
|
||||
caller-owned artifacts. `-c -o <name>`
|
||||
names that artifact instead of the fixed stem for exactly one directory,
|
||||
including a combined internal/external directory:
|
||||
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 leaves no caller-owned artifact for `-o` to name. Without `-c`, it removes the
|
||||
temporary binary and scratch with its workspace. The language runtime
|
||||
owns individual `@test` functions.
|
||||
7.0s sequential, 2.4s at `-j 4`.
|
||||
|
||||
Directory test binaries are always linked under the coordinator's temporary
|
||||
product root. `-c` independently requests a caller-visible executable copy and
|
||||
suppresses execution. `-o` independently requests a copy and still runs the
|
||||
temporary binary unless `-c` is also present. Without `-o`, `-c` publishes
|
||||
`<import-leaf>.test` in the invocation directory; an output ending in `/` or
|
||||
naming an existing directory receives one such name per selected package and
|
||||
missing parents are created. A single non-directory output is legal for one
|
||||
package only. Multi-package non-directory output and duplicate visible binary
|
||||
names reject before tools; exact `/dev/null` discards every copy and permits
|
||||
duplicate names. A package with no selected test source validates production
|
||||
but publishes nothing and does not create an otherwise unneeded output
|
||||
directory. Successful `-c` output is silent apart from no-test reporting.
|
||||
|
||||
The retained executable is byte-identical to the temporary runnable and has
|
||||
executable mode `0777` filtered by the caller's umask, but it is never the path
|
||||
executed by the coordinator. Publication participates in the driver's one
|
||||
request-wide transaction: producer, linker, staging, or installation failure
|
||||
preserves every prior destination and removes stages and newly created output
|
||||
prefixes. A runtime failure occurs after commit and therefore leaves the saved
|
||||
binary. The language runtime owns individual `@test` functions.
|
||||
|
||||
Every actually executed directory product gives its single generated binary
|
||||
the product's canonical absolute physical source directory as child cwd. A
|
||||
@@ -409,16 +419,19 @@ bounded-memory Cstage/WWstage allocation-failure parity across the complete
|
||||
combined package-test graph, under independently discovered ceilings supplied
|
||||
by the repository-built `sep-limitexec` helper.
|
||||
|
||||
`ww build`, an explicit single-file `ww test -o <stem>`, and each successful
|
||||
directory-package `ww test -c` build publish `<stem>.sepwork` as a caller-owned
|
||||
artifact directory. The driver acquires it with one fresh `mkdir` and refuses
|
||||
an existing path; it never clears a collision. A caller keeps only the exact
|
||||
artifacts it observes and removes that exact tree on every later success or
|
||||
failure. `ww run` and no-output single-file `ww test` use driver-owned scratch
|
||||
instead; both driver stages place that scratch and their temporary executable
|
||||
beneath one freshly acquired directory, remove both after every build result,
|
||||
and make cleanup failure fail the command. Make recipes build driver-produced
|
||||
tools in invocation-owned directories and apply the same exact cleanup rule.
|
||||
`ww build` and an explicit single-file `ww test -o <stem>` publish
|
||||
`<stem>.sepwork` as a caller-owned artifact directory. The driver acquires it
|
||||
with one fresh `mkdir` and refuses an existing path; it never clears a
|
||||
collision. A caller keeps only the exact artifacts it observes and removes
|
||||
that exact tree on every later success or failure. Directory-package test
|
||||
plans instead keep their cold semantic-action scratch and runnable binary
|
||||
inside the coordinator's temporary root; only the optional retained executable
|
||||
escapes through the transaction above. `ww run` and no-output single-file
|
||||
`ww test` use driver-owned scratch instead; both driver stages place that
|
||||
scratch and their temporary executable beneath one freshly acquired directory,
|
||||
remove both after every build result, and make cleanup failure fail the
|
||||
command. Make recipes build driver-produced tools in invocation-owned
|
||||
directories and apply the same exact cleanup rule.
|
||||
|
||||
`ww build -w DIR` and single-file `ww test -w DIR` replace that scratch with a
|
||||
caller-owned persistent package-artifact workdir: for these direct routes the
|
||||
@@ -561,8 +574,8 @@ timeout policy in this architecture.
|
||||
|
||||
## Open driver work
|
||||
|
||||
None; the package-level `-o` contract (the last carried bullet) landed as
|
||||
`-c -o <name>` for exactly one package.
|
||||
None; directory-package `-c` and `-o` now have the applicable Go 1.26.5
|
||||
retention, naming, fan-out, execution, and publication behavior.
|
||||
|
||||
## Validation policy
|
||||
|
||||
|
||||
@@ -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 ");
|
||||
|
||||
@@ -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) {
|
||||
&& 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");
|
||||
|
||||
@@ -1159,12 +1159,12 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
caller, stdinpath,
|
||||
(90i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
let defaultbin: str = strings.concat(p2, "/p2.test");
|
||||
let defaultbin: str = strings.concat(caller, "/p2.test");
|
||||
assert(os.exists(defaultbin));
|
||||
assert(!os.exists(strings.concat(p2, "/p2.test")));
|
||||
assert(!os.exists(strings.concat(p2, "/created.txt")));
|
||||
assert(!has(out.stdout, "dep-init") && !has(out.stdout, "p2-external"));
|
||||
assert(os.remove(defaultbin) == 0);
|
||||
clean(strings.concat(defaultbin, ".sepwork"));
|
||||
|
||||
assert(os.remove(strings.concat(p1, "/created.txt")) == 0);
|
||||
let p1cbin: str = strings.concat(root, "/p1-c.test");
|
||||
@@ -1814,6 +1814,12 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
"toexternal_test", "tomixed"];
|
||||
let shapetargetsecond: []str = ["", "shapeexternal_test", "", "",
|
||||
"tomixed_test"];
|
||||
let shapeproductionselector: []str = ["shapeinternal", "shapeexternal",
|
||||
"-", "-", "-"];
|
||||
let shapeinternalselector: []str = ["shapeinternal", "-", "tosame",
|
||||
"-", "tomixed"];
|
||||
let shapeexternalselector: []str = ["-", "shapeexternal_test", "-",
|
||||
"toexternal_test", "tomixed_test"];
|
||||
let shapecompilerrefs: []str = ["", "", "", "", ""];
|
||||
let shapelinkerrefs: []str = ["", "", "", "", ""];
|
||||
let shapemainrefs: []str = ["", "", "", "", ""];
|
||||
@@ -1827,6 +1833,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
let shapebin: str = strings.concat(root, "/shape-product-",
|
||||
boundarypkgname(sj), ".test");
|
||||
let shapework: str = strings.concat(shapebin, ".sepwork");
|
||||
let shapestatus: str = strings.concat(shapebin, ".status");
|
||||
let ctracepath: str = strings.concat(root, "/shape-compiler-",
|
||||
boundarypkgname(sj), ".trace");
|
||||
let ltracepath: str = strings.concat(root, "/shape-linker-",
|
||||
@@ -1835,6 +1842,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
writefile(ltracepath, "");
|
||||
let stagei: i32 = 0;
|
||||
for (stagei < stages.len) {
|
||||
assert(os.mkdir(shapework, 448i32) == 0);
|
||||
if (stagei != 0) {
|
||||
rewritefile(ctracepath, "");
|
||||
rewritefile(ltracepath, "");
|
||||
@@ -1864,8 +1872,12 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
driver(shapecompilers[stagei])));
|
||||
append(env, strings.concat("WW_SHAPE_REAL_LINKER=",
|
||||
driver(shapelinkers[stagei])));
|
||||
let shapeav: []str = [driver(stages[stagei]), "test", "-c", "-o",
|
||||
shapebin, "-I", root, shapes[index]];
|
||||
let shapeav: []str = [driver(stages[stagei]), "test", "-c", "-w",
|
||||
shapework, "-I", root,
|
||||
"--ww-package-test", "test", shapefamilies[index],
|
||||
shapeproductionselector[sj], shapeinternalselector[sj],
|
||||
shapeexternalselector[sj], shapes[index], shapebin, "-",
|
||||
shapestatus, shapes[index]];
|
||||
runcommandenv(root, strings.concat("shape-structure-",
|
||||
stages[stagei], "-", boundarypkgname(sj)), shapeav, env,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
@@ -1876,9 +1888,9 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
shapefamilies[index], ".test")));
|
||||
let ctrace: str = readfile(ctracepath);
|
||||
let ltrace: str = readfile(ltracepath);
|
||||
assert(occurrences(ctrace, "-test-main.unit.ww") == 1);
|
||||
assert(occurrences(ctrace, "-test-main.unit.new") == 1);
|
||||
let mainline: str = linecontaining(ctrace,
|
||||
strings.concat("/", shapemains[sj], ".unit.ww"));
|
||||
strings.concat("/", shapemains[sj], ".unit.new"));
|
||||
let targetcount: i32 = 1;
|
||||
if (shapetargetsecond[sj].len != 0) { targetcount = 2; };
|
||||
assert(occurrences(mainline, "--test-target-package")
|
||||
@@ -1932,6 +1944,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
};
|
||||
clean(shapework);
|
||||
clean(shapebin);
|
||||
clean(shapestatus);
|
||||
stagei += 1;
|
||||
};
|
||||
sj += 1;
|
||||
@@ -2191,37 +2204,39 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
assert(same(outc.stderr, outw.stderr));
|
||||
assert(has(outc.stderr, "usage: wwtest package"));
|
||||
|
||||
let cc: []str = [driver("ww"), "test", "-c", "-I", root, route];
|
||||
let cwork: str = strings.concat(root, "/compile-c-work");
|
||||
let wwork: str = strings.concat(root, "/compile-ww-work");
|
||||
let cbinarypath: str = strings.concat(root, "/route-c.test");
|
||||
let wbinarypath: str = strings.concat(root, "/route-ww.test");
|
||||
let cc: []str = [driver("ww"), "test", "-w", cwork, "-o",
|
||||
cbinarypath, "-I", root, route];
|
||||
runcommand(root, "compile-c", cc,
|
||||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||||
expectexit(&outc, 0);
|
||||
assert(has(outc.stdout, strings.concat(" -> ", route, "/route.test\n")));
|
||||
assert(occurrences(outc.stdout, "built ") == 1);
|
||||
let cwhite: str = readfile(strings.concat(route,
|
||||
"/route.test.sepwork/route-internal-test.s"));
|
||||
let cexternal: str = readfile(strings.concat(route,
|
||||
"/route.test.sepwork/route_test-external-test.s"));
|
||||
let cmain: str = readfile(strings.concat(route,
|
||||
"/route.test.sepwork/route-test-main.s"));
|
||||
let cbinary: str = readfile(strings.concat(route, "/route.test"));
|
||||
// The explicit package `-c` outputs are caller-owned artifacts. Release
|
||||
// the shared C-stage tree before asking the WW driver to acquire the same
|
||||
// stem; the driver never deletes a pre-existing `.sepwork` path.
|
||||
clean(strings.concat(route, "/route.test.sepwork"));
|
||||
assert(has(outc.stdout, "route.white ... ok\n"));
|
||||
assert(has(outc.stdout, "route.external ... ok\n"));
|
||||
let cwhite: str = readfile(strings.concat(cwork,
|
||||
"/route-internal-test.s"));
|
||||
let cexternal: str = readfile(strings.concat(cwork,
|
||||
"/route_test-external-test.s"));
|
||||
let cmain: str = readfile(strings.concat(cwork,
|
||||
"/route-test-main.s"));
|
||||
let cbinary: str = readfile(cbinarypath);
|
||||
|
||||
let wc: []str = [driver("ww_ww"), "test", "-c", "-I", root, route];
|
||||
let wc: []str = [driver("ww_ww"), "test", "-w", wwork, "-o",
|
||||
wbinarypath, "-I", root, route];
|
||||
runcommand(root, "compile-ww", wc,
|
||||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||||
expectexit(&outw, 0);
|
||||
assert(same(outc.stdout, outw.stdout));
|
||||
assert(same(outc.stderr, outw.stderr));
|
||||
assert(same(cwhite, readfile(strings.concat(route,
|
||||
"/route.test.sepwork/route-internal-test.s"))));
|
||||
assert(same(cexternal, readfile(strings.concat(route,
|
||||
"/route.test.sepwork/route_test-external-test.s"))));
|
||||
assert(same(cmain, readfile(strings.concat(route,
|
||||
"/route.test.sepwork/route-test-main.s"))));
|
||||
assert(same(cbinary, readfile(strings.concat(route, "/route.test"))));
|
||||
assert(same(cwhite, readfile(strings.concat(wwork,
|
||||
"/route-internal-test.s"))));
|
||||
assert(same(cexternal, readfile(strings.concat(wwork,
|
||||
"/route_test-external-test.s"))));
|
||||
assert(same(cmain, readfile(strings.concat(wwork,
|
||||
"/route-test-main.s"))));
|
||||
assert(same(cbinary, readfile(wbinarypath)));
|
||||
clean(root);
|
||||
};
|
||||
|
||||
@@ -2464,7 +2479,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
|
||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||||
"pkg_test", pkg, bins[si], statuses[si], pkg];
|
||||
"pkg_test", pkg, bins[si], "-", statuses[si], pkg];
|
||||
runcommandenv(root, strings.concat("graph-cold-", stages[si]), av,
|
||||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
@@ -2728,7 +2743,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
driver(linkers[si])));
|
||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||||
"pkg_test", pkg, bins[si], statuses[si], pkg];
|
||||
"pkg_test", pkg, bins[si], "-", statuses[si], pkg];
|
||||
runcommandenv(root, strings.concat("graph-body-", stages[si]), av,
|
||||
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
@@ -2807,7 +2822,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
driver(linkers[si])));
|
||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||||
"pkg_test", pkg, bins[si], statuses[si], pkg];
|
||||
"pkg_test", pkg, bins[si], "-", statuses[si], pkg];
|
||||
runcommandenv(root, strings.concat("graph-external-", stages[si]), av,
|
||||
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
@@ -2878,7 +2893,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
driver(linkers[si])));
|
||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||||
"pkg_test", pkg, bins[si], statuses[si], pkg];
|
||||
"pkg_test", pkg, bins[si], "-", statuses[si], pkg];
|
||||
runcommandenv(root, strings.concat("graph-export-", stages[si]), av,
|
||||
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
@@ -2981,7 +2996,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
driver(linkers[si])));
|
||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||||
"-", pkg, bins[si], statuses[si], pkg];
|
||||
"-", pkg, bins[si], "-", statuses[si], pkg];
|
||||
runcommandenv(root, strings.concat("graph-remove-external-", stages[si]),
|
||||
av, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
@@ -3106,7 +3121,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
driver(linkers[si])));
|
||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||||
"pkg_test", pkg, bins[si], statuses[si], pkg];
|
||||
"pkg_test", pkg, bins[si], "-", statuses[si], pkg];
|
||||
runcommandenv(root, strings.concat("graph-readd-external-", stages[si]),
|
||||
av, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
@@ -3191,7 +3206,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
runcommand(root, strings.concat("graph-direct-product-", stages[si]),
|
||||
directav, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(occurrences(out.stdout, "built ") == 1);
|
||||
assert(out.stdout.len == 0);
|
||||
assert(os.exists(directbin));
|
||||
if (si == 0) {
|
||||
directbytes = readfile(directbin);
|
||||
@@ -3376,19 +3391,19 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
let forward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||
works[si], "-I", suite, "-I", root,
|
||||
"--ww-package-test", "test", "alpha", "alpha", "alpha",
|
||||
"alpha_test", alpha, bins[0], statuses[0],
|
||||
"alpha_test", alpha, bins[0], "-", statuses[0],
|
||||
"--ww-package-test", "test", "beta", "beta", "beta",
|
||||
"beta_test", beta, bins[1], statuses[1],
|
||||
"beta_test", beta, bins[1], "-", statuses[1],
|
||||
"--ww-package-test", "test", "gamma", "gamma", "-", "-",
|
||||
gamma, gammabin, statuses[2], alpha];
|
||||
gamma, gammabin, "-", statuses[2], alpha];
|
||||
let reverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||
works[si], "-I", suite, "-I", root,
|
||||
"--ww-package-test", "test", "gamma", "gamma", "-", "-",
|
||||
gamma, gammabin, statuses[2],
|
||||
gamma, gammabin, "-", statuses[2],
|
||||
"--ww-package-test", "test", "beta", "beta", "beta",
|
||||
"beta_test", beta, bins[1], statuses[1],
|
||||
"beta_test", beta, bins[1], "-", statuses[1],
|
||||
"--ww-package-test", "test", "alpha", "alpha", "alpha",
|
||||
"alpha_test", alpha, bins[0], statuses[0], alpha];
|
||||
"alpha_test", alpha, bins[0], "-", statuses[0], alpha];
|
||||
if (si == 0) {
|
||||
runcommandenv(root, "multi-forward-c", forward, env,
|
||||
(180i64 * (time.second: i64)): time.duration, &out);
|
||||
@@ -3531,14 +3546,11 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
si += 1;
|
||||
};
|
||||
|
||||
// Public root order is presentation-only: two directories publish one
|
||||
// binary each, while the selected no-test directory publishes no binary.
|
||||
let publicalpha: str = strings.concat(alpha, "/alpha.test");
|
||||
let publicbeta: str = strings.concat(beta, "/beta.test");
|
||||
let publicgamma: str = strings.concat(gamma, "/gamma.test");
|
||||
let publicworks: []str = [strings.concat(publicalpha, ".sepwork"),
|
||||
strings.concat(publicbeta, ".sepwork"),
|
||||
strings.concat(publicgamma, ".sepwork")];
|
||||
// Public root order is presentation-only. Default -c destinations are in
|
||||
// the invocation cwd, and the selected no-test package publishes nothing.
|
||||
let publicalpha: str = strings.concat(root, "/alpha.test");
|
||||
let publicbeta: str = strings.concat(root, "/beta.test");
|
||||
let publicgamma: str = strings.concat(root, "/gamma.test");
|
||||
let publicout: str = "";
|
||||
let publicerr: str = "";
|
||||
let publicalphabytes: str = "";
|
||||
@@ -3562,11 +3574,11 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
append(publicav, gamma); append(publicav, beta);
|
||||
append(publicav, alpha);
|
||||
};
|
||||
runcommand(root, strings.concat("multi-public-order-",
|
||||
publiclabels[pi]), publicav,
|
||||
runcommanddir(root, strings.concat("multi-public-order-",
|
||||
publiclabels[pi]), root, publicav,
|
||||
(180i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(occurrences(out.stdout, "built ") == 2);
|
||||
assert(occurrences(out.stdout, "built ") == 0);
|
||||
assert(has(out.stdout, strings.concat(
|
||||
"? ", gamma, " [no tests]\n")));
|
||||
assert(os.exists(publicalpha) && os.exists(publicbeta));
|
||||
@@ -3584,8 +3596,6 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
assert(same(publicalphabytes, readfile(publicalpha)));
|
||||
assert(same(publicbetabytes, readfile(publicbeta)));
|
||||
};
|
||||
let wi: i32 = 0;
|
||||
for (wi < publicworks.len) { clean(publicworks[wi]); wi += 1; };
|
||||
clean(publicalpha); clean(publicbeta); clean(publicgamma);
|
||||
pi += 1;
|
||||
};
|
||||
@@ -3756,6 +3766,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
append(linkenv, strings.concat("WW_MIXED_W6L=", driver("w6l")));
|
||||
let bins: []str = [strings.concat(named, "/test.test"),
|
||||
strings.concat(consumer, "/zconsumer.test")];
|
||||
let statuses: []str = [strings.concat(root, "/mixed-test.status"),
|
||||
strings.concat(root, "/mixed-zconsumer.status")];
|
||||
let keys: []str = ["test-internal-test", "test_test-external-test",
|
||||
"test-test-main", "zconsumer-internal-test",
|
||||
"zconsumer_test-external-test", "zconsumer-test-main"];
|
||||
@@ -3766,8 +3778,14 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
let out: commandout;
|
||||
let si: i32 = 0;
|
||||
for (si < stages.len) {
|
||||
let av: []str = [driver(stages[si]), "test", "-c", "-j", "1",
|
||||
"-I", suite, strings.concat(suite, "/...")];
|
||||
assert(os.mkdir(workroot, 448i32) == 0);
|
||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", workroot,
|
||||
"-I", suite,
|
||||
"--ww-package-test", "test", "test", "test", "test",
|
||||
"test_test", named, bins[0], "-", statuses[0],
|
||||
"--ww-package-test", "test", "zconsumer", "zconsumer",
|
||||
"zconsumer", "zconsumer_test", consumer, bins[1], "-",
|
||||
statuses[1], named];
|
||||
if (si == 0) {
|
||||
runcommandenv(root, "mixed-actions-c", av, linkenv,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
@@ -3776,7 +3794,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
};
|
||||
expectexit(&out, 0);
|
||||
assert(occurrences(out.stdout, "built ") == 2);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||
let namedprod: str = readfile(strings.concat(work,
|
||||
"test-internal-test.unit.ww"));
|
||||
let support: str = readfile(strings.concat(work, "__wwtest.unit.ww"));
|
||||
@@ -4441,7 +4459,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
append(env, strings.concat("WW_UNIVERSE_BUILDER_TRACE=",
|
||||
buildertraces[si]));
|
||||
|
||||
let av: []str = alloc([], (8 + productcount * 9): u64)!;
|
||||
let av: []str = alloc([], (8 + productcount * 10): u64)!;
|
||||
append(av, driver(stages[si]));
|
||||
append(av, "test"); append(av, "-c");
|
||||
append(av, "-w"); append(av, works[si]);
|
||||
@@ -4461,6 +4479,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
};
|
||||
append(av, dir);
|
||||
append(av, strings.concat(root, "/product-", name, ".test"));
|
||||
append(av, "-");
|
||||
append(av, strings.concat(root, "/product-", name, ".status"));
|
||||
i += 1;
|
||||
};
|
||||
@@ -4479,12 +4498,13 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
};
|
||||
append(av, dir);
|
||||
append(av, strings.concat(root, "/product-", name, ".test"));
|
||||
append(av, "-");
|
||||
append(av, strings.concat(root, "/product-", name, ".status"));
|
||||
i -= 1;
|
||||
};
|
||||
};
|
||||
append(av, strings.concat(suite, "/p000"));
|
||||
assert(av.len == 8 + productcount * 9);
|
||||
assert(av.len == 8 + productcount * 10);
|
||||
runcommandenv(root, strings.concat("dynamic-universe-", stages[si]), av,
|
||||
env, (1200i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
@@ -5038,7 +5058,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
let seedav: []str = [driver(stages[i]), "test", "-c",
|
||||
"-w", lateworks[i], "-I", root, "--ww-package-test", "test",
|
||||
"late", "late", "late", "late_test", late, latebins[i],
|
||||
latestatuses[i], late];
|
||||
"-", latestatuses[i], late];
|
||||
runcommandenv(root, strings.concat("late-seed-", tags[i]), seedav,
|
||||
seedenv, (180i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
@@ -5164,7 +5184,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
let failav: []str = [driver(stages[i]), "test", "-c",
|
||||
"-w", lateworks[i], "-I", root, "--ww-package-test", "test",
|
||||
"late", "late", "late", "late_test", late, attemptbin,
|
||||
attemptstatus, late];
|
||||
"-", attemptstatus, late];
|
||||
runcommandenv(root, strings.concat("late-failure-",
|
||||
phaselabels[phasei], "-", tags[i], "-", retrylabel), failav,
|
||||
env, (180i64 * (time.second: i64)): time.duration, &out);
|
||||
@@ -5276,8 +5296,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
let out: commandout;
|
||||
let i: i32 = 0;
|
||||
for (i < stages.len) {
|
||||
let av: []str = [driver(stages[i]), "test", "-c", "-I", root,
|
||||
"-o", bin, pkg];
|
||||
let av: []str = [driver(stages[i]), "test", "-w", workroot,
|
||||
"-I", root, "-o", bin, pkg];
|
||||
runcommand(root, strings.concat("named-test-build-", stages[i]), av,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
@@ -5914,18 +5934,22 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
orderwork, "-I", source,
|
||||
"--ww-package-test", "test", "main", "main", "-", "-", client,
|
||||
strings.concat(root, "/order-client-", stages[si]),
|
||||
"-",
|
||||
strings.concat(root, "/order-client-", stages[si], ".status"),
|
||||
"--ww-package-test", "test", "main", "main", "-", "-", outsider,
|
||||
strings.concat(root, "/order-outsider-", stages[si]),
|
||||
"-",
|
||||
strings.concat(root, "/order-outsider-", stages[si], ".status"),
|
||||
client];
|
||||
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||
orderwork, "-I", source,
|
||||
"--ww-package-test", "test", "main", "main", "-", "-", outsider,
|
||||
strings.concat(root, "/order-outsider-", stages[si]),
|
||||
"-",
|
||||
strings.concat(root, "/order-outsider-", stages[si], ".status"),
|
||||
"--ww-package-test", "test", "main", "main", "-", "-", client,
|
||||
strings.concat(root, "/order-client-", stages[si]),
|
||||
"-",
|
||||
strings.concat(root, "/order-client-", stages[si], ".status"),
|
||||
client];
|
||||
let orderrequests: [][]str = [orderforward, orderreverse];
|
||||
@@ -6352,10 +6376,10 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
source, "--ww-package-build",
|
||||
"--ww-package-test", "build", "paritybase", "paritybase",
|
||||
"-", "-", base,
|
||||
combinedbase, combinedbasestatus,
|
||||
combinedbase, "-", combinedbasestatus,
|
||||
"--ww-package-test", "build", "parityleft", "parityleft",
|
||||
"-", "-", left,
|
||||
combinedleft, combinedleftstatus, left];
|
||||
combinedleft, "-", combinedleftstatus, left];
|
||||
runcommandenv(root, strings.concat("long-combined-", tags[si]),
|
||||
combinedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
@@ -9113,13 +9137,17 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
strings.concat(root, "/stem"), spec];
|
||||
let ow: []str = [driver("ww_ww"), "test", "-o",
|
||||
strings.concat(root, "/stem"), spec];
|
||||
runcommand(root, "tree-o-c", oc, time.second, &outc);
|
||||
runcommand(root, "tree-o-ww", ow, time.second, &outw);
|
||||
expectexit(&outc, 2);
|
||||
expectexit(&outw, 2);
|
||||
runcommand(root, "tree-o-c", oc,
|
||||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||||
runcommand(root, "tree-o-ww", ow,
|
||||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||||
expectexit(&outc, 1);
|
||||
expectexit(&outw, 1);
|
||||
assert(outc.stdout.len == 0 && outw.stdout.len == 0);
|
||||
assert(same(outc.stderr, outw.stderr));
|
||||
assert(has(outc.stderr,
|
||||
"ww test: -o needs -c for a package target\n"));
|
||||
"with multiple packages, -o must refer to a directory or /dev/null"));
|
||||
assert(!os.exists(strings.concat(root, "/stem")));
|
||||
|
||||
// -w on a package target forwards to the coordinator: one shared
|
||||
// persistent driver workdir for the complete selected request,
|
||||
@@ -9691,9 +9719,11 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
"--ww-package-build",
|
||||
"--ww-package-test", "build", "foo", "foo", "-", "-",
|
||||
localleft, leftout,
|
||||
"-",
|
||||
strings.concat(root, "/local-", stages[si], "-left.status"),
|
||||
"--ww-package-test", "build", "foo", "foo", "-", "-",
|
||||
localright, rightout,
|
||||
"-",
|
||||
strings.concat(root, "/local-", stages[si], "-right.status"),
|
||||
localleft];
|
||||
runcommand(root, strings.concat("local-command-union-", stages[si]),
|
||||
@@ -10440,15 +10470,15 @@ fn runtimepath(relative: str) str = {
|
||||
let foldforward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||
foldmultiwork, "-I", source, "--ww-package-build",
|
||||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||||
foldallowedout, foldallowedstatus,
|
||||
foldallowedout, "-", foldallowedstatus,
|
||||
"--ww-package-test", "build", "main", "main", "-", "-",
|
||||
foldvendorclient, foldcollisionout, foldcollisionstatus, client];
|
||||
foldvendorclient, foldcollisionout, "-", foldcollisionstatus, client];
|
||||
let foldreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||
foldmultiwork, "-I", source, "--ww-package-build",
|
||||
"--ww-package-test", "build", "main", "main", "-", "-",
|
||||
foldvendorclient, foldcollisionout, foldcollisionstatus,
|
||||
foldvendorclient, foldcollisionout, "-", foldcollisionstatus,
|
||||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||||
foldallowedout, foldallowedstatus, client];
|
||||
foldallowedout, "-", foldallowedstatus, client];
|
||||
let foldrequests: [][]str = [foldforward, foldreverse];
|
||||
let foldmultidiags: []str = ["", ""];
|
||||
let foldorderlabels: []str = ["forward", "reverse"];
|
||||
@@ -10793,7 +10823,7 @@ fn runtimepath(relative: str) str = {
|
||||
strings.concat(combinedoutputs[5], ".status"),
|
||||
strings.concat(combinedoutputs[6], ".status")];
|
||||
let combinedav: []str = alloc([],
|
||||
(9 + combinedtargets.len * 9): u64)!;
|
||||
(9 + combinedtargets.len * 10): u64)!;
|
||||
append(combinedav, driver(stages[si]));
|
||||
append(combinedav, "test"); append(combinedav, "-c");
|
||||
append(combinedav, "-w"); append(combinedav, combinedwork);
|
||||
@@ -10809,6 +10839,7 @@ fn runtimepath(relative: str) str = {
|
||||
append(combinedav, "-");
|
||||
append(combinedav, combinedtargets[ui]);
|
||||
append(combinedav, combinedoutputs[ui]);
|
||||
append(combinedav, "-");
|
||||
append(combinedav, combinedstatuses[ui]);
|
||||
ui += 1;
|
||||
};
|
||||
@@ -10882,15 +10913,15 @@ fn runtimepath(relative: str) str = {
|
||||
let orderforward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||
orderwork, "-I", source, "--ww-package-build",
|
||||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||||
orderallowed, orderallowedstatus,
|
||||
orderallowed, "-", orderallowedstatus,
|
||||
"--ww-package-test", "build", "main", "main", "-", "-", outsider,
|
||||
orderforbidden, orderforbiddenstatus, client];
|
||||
orderforbidden, "-", orderforbiddenstatus, client];
|
||||
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||
orderwork, "-I", source, "--ww-package-build",
|
||||
"--ww-package-test", "build", "main", "main", "-", "-", outsider,
|
||||
orderforbidden, orderforbiddenstatus,
|
||||
orderforbidden, "-", orderforbiddenstatus,
|
||||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||||
orderallowed, orderallowedstatus, client];
|
||||
orderallowed, "-", orderallowedstatus, client];
|
||||
let orderrequests: [][]str = [orderforward, orderreverse];
|
||||
let orderdiags: []str = ["", ""];
|
||||
let oi: i32 = 0;
|
||||
@@ -12162,23 +12193,32 @@ fn runtimepath(relative: str) str = {
|
||||
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"));
|
||||
"package family;\nexport fn v() i32 = { return 7; };\n");
|
||||
writefile(strings.concat(pdir, "/same_test.ww"), strings.concat(
|
||||
"package family;\nimport os;\n",
|
||||
"@test fn seven() void = { let av: []str = os.args();",
|
||||
" if (av.len != 0) { os.write(os.STDOUT_FILENO, av[0].ptr,",
|
||||
" av[0].len: u64); os.write(os.STDOUT_FILENO, \"\\n\".ptr, 1u64); };",
|
||||
" assert(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.
|
||||
writefile(strings.concat(adir, "/a.ww"),
|
||||
"package other;\nexport fn v() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(adir, "/a_test.ww"),
|
||||
"package other;\n@test fn one() void = { assert(v() == 1); };\n");
|
||||
let none: str = strings.concat(root, "/znone");
|
||||
assert(os.mkdir(none, 493i32) == 0);
|
||||
writefile(strings.concat(none, "/none.ww"),
|
||||
"package renamed;\nexport fn v() i32 = { return 9; };\n");
|
||||
// '_' roots are excluded from recursive 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 namedbytes: str = "";
|
||||
let defaultbytes: str = "";
|
||||
let fanabytes: str = "";
|
||||
let fanpkgbytes: str = "";
|
||||
let i: i32 = 0;
|
||||
for (i < 2) {
|
||||
let named: str = strings.concat(outdir, "/out_", drvs[i],
|
||||
@@ -12193,31 +12233,379 @@ fn runtimepath(relative: str) str = {
|
||||
case void => void;
|
||||
case let e: os.oserror => abort("-c -o artifact missing");
|
||||
};
|
||||
assert(((fi.mode: u32) & 73u32) != 0u32);
|
||||
assert(!has(out.stdout, "seven ... "));
|
||||
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;
|
||||
};
|
||||
if (i == 0) { namedbytes = readfile(named); }
|
||||
else { assert(same(namedbytes, readfile(named))); };
|
||||
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"));
|
||||
assert(has(out.stdout, "seven ... "));
|
||||
|
||||
let runnamed: str = strings.concat(outdir, "/run_", drvs[i], ".bin");
|
||||
let nocav: []str = [driver(drvs[i]), "test", "-I", root,
|
||||
"-o", named, pdir];
|
||||
"-o", runnamed, 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"));
|
||||
expectexit(&out, 0);
|
||||
assert(out.stderr.len == 0 && os.exists(runnamed));
|
||||
assert(has(out.stdout, "seven ... "));
|
||||
assert(has(out.stdout, "/package.test\n"));
|
||||
assert(!has(out.stdout, runnamed));
|
||||
|
||||
let defaultbin: str = strings.concat(root, "/pkg.test");
|
||||
let defaultav: []str = [driver(drvs[i]), "test", "-c", "-I", root,
|
||||
pdir];
|
||||
runcommanddir(root, strings.concat("default_", drvs[i]), root,
|
||||
defaultav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(os.exists(defaultbin));
|
||||
assert(!os.exists(strings.concat(root, "/family.test")));
|
||||
assert(!os.exists(strings.concat(pdir, "/family.test")));
|
||||
if (i == 0) { defaultbytes = readfile(defaultbin); }
|
||||
else { assert(same(defaultbytes, readfile(defaultbin))); };
|
||||
clean(defaultbin);
|
||||
|
||||
let fanroot: str = strings.concat(root, "/_fan_", drvs[i]);
|
||||
let fanarg: str = strings.concat(fanroot, "/nested/");
|
||||
let fanav: []str = [driver(drvs[i]), "test", "-c", "-j", "2",
|
||||
"-I", root, "-o", fanarg, strings.concat(root, "/...")];
|
||||
runcommand(root, strings.concat("fan_", drvs[i]), fanav,
|
||||
(60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
let fanabin: str = strings.concat(fanarg, "a.test");
|
||||
let fanpkg: str = strings.concat(fanarg, "pkg.test");
|
||||
assert(os.exists(fanabin) && os.exists(fanpkg));
|
||||
assert(!os.exists(strings.concat(fanarg, "znone.test")));
|
||||
assert(has(out.stdout, strings.concat("? ", none, " [no tests]\n")));
|
||||
if (i == 0) {
|
||||
fanabytes = readfile(fanabin);
|
||||
fanpkgbytes = readfile(fanpkg);
|
||||
} else {
|
||||
assert(same(fanabytes, readfile(fanabin)));
|
||||
assert(same(fanpkgbytes, readfile(fanpkg)));
|
||||
};
|
||||
|
||||
let badout: str = strings.concat(outdir, "/multi-file-", drvs[i]);
|
||||
let mulav: []str = [driver(drvs[i]), "test", "-c", "-o",
|
||||
named, strings.concat(root, "/...")];
|
||||
badout, strings.concat(root, "/...")];
|
||||
runcommand(root, strings.concat("multi_", drvs[i]), mulav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 2);
|
||||
expectexit(&out, 1);
|
||||
assert(has(out.stderr,
|
||||
"cannot use -o with multiple packages"));
|
||||
"with multiple packages, -o must refer to a directory or /dev/null"));
|
||||
assert(!os.exists(badout));
|
||||
|
||||
let nullcompile: []str = [driver(drvs[i]), "test", "-c", "-I",
|
||||
root, "-o", "/dev/null", pdir];
|
||||
runcommand(root, strings.concat("null-compile_", drvs[i]), nullcompile,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(!has(out.stdout, "seven ... "));
|
||||
assert(!os.exists("/dev/null.new"));
|
||||
let nullrun: []str = [driver(drvs[i]), "test", "-I", root,
|
||||
"-o", "/dev/null", pdir];
|
||||
runcommand(root, strings.concat("null-run_", drvs[i]), nullrun,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(has(out.stdout, "seven ... "));
|
||||
i += 1;
|
||||
};
|
||||
clean(root);
|
||||
};
|
||||
|
||||
@test fn test_binary_publication_transaction() void = {
|
||||
let root: str = fresh();
|
||||
let suite: str = strings.concat(root, "/suite");
|
||||
let leftsame: str = strings.concat(suite, "/left/same");
|
||||
let rightsame: str = strings.concat(suite, "/right/same");
|
||||
let alpha: str = strings.concat(suite, "/alpha");
|
||||
let beta: str = strings.concat(suite, "/beta");
|
||||
let failed: str = strings.concat(suite, "/failed");
|
||||
let none: str = strings.concat(suite, "/none");
|
||||
let persist: str = strings.concat(suite, "/persist");
|
||||
mkdirall(leftsame); mkdirall(rightsame); mkdirall(alpha);
|
||||
mkdirall(beta); mkdirall(failed); mkdirall(none); mkdirall(persist);
|
||||
writefile(strings.concat(leftsame, "/same.ww"),
|
||||
"package declared_left;\nfn value() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(leftsame, "/same_test.ww"), strings.concat(
|
||||
"package declared_left;\n",
|
||||
"@test fn left() void = { assert(value() == 1); };\n"));
|
||||
writefile(strings.concat(rightsame, "/same.ww"),
|
||||
"package declared_right;\nfn value() i32 = { return 2; };\n");
|
||||
writefile(strings.concat(rightsame, "/same_test.ww"), strings.concat(
|
||||
"package declared_right;\n",
|
||||
"@test fn right() void = { assert(value() == 2); };\n"));
|
||||
writefile(strings.concat(alpha, "/alpha.ww"),
|
||||
"package alpha_decl;\nfn value() i32 = { return 3; };\n");
|
||||
writefile(strings.concat(alpha, "/alpha_test.ww"), strings.concat(
|
||||
"package alpha_decl;\n",
|
||||
"@test fn alpha_ok() void = { assert(value() == 3); };\n"));
|
||||
writefile(strings.concat(beta, "/beta.ww"),
|
||||
"package beta_decl;\nfn value() i32 = { return 4; };\n");
|
||||
writefile(strings.concat(beta, "/beta_test.ww"), strings.concat(
|
||||
"package beta_decl;\n",
|
||||
"@test fn beta_ok() void = { assert(value() == 4); };\n"));
|
||||
writefile(strings.concat(failed, "/failed.ww"),
|
||||
"package failed_decl;\nfn value() i32 = { return 5; };\n");
|
||||
writefile(strings.concat(failed, "/failed_test.ww"), strings.concat(
|
||||
"package failed_decl;\n",
|
||||
"@test fn runtime_failure() void = { assert(false); };\n"));
|
||||
writefile(strings.concat(none, "/none.ww"),
|
||||
"package none_decl;\nfn value() i32 = { return 6; };\n");
|
||||
let persisttest: str = strings.concat(persist, "/persist_test.ww");
|
||||
let persistbase: str = strings.concat(
|
||||
"package persist_decl;\n",
|
||||
"@test fn persistent_base() void = { assert(true); };\n");
|
||||
let persistchanged: str = strings.concat(
|
||||
"package persist_decl;\n",
|
||||
"@test fn persistent_changed() void = { assert(1 + 1 == 2); };\n");
|
||||
writefile(strings.concat(persist, "/persist.ww"),
|
||||
"package persist_decl;\nfn value() i32 = { return 7; };\n");
|
||||
writefile(persisttest, persistbase);
|
||||
|
||||
let compilerwrapper: str = strings.concat(root, "/publish-w6c.sh");
|
||||
let linkerwrapper: str = strings.concat(root, "/publish-w6l.sh");
|
||||
writeexecutable(compilerwrapper, strings.concat(
|
||||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_PUBLISH_CTRACE\"\n",
|
||||
"exec \"$WW_PUBLISH_REAL_C\" \"$@\"\n"));
|
||||
writeexecutable(linkerwrapper, strings.concat(
|
||||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_PUBLISH_LTRACE\"\n",
|
||||
"if test -n \"$WW_PUBLISH_FAIL\"; then\n",
|
||||
" for arg do case \"$arg\" in *\"$WW_PUBLISH_FAIL\"*)\n",
|
||||
" printf 'injected publication linker failure\\n' >&2\n",
|
||||
" exit 97;; esac; done\nfi\n",
|
||||
"exec \"$WW_PUBLISH_REAL_L\" \"$@\"\n"));
|
||||
let stages: []str = ["ww", "ww_ww"];
|
||||
let compilers: []str = ["w6c", "w6c_ww"];
|
||||
let linkers: []str = ["w6l", "w6l_ww"];
|
||||
let tags: []str = ["c", "ww"];
|
||||
let duplicatediag: str = "";
|
||||
let rollbackdiag: str = "";
|
||||
let missingdiag: str = "";
|
||||
let occupieddiag: str = "";
|
||||
let failurebytes: str = "";
|
||||
let persistbasebytes: str = "";
|
||||
let persistchangedbytes: str = "";
|
||||
let baseenv: []str = os.getenvs();
|
||||
let out: commandout;
|
||||
let si: i32 = 0;
|
||||
for (si < stages.len) {
|
||||
let ctrace: str = strings.concat(root, "/publish-", tags[si],
|
||||
"-w6c.trace");
|
||||
let ltrace: str = strings.concat(root, "/publish-", tags[si],
|
||||
"-w6l.trace");
|
||||
writefile(ctrace, ""); writefile(ltrace, "");
|
||||
let env: []str = alloc([], (baseenv.len + 7): u64)!;
|
||||
let ei: i32 = 0;
|
||||
for (ei < baseenv.len) {
|
||||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_PUBLISH_CTRACE=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_PUBLISH_LTRACE=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_PUBLISH_REAL_C=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_PUBLISH_REAL_L=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_PUBLISH_FAIL=")) {
|
||||
append(env, baseenv[ei]);
|
||||
};
|
||||
ei += 1;
|
||||
};
|
||||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||||
append(env, strings.concat("WW_PUBLISH_CTRACE=", ctrace));
|
||||
append(env, strings.concat("WW_PUBLISH_LTRACE=", ltrace));
|
||||
append(env, strings.concat("WW_PUBLISH_REAL_C=",
|
||||
driver(compilers[si])));
|
||||
append(env, strings.concat("WW_PUBLISH_REAL_L=",
|
||||
driver(linkers[si])));
|
||||
append(env, "WW_PUBLISH_FAIL=");
|
||||
|
||||
// Equal import leaves are rejected before tools or output creation,
|
||||
// independent of their unequal declared package names.
|
||||
let duplicateout: str = strings.concat(root, "/duplicate-output/");
|
||||
let duplicateav: []str = [driver(stages[si]), "test", "-c",
|
||||
"-j", "2", "-I", suite, "-o", duplicateout,
|
||||
leftsame, rightsame];
|
||||
runcommandenvdir(root, strings.concat("publish-duplicate-", tags[si]),
|
||||
duplicateav, env, root,
|
||||
(60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(out.stdout.len == 0);
|
||||
assert(has(out.stderr,
|
||||
"ww test: cannot write test binary same.test for multiple packages:\n"));
|
||||
assert(has(out.stderr, strings.concat(leftsame, "\n")));
|
||||
assert(has(out.stderr, strings.concat(rightsame, "\n")));
|
||||
assert(readfile(ctrace).len == 0 && readfile(ltrace).len == 0);
|
||||
assert(!os.exists(duplicateout));
|
||||
if (si == 0) { duplicatediag = strings.dup(out.stderr); }
|
||||
else { assert(same(duplicatediag, out.stderr)); };
|
||||
|
||||
// /dev/null is Go's exact multi-package discard exception: both
|
||||
// products build, neither publishes, and duplicate names are harmless.
|
||||
let nullav: []str = [driver(stages[si]), "test", "-c", "-j", "2",
|
||||
"-I", suite, "-o", "/dev/null", leftsame, rightsame];
|
||||
runcommandenvdir(root, strings.concat("publish-null-", tags[si]),
|
||||
nullav, env, root,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||
assert(readfile(ctrace).len != 0);
|
||||
assert(occurrences(readfile(ltrace), "\n") == 2);
|
||||
assert(!os.exists("/dev/null.new"));
|
||||
|
||||
// An occupied retained-binary stage is a pre-tool rejection. The
|
||||
// caller's old destination and occupied stage both remain untouched.
|
||||
let occupied: str = strings.concat(root, "/occupied");
|
||||
if (si == 0) {
|
||||
assert(os.mkdir(occupied, 448i32) == 0);
|
||||
writefile(strings.concat(occupied, "/alpha.test"), "old-alpha\n");
|
||||
writefile(strings.concat(occupied, "/alpha.test.new"),
|
||||
"occupied-stage\n");
|
||||
};
|
||||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||||
let occupiedav: []str = [driver(stages[si]), "test", "-c", "-I",
|
||||
suite, "-o", strings.concat(occupied, "/"), alpha];
|
||||
runcommandenvdir(root, strings.concat("publish-occupied-", tags[si]),
|
||||
occupiedav, env, root,
|
||||
(60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(has(out.stderr, strings.concat(
|
||||
"ww: product staging path already exists: ", occupied,
|
||||
"/alpha.test.new\n")));
|
||||
assert(same(readfile(strings.concat(occupied, "/alpha.test")),
|
||||
"old-alpha\n"));
|
||||
assert(same(readfile(strings.concat(occupied, "/alpha.test.new")),
|
||||
"occupied-stage\n"));
|
||||
assert(readfile(ctrace).len == 0 && readfile(ltrace).len == 0);
|
||||
if (si == 0) { occupieddiag = strings.dup(out.stderr); }
|
||||
else { assert(same(occupieddiag, out.stderr)); };
|
||||
|
||||
// Link failure after an earlier sibling has staged a retained copy
|
||||
// rejects the whole request and preserves every old destination.
|
||||
let rollback: str = strings.concat(root, "/rollback-", tags[si]);
|
||||
assert(os.mkdir(rollback, 448i32) == 0);
|
||||
writefile(strings.concat(rollback, "/alpha.test"), "alpha-sentinel\n");
|
||||
writefile(strings.concat(rollback, "/beta.test"), "beta-sentinel\n");
|
||||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||||
env[env.len - 1] = "WW_PUBLISH_FAIL=beta-test-main";
|
||||
let rollbackav: []str = [driver(stages[si]), "test", "-c", "-j",
|
||||
"1", "-I", suite, "-o", strings.concat(rollback, "/"),
|
||||
alpha, beta];
|
||||
runcommandenvdir(root, strings.concat("publish-rollback-", tags[si]),
|
||||
rollbackav, env, root,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(has(out.stderr, "injected publication linker failure\n"));
|
||||
assert(has(out.stderr, "ww: w6l failed\n"));
|
||||
assert(same(readfile(strings.concat(rollback, "/alpha.test")),
|
||||
"alpha-sentinel\n"));
|
||||
assert(same(readfile(strings.concat(rollback, "/beta.test")),
|
||||
"beta-sentinel\n"));
|
||||
assert(!directoryhasnew(rollback));
|
||||
assert(occurrences(readfile(ltrace), "\n") == 2);
|
||||
if (si == 0) { rollbackdiag = strings.dup(out.stderr); }
|
||||
else { assert(same(rollbackdiag, out.stderr)); };
|
||||
|
||||
// Output directories minted for a rejected request are rolled back.
|
||||
let missingroot: str = strings.concat(root, "/missing-", tags[si]);
|
||||
let missingout: str = strings.concat(missingroot, "/nested/");
|
||||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||||
env[env.len - 1] = "WW_PUBLISH_FAIL=alpha-test-main";
|
||||
let missingav: []str = [driver(stages[si]), "test", "-c", "-j",
|
||||
"1", "-I", suite, "-o", missingout, alpha];
|
||||
runcommandenvdir(root, strings.concat("publish-missing-", tags[si]),
|
||||
missingav, env, root,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(!os.exists(missingroot));
|
||||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||||
if (si == 0) { missingdiag = strings.dup(out.stderr); }
|
||||
else { assert(same(missingdiag, out.stderr)); };
|
||||
|
||||
// A package with no tests never claims a binary or creates a requested
|
||||
// output hierarchy, although its production package is still checked.
|
||||
let noneroot: str = strings.concat(root, "/none-output-", tags[si]);
|
||||
let noneav: []str = [driver(stages[si]), "test", "-c", "-I", suite,
|
||||
"-o", strings.concat(noneroot, "/nested/"), none];
|
||||
env[env.len - 1] = "WW_PUBLISH_FAIL=";
|
||||
runcommandenvdir(root, strings.concat("publish-none-", tags[si]),
|
||||
noneav, env, root,
|
||||
(60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(has(out.stdout, strings.concat("? ", none,
|
||||
" [no tests]\n")));
|
||||
assert(out.stderr.len == 0 && !os.exists(noneroot));
|
||||
|
||||
// Publication commits before execution. A failing test therefore leaves
|
||||
// a runnable retained binary while its product failure stays on stdout.
|
||||
let failurebin: str = strings.concat(root, "/failure-", tags[si],
|
||||
".test");
|
||||
let failureav: []str = [driver(stages[si]), "test", "-I", suite,
|
||||
"-o", failurebin, failed];
|
||||
runcommandenvdir(root, strings.concat("publish-runtime-", tags[si]),
|
||||
failureav, env, root,
|
||||
(60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(os.exists(failurebin) && !os.exists(strings.concat(failurebin,
|
||||
".new")));
|
||||
assert(has(out.stdout,
|
||||
"failed_decl.runtime_failure ... FAIL (exit 1)\n"));
|
||||
assert(out.stderr.len == 0);
|
||||
let fi: os.filestat;
|
||||
match (os.stat(&fi, failurebin)) {
|
||||
case void => void;
|
||||
case let e: os.oserror => abort("retained failure binary missing");
|
||||
};
|
||||
assert(((fi.mode: u32) & 73u32) != 0u32);
|
||||
if (si == 0) { failurebytes = readfile(failurebin); }
|
||||
else { assert(same(failurebytes, readfile(failurebin))); };
|
||||
|
||||
// -w keeps semantic actions persistent while -o remains a presentation
|
||||
// copy. Unchanged input reuses actions; changed input replaces the copy.
|
||||
rewritefile(persisttest, persistbase);
|
||||
let persistwork: str = strings.concat(root, "/persist-work-", tags[si]);
|
||||
let persistbin: str = strings.concat(root, "/persist.test");
|
||||
if (os.exists(persistbin)) { clean(persistbin); };
|
||||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||||
let persistav: []str = [driver(stages[si]), "test", "-w",
|
||||
persistwork, "-I", suite, "-o", persistbin, persist];
|
||||
runcommandenvdir(root, strings.concat("publish-persist-cold-", tags[si]),
|
||||
persistav, env, root,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(has(out.stdout, "persistent_base ... ok\n"));
|
||||
let basebytes: str = readfile(persistbin);
|
||||
if (si == 0) { persistbasebytes = strings.dup(basebytes); }
|
||||
else { assert(same(persistbasebytes, basebytes)); };
|
||||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||||
runcommandenvdir(root, strings.concat("publish-persist-warm-", tags[si]),
|
||||
persistav, env, root,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(readfile(ctrace).len == 0);
|
||||
assert(same(basebytes, readfile(persistbin)));
|
||||
rewritefile(persisttest, persistchanged);
|
||||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||||
runcommandenvdir(root, strings.concat("publish-persist-change-", tags[si]),
|
||||
persistav, env, root,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(has(out.stdout, "persistent_changed ... ok\n"));
|
||||
assert(readfile(ctrace).len != 0);
|
||||
let changedbytes: str = readfile(persistbin);
|
||||
assert(!same(basebytes, changedbytes));
|
||||
if (si == 0) { persistchangedbytes = strings.dup(changedbytes); }
|
||||
else { assert(same(persistchangedbytes, changedbytes)); };
|
||||
assert(!directoryhasnew(persistwork));
|
||||
si += 1;
|
||||
};
|
||||
assert(os.remove(strings.concat(root,
|
||||
"/occupied/alpha.test.new")) == 0);
|
||||
clean(root);
|
||||
};
|
||||
|
||||
@@ -197,18 +197,21 @@ fn samefile(a: str, b: str, why: str) void = {
|
||||
"@test fn directory_import() void = { assert(foo.value() == 42); };\n"));
|
||||
let testbins: []str = [strings.concat(td, "/test-c"),
|
||||
strings.concat(td, "/test-w")];
|
||||
let testworks: []str = [strings.concat(td, "/test-work-c"),
|
||||
strings.concat(td, "/test-work-w")];
|
||||
i = 0;
|
||||
for (i < 2) {
|
||||
let tav: []str = [testenv.driver(drivers[i * 2]), "test", "-c",
|
||||
"-I", early, "-I", late, "-o", testbins[i], checks];
|
||||
let tav: []str = [testenv.driver(drivers[i * 2]), "test", "-w",
|
||||
testworks[i], "-I", early, "-I", late, "-o", testbins[i],
|
||||
checks];
|
||||
expectcode(td, strings.concat("test_build_", tags[i * 2]), tav, 0);
|
||||
let trav: []str = [testbins[i]];
|
||||
expectcode(td, strings.concat("test_run_", tags[i * 2]), trav, 0);
|
||||
i += 1;
|
||||
};
|
||||
samefile(testbins[0], testbins[1], "directory-test binaries differ");
|
||||
samefile(strings.concat(testbins[0], ".sepwork/example.foo.a"),
|
||||
strings.concat(testbins[1], ".sepwork/example.foo.a"),
|
||||
samefile(strings.concat(testworks[0], "/example.foo.a"),
|
||||
strings.concat(testworks[1], "/example.foo.a"),
|
||||
"directory-test dependency archives differ");
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
@@ -814,7 +814,7 @@ fn rejectrow(td: str, label: str, src: str, expected: str) void = {
|
||||
let av: []str = [launcher, limitstr, testenv.driver(stages[stagei]),
|
||||
"test", "-c", "-w", work, "-I", tree,
|
||||
"--ww-package-test", "test", "target", "target", "target",
|
||||
"target_test", target, output, status, target];
|
||||
"target_test", target, output, "-", status, target];
|
||||
let out: testenv.commandout;
|
||||
runenv(td, strings.concat("allocation-", stages[stagei], "-", tag),
|
||||
av, env, &out);
|
||||
|
||||
@@ -167,13 +167,12 @@ fn runrootargv(dir: str, root: str, name: str, drv: str,
|
||||
// a3 == "" means a two-token argv tail; no row passes a literal "".
|
||||
@test fn flagargs() void = {
|
||||
let a1: []str = ["build", "build", "build", "build", "build",
|
||||
"run", "run", "run", "test", "test", "test", "test", "test",
|
||||
"test"];
|
||||
"run", "run", "run", "test", "test", "test", "test", "test"];
|
||||
let a2: []str = ["-o", "-I", "-L", "-l", "-zz",
|
||||
"-o", "-l", "-zz", "-l", "-zz", "-I", "-o", "-o",
|
||||
"-o", "-l", "-zz", "-l", "-zz", "-I", "-o",
|
||||
"-run"];
|
||||
let a3: []str = ["", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "x",
|
||||
"", "", "", "", "", "", "",
|
||||
""];
|
||||
let subs: []str = [
|
||||
"ww build: -o needs an argument",
|
||||
@@ -188,7 +187,6 @@ fn runrootargv(dir: str, root: str, name: str, drv: str,
|
||||
"ww test: unknown flag",
|
||||
"ww test: -I needs an argument",
|
||||
"ww test: -o needs an argument",
|
||||
"ww test: -o needs -c for a package target",
|
||||
"ww test: -run needs an argument"];
|
||||
let i: i32 = 0;
|
||||
for (i < subs.len) {
|
||||
|
||||
Reference in New Issue
Block a user