ww: implement recursive local package patterns

This commit is contained in:
2026-08-13 23:42:37 +09:00
parent 5d50788e46
commit d5fdc27c0a
5 changed files with 1976 additions and 360 deletions

View File

@@ -24,9 +24,9 @@
static const char *usage =
"usage: ww [-V] <subcommand> [args...]\n"
" -V print version and exit\n"
" build [-S] [-w DIR] [-I DIR] [-o FILE] [path] build a local package graph\n"
" build [-S] [-w DIR] [-I DIR] [-o FILE] [path ...] build local package graphs\n"
" run [path] ... build then exec, passing extra args to the program\n"
" test [-S -o STEM] [-w DIR] [options] [path] build/run tests; -S emits package asm\n"
" test [-S -o STEM] [-w DIR] [options] [path ...] build/run tests; -S emits package asm\n"
" version print version and exit\n"
"\n"
" path forms:\n"
@@ -34,13 +34,14 @@ static const char *usage =
" foo search cwd, -I dirs, then the source library for foo.ww or foo/\n"
" lib/foo directory: build its package sources\n"
" -o publishes a non-main archive FILE + FILE.wwi\n"
" lib/... every package under lib, recursively (test only)\n"
" lib/... every eligible package under lib, recursively\n"
" . build the cwd's <basename>.ww\n";
static char *self_dir;
static const char *self_path;
static char *sep_sprintf(const char *, ...);
static int sep_reserve(void **, int *, int, size_t);
static int sep_fail_size(void);
static const char *
envpath(const char *name)
@@ -115,28 +116,38 @@ run_test_bin(const char *bin, const char *pattern)
* package root through `ww test -c ... package.ww`, so that file boundary also
* prevents delegation recursion. */
static int
exec_package_tests(int argc, char **argv, const char *target,
const char *resolved, const char *root_identity, int add_dot)
exec_package_command(int argc, char **argv, const char *target,
const char *resolved, const char *root_identity, int add_dot,
int build_only)
{
const char *override = getenv("WW_WWTEST");
char fallback[PATH_MAX];
const char *prog = override && override[0] ? override : fallback;
if (prog == fallback) {
int pn = snprintf(fallback, sizeof fallback, "%s/wwtest", self_dir);
if (pn < 0 || (size_t)pn >= sizeof fallback) {
fputs("ww test: package coordinator path is too long\n", stderr);
return 1;
}
char *fallback = NULL;
const char *prog = override && override[0] ? override : NULL;
if (prog == NULL) {
fallback = sep_sprintf("%s/wwtest", self_dir);
if (fallback == NULL) return 1;
prog = fallback;
}
char **xargv = calloc((size_t)argc + 8, sizeof *xargv);
if (argc < 0 || argc > INT_MAX - 10) {
free(fallback);
sep_fail_size();
return 1;
}
char **xargv = calloc((size_t)argc + 10, sizeof *xargv);
if (xargv == NULL) {
fputs("ww test: cannot allocate package coordinator arguments\n",
stderr);
fprintf(stderr,
"ww %s: cannot allocate package coordinator arguments\n",
build_only ? "build" : "test");
free(fallback);
return 1;
}
int n = 0, dotted = 0;
xargv[n++] = (char *)prog;
xargv[n++] = "package";
if (build_only) {
xargv[n++] = "--ww-operation";
xargv[n++] = "build";
}
xargv[n++] = "--ww-driver";
xargv[n++] = (char *)self_path;
if (root_identity != NULL) {
@@ -154,8 +165,10 @@ exec_package_tests(int argc, char **argv, const char *target,
if (add_dot && !dotted) xargv[n++] = ".";
xargv[n] = NULL;
execv(prog, xargv); /* inherit the caller's environment */
fputs("ww test: cannot exec package test coordinator\n", stderr);
fprintf(stderr, "ww %s: cannot exec package coordinator\n",
build_only ? "build" : "test");
free(xargv);
free(fallback);
return 1;
}
@@ -558,6 +571,7 @@ enumerate_dir_ww(const char *dirpath, int variant, const char *test_package,
const char *nm = ent->d_name;
size_t nl = strlen(nm);
if (nl <= 3) continue;
if (nm[0] == '.' || nm[0] == '_') continue;
if (strcmp(nm + nl - 3, ".ww") != 0) continue;
int is_test = nl >= 8 && strcmp(nm + nl - 8, "_test.ww") == 0;
if (variant == SEP_VARIANT_PRODUCTION && is_test) continue;
@@ -573,7 +587,30 @@ enumerate_dir_ww(const char *dirpath, int variant, const char *test_package,
return -2;
}
struct stat st;
if (lstat(path, &st) != 0 || !S_ISREG(st.st_mode)) {
if (lstat(path, &st) != 0) {
fprintf(stderr,
"ww: %s: package source is not a regular file\n", path);
source_list_free(prod, nprod);
source_list_free(tests, ntests);
closedir(d);
*out_files = NULL;
return -2;
}
if (S_ISLNK(st.st_mode)) {
if (stat(path, &st) != 0) {
fprintf(stderr,
"ww: %s: package source is not a regular file\n",
path);
source_list_free(prod, nprod);
source_list_free(tests, ntests);
closedir(d);
*out_files = NULL;
return -2;
}
/* go/build ignores a source-shaped symlink to a directory. */
if (S_ISDIR(st.st_mode)) continue;
}
if (!S_ISREG(st.st_mode)) {
fprintf(stderr,
"ww: %s: package source is not a regular file\n", path);
source_list_free(prod, nprod);
@@ -3553,6 +3590,16 @@ validate_package_output_path(const char *out)
return 0;
}
static int
validate_command_output_path(const char *out)
{
if (out == NULL || strlen(out) + 1 > (size_t)PATH_MAX) {
fprintf(stderr, "ww: command output path is too long\n");
return -1;
}
return 0;
}
/* A coordinator-private completion marker distinguishes a newly linked
* product from a caller-owned binary left behind by an earlier invocation. */
static int
@@ -3611,6 +3658,66 @@ invalidate_workdir_units(const char *scratch)
return rc;
}
struct sep_created_dirs {
char path[PATH_MAX];
unsigned short offset[(PATH_MAX + 1) / 2];
int n;
};
static void
sep_rollback_dirs(struct sep_created_dirs *created)
{
while (created->n > 0) {
size_t at = created->offset[--created->n];
char saved = created->path[at];
created->path[at] = '\0';
(void)rmdir(created->path);
created->path[at] = saved;
}
}
/* Create one caller-requested directory tree after semantic preflight. Record
* exactly the prefixes minted by this call so a later pre-producer setup
* failure can roll them back without touching pre-existing caller state. */
static int
sep_mkdirs(const char *path, mode_t mode, struct sep_created_dirs *created)
{
memset(created, 0, sizeof *created);
size_t n = strlen(path);
if (n == 0 || n >= PATH_MAX) return -1;
char buf[PATH_MAX];
memcpy(buf, path, n + 1);
while (n > 1 && buf[n - 1] == '/') buf[--n] = '\0';
memcpy(created->path, buf, n + 1);
for (char *p = buf + (buf[0] == '/' ? 1 : 0); ; p++) {
if (*p != '/' && *p != '\0') continue;
char saved = *p;
*p = '\0';
if (buf[0] != '\0') {
struct stat st;
if (stat(buf, &st) != 0) {
if (errno != ENOENT
|| created->n >= (int)(sizeof created->offset
/ sizeof created->offset[0])
|| mkdir(buf, mode) != 0) {
*p = saved;
sep_rollback_dirs(created);
return -1;
}
created->offset[created->n++] =
(unsigned short)(p - buf);
} else if (!S_ISDIR(st.st_mode)) {
*p = saved;
sep_rollback_dirs(created);
return -1;
}
}
*p = saved;
if (saved == '\0') break;
}
return 0;
}
/* build_sep_plan — discover dependencies for every requested product in one
* package universe, compile the dependency-first union once, then link each
* root from its own complete reachable archive closure. The dependency-first
@@ -3625,7 +3732,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
const struct seplinkflags *linkflags, int publish_package,
int require_command, int is_test,
struct sepproduct *products, int nproducts, int emit_asm,
const char *workdir, char *scratchout, size_t scratchoutsz,
const char *workdir, int create_workdir, const char *create_output_dir,
char *scratchout, size_t scratchoutsz,
struct sepgraph **graphout)
{
sep_fatal_allocation = 0;
@@ -3679,10 +3787,14 @@ build_one_sep_impl(const char *src, int entry_is_dir,
if (stem == NULL) return 1;
const char *ostem = (objstem && objstem[0]) ? objstem : stem;
int warm = workdir != NULL && workdir[0] != 0;
int workdir_exists = 0;
char scratch[PATH_MAX];
if (warm) {
struct stat wst;
if (stat(workdir, &wst) != 0 || !S_ISDIR(wst.st_mode)) {
int wr = stat(workdir, &wst);
if (wr == 0 && S_ISDIR(wst.st_mode)) {
workdir_exists = 1;
} else if (wr == 0 || !create_workdir || errno != ENOENT) {
fprintf(stderr, "ww: workdir %s is not a directory\n",
workdir);
return 1;
@@ -3690,7 +3802,10 @@ build_one_sep_impl(const char *src, int entry_is_dir,
/* The workdir is caller-owned and persistent: no acquisition,
* no refusal, and scratchout stays empty so the wrapper never
* cleans it. */
if (strlen(workdir) + 1 > sizeof scratch) return 1;
if (strlen(workdir) + 1 > sizeof scratch) {
fprintf(stderr, "ww: workdir path is too long\n");
return 1;
}
memcpy(scratch, workdir, strlen(workdir) + 1);
} else {
int n = snprintf(scratch, sizeof scratch, "%s.sepwork", ostem);
@@ -3930,12 +4045,18 @@ build_one_sep_impl(const char *src, int entry_is_dir,
products[i].root = mainpkg;
}
}
int root_package = !is_test
for (int i = 0; i < nproducts; i++) {
int root = products[i].root;
if (!g->pkg[root].failed && sep_root_is_command(&g->pkg[root])
&& validate_command_output_path(products[i].out) < 0)
return 1;
}
int root_package = !is_test && nproducts == 1
&& !g->pkg[products[0].root].failed
&& !sep_root_is_command(&g->pkg[products[0].root]);
if (sep_validate_artifact_paths(g, scratch) < 0)
return 1;
if (warm && sep_validate_workdir_owners(g, scratch) < 0)
if (warm && workdir_exists && sep_validate_workdir_owners(g, scratch) < 0)
return 1;
int *order = calloc((size_t)g->n, sizeof *order);
int *stack = calloc((size_t)g->n, sizeof *stack);
@@ -4004,9 +4125,30 @@ build_one_sep_impl(const char *src, int entry_is_dir,
/* Product completion and persistent package state remain untouched until
* all source-derived imports, contextual legality, cycles, command kind,
* output paths, and action closures have passed their pre-tool checks. */
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",
create_output_dir);
free(order);
return 1;
}
if (warm && !workdir_exists) {
if (!create_workdir
|| sep_mkdirs(scratch, 0700, &created_work) != 0) {
fprintf(stderr, "ww: workdir %s is not a directory\n", scratch);
sep_rollback_dirs(&created_output);
free(order);
return 1;
}
workdir_exists = 1;
}
if (!warm) {
if (mkdir(scratch, 0755) != 0) {
fprintf(stderr, "ww: cannot create scratch %s\n", scratch);
sep_rollback_dirs(&created_work);
sep_rollback_dirs(&created_output);
free(order);
return 1;
}
@@ -4251,7 +4393,19 @@ build_one_sep_impl(const char *src, int entry_is_dir,
}
}
}
if (emit_asm) { free(order); return any_failed ? 1 : 0; }
if (emit_asm) {
for (int i = 0; i < nproducts; i++) {
int root = products[i].root;
if (g->pkg[root].failed) { any_failed = 1; continue; }
if (record_product_status(products[i].status) != 0) {
fprintf(stderr, "ww: cannot record package-build product\n");
g->pkg[root].failed = 1;
any_failed = 1;
}
}
free(order);
return any_failed ? 1 : 0;
}
if (root_package) {
int root = products[0].root;
if (g->pkg[root].failed) { free(order); return 1; }
@@ -4274,6 +4428,11 @@ build_one_sep_impl(const char *src, int entry_is_dir,
return 1;
}
}
if (record_product_status(products[0].status) != 0) {
fprintf(stderr, "ww: cannot record package-build product\n");
free(order);
return 1;
}
free(order);
return 0;
}
@@ -4303,6 +4462,15 @@ build_one_sep_impl(const char *src, int entry_is_dir,
int root = products[i].root;
int variant_root = products[i].variant_root;
if (g->pkg[root].failed) { any_failed = 1; continue; }
if (!is_test && !sep_root_is_command(&g->pkg[root])) {
if (record_product_status(products[i].status) != 0) {
fprintf(stderr,
"ww: cannot record package-build product\n");
g->pkg[root].failed = 1;
any_failed = 1;
}
continue;
}
for (int pi = 0; pi < g->n; pi++) g->pkg[pi].color = 0;
int *linkorder = calloc((size_t)g->n, sizeof *linkorder);
int *linkstack = calloc((size_t)g->n, sizeof *linkstack);
@@ -4422,7 +4590,7 @@ build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
product.artifact = "__root";
int r = build_one_sep_impl(src, entry_is_dir, out, objstem,
extra_includes, linkflags, publish_package, require_command, is_test,
&product, 1, emit_asm, workdir, scratch,
&product, 1, emit_asm, workdir, 0, NULL, scratch,
sizeof scratch, &g);
sep_graph_free(g);
if (!keepscratch && scratch[0]) {
@@ -4456,15 +4624,18 @@ build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
static int
build_package_tests(const char *src, const char *root_identity,
const char *extra_includes, const char *workdir,
struct sepproduct *products, int nproducts)
struct sepproduct *products, int nproducts, int is_test,
int publish_package, const struct seplinkflags *linkflags, int emit_asm,
int create_workdir, const char *create_output_dir)
{
char scratch[PATH_MAX] = {0};
struct sepgraph *g = NULL;
for (int i = 0; i < nproducts; i++)
products[i].identity = root_identity;
int r = build_one_sep_impl(src, 1, products[0].out,
products[0].out, extra_includes, NULL, 0, 0, 1,
products, nproducts, 0, workdir, scratch, sizeof scratch, &g);
products[0].out, extra_includes, linkflags, publish_package, 0, is_test,
products, nproducts, emit_asm, workdir, create_workdir,
create_output_dir, scratch, sizeof scratch, &g);
sep_graph_free(g);
return r;
}
@@ -4607,13 +4778,18 @@ parse_build_flags(const char *cmd, int argc, char **argv,
struct seplinkflags *linkflags,
char *outpath, size_t outsz,
char *workdir, size_t workdirsz,
const char **src_out, int *emit_asm_out)
const char **src_out, int *emit_asm_out, int *terminator_out)
{
*src_out = NULL;
if (emit_asm_out) *emit_asm_out = 0;
if (terminator_out) *terminator_out = 0;
int i = 0;
for (; i < argc; i++) {
if (strcmp(argv[i], "-S") == 0) {
if (strcmp(cmd, "build") == 0 && strcmp(argv[i], "--") == 0) {
if (terminator_out) *terminator_out = 1;
i++;
break;
} else if (strcmp(argv[i], "-S") == 0) {
if (emit_asm_out == NULL) {
fprintf(stderr, "ww %s: unknown flag\n", cmd);
return -1;
@@ -4699,6 +4875,12 @@ parse_build_flags(const char *cmd, int argc, char **argv,
return -1;
} else if (*src_out == NULL) {
*src_out = argv[i];
/* Go's FlagSet stops at the first positional. For build,
* everything after it is package-request input, even "--". */
if (strcmp(cmd, "build") == 0) {
i++;
break;
}
} else {
break; /* leave remaining argv to caller (run-args) */
}
@@ -4717,14 +4899,20 @@ do_build(int argc, char **argv)
char outflag[PATH_MAX] = {0};
char workdir[PATH_MAX] = {0};
int emit_asm = 0;
if (parse_build_flags("build", argc, argv, incs, incsz,
int saw_terminator = 0;
int next = parse_build_flags("build", argc, argv, incs, incsz,
&linkflags,
outflag, sizeof outflag, workdir, sizeof workdir,
&src, &emit_asm) < 0) {
&src, &emit_asm, &saw_terminator);
if (next < 0) {
free(incs);
return 2;
}
if (src == NULL) src = ".";
if (strstr(src, "...") != NULL || next < argc || saw_terminator) {
free(incs);
return exec_package_command(argc, argv, src, NULL, NULL, 0, 1);
}
struct stat requested;
int literal = stat(src, &requested) == 0;
char resolved[PATH_MAX];
@@ -4770,7 +4958,7 @@ do_run(int argc, char **argv)
char outflag[PATH_MAX] = {0}; /* -o accepted+ignored: run always uses the temp */
int next = parse_build_flags("run", argc, argv, incs, incsz,
&linkflags,
outflag, sizeof outflag, NULL, 0, &src, NULL);
outflag, sizeof outflag, NULL, 0, &src, NULL, NULL);
if (next < 0) { free(incs); return 2; }
if (src == NULL) src = ".";
struct stat requested;
@@ -4865,6 +5053,11 @@ do_test(int argc, char **argv)
char outstem[PATH_MAX] = {0};
char workdir[PATH_MAX] = {0};
int packageopts = 0;
int package_build = 0;
int package_publish = 0;
int package_create_workdir = 0;
const char *package_create_output_dir = NULL;
struct seplinkflags package_linkflags = {0};
int afterdash = 0;
const char *request_identity = NULL;
/* #17: an optional second positional after the target is a fnmatch
@@ -4904,6 +5097,36 @@ do_test(int argc, char **argv)
return 2;
}
request_identity = argv[++i];
} else if (strcmp(argv[i], "--ww-package-build") == 0) {
if (package_build) {
fprintf(stderr,
"ww test: invalid --ww-package-build\n");
return 2;
}
package_build = 1;
compileonly = 1;
} else if (strcmp(argv[i], "--ww-package-publish") == 0) {
if (package_publish) {
fprintf(stderr,
"ww test: invalid --ww-package-publish\n");
return 2;
}
package_publish = 1;
} else if (strcmp(argv[i], "--ww-create-workdir") == 0) {
if (package_create_workdir) {
fprintf(stderr,
"ww test: invalid --ww-create-workdir\n");
return 2;
}
package_create_workdir = 1;
} else if (strcmp(argv[i], "--ww-create-output-dir") == 0) {
if (i + 1 >= argc || package_create_output_dir != NULL
|| argv[i + 1][0] == '\0') {
fprintf(stderr,
"ww test: invalid --ww-create-output-dir\n");
return 2;
}
package_create_output_dir = argv[++i];
} else if (strcmp(argv[i], "--ww-package-test") == 0) {
if (i + 5 >= argc) {
fprintf(stderr,
@@ -4954,6 +5177,40 @@ do_test(int argc, char **argv)
nproducts++;
} else if (strcmp(argv[i], "-S") == 0) {
emit_asm = 1;
} else if (package_build
&& strncmp(argv[i], "-l", 2) == 0 && argv[i][2]) {
if (package_linkflags.nlibs >= SEP_MAXLFLAGS) {
fprintf(stderr, "ww test: too many -l\n");
return 2;
}
package_linkflags.libs[package_linkflags.nlibs++] = argv[i] + 2;
} else if (package_build && strcmp(argv[i], "-l") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "ww test: -l needs an argument\n");
return 2;
}
if (package_linkflags.nlibs >= SEP_MAXLFLAGS) {
fprintf(stderr, "ww test: too many -l\n");
return 2;
}
package_linkflags.libs[package_linkflags.nlibs++] = argv[++i];
} else if (package_build && strcmp(argv[i], "-L") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "ww test: -L needs an argument\n");
return 2;
}
if (package_linkflags.nlibdirs >= SEP_MAXLFLAGS) {
fprintf(stderr, "ww test: too many -L\n");
return 2;
}
package_linkflags.libdirs[package_linkflags.nlibdirs++] = argv[++i];
} else if (package_build
&& strncmp(argv[i], "-L", 2) == 0 && argv[i][2]) {
if (package_linkflags.nlibdirs >= SEP_MAXLFLAGS) {
fprintf(stderr, "ww test: too many -L\n");
return 2;
}
package_linkflags.libdirs[package_linkflags.nlibdirs++] = argv[i] + 2;
} else if (strcmp(argv[i], "-list") == 0) {
packageopts = 1;
} else if (strcmp(argv[i], "-j") == 0 ||
@@ -5002,7 +5259,7 @@ do_test(int argc, char **argv)
}
}
const char *target = src ? src : ".";
if (emit_asm && !outstem[0]) {
if (emit_asm && !outstem[0] && nproducts == 0) {
fprintf(stderr, "ww test: -S needs -o\n");
return 2;
}
@@ -5033,17 +5290,66 @@ do_test(int argc, char **argv)
"ww test: package-test variant rejects package options\n");
return 2;
}
if (package_build && nproducts == 0) {
fprintf(stderr, "ww test: --ww-package-build needs products\n");
return 2;
}
if (package_publish && (!package_build || nproducts != 1)) {
fprintf(stderr, "ww test: invalid --ww-package-publish\n");
return 2;
}
if (package_create_output_dir != NULL && !package_build) {
fprintf(stderr, "ww test: invalid private directory creation\n");
return 2;
}
if (package_create_workdir && nproducts == 0) {
fprintf(stderr, "ww test: invalid private directory creation\n");
return 2;
}
if (package_create_workdir && !workdir[0]) {
fprintf(stderr, "ww test: --ww-create-workdir needs -w\n");
return 2;
}
if (!package_build && (package_linkflags.nlibdirs != 0
|| package_linkflags.nlibs != 0)) {
fprintf(stderr, "ww test: unknown flag\n");
return 2;
}
if (package_build) {
for (int i = 0; i < nproducts; i++) {
if (products[i].variant != SEP_VARIANT_PRODUCTION) {
fprintf(stderr,
"ww test: package-build products must be production\n");
return 2;
}
}
}
if (pattern != NULL) {
struct stat first;
if (stat(target, &first) != 0 || !S_ISREG(first.st_mode)) {
char first_resolved[PATH_MAX];
int first_is_dir = 0;
if (stat(target, &first) == 0
|| !resolve_module(target, incs, first_resolved,
sizeof first_resolved, &first_is_dir)
|| first_is_dir) {
return exec_package_command(argc, argv, src,
first_is_dir ? first_resolved : NULL,
first_is_dir ? target : request_identity,
0, 0);
}
/* A logical import that resolves to one regular source keeps
* the historical second-positional test-name filter. */
}
}
if (nproducts != 0 && outstem[0]) {
fprintf(stderr,
"ww test: package-test products reject -o\n");
return 2;
}
/* Go's ./... form: a trailing "..." element is a package-tree
* request for the coordinator, never a literal path — recognized
* before stat, with the directory-mode rejects. */
size_t tlen = strlen(target);
if (strcmp(target, "...") == 0 ||
(tlen >= 4 && strcmp(target + tlen - 4, "/...") == 0)) {
/* Every local spelling containing "..." is a package pattern. It enters
* request selection before literal-path or logical-import resolution. */
if (strstr(target, "...") != NULL) {
if (nproducts != 0) {
fprintf(stderr,
"ww test: package-test variant needs one directory\n");
@@ -5061,14 +5367,9 @@ do_test(int argc, char **argv)
"ww test: -o needs -c for a package target\n");
return 2;
}
if (pattern) {
fprintf(stderr,
"ww test: pattern needs a single test file\n");
return 2;
}
/* -w forwards: the coordinator keys one persistent driver
* workdir for the complete selected test request. */
return exec_package_tests(argc, argv, src, NULL, NULL, 0);
/* -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);
}
struct stat st;
if (stat(target, &st) != 0) {
@@ -5082,7 +5383,7 @@ do_test(int argc, char **argv)
return 1;
}
if (is_dir) {
if (emit_asm) {
if (emit_asm && nproducts == 0) {
fprintf(stderr,
"ww test: -S needs a single test file\n");
return 2;
@@ -5092,11 +5393,6 @@ do_test(int argc, char **argv)
"ww test: -o needs -c for a package target\n");
return 2;
}
if (pattern) {
fprintf(stderr,
"ww test: pattern needs a single test file\n");
return 2;
}
if (nproducts != 0) {
if (!compileonly) {
fprintf(stderr,
@@ -5104,13 +5400,16 @@ do_test(int argc, char **argv)
return 2;
}
int r = build_package_tests(resolved, request_identity,
incs, workdir, products, nproducts);
incs, workdir, products, nproducts,
package_build ? 0 : 1, package_publish,
package_build ? &package_linkflags : NULL, emit_asm,
package_create_workdir, package_create_output_dir);
free(products);
free(incs);
return r;
}
return exec_package_tests(argc, argv, src, resolved,
request_identity != NULL ? request_identity : target, 0);
return exec_package_command(argc, argv, src, resolved,
request_identity != NULL ? request_identity : target, 0, 0);
}
if (packageopts) {
fprintf(stderr,
@@ -5253,7 +5552,7 @@ do_test(int argc, char **argv)
fprintf(stderr, "ww test: %s is neither file nor directory\n", target);
return 1;
}
if (emit_asm) {
if (emit_asm && nproducts == 0) {
fprintf(stderr, "ww test: -S needs a single test file\n");
return 2;
}
@@ -5261,12 +5560,6 @@ do_test(int argc, char **argv)
fprintf(stderr, "ww test: -o needs -c for a package target\n");
return 2;
}
/* The second bare positional remains the legacy single-file filter form;
* package filtering uses explicit -run/-filter options. */
if (pattern) {
fprintf(stderr, "ww test: pattern needs a single test file\n");
return 2;
}
if (nproducts != 0) {
if (!compileonly) {
fprintf(stderr,
@@ -5274,13 +5567,15 @@ do_test(int argc, char **argv)
return 2;
}
int r = build_package_tests(target, request_identity, incs, workdir,
products, nproducts);
products, nproducts, package_build ? 0 : 1,
package_publish, package_build ? &package_linkflags : NULL,
emit_asm, package_create_workdir, package_create_output_dir);
free(products);
free(incs);
return r;
}
return exec_package_tests(argc, argv, src, NULL, request_identity,
src == NULL);
return exec_package_command(argc, argv, src, NULL, request_identity,
src == NULL, 0);
}
int