ww: implement recursive local package patterns
This commit is contained in:
429
cmd/ww/main.c
429
cmd/ww/main.c
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user