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/wait.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
@@ -1300,6 +1301,7 @@ struct sepproduct {
|
|||||||
const char *internal_package;
|
const char *internal_package;
|
||||||
const char *external_package;
|
const char *external_package;
|
||||||
const char *status;
|
const char *status;
|
||||||
|
const char *publish; /* optional retained test executable */
|
||||||
const char *artifact;
|
const char *artifact;
|
||||||
int variant;
|
int variant;
|
||||||
int directory_product;
|
int directory_product;
|
||||||
@@ -1312,6 +1314,7 @@ struct sepproduct {
|
|||||||
int pxtest;
|
int pxtest;
|
||||||
int support; /* direct generated-main support action, or -1 */
|
int support; /* direct generated-main support action, or -1 */
|
||||||
char *stage_out; /* request-private linked/published output */
|
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_iface; /* request-private published package interface */
|
||||||
char *stage_status; /* request-private completion marker */
|
char *stage_status; /* request-private completion marker */
|
||||||
};
|
};
|
||||||
@@ -4932,6 +4935,34 @@ copy_file_stage(const char *src, const char *dst)
|
|||||||
return bad ? -1 : 0;
|
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 {
|
struct septxnentry {
|
||||||
char *stage;
|
char *stage;
|
||||||
char *dst;
|
char *dst;
|
||||||
@@ -5117,9 +5148,11 @@ sep_free_product_staging(struct sepproduct *products, int nproducts)
|
|||||||
for (int i = 0; i < nproducts; i++) {
|
for (int i = 0; i < nproducts; i++) {
|
||||||
free(products[i].stage_status);
|
free(products[i].stage_status);
|
||||||
free(products[i].stage_iface);
|
free(products[i].stage_iface);
|
||||||
|
free(products[i].stage_publish);
|
||||||
free(products[i].stage_out);
|
free(products[i].stage_out);
|
||||||
products[i].stage_status = NULL;
|
products[i].stage_status = NULL;
|
||||||
products[i].stage_iface = NULL;
|
products[i].stage_iface = NULL;
|
||||||
|
products[i].stage_publish = NULL;
|
||||||
products[i].stage_out = NULL;
|
products[i].stage_out = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5158,11 +5191,13 @@ sep_validate_product_path_pair(const struct sepproduct *a,
|
|||||||
const char *ap[] = {
|
const char *ap[] = {
|
||||||
a->stage_status != NULL ? a->status : NULL,
|
a->stage_status != NULL ? a->status : NULL,
|
||||||
a->stage_out != NULL ? a->out : 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[] = {
|
const char *bp[] = {
|
||||||
b->stage_status != NULL ? b->status : NULL,
|
b->stage_status != NULL ? b->status : NULL,
|
||||||
b->stage_out != NULL ? b->out : 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++) {
|
for (size_t i = 0; i < nelem(ap); i++) {
|
||||||
if (ap[i] == NULL) continue;
|
if (ap[i] == NULL) continue;
|
||||||
for (size_t j = 0; j < nelem(bp); j++) {
|
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,
|
&& sep_prepare_product_stage(&products[i].stage_status,
|
||||||
products[i].status) < 0)
|
products[i].status) < 0)
|
||||||
return -1;
|
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;
|
if (emit_asm) continue;
|
||||||
int owns_output = root_package ? publish_package
|
int owns_output = root_package ? publish_package
|
||||||
: is_test ? !products[i].no_tests
|
: 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++) {
|
for (int i = 0; i < nproducts; i++) {
|
||||||
const char *path[] = { products[i].stage_out,
|
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++)
|
for (size_t j = 0; j < nelem(path); j++)
|
||||||
if (path[j] != NULL
|
if (path[j] != NULL
|
||||||
&& unlink(path[j]) != 0 && errno != ENOENT)
|
&& 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].ptest = -1;
|
||||||
products[i].pxtest = -1;
|
products[i].pxtest = -1;
|
||||||
products[i].stage_out = NULL;
|
products[i].stage_out = NULL;
|
||||||
|
products[i].stage_publish = NULL;
|
||||||
products[i].stage_iface = NULL;
|
products[i].stage_iface = NULL;
|
||||||
products[i].stage_status = 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])
|
if (!g->pkg[root].failed && sep_root_is_command(&g->pkg[root])
|
||||||
&& validate_command_output_path(products[i].out) < 0)
|
&& validate_command_output_path(products[i].out) < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
if (products[i].publish != NULL
|
||||||
|
&& validate_command_output_path(products[i].publish) < 0)
|
||||||
|
return 1;
|
||||||
if (products[i].status != NULL
|
if (products[i].status != NULL
|
||||||
&& validate_command_output_path(products[i].status) < 0)
|
&& validate_command_output_path(products[i].status) < 0)
|
||||||
return 1;
|
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_output = {0};
|
||||||
struct sep_created_dirs created_work = {0};
|
struct sep_created_dirs created_work = {0};
|
||||||
if (create_output_dir != NULL && create_output_dir[0] != '\0'
|
if (create_output_dir != NULL && create_output_dir[0] != '\0'
|
||||||
&& sep_mkdirs(create_output_dir, 0700, &created_output) != 0) {
|
&& sep_mkdirs(create_output_dir, is_test ? 0777 : 0700,
|
||||||
fprintf(stderr, "ww: cannot create build output directory %s\n",
|
&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);
|
create_output_dir);
|
||||||
free(order);
|
free(order);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -6527,6 +6574,14 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
|||||||
g->pkg[root].failed = 1;
|
g->pkg[root].failed = 1;
|
||||||
goto request_fail;
|
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) {
|
if (sep_stage_product_status(&products[i]) != 0) {
|
||||||
fprintf(stderr, "ww: cannot stage package-test product\n");
|
fprintf(stderr, "ww: cannot stage package-test product\n");
|
||||||
goto request_fail;
|
goto request_fail;
|
||||||
@@ -6594,6 +6649,10 @@ prepare_transaction:
|
|||||||
&& sep_txn_add(&tx, products[i].stage_out,
|
&& sep_txn_add(&tx, products[i].stage_out,
|
||||||
products[i].out) < 0)
|
products[i].out) < 0)
|
||||||
goto request_fail;
|
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) {
|
if (products[i].stage_iface != NULL) {
|
||||||
char outiface[SEP_ARTIFACT_MAX];
|
char outiface[SEP_ARTIFACT_MAX];
|
||||||
int on = snprintf(outiface, sizeof outiface, "%s.wwi",
|
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,
|
.identity = root_identity,
|
||||||
.test_package = test_package,
|
.test_package = test_package,
|
||||||
.status = NULL,
|
.status = NULL,
|
||||||
|
.publish = NULL,
|
||||||
.artifact = NULL,
|
.artifact = NULL,
|
||||||
.variant = root_variant,
|
.variant = root_variant,
|
||||||
.root = -1,
|
.root = -1,
|
||||||
@@ -7203,9 +7263,9 @@ do_test(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
package_create_output_dir = argv[++i];
|
package_create_output_dir = argv[++i];
|
||||||
} else if (strcmp(argv[i], "--ww-package-test") == 0) {
|
} else if (strcmp(argv[i], "--ww-package-test") == 0) {
|
||||||
if (i + 8 >= argc) {
|
if (i + 9 >= argc) {
|
||||||
fprintf(stderr,
|
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;
|
return 2;
|
||||||
}
|
}
|
||||||
const char *kind = argv[++i];
|
const char *kind = argv[++i];
|
||||||
@@ -7215,6 +7275,7 @@ do_test(int argc, char **argv)
|
|||||||
const char *external = argv[++i];
|
const char *external = argv[++i];
|
||||||
const char *dir = argv[++i];
|
const char *dir = argv[++i];
|
||||||
const char *output = argv[++i];
|
const char *output = argv[++i];
|
||||||
|
const char *publish = argv[++i];
|
||||||
const char *status = argv[++i];
|
const char *status = argv[++i];
|
||||||
size_t pn = strlen(name);
|
size_t pn = strlen(name);
|
||||||
int build_product = strcmp(kind, "build") == 0;
|
int build_product = strcmp(kind, "build") == 0;
|
||||||
@@ -7225,6 +7286,7 @@ do_test(int argc, char **argv)
|
|||||||
if ((!build_product && !test_product)
|
if ((!build_product && !test_product)
|
||||||
|| pn == 0
|
|| pn == 0
|
||||||
|| dir[0] == '\0' || output[0] == '\0'
|
|| dir[0] == '\0' || output[0] == '\0'
|
||||||
|
|| publish[0] == '\0'
|
||||||
|| status[0] == '\0'
|
|| status[0] == '\0'
|
||||||
|| (has_production && strcmp(production, name) != 0)
|
|| (has_production && strcmp(production, name) != 0)
|
||||||
|| (has_internal && strcmp(internal, name) != 0)
|
|| (has_internal && strcmp(internal, name) != 0)
|
||||||
@@ -7232,9 +7294,12 @@ do_test(int argc, char **argv)
|
|||||||
|| strncmp(external, name, pn) != 0
|
|| strncmp(external, name, pn) != 0
|
||||||
|| strcmp(external + pn, "_test") != 0))
|
|| strcmp(external + pn, "_test") != 0))
|
||||||
|| (build_product && (!has_production
|
|| (build_product && (!has_production
|
||||||
|| has_internal || has_external))
|
|| has_internal || has_external
|
||||||
|
|| strcmp(publish, "-") != 0))
|
||||||
|| (test_product && !has_production
|
|| (test_product && !has_production
|
||||||
&& !has_internal && !has_external)) {
|
&& !has_internal && !has_external)
|
||||||
|
|| (test_product && !has_internal && !has_external
|
||||||
|
&& strcmp(publish, "-") != 0)) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"ww test: invalid --ww-package-test product\n");
|
"ww test: invalid --ww-package-test product\n");
|
||||||
return 2;
|
return 2;
|
||||||
@@ -7256,6 +7321,8 @@ do_test(int argc, char **argv)
|
|||||||
products[nproducts].external_package = has_external
|
products[nproducts].external_package = has_external
|
||||||
? external : NULL;
|
? external : NULL;
|
||||||
products[nproducts].status = status;
|
products[nproducts].status = status;
|
||||||
|
products[nproducts].publish = strcmp(publish, "-") == 0
|
||||||
|
? NULL : publish;
|
||||||
products[nproducts].artifact = NULL;
|
products[nproducts].artifact = NULL;
|
||||||
products[nproducts].variant = build_product
|
products[nproducts].variant = build_product
|
||||||
? SEP_VARIANT_PRODUCTION : SEP_VARIANT_TEST_MAIN;
|
? 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");
|
fprintf(stderr, "ww test: invalid --ww-package-publish\n");
|
||||||
return 2;
|
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");
|
fprintf(stderr, "ww test: invalid private directory creation\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
@@ -7460,13 +7527,8 @@ do_test(int argc, char **argv)
|
|||||||
"ww test: -S needs a single test file\n");
|
"ww test: -S needs a single test file\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
/* -c -o forwards: the coordinator names the single
|
/* The coordinator independently wires -o retention and -c run
|
||||||
* package's artifact and rejects a multi-package fan-out. */
|
* suppression after it has loaded the complete package set. */
|
||||||
if (outstem[0] && !compileonly) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ww test: -o needs -c for a package target\n");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
/* -w forwards one caller-owned semantic-action store shared by
|
/* -w forwards one caller-owned semantic-action store shared by
|
||||||
* the complete selected package universe. */
|
* the complete selected package universe. */
|
||||||
return exec_package_command(argc, argv, src, NULL, NULL, 0, 0);
|
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");
|
"ww test: -S needs a single test file\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (outstem[0] && !compileonly) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ww test: -o needs -c for a package target\n");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if (nproducts != 0) {
|
if (nproducts != 0) {
|
||||||
if (!compileonly) {
|
if (!compileonly) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
@@ -7656,10 +7713,6 @@ do_test(int argc, char **argv)
|
|||||||
fprintf(stderr, "ww test: -S needs a single test file\n");
|
fprintf(stderr, "ww test: -S needs a single test file\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (outstem[0] && !compileonly) {
|
|
||||||
fprintf(stderr, "ww test: -o needs -c for a package target\n");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if (nproducts != 0) {
|
if (nproducts != 0) {
|
||||||
if (!compileonly) {
|
if (!compileonly) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
|||||||
@@ -5474,11 +5474,13 @@ as same/external rather than blindly stripping every suffix. The private driver
|
|||||||
descriptor is an ordered directory record:
|
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
|
Missing action selectors and absent publication are `-`. `OUTPUT` is the
|
||||||
one status. Canonically duplicate products and pairwise output/status/staging
|
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
|
collisions reject before producer execution. Declared names and output stems do
|
||||||
not identify products or actions.
|
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
|
binaries to remain byte-identical, and every temporary or staged path to be
|
||||||
cleaned.
|
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
|
## 12. Candidate architectures and hard-gate decision
|
||||||
|
|
||||||
Five candidates were developed as coherent systems, not as feature bins.
|
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
|
with no selected test file validates ordinary production but creates no test
|
||||||
support, generated main, link, binary, result, or process.
|
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
|
When `ww test` executes a directory-owned product, the child process working
|
||||||
directory is that product's canonical absolute physical package source
|
directory is that product's canonical absolute physical package source
|
||||||
directory. Its per-run environment has exactly one effective uppercase `PWD`,
|
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
|
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
|
every `-j` level, and `-j 1` — the default — matches the former sequential
|
||||||
run loop exactly. Measured on the 31-package `lib/...` walk:
|
run loop exactly. Measured on the 31-package `lib/...` walk:
|
||||||
7.0s sequential, 2.4s at `-j 4`. With `-c`, it publishes each exact
|
7.0s sequential, 2.4s at `-j 4`.
|
||||||
`<package>.test` directory binary; the first output owns the one
|
|
||||||
shared cold sepwork containing the command-global action universe. Those become
|
Directory test binaries are always linked under the coordinator's temporary
|
||||||
caller-owned artifacts. `-c -o <name>`
|
product root. `-c` independently requests a caller-visible executable copy and
|
||||||
names that artifact instead of the fixed stem for exactly one directory,
|
suppresses execution. `-o` independently requests a copy and still runs the
|
||||||
including a combined internal/external directory:
|
temporary binary unless `-c` is also present. Without `-o`, `-c` publishes
|
||||||
the coordinator rejects a multi-package fan-out ("cannot use -o with
|
`<import-leaf>.test` in the invocation directory; an output ending in `/` or
|
||||||
multiple packages", Go's `go test -o` rule), and `-o` without `-c` is
|
naming an existing directory receives one such name per selected package and
|
||||||
rejected at the driver ("needs -c for a package target") because a plain
|
missing parents are created. A single non-directory output is legal for one
|
||||||
run leaves no caller-owned artifact for `-o` to name. Without `-c`, it removes the
|
package only. Multi-package non-directory output and duplicate visible binary
|
||||||
temporary binary and scratch with its workspace. The language runtime
|
names reject before tools; exact `/dev/null` discards every copy and permits
|
||||||
owns individual `@test` functions.
|
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
|
Every actually executed directory product gives its single generated binary
|
||||||
the product's canonical absolute physical source directory as child cwd. A
|
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
|
combined package-test graph, under independently discovered ceilings supplied
|
||||||
by the repository-built `sep-limitexec` helper.
|
by the repository-built `sep-limitexec` helper.
|
||||||
|
|
||||||
`ww build`, an explicit single-file `ww test -o <stem>`, and each successful
|
`ww build` and an explicit single-file `ww test -o <stem>` publish
|
||||||
directory-package `ww test -c` build publish `<stem>.sepwork` as a caller-owned
|
`<stem>.sepwork` as a caller-owned artifact directory. The driver acquires it
|
||||||
artifact directory. The driver acquires it with one fresh `mkdir` and refuses
|
with one fresh `mkdir` and refuses an existing path; it never clears a
|
||||||
an existing path; it never clears a collision. A caller keeps only the exact
|
collision. A caller keeps only the exact artifacts it observes and removes
|
||||||
artifacts it observes and removes that exact tree on every later success or
|
that exact tree on every later success or failure. Directory-package test
|
||||||
failure. `ww run` and no-output single-file `ww test` use driver-owned scratch
|
plans instead keep their cold semantic-action scratch and runnable binary
|
||||||
instead; both driver stages place that scratch and their temporary executable
|
inside the coordinator's temporary root; only the optional retained executable
|
||||||
beneath one freshly acquired directory, remove both after every build result,
|
escapes through the transaction above. `ww run` and no-output single-file
|
||||||
and make cleanup failure fail the command. Make recipes build driver-produced
|
`ww test` use driver-owned scratch instead; both driver stages place that
|
||||||
tools in invocation-owned directories and apply the same exact cleanup rule.
|
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
|
`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
|
caller-owned persistent package-artifact workdir: for these direct routes the
|
||||||
@@ -561,8 +574,8 @@ timeout policy in this architecture.
|
|||||||
|
|
||||||
## Open driver work
|
## Open driver work
|
||||||
|
|
||||||
None; the package-level `-o` contract (the last carried bullet) landed as
|
None; directory-package `-c` and `-o` now have the applicable Go 1.26.5
|
||||||
`-c -o <name>` for exactly one package.
|
retention, naming, fan-out, execution, and publication behavior.
|
||||||
|
|
||||||
## Validation policy
|
## Validation policy
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ type pkgfolder = struct {
|
|||||||
type pkggroup = struct {
|
type pkggroup = struct {
|
||||||
dir: str,
|
dir: str,
|
||||||
pkg: str,
|
pkg: str,
|
||||||
|
testname: str,
|
||||||
|
publish: str,
|
||||||
prodpkg: str,
|
prodpkg: str,
|
||||||
samepkg: str,
|
samepkg: str,
|
||||||
externalpkg: str,
|
externalpkg: str,
|
||||||
@@ -467,7 +469,7 @@ fn pkgusage() void = {
|
|||||||
pkgput(os.STDERR_FILENO,
|
pkgput(os.STDERR_FILENO,
|
||||||
" *_test.ww is the sole test-source form; @test elsewhere is rejected\n");
|
" *_test.ww is the sole test-source form; @test elsewhere is rejected\n");
|
||||||
pkgput(os.STDERR_FILENO,
|
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,
|
pkgput(os.STDERR_FILENO,
|
||||||
" -w DIR is one persistent semantic-action store shared by the selected packages\n");
|
" -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;
|
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 = {
|
fn pkgmodeis(m: os.mode, want: os.mode) bool = {
|
||||||
return (((m: u32) & 61440u32) == (want: u32));
|
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,
|
fn pkgsetplanpaths(p: *pkgplan, groups: []pkggroup, root: str, index: i32,
|
||||||
compileonly: bool, buildonly: bool, outname: str, outputdir: bool,
|
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);
|
let num: str = strconv.i32tos(index, strconv.base.DEC);
|
||||||
if (!pkgstring(&p.root, root, "/plan-", num)) { return false; };
|
if (!pkgstring(&p.root, root, "/plan-", num)) { return false; };
|
||||||
if (!pkgmakedir(p.root)) {
|
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.
|
// spelling or traversal prefix must never select another cache container.
|
||||||
// The private driver creates it only after graph preflight succeeds.
|
// The private driver creates it only after graph preflight succeeds.
|
||||||
p.workdir = workroot;
|
p.workdir = workroot;
|
||||||
p.outputdir = "";
|
p.outputdir = createdir;
|
||||||
if (outputdir) { p.outputdir = outname; };
|
|
||||||
if (!pkgstring(&p.buildout, p.root, "/build.stdout")
|
if (!pkgstring(&p.buildout, p.root, "/build.stdout")
|
||||||
|| !pkgstring(&p.builderr, p.root, "/build.stderr")) { return false; };
|
|| !pkgstring(&p.builderr, p.root, "/build.stderr")) { return false; };
|
||||||
let i: i32 = p.start;
|
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; };
|
if (!pkgjoinpath(outname, pkgbase(g.dir), &g.bin)) { return false; };
|
||||||
} else if (outname.len != 0 && !outputdir) { g.bin = outname; }
|
} else if (outname.len != 0 && !outputdir) { g.bin = outname; }
|
||||||
else if (!pkgstring(&g.bin, g.root, "/package.build")) { return false; };
|
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 {
|
} else {
|
||||||
if (!pkgstring(&g.bin, g.root, "/package.test")) { return false; };
|
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 = {
|
libdirs: []str, libs: []str, h: *exec.process) bool = {
|
||||||
let nproducts: i32 = p.end - p.start;
|
let nproducts: i32 = p.end - p.start;
|
||||||
let capacity: i32 = 16;
|
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");
|
pkgputln(os.STDERR_FILENO, "wwtest package: package graph is too large");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
capacity += nproducts * 9;
|
capacity += nproducts * 10;
|
||||||
if (includes.len > (PKG_COUNT_MAX - capacity) / 2) {
|
if (includes.len > (PKG_COUNT_MAX - capacity) / 2) {
|
||||||
pkgputln(os.STDERR_FILENO, "wwtest package: package graph is too large");
|
pkgputln(os.STDERR_FILENO, "wwtest package: package graph is too large");
|
||||||
return false;
|
return false;
|
||||||
@@ -1459,6 +1476,8 @@ fn pkgstartbuild(p: *pkgplan, groups: []pkggroup, builder: str, includes: []str,
|
|||||||
else { append(ba, "-"); };
|
else { append(ba, "-"); };
|
||||||
append(ba, g.dir);
|
append(ba, g.dir);
|
||||||
append(ba, g.bin);
|
append(ba, g.bin);
|
||||||
|
if (g.publish.len != 0) { append(ba, g.publish); }
|
||||||
|
else { append(ba, "-"); };
|
||||||
append(ba, g.buildok);
|
append(ba, g.buildok);
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
@@ -1572,10 +1591,9 @@ fn pkgemitgroup(g: *pkggroup, compileonly: bool) bool = {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (compileonly) {
|
if (compileonly) {
|
||||||
pkgput(os.STDOUT_FILENO, "built ");
|
// Go's compile-only print action is a nop after the retained binary
|
||||||
pkglabel(g);
|
// install completes. Build and publication failures still diagnose on
|
||||||
pkgput(os.STDOUT_FILENO, " -> ");
|
// stderr through the plan result above.
|
||||||
pkgputln(os.STDOUT_FILENO, g.bin);
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (g.runstartfailed) { return false; };
|
if (g.runstartfailed) { return false; };
|
||||||
@@ -1835,14 +1853,9 @@ export fn packagecommand(args: []str) int = {
|
|||||||
pkgusage();
|
pkgusage();
|
||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
// A non-compile run leaves no caller-owned artifact for -o to name.
|
// -c suppresses execution and requests a caller-visible binary. Keep it
|
||||||
if (outname.len != 0 && !compileonly) {
|
// separate from -w until compile-only persistent-action ownership is wired;
|
||||||
pkgputln(os.STDERR_FILENO, "wwtest package: -o needs -c");
|
// -o without -c already permits a retained copy beside a persistent store.
|
||||||
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.
|
|
||||||
if (workroot.len != 0 && compileonly && !buildonly) {
|
if (workroot.len != 0 && compileonly && !buildonly) {
|
||||||
pkgputln(os.STDERR_FILENO,
|
pkgputln(os.STDERR_FILENO,
|
||||||
"wwtest package: -w conflicts with -c");
|
"wwtest package: -w conflicts with -c");
|
||||||
@@ -2034,6 +2047,8 @@ export fn packagecommand(args: []str) int = {
|
|||||||
let g: pkggroup;
|
let g: pkggroup;
|
||||||
g.dir = f.path;
|
g.dir = f.path;
|
||||||
g.pkg = f.prodpkg;
|
g.pkg = f.prodpkg;
|
||||||
|
g.testname = "";
|
||||||
|
g.publish = "";
|
||||||
g.prodpkg = f.prodpkg;
|
g.prodpkg = f.prodpkg;
|
||||||
g.samepkg = "";
|
g.samepkg = "";
|
||||||
g.externalpkg = "";
|
g.externalpkg = "";
|
||||||
@@ -2157,6 +2172,8 @@ export fn packagecommand(args: []str) int = {
|
|||||||
g.dir = f.path;
|
g.dir = f.path;
|
||||||
g.pkg = family;
|
g.pkg = family;
|
||||||
if (g.pkg.len == 0) { g.pkg = srcs[f.start].pkg; };
|
if (g.pkg.len == 0) { g.pkg = srcs[f.start].pkg; };
|
||||||
|
g.testname = "";
|
||||||
|
g.publish = "";
|
||||||
g.prodpkg = f.prodpkg;
|
g.prodpkg = f.prodpkg;
|
||||||
g.samepkg = samepkg;
|
g.samepkg = samepkg;
|
||||||
g.externalpkg = externalpkg;
|
g.externalpkg = externalpkg;
|
||||||
@@ -2240,9 +2257,89 @@ export fn packagecommand(args: []str) int = {
|
|||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// A non-directory caller-owned name cannot fan out. A directory output
|
// Go's test binary is always linked into request-private storage. -c and
|
||||||
// publishes each selected command under its canonical directory basename.
|
// -o independently request a caller-visible executable copy; only -c
|
||||||
if (outname.len != 0 && !outputdir && groups.len > 1) {
|
// 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,
|
pkgputln(os.STDERR_FILENO,
|
||||||
"wwtest package: cannot use -o with multiple packages");
|
"wwtest package: cannot use -o with multiple packages");
|
||||||
return 2;
|
return 2;
|
||||||
@@ -2277,13 +2374,27 @@ export fn packagecommand(args: []str) int = {
|
|||||||
&& strings.compare(groups[0].pkg, "main") != 0;
|
&& strings.compare(groups[0].pkg, "main") != 0;
|
||||||
plan.emitasm = emitasm;
|
plan.emitasm = emitasm;
|
||||||
append(plans, plan);
|
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 tmproot: str = temp.dir();
|
||||||
let failed: i32 = 0;
|
let failed: i32 = 0;
|
||||||
i = 0;
|
i = 0;
|
||||||
for (i < plans.len) {
|
for (i < plans.len) {
|
||||||
if (!pkgsetplanpaths(&plans[i], groups, tmproot, i,
|
if (!pkgsetplanpaths(&plans[i], groups, tmproot, i,
|
||||||
compileonly, buildonly, outname, outputdir, workroot)) {
|
compileonly, buildonly, outname, outputdir, createdir,
|
||||||
|
workroot)) {
|
||||||
if (!pkgremoveall(tmproot)) {
|
if (!pkgremoveall(tmproot)) {
|
||||||
pkgput(os.STDERR_FILENO,
|
pkgput(os.STDERR_FILENO,
|
||||||
"wwtest package: cleanup failed; retained ");
|
"wwtest package: cleanup failed; retained ");
|
||||||
|
|||||||
@@ -1508,6 +1508,7 @@ type sepproduct = struct {
|
|||||||
internalpackage: *u8,
|
internalpackage: *u8,
|
||||||
externalpackage: *u8,
|
externalpackage: *u8,
|
||||||
status: *u8,
|
status: *u8,
|
||||||
|
publish: *u8,
|
||||||
artifact: *u8,
|
artifact: *u8,
|
||||||
variant: i32,
|
variant: i32,
|
||||||
directoryproduct: bool,
|
directoryproduct: bool,
|
||||||
@@ -1520,6 +1521,7 @@ type sepproduct = struct {
|
|||||||
pxtest: i32,
|
pxtest: i32,
|
||||||
support: i32,
|
support: i32,
|
||||||
stageout: *u8,
|
stageout: *u8,
|
||||||
|
stagepublish: *u8,
|
||||||
stageiface: *u8,
|
stageiface: *u8,
|
||||||
stagestatus: *u8,
|
stagestatus: *u8,
|
||||||
};
|
};
|
||||||
@@ -5914,6 +5916,32 @@ fn copyfilestage(src: *u8, dst: *u8) i32 = {
|
|||||||
return 0;
|
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 = {
|
fn sepproductstagepath(dst: *u8) *u8 = {
|
||||||
let path: *u8 = sepappendlit(dst, ".new");
|
let path: *u8 = sepappendlit(dst, ".new");
|
||||||
if (path != nil && cstrlen(path) + 1u64 > os.PATH_MAX: u64) {
|
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 = {
|
fn sepvalidateproductpathpair(a: *sepproduct, b: *sepproduct) i32 = {
|
||||||
let ap: []*u8 = [nil, nil, a.stagestatus, a.stageout, a.stageiface];
|
let ap: []*u8 = [nil, nil, nil, a.stagestatus, a.stageout,
|
||||||
let bp: []*u8 = [nil, nil, b.stagestatus, b.stageout, b.stageiface];
|
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.stagestatus != nil) { ap[0] = a.status; };
|
||||||
if (a.stageout != nil) { ap[1] = a.out; };
|
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.stagestatus != nil) { bp[0] = b.status; };
|
||||||
if (b.stageout != nil) { bp[1] = b.out; };
|
if (b.stageout != nil) { bp[1] = b.out; };
|
||||||
|
if (b.stagepublish != nil) { bp[2] = b.publish; };
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < ap.len) {
|
for (i < ap.len) {
|
||||||
if (ap[i] != nil) {
|
if (ap[i] != nil) {
|
||||||
@@ -6037,6 +6069,11 @@ fn sepvalidaterequeststaging(g: *sepgraph, scratch: *u8, warm: bool,
|
|||||||
products[i].stagestatus, products[i].status);
|
products[i].stagestatus, products[i].status);
|
||||||
if (products[i].stagestatus == nil) { return -1; };
|
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) {
|
if (emitasm == 0) {
|
||||||
let ownsoutput: bool = false;
|
let ownsoutput: bool = false;
|
||||||
if (rootpackage) { ownsoutput = publishpackage != 0; }
|
if (rootpackage) { ownsoutput = publishpackage != 0; }
|
||||||
@@ -6413,7 +6450,8 @@ fn sepdiscardrequeststaging(g: *sepgraph, scratch: *u8, warm: bool,
|
|||||||
let producti: i32 = 0;
|
let producti: i32 = 0;
|
||||||
for (producti < nproducts) {
|
for (producti < nproducts) {
|
||||||
let paths: []*u8 = [products[producti].stageout,
|
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;
|
let si: i32 = 0;
|
||||||
for (si < paths.len) {
|
for (si < paths.len) {
|
||||||
if (paths[si] != nil) {
|
if (paths[si] != nil) {
|
||||||
@@ -6564,8 +6602,8 @@ fn sepfinishfail(entries: *[]septxnentry, n: i32) i32 = {
|
|||||||
fn sepfreeproductstaging(products: *sepproduct, nproducts: i32) void = {
|
fn sepfreeproductstaging(products: *sepproduct, nproducts: i32) void = {
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < nproducts) {
|
for (i < nproducts) {
|
||||||
let paths: []*u8 = [products[i].stageout, products[i].stageiface,
|
let paths: []*u8 = [products[i].stageout, products[i].stagepublish,
|
||||||
products[i].stagestatus];
|
products[i].stageiface, products[i].stagestatus];
|
||||||
let k: i32 = 0;
|
let k: i32 = 0;
|
||||||
for (k < paths.len) {
|
for (k < paths.len) {
|
||||||
if (paths[k] != nil) {
|
if (paths[k] != nil) {
|
||||||
@@ -6574,6 +6612,7 @@ fn sepfreeproductstaging(products: *sepproduct, nproducts: i32) void = {
|
|||||||
k += 1;
|
k += 1;
|
||||||
};
|
};
|
||||||
products[i].stageout = nil;
|
products[i].stageout = nil;
|
||||||
|
products[i].stagepublish = nil;
|
||||||
products[i].stageiface = nil;
|
products[i].stageiface = nil;
|
||||||
products[i].stagestatus = nil;
|
products[i].stagestatus = nil;
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -6779,6 +6818,14 @@ fn sepfinishrequest(selfdir: *u8, l6: *u8, c6: *u8, a6: *u8,
|
|||||||
g.pkg[root].failed = true;
|
g.pkg[root].failed = true;
|
||||||
return sepfinishfail(&entries, ntxn);
|
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) {
|
if (sepstageproductstatus(&products[producti]) != 0) {
|
||||||
cerr("ww: cannot stage package-test product\n");
|
cerr("ww: cannot stage package-test product\n");
|
||||||
return sepfinishfail(&entries, ntxn);
|
return sepfinishfail(&entries, ntxn);
|
||||||
@@ -6860,6 +6907,11 @@ fn sepfinishrequest(selfdir: *u8, l6: *u8, c6: *u8, a6: *u8,
|
|||||||
products[producti].out)) {
|
products[producti].out)) {
|
||||||
return sepfinishfail(&entries, ntxn);
|
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) {
|
if (products[producti].stageiface != nil) {
|
||||||
let outiface: *u8 = sepappendlit(products[producti].out, ".wwi");
|
let outiface: *u8 = sepappendlit(products[producti].out, ".wwi");
|
||||||
if (outiface == nil
|
if (outiface == nil
|
||||||
@@ -7091,6 +7143,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
products[producti].ptest = -1;
|
products[producti].ptest = -1;
|
||||||
products[producti].pxtest = -1;
|
products[producti].pxtest = -1;
|
||||||
products[producti].stageout = nil;
|
products[producti].stageout = nil;
|
||||||
|
products[producti].stagepublish = nil;
|
||||||
products[producti].stageiface = nil;
|
products[producti].stageiface = nil;
|
||||||
products[producti].stagestatus = nil;
|
products[producti].stagestatus = nil;
|
||||||
producti += 1;
|
producti += 1;
|
||||||
@@ -7443,6 +7496,10 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
&& validatecommandoutputpath(products[producti].out) < 0) {
|
&& validatecommandoutputpath(products[producti].out) < 0) {
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
|
if (products[producti].publish != nil
|
||||||
|
&& validatecommandoutputpath(products[producti].publish) < 0) {
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
if (products[producti].status != nil
|
if (products[producti].status != nil
|
||||||
&& validatecommandoutputpath(products[producti].status) < 0) {
|
&& validatecommandoutputpath(products[producti].status) < 0) {
|
||||||
return 1;
|
return 1;
|
||||||
@@ -7563,10 +7620,17 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
let createdwork: sepcreateddirs;
|
let createdwork: sepcreateddirs;
|
||||||
createdoutput.n = 0;
|
createdoutput.n = 0;
|
||||||
createdwork.n = 0;
|
createdwork.n = 0;
|
||||||
|
let outputmode: i32 = 448;
|
||||||
|
if (istest != 0) { outputmode = 511; };
|
||||||
if (createoutputdir != nil
|
if (createoutputdir != nil
|
||||||
&& sepmkdirsrecord(createoutputdir, 448, &createdoutput) != 0) {
|
&& sepmkdirsrecord(createoutputdir, outputmode, &createdoutput) != 0) {
|
||||||
cerrpath("ww: cannot create build output directory ",
|
if (istest != 0) {
|
||||||
createoutputdir, "\n");
|
cerrpath("ww: cannot create test output directory ",
|
||||||
|
createoutputdir, "\n");
|
||||||
|
} else {
|
||||||
|
cerrpath("ww: cannot create build output directory ",
|
||||||
|
createoutputdir, "\n");
|
||||||
|
};
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
if (warm && !workdirexists) {
|
if (warm && !workdirexists) {
|
||||||
@@ -8095,6 +8159,7 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
product.internalpackage = nil;
|
product.internalpackage = nil;
|
||||||
product.externalpackage = nil;
|
product.externalpackage = nil;
|
||||||
product.status = nil;
|
product.status = nil;
|
||||||
|
product.publish = nil;
|
||||||
product.artifact = nil;
|
product.artifact = nil;
|
||||||
if (entryisdir == 0) {
|
if (entryisdir == 0) {
|
||||||
product.artifact = "__root\0".ptr;
|
product.artifact = "__root\0".ptr;
|
||||||
@@ -8910,8 +8975,8 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if (cstreqlit(p, "--ww-package-test")) {
|
if (cstreqlit(p, "--ww-package-test")) {
|
||||||
if (i + 8 >= argc) {
|
if (i + 9 >= argc) {
|
||||||
cerr("ww test: --ww-package-test needs kind, package, production, internal, external, directory, output, and status\n");
|
cerr("ww test: --ww-package-test needs kind, package, production, internal, external, directory, output, publication, and status\n");
|
||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
let kind: *u8 = argv[i + 1];
|
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 external: *u8 = argv[i + 5];
|
||||||
let dir: *u8 = argv[i + 6];
|
let dir: *u8 = argv[i + 6];
|
||||||
let output: *u8 = argv[i + 7];
|
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 pn: u64 = cstrlen(name);
|
||||||
let buildproduct: bool = cstreqlit(kind, "build");
|
let buildproduct: bool = cstreqlit(kind, "build");
|
||||||
let testproduct: bool = cstreqlit(kind, "test");
|
let testproduct: bool = cstreqlit(kind, "test");
|
||||||
@@ -8931,6 +8997,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
if ((!buildproduct && !testproduct)
|
if ((!buildproduct && !testproduct)
|
||||||
|| pn == 0u64
|
|| pn == 0u64
|
||||||
|| dir[0u64] == 0u8 || output[0u64] == 0u8
|
|| dir[0u64] == 0u8 || output[0u64] == 0u8
|
||||||
|
|| publish[0u64] == 0u8
|
||||||
|| status[0u64] == 0u8
|
|| status[0u64] == 0u8
|
||||||
|| (hasproduction && !cstreq(production, name))
|
|| (hasproduction && !cstreq(production, name))
|
||||||
|| (hasinternal && !cstreq(internal, 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))
|
|| !strings.hasprefix(pathstr(external), pathstr(name))
|
||||||
|| !cstrendswithlit(external, "_test")))
|
|| !cstrendswithlit(external, "_test")))
|
||||||
|| (buildproduct && (!hasproduction
|
|| (buildproduct && (!hasproduction
|
||||||
|| hasinternal || hasexternal))
|
|| hasinternal || hasexternal
|
||||||
|
|| !cstreqlit(publish, "-")))
|
||||||
|| (testproduct && !hasproduction
|
|| (testproduct && !hasproduction
|
||||||
&& !hasinternal && !hasexternal)) {
|
&& !hasinternal && !hasexternal)
|
||||||
|
|| (testproduct && !hasinternal && !hasexternal
|
||||||
|
&& !cstreqlit(publish, "-"))) {
|
||||||
cerr("ww test: invalid --ww-package-test product\n");
|
cerr("ww test: invalid --ww-package-test product\n");
|
||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
@@ -8956,6 +9026,8 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
product.externalpackage = nil;
|
product.externalpackage = nil;
|
||||||
if (hasexternal) { product.externalpackage = external; };
|
if (hasexternal) { product.externalpackage = external; };
|
||||||
product.status = status;
|
product.status = status;
|
||||||
|
product.publish = nil;
|
||||||
|
if (!cstreqlit(publish, "-")) { product.publish = publish; };
|
||||||
product.artifact = nil;
|
product.artifact = nil;
|
||||||
product.variant = SEP_VARIANT_TEST_MAIN;
|
product.variant = SEP_VARIANT_TEST_MAIN;
|
||||||
if (buildproduct) { product.variant = SEP_VARIANT_PRODUCTION; };
|
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,
|
if (!sepreserveproducts(&products,
|
||||||
products.len + 1)) { return 1; };
|
products.len + 1)) { return 1; };
|
||||||
append(products, product);
|
append(products, product);
|
||||||
i += 9;
|
i += 10;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if (p[1u64] == 73u8) { // '-I'
|
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");
|
cerr("ww test: invalid --ww-package-publish\n");
|
||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
if (packagecreateoutputdir != nil && !packagebuild) {
|
if (packagecreateoutputdir != nil && products.len == 0) {
|
||||||
cerr("ww test: invalid private directory creation\n");
|
cerr("ww test: invalid private directory creation\n");
|
||||||
return 2;
|
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");
|
cerr("ww test: -S needs a single test file\n");
|
||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
// -c -o forwards: the coordinator names the single
|
// The coordinator independently wires -o retention and -c run
|
||||||
// package's artifact and rejects a multi-package fan-out.
|
// suppression after loading the complete package set.
|
||||||
if (outstem != nil && compileonly == 0) {
|
|
||||||
cerr("ww test: -o needs -c for a package target\n");
|
|
||||||
return 2;
|
|
||||||
};
|
|
||||||
// -w forwards one caller-owned semantic-action store shared by
|
// -w forwards one caller-owned semantic-action store shared by
|
||||||
// the complete selected package universe.
|
// the complete selected package universe.
|
||||||
return execpackagetests(selfdir, argv, argc, start,
|
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");
|
cerr("ww test: -S needs a single test file\n");
|
||||||
return 2;
|
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 (products.len != 0) {
|
||||||
if (compileonly == 0) {
|
if (compileonly == 0) {
|
||||||
cerr("ww test: package-test products need -c\n");
|
cerr("ww test: package-test products need -c\n");
|
||||||
|
|||||||
@@ -1159,12 +1159,12 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
caller, stdinpath,
|
caller, stdinpath,
|
||||||
(90i64 * (time.second: i64)): time.duration, &out);
|
(90i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
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(defaultbin));
|
||||||
|
assert(!os.exists(strings.concat(p2, "/p2.test")));
|
||||||
assert(!os.exists(strings.concat(p2, "/created.txt")));
|
assert(!os.exists(strings.concat(p2, "/created.txt")));
|
||||||
assert(!has(out.stdout, "dep-init") && !has(out.stdout, "p2-external"));
|
assert(!has(out.stdout, "dep-init") && !has(out.stdout, "p2-external"));
|
||||||
assert(os.remove(defaultbin) == 0);
|
assert(os.remove(defaultbin) == 0);
|
||||||
clean(strings.concat(defaultbin, ".sepwork"));
|
|
||||||
|
|
||||||
assert(os.remove(strings.concat(p1, "/created.txt")) == 0);
|
assert(os.remove(strings.concat(p1, "/created.txt")) == 0);
|
||||||
let p1cbin: str = strings.concat(root, "/p1-c.test");
|
let p1cbin: str = strings.concat(root, "/p1-c.test");
|
||||||
@@ -1814,6 +1814,12 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
"toexternal_test", "tomixed"];
|
"toexternal_test", "tomixed"];
|
||||||
let shapetargetsecond: []str = ["", "shapeexternal_test", "", "",
|
let shapetargetsecond: []str = ["", "shapeexternal_test", "", "",
|
||||||
"tomixed_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 shapecompilerrefs: []str = ["", "", "", "", ""];
|
||||||
let shapelinkerrefs: []str = ["", "", "", "", ""];
|
let shapelinkerrefs: []str = ["", "", "", "", ""];
|
||||||
let shapemainrefs: []str = ["", "", "", "", ""];
|
let shapemainrefs: []str = ["", "", "", "", ""];
|
||||||
@@ -1827,6 +1833,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
let shapebin: str = strings.concat(root, "/shape-product-",
|
let shapebin: str = strings.concat(root, "/shape-product-",
|
||||||
boundarypkgname(sj), ".test");
|
boundarypkgname(sj), ".test");
|
||||||
let shapework: str = strings.concat(shapebin, ".sepwork");
|
let shapework: str = strings.concat(shapebin, ".sepwork");
|
||||||
|
let shapestatus: str = strings.concat(shapebin, ".status");
|
||||||
let ctracepath: str = strings.concat(root, "/shape-compiler-",
|
let ctracepath: str = strings.concat(root, "/shape-compiler-",
|
||||||
boundarypkgname(sj), ".trace");
|
boundarypkgname(sj), ".trace");
|
||||||
let ltracepath: str = strings.concat(root, "/shape-linker-",
|
let ltracepath: str = strings.concat(root, "/shape-linker-",
|
||||||
@@ -1835,6 +1842,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
writefile(ltracepath, "");
|
writefile(ltracepath, "");
|
||||||
let stagei: i32 = 0;
|
let stagei: i32 = 0;
|
||||||
for (stagei < stages.len) {
|
for (stagei < stages.len) {
|
||||||
|
assert(os.mkdir(shapework, 448i32) == 0);
|
||||||
if (stagei != 0) {
|
if (stagei != 0) {
|
||||||
rewritefile(ctracepath, "");
|
rewritefile(ctracepath, "");
|
||||||
rewritefile(ltracepath, "");
|
rewritefile(ltracepath, "");
|
||||||
@@ -1864,8 +1872,12 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
driver(shapecompilers[stagei])));
|
driver(shapecompilers[stagei])));
|
||||||
append(env, strings.concat("WW_SHAPE_REAL_LINKER=",
|
append(env, strings.concat("WW_SHAPE_REAL_LINKER=",
|
||||||
driver(shapelinkers[stagei])));
|
driver(shapelinkers[stagei])));
|
||||||
let shapeav: []str = [driver(stages[stagei]), "test", "-c", "-o",
|
let shapeav: []str = [driver(stages[stagei]), "test", "-c", "-w",
|
||||||
shapebin, "-I", root, shapes[index]];
|
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-",
|
runcommandenv(root, strings.concat("shape-structure-",
|
||||||
stages[stagei], "-", boundarypkgname(sj)), shapeav, env,
|
stages[stagei], "-", boundarypkgname(sj)), shapeav, env,
|
||||||
(120i64 * (time.second: i64)): time.duration, &out);
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
@@ -1876,9 +1888,9 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
shapefamilies[index], ".test")));
|
shapefamilies[index], ".test")));
|
||||||
let ctrace: str = readfile(ctracepath);
|
let ctrace: str = readfile(ctracepath);
|
||||||
let ltrace: str = readfile(ltracepath);
|
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,
|
let mainline: str = linecontaining(ctrace,
|
||||||
strings.concat("/", shapemains[sj], ".unit.ww"));
|
strings.concat("/", shapemains[sj], ".unit.new"));
|
||||||
let targetcount: i32 = 1;
|
let targetcount: i32 = 1;
|
||||||
if (shapetargetsecond[sj].len != 0) { targetcount = 2; };
|
if (shapetargetsecond[sj].len != 0) { targetcount = 2; };
|
||||||
assert(occurrences(mainline, "--test-target-package")
|
assert(occurrences(mainline, "--test-target-package")
|
||||||
@@ -1932,6 +1944,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
};
|
};
|
||||||
clean(shapework);
|
clean(shapework);
|
||||||
clean(shapebin);
|
clean(shapebin);
|
||||||
|
clean(shapestatus);
|
||||||
stagei += 1;
|
stagei += 1;
|
||||||
};
|
};
|
||||||
sj += 1;
|
sj += 1;
|
||||||
@@ -2191,37 +2204,39 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
assert(same(outc.stderr, outw.stderr));
|
assert(same(outc.stderr, outw.stderr));
|
||||||
assert(has(outc.stderr, "usage: wwtest package"));
|
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,
|
runcommand(root, "compile-c", cc,
|
||||||
(30i64 * (time.second: i64)): time.duration, &outc);
|
(30i64 * (time.second: i64)): time.duration, &outc);
|
||||||
expectexit(&outc, 0);
|
expectexit(&outc, 0);
|
||||||
assert(has(outc.stdout, strings.concat(" -> ", route, "/route.test\n")));
|
assert(has(outc.stdout, "route.white ... ok\n"));
|
||||||
assert(occurrences(outc.stdout, "built ") == 1);
|
assert(has(outc.stdout, "route.external ... ok\n"));
|
||||||
let cwhite: str = readfile(strings.concat(route,
|
let cwhite: str = readfile(strings.concat(cwork,
|
||||||
"/route.test.sepwork/route-internal-test.s"));
|
"/route-internal-test.s"));
|
||||||
let cexternal: str = readfile(strings.concat(route,
|
let cexternal: str = readfile(strings.concat(cwork,
|
||||||
"/route.test.sepwork/route_test-external-test.s"));
|
"/route_test-external-test.s"));
|
||||||
let cmain: str = readfile(strings.concat(route,
|
let cmain: str = readfile(strings.concat(cwork,
|
||||||
"/route.test.sepwork/route-test-main.s"));
|
"/route-test-main.s"));
|
||||||
let cbinary: str = readfile(strings.concat(route, "/route.test"));
|
let cbinary: str = readfile(cbinarypath);
|
||||||
// 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"));
|
|
||||||
|
|
||||||
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,
|
runcommand(root, "compile-ww", wc,
|
||||||
(30i64 * (time.second: i64)): time.duration, &outw);
|
(30i64 * (time.second: i64)): time.duration, &outw);
|
||||||
expectexit(&outw, 0);
|
expectexit(&outw, 0);
|
||||||
assert(same(outc.stdout, outw.stdout));
|
assert(same(outc.stdout, outw.stdout));
|
||||||
assert(same(outc.stderr, outw.stderr));
|
assert(same(outc.stderr, outw.stderr));
|
||||||
assert(same(cwhite, readfile(strings.concat(route,
|
assert(same(cwhite, readfile(strings.concat(wwork,
|
||||||
"/route.test.sepwork/route-internal-test.s"))));
|
"/route-internal-test.s"))));
|
||||||
assert(same(cexternal, readfile(strings.concat(route,
|
assert(same(cexternal, readfile(strings.concat(wwork,
|
||||||
"/route.test.sepwork/route_test-external-test.s"))));
|
"/route_test-external-test.s"))));
|
||||||
assert(same(cmain, readfile(strings.concat(route,
|
assert(same(cmain, readfile(strings.concat(wwork,
|
||||||
"/route.test.sepwork/route-test-main.s"))));
|
"/route-test-main.s"))));
|
||||||
assert(same(cbinary, readfile(strings.concat(route, "/route.test"))));
|
assert(same(cbinary, readfile(wbinarypath)));
|
||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2464,7 +2479,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
|
|
||||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
"-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,
|
runcommandenv(root, strings.concat("graph-cold-", stages[si]), av,
|
||||||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
@@ -2728,7 +2743,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
driver(linkers[si])));
|
driver(linkers[si])));
|
||||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
"-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,
|
runcommandenv(root, strings.concat("graph-body-", stages[si]), av,
|
||||||
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
@@ -2807,7 +2822,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
driver(linkers[si])));
|
driver(linkers[si])));
|
||||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
"-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,
|
runcommandenv(root, strings.concat("graph-external-", stages[si]), av,
|
||||||
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
@@ -2878,7 +2893,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
driver(linkers[si])));
|
driver(linkers[si])));
|
||||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
"-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,
|
runcommandenv(root, strings.concat("graph-export-", stages[si]), av,
|
||||||
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
@@ -2981,7 +2996,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
driver(linkers[si])));
|
driver(linkers[si])));
|
||||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
"-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]),
|
runcommandenv(root, strings.concat("graph-remove-external-", stages[si]),
|
||||||
av, env, (120i64 * (time.second: i64)): time.duration, &out);
|
av, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
@@ -3106,7 +3121,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
driver(linkers[si])));
|
driver(linkers[si])));
|
||||||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||||||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
"-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]),
|
runcommandenv(root, strings.concat("graph-readd-external-", stages[si]),
|
||||||
av, env, (120i64 * (time.second: i64)): time.duration, &out);
|
av, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
@@ -3191,7 +3206,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
runcommand(root, strings.concat("graph-direct-product-", stages[si]),
|
runcommand(root, strings.concat("graph-direct-product-", stages[si]),
|
||||||
directav, (120i64 * (time.second: i64)): time.duration, &out);
|
directav, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
assert(occurrences(out.stdout, "built ") == 1);
|
assert(out.stdout.len == 0);
|
||||||
assert(os.exists(directbin));
|
assert(os.exists(directbin));
|
||||||
if (si == 0) {
|
if (si == 0) {
|
||||||
directbytes = readfile(directbin);
|
directbytes = readfile(directbin);
|
||||||
@@ -3376,19 +3391,19 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
let forward: []str = [driver(stages[si]), "test", "-c", "-w",
|
let forward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
works[si], "-I", suite, "-I", root,
|
works[si], "-I", suite, "-I", root,
|
||||||
"--ww-package-test", "test", "alpha", "alpha", "alpha",
|
"--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",
|
"--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", "-", "-",
|
"--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",
|
let reverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
works[si], "-I", suite, "-I", root,
|
works[si], "-I", suite, "-I", root,
|
||||||
"--ww-package-test", "test", "gamma", "gamma", "-", "-",
|
"--ww-package-test", "test", "gamma", "gamma", "-", "-",
|
||||||
gamma, gammabin, statuses[2],
|
gamma, gammabin, "-", statuses[2],
|
||||||
"--ww-package-test", "test", "beta", "beta", "beta",
|
"--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",
|
"--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) {
|
if (si == 0) {
|
||||||
runcommandenv(root, "multi-forward-c", forward, env,
|
runcommandenv(root, "multi-forward-c", forward, env,
|
||||||
(180i64 * (time.second: i64)): time.duration, &out);
|
(180i64 * (time.second: i64)): time.duration, &out);
|
||||||
@@ -3531,14 +3546,11 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
si += 1;
|
si += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Public root order is presentation-only: two directories publish one
|
// Public root order is presentation-only. Default -c destinations are in
|
||||||
// binary each, while the selected no-test directory publishes no binary.
|
// the invocation cwd, and the selected no-test package publishes nothing.
|
||||||
let publicalpha: str = strings.concat(alpha, "/alpha.test");
|
let publicalpha: str = strings.concat(root, "/alpha.test");
|
||||||
let publicbeta: str = strings.concat(beta, "/beta.test");
|
let publicbeta: str = strings.concat(root, "/beta.test");
|
||||||
let publicgamma: str = strings.concat(gamma, "/gamma.test");
|
let publicgamma: str = strings.concat(root, "/gamma.test");
|
||||||
let publicworks: []str = [strings.concat(publicalpha, ".sepwork"),
|
|
||||||
strings.concat(publicbeta, ".sepwork"),
|
|
||||||
strings.concat(publicgamma, ".sepwork")];
|
|
||||||
let publicout: str = "";
|
let publicout: str = "";
|
||||||
let publicerr: str = "";
|
let publicerr: str = "";
|
||||||
let publicalphabytes: str = "";
|
let publicalphabytes: str = "";
|
||||||
@@ -3562,11 +3574,11 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
append(publicav, gamma); append(publicav, beta);
|
append(publicav, gamma); append(publicav, beta);
|
||||||
append(publicav, alpha);
|
append(publicav, alpha);
|
||||||
};
|
};
|
||||||
runcommand(root, strings.concat("multi-public-order-",
|
runcommanddir(root, strings.concat("multi-public-order-",
|
||||||
publiclabels[pi]), publicav,
|
publiclabels[pi]), root, publicav,
|
||||||
(180i64 * (time.second: i64)): time.duration, &out);
|
(180i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
assert(occurrences(out.stdout, "built ") == 2);
|
assert(occurrences(out.stdout, "built ") == 0);
|
||||||
assert(has(out.stdout, strings.concat(
|
assert(has(out.stdout, strings.concat(
|
||||||
"? ", gamma, " [no tests]\n")));
|
"? ", gamma, " [no tests]\n")));
|
||||||
assert(os.exists(publicalpha) && os.exists(publicbeta));
|
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(publicalphabytes, readfile(publicalpha)));
|
||||||
assert(same(publicbetabytes, readfile(publicbeta)));
|
assert(same(publicbetabytes, readfile(publicbeta)));
|
||||||
};
|
};
|
||||||
let wi: i32 = 0;
|
|
||||||
for (wi < publicworks.len) { clean(publicworks[wi]); wi += 1; };
|
|
||||||
clean(publicalpha); clean(publicbeta); clean(publicgamma);
|
clean(publicalpha); clean(publicbeta); clean(publicgamma);
|
||||||
pi += 1;
|
pi += 1;
|
||||||
};
|
};
|
||||||
@@ -3756,6 +3766,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
append(linkenv, strings.concat("WW_MIXED_W6L=", driver("w6l")));
|
append(linkenv, strings.concat("WW_MIXED_W6L=", driver("w6l")));
|
||||||
let bins: []str = [strings.concat(named, "/test.test"),
|
let bins: []str = [strings.concat(named, "/test.test"),
|
||||||
strings.concat(consumer, "/zconsumer.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",
|
let keys: []str = ["test-internal-test", "test_test-external-test",
|
||||||
"test-test-main", "zconsumer-internal-test",
|
"test-test-main", "zconsumer-internal-test",
|
||||||
"zconsumer_test-external-test", "zconsumer-test-main"];
|
"zconsumer_test-external-test", "zconsumer-test-main"];
|
||||||
@@ -3766,8 +3778,14 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
let out: commandout;
|
let out: commandout;
|
||||||
let si: i32 = 0;
|
let si: i32 = 0;
|
||||||
for (si < stages.len) {
|
for (si < stages.len) {
|
||||||
let av: []str = [driver(stages[si]), "test", "-c", "-j", "1",
|
assert(os.mkdir(workroot, 448i32) == 0);
|
||||||
"-I", suite, strings.concat(suite, "/...")];
|
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) {
|
if (si == 0) {
|
||||||
runcommandenv(root, "mixed-actions-c", av, linkenv,
|
runcommandenv(root, "mixed-actions-c", av, linkenv,
|
||||||
(120i64 * (time.second: i64)): time.duration, &out);
|
(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);
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
};
|
};
|
||||||
expectexit(&out, 0);
|
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,
|
let namedprod: str = readfile(strings.concat(work,
|
||||||
"test-internal-test.unit.ww"));
|
"test-internal-test.unit.ww"));
|
||||||
let support: str = readfile(strings.concat(work, "__wwtest.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=",
|
append(env, strings.concat("WW_UNIVERSE_BUILDER_TRACE=",
|
||||||
buildertraces[si]));
|
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, driver(stages[si]));
|
||||||
append(av, "test"); append(av, "-c");
|
append(av, "test"); append(av, "-c");
|
||||||
append(av, "-w"); append(av, works[si]);
|
append(av, "-w"); append(av, works[si]);
|
||||||
@@ -4461,6 +4479,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
};
|
};
|
||||||
append(av, dir);
|
append(av, dir);
|
||||||
append(av, strings.concat(root, "/product-", name, ".test"));
|
append(av, strings.concat(root, "/product-", name, ".test"));
|
||||||
|
append(av, "-");
|
||||||
append(av, strings.concat(root, "/product-", name, ".status"));
|
append(av, strings.concat(root, "/product-", name, ".status"));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
@@ -4479,12 +4498,13 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
};
|
};
|
||||||
append(av, dir);
|
append(av, dir);
|
||||||
append(av, strings.concat(root, "/product-", name, ".test"));
|
append(av, strings.concat(root, "/product-", name, ".test"));
|
||||||
|
append(av, "-");
|
||||||
append(av, strings.concat(root, "/product-", name, ".status"));
|
append(av, strings.concat(root, "/product-", name, ".status"));
|
||||||
i -= 1;
|
i -= 1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
append(av, strings.concat(suite, "/p000"));
|
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,
|
runcommandenv(root, strings.concat("dynamic-universe-", stages[si]), av,
|
||||||
env, (1200i64 * (time.second: i64)): time.duration, &out);
|
env, (1200i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
@@ -5038,7 +5058,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
let seedav: []str = [driver(stages[i]), "test", "-c",
|
let seedav: []str = [driver(stages[i]), "test", "-c",
|
||||||
"-w", lateworks[i], "-I", root, "--ww-package-test", "test",
|
"-w", lateworks[i], "-I", root, "--ww-package-test", "test",
|
||||||
"late", "late", "late", "late_test", late, latebins[i],
|
"late", "late", "late", "late_test", late, latebins[i],
|
||||||
latestatuses[i], late];
|
"-", latestatuses[i], late];
|
||||||
runcommandenv(root, strings.concat("late-seed-", tags[i]), seedav,
|
runcommandenv(root, strings.concat("late-seed-", tags[i]), seedav,
|
||||||
seedenv, (180i64 * (time.second: i64)): time.duration, &out);
|
seedenv, (180i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
@@ -5164,7 +5184,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
let failav: []str = [driver(stages[i]), "test", "-c",
|
let failav: []str = [driver(stages[i]), "test", "-c",
|
||||||
"-w", lateworks[i], "-I", root, "--ww-package-test", "test",
|
"-w", lateworks[i], "-I", root, "--ww-package-test", "test",
|
||||||
"late", "late", "late", "late_test", late, attemptbin,
|
"late", "late", "late", "late_test", late, attemptbin,
|
||||||
attemptstatus, late];
|
"-", attemptstatus, late];
|
||||||
runcommandenv(root, strings.concat("late-failure-",
|
runcommandenv(root, strings.concat("late-failure-",
|
||||||
phaselabels[phasei], "-", tags[i], "-", retrylabel), failav,
|
phaselabels[phasei], "-", tags[i], "-", retrylabel), failav,
|
||||||
env, (180i64 * (time.second: i64)): time.duration, &out);
|
env, (180i64 * (time.second: i64)): time.duration, &out);
|
||||||
@@ -5276,8 +5296,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
let out: commandout;
|
let out: commandout;
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < stages.len) {
|
for (i < stages.len) {
|
||||||
let av: []str = [driver(stages[i]), "test", "-c", "-I", root,
|
let av: []str = [driver(stages[i]), "test", "-w", workroot,
|
||||||
"-o", bin, pkg];
|
"-I", root, "-o", bin, pkg];
|
||||||
runcommand(root, strings.concat("named-test-build-", stages[i]), av,
|
runcommand(root, strings.concat("named-test-build-", stages[i]), av,
|
||||||
(120i64 * (time.second: i64)): time.duration, &out);
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
@@ -5914,18 +5934,22 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
orderwork, "-I", source,
|
orderwork, "-I", source,
|
||||||
"--ww-package-test", "test", "main", "main", "-", "-", client,
|
"--ww-package-test", "test", "main", "main", "-", "-", client,
|
||||||
strings.concat(root, "/order-client-", stages[si]),
|
strings.concat(root, "/order-client-", stages[si]),
|
||||||
|
"-",
|
||||||
strings.concat(root, "/order-client-", stages[si], ".status"),
|
strings.concat(root, "/order-client-", stages[si], ".status"),
|
||||||
"--ww-package-test", "test", "main", "main", "-", "-", outsider,
|
"--ww-package-test", "test", "main", "main", "-", "-", outsider,
|
||||||
strings.concat(root, "/order-outsider-", stages[si]),
|
strings.concat(root, "/order-outsider-", stages[si]),
|
||||||
|
"-",
|
||||||
strings.concat(root, "/order-outsider-", stages[si], ".status"),
|
strings.concat(root, "/order-outsider-", stages[si], ".status"),
|
||||||
client];
|
client];
|
||||||
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
orderwork, "-I", source,
|
orderwork, "-I", source,
|
||||||
"--ww-package-test", "test", "main", "main", "-", "-", outsider,
|
"--ww-package-test", "test", "main", "main", "-", "-", outsider,
|
||||||
strings.concat(root, "/order-outsider-", stages[si]),
|
strings.concat(root, "/order-outsider-", stages[si]),
|
||||||
|
"-",
|
||||||
strings.concat(root, "/order-outsider-", stages[si], ".status"),
|
strings.concat(root, "/order-outsider-", stages[si], ".status"),
|
||||||
"--ww-package-test", "test", "main", "main", "-", "-", client,
|
"--ww-package-test", "test", "main", "main", "-", "-", client,
|
||||||
strings.concat(root, "/order-client-", stages[si]),
|
strings.concat(root, "/order-client-", stages[si]),
|
||||||
|
"-",
|
||||||
strings.concat(root, "/order-client-", stages[si], ".status"),
|
strings.concat(root, "/order-client-", stages[si], ".status"),
|
||||||
client];
|
client];
|
||||||
let orderrequests: [][]str = [orderforward, orderreverse];
|
let orderrequests: [][]str = [orderforward, orderreverse];
|
||||||
@@ -6352,10 +6376,10 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
source, "--ww-package-build",
|
source, "--ww-package-build",
|
||||||
"--ww-package-test", "build", "paritybase", "paritybase",
|
"--ww-package-test", "build", "paritybase", "paritybase",
|
||||||
"-", "-", base,
|
"-", "-", base,
|
||||||
combinedbase, combinedbasestatus,
|
combinedbase, "-", combinedbasestatus,
|
||||||
"--ww-package-test", "build", "parityleft", "parityleft",
|
"--ww-package-test", "build", "parityleft", "parityleft",
|
||||||
"-", "-", left,
|
"-", "-", left,
|
||||||
combinedleft, combinedleftstatus, left];
|
combinedleft, "-", combinedleftstatus, left];
|
||||||
runcommandenv(root, strings.concat("long-combined-", tags[si]),
|
runcommandenv(root, strings.concat("long-combined-", tags[si]),
|
||||||
combinedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
combinedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
@@ -9113,13 +9137,17 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
strings.concat(root, "/stem"), spec];
|
strings.concat(root, "/stem"), spec];
|
||||||
let ow: []str = [driver("ww_ww"), "test", "-o",
|
let ow: []str = [driver("ww_ww"), "test", "-o",
|
||||||
strings.concat(root, "/stem"), spec];
|
strings.concat(root, "/stem"), spec];
|
||||||
runcommand(root, "tree-o-c", oc, time.second, &outc);
|
runcommand(root, "tree-o-c", oc,
|
||||||
runcommand(root, "tree-o-ww", ow, time.second, &outw);
|
(30i64 * (time.second: i64)): time.duration, &outc);
|
||||||
expectexit(&outc, 2);
|
runcommand(root, "tree-o-ww", ow,
|
||||||
expectexit(&outw, 2);
|
(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(same(outc.stderr, outw.stderr));
|
||||||
assert(has(outc.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
|
// -w on a package target forwards to the coordinator: one shared
|
||||||
// persistent driver workdir for the complete selected request,
|
// persistent driver workdir for the complete selected request,
|
||||||
@@ -9691,9 +9719,11 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
"--ww-package-build",
|
"--ww-package-build",
|
||||||
"--ww-package-test", "build", "foo", "foo", "-", "-",
|
"--ww-package-test", "build", "foo", "foo", "-", "-",
|
||||||
localleft, leftout,
|
localleft, leftout,
|
||||||
|
"-",
|
||||||
strings.concat(root, "/local-", stages[si], "-left.status"),
|
strings.concat(root, "/local-", stages[si], "-left.status"),
|
||||||
"--ww-package-test", "build", "foo", "foo", "-", "-",
|
"--ww-package-test", "build", "foo", "foo", "-", "-",
|
||||||
localright, rightout,
|
localright, rightout,
|
||||||
|
"-",
|
||||||
strings.concat(root, "/local-", stages[si], "-right.status"),
|
strings.concat(root, "/local-", stages[si], "-right.status"),
|
||||||
localleft];
|
localleft];
|
||||||
runcommand(root, strings.concat("local-command-union-", stages[si]),
|
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",
|
let foldforward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
foldmultiwork, "-I", source, "--ww-package-build",
|
foldmultiwork, "-I", source, "--ww-package-build",
|
||||||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||||||
foldallowedout, foldallowedstatus,
|
foldallowedout, "-", foldallowedstatus,
|
||||||
"--ww-package-test", "build", "main", "main", "-", "-",
|
"--ww-package-test", "build", "main", "main", "-", "-",
|
||||||
foldvendorclient, foldcollisionout, foldcollisionstatus, client];
|
foldvendorclient, foldcollisionout, "-", foldcollisionstatus, client];
|
||||||
let foldreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
let foldreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
foldmultiwork, "-I", source, "--ww-package-build",
|
foldmultiwork, "-I", source, "--ww-package-build",
|
||||||
"--ww-package-test", "build", "main", "main", "-", "-",
|
"--ww-package-test", "build", "main", "main", "-", "-",
|
||||||
foldvendorclient, foldcollisionout, foldcollisionstatus,
|
foldvendorclient, foldcollisionout, "-", foldcollisionstatus,
|
||||||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||||||
foldallowedout, foldallowedstatus, client];
|
foldallowedout, "-", foldallowedstatus, client];
|
||||||
let foldrequests: [][]str = [foldforward, foldreverse];
|
let foldrequests: [][]str = [foldforward, foldreverse];
|
||||||
let foldmultidiags: []str = ["", ""];
|
let foldmultidiags: []str = ["", ""];
|
||||||
let foldorderlabels: []str = ["forward", "reverse"];
|
let foldorderlabels: []str = ["forward", "reverse"];
|
||||||
@@ -10793,7 +10823,7 @@ fn runtimepath(relative: str) str = {
|
|||||||
strings.concat(combinedoutputs[5], ".status"),
|
strings.concat(combinedoutputs[5], ".status"),
|
||||||
strings.concat(combinedoutputs[6], ".status")];
|
strings.concat(combinedoutputs[6], ".status")];
|
||||||
let combinedav: []str = alloc([],
|
let combinedav: []str = alloc([],
|
||||||
(9 + combinedtargets.len * 9): u64)!;
|
(9 + combinedtargets.len * 10): u64)!;
|
||||||
append(combinedav, driver(stages[si]));
|
append(combinedav, driver(stages[si]));
|
||||||
append(combinedav, "test"); append(combinedav, "-c");
|
append(combinedav, "test"); append(combinedav, "-c");
|
||||||
append(combinedav, "-w"); append(combinedav, combinedwork);
|
append(combinedav, "-w"); append(combinedav, combinedwork);
|
||||||
@@ -10809,6 +10839,7 @@ fn runtimepath(relative: str) str = {
|
|||||||
append(combinedav, "-");
|
append(combinedav, "-");
|
||||||
append(combinedav, combinedtargets[ui]);
|
append(combinedav, combinedtargets[ui]);
|
||||||
append(combinedav, combinedoutputs[ui]);
|
append(combinedav, combinedoutputs[ui]);
|
||||||
|
append(combinedav, "-");
|
||||||
append(combinedav, combinedstatuses[ui]);
|
append(combinedav, combinedstatuses[ui]);
|
||||||
ui += 1;
|
ui += 1;
|
||||||
};
|
};
|
||||||
@@ -10882,15 +10913,15 @@ fn runtimepath(relative: str) str = {
|
|||||||
let orderforward: []str = [driver(stages[si]), "test", "-c", "-w",
|
let orderforward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
orderwork, "-I", source, "--ww-package-build",
|
orderwork, "-I", source, "--ww-package-build",
|
||||||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||||||
orderallowed, orderallowedstatus,
|
orderallowed, "-", orderallowedstatus,
|
||||||
"--ww-package-test", "build", "main", "main", "-", "-", outsider,
|
"--ww-package-test", "build", "main", "main", "-", "-", outsider,
|
||||||
orderforbidden, orderforbiddenstatus, client];
|
orderforbidden, "-", orderforbiddenstatus, client];
|
||||||
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
orderwork, "-I", source, "--ww-package-build",
|
orderwork, "-I", source, "--ww-package-build",
|
||||||
"--ww-package-test", "build", "main", "main", "-", "-", outsider,
|
"--ww-package-test", "build", "main", "main", "-", "-", outsider,
|
||||||
orderforbidden, orderforbiddenstatus,
|
orderforbidden, "-", orderforbiddenstatus,
|
||||||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||||||
orderallowed, orderallowedstatus, client];
|
orderallowed, "-", orderallowedstatus, client];
|
||||||
let orderrequests: [][]str = [orderforward, orderreverse];
|
let orderrequests: [][]str = [orderforward, orderreverse];
|
||||||
let orderdiags: []str = ["", ""];
|
let orderdiags: []str = ["", ""];
|
||||||
let oi: i32 = 0;
|
let oi: i32 = 0;
|
||||||
@@ -12162,23 +12193,32 @@ fn runtimepath(relative: str) str = {
|
|||||||
let pdir: str = strings.concat(root, "/pkg");
|
let pdir: str = strings.concat(root, "/pkg");
|
||||||
assert(os.mkdir(pdir, 493i32) == 0);
|
assert(os.mkdir(pdir, 493i32) == 0);
|
||||||
writefile(strings.concat(pdir, "/pkg.ww"),
|
writefile(strings.concat(pdir, "/pkg.ww"),
|
||||||
"package pkg;\nexport fn v() i32 = { return 7; };\n");
|
"package family;\nexport fn v() i32 = { return 7; };\n");
|
||||||
writefile(strings.concat(pdir, "/pkg_test.ww"), strings.concat(
|
writefile(strings.concat(pdir, "/same_test.ww"), strings.concat(
|
||||||
"package pkg_test;\nimport pkg;\n",
|
"package family;\nimport os;\n",
|
||||||
"@test fn seven() void = { assert(pkg.v() == 7); };\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");
|
let adir: str = strings.concat(root, "/a");
|
||||||
assert(os.mkdir(adir, 493i32) == 0);
|
assert(os.mkdir(adir, 493i32) == 0);
|
||||||
// '_'-prefixed: the tree walk skips it, so the artifacts and
|
writefile(strings.concat(adir, "/a.ww"),
|
||||||
// their .sepwork trees never pollute the multi-package discovery.
|
"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");
|
let outdir: str = strings.concat(root, "/_out");
|
||||||
assert(os.mkdir(outdir, 493i32) == 0);
|
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 out: commandout;
|
||||||
let drvs: []str = ["ww", "ww_ww"];
|
let drvs: []str = ["ww", "ww_ww"];
|
||||||
|
let namedbytes: str = "";
|
||||||
|
let defaultbytes: str = "";
|
||||||
|
let fanabytes: str = "";
|
||||||
|
let fanpkgbytes: str = "";
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < 2) {
|
for (i < 2) {
|
||||||
let named: str = strings.concat(outdir, "/out_", drvs[i],
|
let named: str = strings.concat(outdir, "/out_", drvs[i],
|
||||||
@@ -12193,31 +12233,379 @@ fn runtimepath(relative: str) str = {
|
|||||||
case void => void;
|
case void => void;
|
||||||
case let e: os.oserror => abort("-c -o artifact missing");
|
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"))) {
|
match (os.stat(&fi, strings.concat(pdir, "/pkg.test"))) {
|
||||||
case void => abort("-c -o still published the fixed stem");
|
case void => abort("-c -o still published the fixed stem");
|
||||||
case let e: os.oserror => void;
|
case let e: os.oserror => void;
|
||||||
};
|
};
|
||||||
|
if (i == 0) { namedbytes = readfile(named); }
|
||||||
|
else { assert(same(namedbytes, readfile(named))); };
|
||||||
let runav: []str = [named];
|
let runav: []str = [named];
|
||||||
runcommand(root, strings.concat("namerun_", drvs[i]), runav,
|
runcommand(root, strings.concat("namerun_", drvs[i]), runav,
|
||||||
(30i64 * (time.second: i64)): time.duration, &out);
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
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,
|
let nocav: []str = [driver(drvs[i]), "test", "-I", root,
|
||||||
"-o", named, pdir];
|
"-o", runnamed, pdir];
|
||||||
runcommand(root, strings.concat("noc_", drvs[i]), nocav,
|
runcommand(root, strings.concat("noc_", drvs[i]), nocav,
|
||||||
(30i64 * (time.second: i64)): time.duration, &out);
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 2);
|
expectexit(&out, 0);
|
||||||
assert(has(out.stderr, "-o needs -c for a package target"));
|
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",
|
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,
|
runcommand(root, strings.concat("multi_", drvs[i]), mulav,
|
||||||
(30i64 * (time.second: i64)): time.duration, &out);
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 2);
|
expectexit(&out, 1);
|
||||||
assert(has(out.stderr,
|
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;
|
i += 1;
|
||||||
};
|
};
|
||||||
clean(root);
|
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"));
|
"@test fn directory_import() void = { assert(foo.value() == 42); };\n"));
|
||||||
let testbins: []str = [strings.concat(td, "/test-c"),
|
let testbins: []str = [strings.concat(td, "/test-c"),
|
||||||
strings.concat(td, "/test-w")];
|
strings.concat(td, "/test-w")];
|
||||||
|
let testworks: []str = [strings.concat(td, "/test-work-c"),
|
||||||
|
strings.concat(td, "/test-work-w")];
|
||||||
i = 0;
|
i = 0;
|
||||||
for (i < 2) {
|
for (i < 2) {
|
||||||
let tav: []str = [testenv.driver(drivers[i * 2]), "test", "-c",
|
let tav: []str = [testenv.driver(drivers[i * 2]), "test", "-w",
|
||||||
"-I", early, "-I", late, "-o", testbins[i], checks];
|
testworks[i], "-I", early, "-I", late, "-o", testbins[i],
|
||||||
|
checks];
|
||||||
expectcode(td, strings.concat("test_build_", tags[i * 2]), tav, 0);
|
expectcode(td, strings.concat("test_build_", tags[i * 2]), tav, 0);
|
||||||
let trav: []str = [testbins[i]];
|
let trav: []str = [testbins[i]];
|
||||||
expectcode(td, strings.concat("test_run_", tags[i * 2]), trav, 0);
|
expectcode(td, strings.concat("test_run_", tags[i * 2]), trav, 0);
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
samefile(testbins[0], testbins[1], "directory-test binaries differ");
|
samefile(testbins[0], testbins[1], "directory-test binaries differ");
|
||||||
samefile(strings.concat(testbins[0], ".sepwork/example.foo.a"),
|
samefile(strings.concat(testworks[0], "/example.foo.a"),
|
||||||
strings.concat(testbins[1], ".sepwork/example.foo.a"),
|
strings.concat(testworks[1], "/example.foo.a"),
|
||||||
"directory-test dependency archives differ");
|
"directory-test dependency archives differ");
|
||||||
testenv.clean(td);
|
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]),
|
let av: []str = [launcher, limitstr, testenv.driver(stages[stagei]),
|
||||||
"test", "-c", "-w", work, "-I", tree,
|
"test", "-c", "-w", work, "-I", tree,
|
||||||
"--ww-package-test", "test", "target", "target", "target",
|
"--ww-package-test", "test", "target", "target", "target",
|
||||||
"target_test", target, output, status, target];
|
"target_test", target, output, "-", status, target];
|
||||||
let out: testenv.commandout;
|
let out: testenv.commandout;
|
||||||
runenv(td, strings.concat("allocation-", stages[stagei], "-", tag),
|
runenv(td, strings.concat("allocation-", stages[stagei], "-", tag),
|
||||||
av, env, &out);
|
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 "".
|
// a3 == "" means a two-token argv tail; no row passes a literal "".
|
||||||
@test fn flagargs() void = {
|
@test fn flagargs() void = {
|
||||||
let a1: []str = ["build", "build", "build", "build", "build",
|
let a1: []str = ["build", "build", "build", "build", "build",
|
||||||
"run", "run", "run", "test", "test", "test", "test", "test",
|
"run", "run", "run", "test", "test", "test", "test", "test"];
|
||||||
"test"];
|
|
||||||
let a2: []str = ["-o", "-I", "-L", "-l", "-zz",
|
let a2: []str = ["-o", "-I", "-L", "-l", "-zz",
|
||||||
"-o", "-l", "-zz", "-l", "-zz", "-I", "-o", "-o",
|
"-o", "-l", "-zz", "-l", "-zz", "-I", "-o",
|
||||||
"-run"];
|
"-run"];
|
||||||
let a3: []str = ["", "", "", "", "",
|
let a3: []str = ["", "", "", "", "",
|
||||||
"", "", "", "", "", "", "", "x",
|
"", "", "", "", "", "", "",
|
||||||
""];
|
""];
|
||||||
let subs: []str = [
|
let subs: []str = [
|
||||||
"ww build: -o needs an argument",
|
"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: unknown flag",
|
||||||
"ww test: -I needs an argument",
|
"ww test: -I needs an argument",
|
||||||
"ww test: -o needs an argument",
|
"ww test: -o needs an argument",
|
||||||
"ww test: -o needs -c for a package target",
|
|
||||||
"ww test: -run needs an argument"];
|
"ww test: -run needs an argument"];
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < subs.len) {
|
for (i < subs.len) {
|
||||||
|
|||||||
Reference in New Issue
Block a user