ww test: retain directory test binaries like Go

This commit is contained in:
2026-08-20 20:13:08 +09:00
parent f28843f2f5
commit db5782eff9
10 changed files with 1014 additions and 213 deletions

View File

@@ -13,6 +13,7 @@
#include <sys/wait.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <stdarg.h>
@@ -1300,6 +1301,7 @@ struct sepproduct {
const char *internal_package;
const char *external_package;
const char *status;
const char *publish; /* optional retained test executable */
const char *artifact;
int variant;
int directory_product;
@@ -1312,6 +1314,7 @@ struct sepproduct {
int pxtest;
int support; /* direct generated-main support action, or -1 */
char *stage_out; /* request-private linked/published output */
char *stage_publish; /* request-private retained executable copy */
char *stage_iface; /* request-private published package interface */
char *stage_status; /* request-private completion marker */
};
@@ -4932,6 +4935,34 @@ copy_file_stage(const char *src, const char *dst)
return bad ? -1 : 0;
}
/* Go's BuildInstallFunc installs linked test binaries with 0777 filtered by
* the caller's umask. Keep the retained copy byte-identical to the temporary
* runnable while giving the new staging inode that executable mode. */
static int
copy_executable_stage(const char *src, const char *dst)
{
FILE *in = fopen(src, "rb");
if (in == NULL) return -1;
int fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0777);
if (fd < 0) { fclose(in); return -1; }
FILE *out = fdopen(fd, "wb");
if (out == NULL) { close(fd); fclose(in); (void)unlink(dst); return -1; }
unsigned char buf[65536];
int bad = 0;
for (;;) {
size_t n = fread(buf, 1, sizeof buf, in);
if (n != 0 && fwrite(buf, 1, n, out) != n) bad = 1;
if (bad || n < sizeof buf) {
if (ferror(in)) bad = 1;
break;
}
}
if (fclose(in) != 0) bad = 1;
if (fclose(out) != 0) bad = 1;
if (bad) (void)unlink(dst);
return bad ? -1 : 0;
}
struct septxnentry {
char *stage;
char *dst;
@@ -5117,9 +5148,11 @@ sep_free_product_staging(struct sepproduct *products, int nproducts)
for (int i = 0; i < nproducts; i++) {
free(products[i].stage_status);
free(products[i].stage_iface);
free(products[i].stage_publish);
free(products[i].stage_out);
products[i].stage_status = NULL;
products[i].stage_iface = NULL;
products[i].stage_publish = NULL;
products[i].stage_out = NULL;
}
}
@@ -5158,11 +5191,13 @@ sep_validate_product_path_pair(const struct sepproduct *a,
const char *ap[] = {
a->stage_status != NULL ? a->status : NULL,
a->stage_out != NULL ? a->out : NULL,
a->stage_status, a->stage_out, a->stage_iface };
a->stage_publish != NULL ? a->publish : NULL,
a->stage_status, a->stage_out, a->stage_publish, a->stage_iface };
const char *bp[] = {
b->stage_status != NULL ? b->status : NULL,
b->stage_out != NULL ? b->out : NULL,
b->stage_status, b->stage_out, b->stage_iface };
b->stage_publish != NULL ? b->publish : NULL,
b->stage_status, b->stage_out, b->stage_publish, b->stage_iface };
for (size_t i = 0; i < nelem(ap); i++) {
if (ap[i] == NULL) continue;
for (size_t j = 0; j < nelem(bp); j++) {
@@ -5221,6 +5256,10 @@ sep_validate_request_staging(struct sepgraph *g, const char *scratch, int warm,
&& sep_prepare_product_stage(&products[i].stage_status,
products[i].status) < 0)
return -1;
if (products[i].publish != NULL && !products[i].no_tests
&& sep_prepare_product_stage(&products[i].stage_publish,
products[i].publish) < 0)
return -1;
if (emit_asm) continue;
int owns_output = root_package ? publish_package
: is_test ? !products[i].no_tests
@@ -5336,7 +5375,8 @@ sep_discard_request_staging(struct sepgraph *g, const char *scratch, int warm,
}
for (int i = 0; i < nproducts; i++) {
const char *path[] = { products[i].stage_out,
products[i].stage_iface, products[i].stage_status };
products[i].stage_publish, products[i].stage_iface,
products[i].stage_status };
for (size_t j = 0; j < nelem(path); j++)
if (path[j] != NULL
&& unlink(path[j]) != 0 && errno != ENOENT)
@@ -5564,6 +5604,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
products[i].ptest = -1;
products[i].pxtest = -1;
products[i].stage_out = NULL;
products[i].stage_publish = NULL;
products[i].stage_iface = NULL;
products[i].stage_status = NULL;
}
@@ -5876,6 +5917,9 @@ build_one_sep_impl(const char *src, int entry_is_dir,
if (!g->pkg[root].failed && sep_root_is_command(&g->pkg[root])
&& validate_command_output_path(products[i].out) < 0)
return 1;
if (products[i].publish != NULL
&& validate_command_output_path(products[i].publish) < 0)
return 1;
if (products[i].status != NULL
&& validate_command_output_path(products[i].status) < 0)
return 1;
@@ -5980,8 +6024,11 @@ build_one_sep_impl(const char *src, int entry_is_dir,
struct sep_created_dirs created_output = {0};
struct sep_created_dirs created_work = {0};
if (create_output_dir != NULL && create_output_dir[0] != '\0'
&& sep_mkdirs(create_output_dir, 0700, &created_output) != 0) {
fprintf(stderr, "ww: cannot create build output directory %s\n",
&& sep_mkdirs(create_output_dir, is_test ? 0777 : 0700,
&created_output) != 0) {
fprintf(stderr, is_test
? "ww: cannot create test output directory %s\n"
: "ww: cannot create build output directory %s\n",
create_output_dir);
free(order);
return 1;
@@ -6527,6 +6574,14 @@ build_one_sep_impl(const char *src, int entry_is_dir,
g->pkg[root].failed = 1;
goto request_fail;
}
if (products[i].publish != NULL
&& (products[i].stage_publish == NULL
|| copy_executable_stage(products[i].stage_out,
products[i].stage_publish) != 0)) {
fprintf(stderr, "ww: cannot stage test binary %s\n",
products[i].publish);
goto request_fail;
}
if (sep_stage_product_status(&products[i]) != 0) {
fprintf(stderr, "ww: cannot stage package-test product\n");
goto request_fail;
@@ -6594,6 +6649,10 @@ prepare_transaction:
&& sep_txn_add(&tx, products[i].stage_out,
products[i].out) < 0)
goto request_fail;
if (products[i].stage_publish != NULL
&& sep_txn_add(&tx, products[i].stage_publish,
products[i].publish) < 0)
goto request_fail;
if (products[i].stage_iface != NULL) {
char outiface[SEP_ARTIFACT_MAX];
int on = snprintf(outiface, sizeof outiface, "%s.wwi",
@@ -6655,6 +6714,7 @@ build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
.identity = root_identity,
.test_package = test_package,
.status = NULL,
.publish = NULL,
.artifact = NULL,
.variant = root_variant,
.root = -1,
@@ -7203,9 +7263,9 @@ do_test(int argc, char **argv)
}
package_create_output_dir = argv[++i];
} else if (strcmp(argv[i], "--ww-package-test") == 0) {
if (i + 8 >= argc) {
if (i + 9 >= argc) {
fprintf(stderr,
"ww test: --ww-package-test needs kind, package, production, internal, external, directory, output, and status\n");
"ww test: --ww-package-test needs kind, package, production, internal, external, directory, output, publication, and status\n");
return 2;
}
const char *kind = argv[++i];
@@ -7215,6 +7275,7 @@ do_test(int argc, char **argv)
const char *external = argv[++i];
const char *dir = argv[++i];
const char *output = argv[++i];
const char *publish = argv[++i];
const char *status = argv[++i];
size_t pn = strlen(name);
int build_product = strcmp(kind, "build") == 0;
@@ -7225,6 +7286,7 @@ do_test(int argc, char **argv)
if ((!build_product && !test_product)
|| pn == 0
|| dir[0] == '\0' || output[0] == '\0'
|| publish[0] == '\0'
|| status[0] == '\0'
|| (has_production && strcmp(production, name) != 0)
|| (has_internal && strcmp(internal, name) != 0)
@@ -7232,9 +7294,12 @@ do_test(int argc, char **argv)
|| strncmp(external, name, pn) != 0
|| strcmp(external + pn, "_test") != 0))
|| (build_product && (!has_production
|| has_internal || has_external))
|| has_internal || has_external
|| strcmp(publish, "-") != 0))
|| (test_product && !has_production
&& !has_internal && !has_external)) {
&& !has_internal && !has_external)
|| (test_product && !has_internal && !has_external
&& strcmp(publish, "-") != 0)) {
fprintf(stderr,
"ww test: invalid --ww-package-test product\n");
return 2;
@@ -7256,6 +7321,8 @@ do_test(int argc, char **argv)
products[nproducts].external_package = has_external
? external : NULL;
products[nproducts].status = status;
products[nproducts].publish = strcmp(publish, "-") == 0
? NULL : publish;
products[nproducts].artifact = NULL;
products[nproducts].variant = build_product
? SEP_VARIANT_PRODUCTION : SEP_VARIANT_TEST_MAIN;
@@ -7389,7 +7456,7 @@ do_test(int argc, char **argv)
fprintf(stderr, "ww test: invalid --ww-package-publish\n");
return 2;
}
if (package_create_output_dir != NULL && !package_build) {
if (package_create_output_dir != NULL && nproducts == 0) {
fprintf(stderr, "ww test: invalid private directory creation\n");
return 2;
}
@@ -7460,13 +7527,8 @@ do_test(int argc, char **argv)
"ww test: -S needs a single test file\n");
return 2;
}
/* -c -o forwards: the coordinator names the single
* package's artifact and rejects a multi-package fan-out. */
if (outstem[0] && !compileonly) {
fprintf(stderr,
"ww test: -o needs -c for a package target\n");
return 2;
}
/* The coordinator independently wires -o retention and -c run
* suppression after it has loaded the complete package set. */
/* -w forwards one caller-owned semantic-action store shared by
* the complete selected package universe. */
return exec_package_command(argc, argv, src, NULL, NULL, 0, 0);
@@ -7488,11 +7550,6 @@ do_test(int argc, char **argv)
"ww test: -S needs a single test file\n");
return 2;
}
if (outstem[0] && !compileonly) {
fprintf(stderr,
"ww test: -o needs -c for a package target\n");
return 2;
}
if (nproducts != 0) {
if (!compileonly) {
fprintf(stderr,
@@ -7656,10 +7713,6 @@ do_test(int argc, char **argv)
fprintf(stderr, "ww test: -S needs a single test file\n");
return 2;
}
if (outstem[0] && !compileonly) {
fprintf(stderr, "ww test: -o needs -c for a package target\n");
return 2;
}
if (nproducts != 0) {
if (!compileonly) {
fprintf(stderr,