ww: canonicalize directory package identities
This commit is contained in:
322
cmd/ww/main.c
322
cmd/ww/main.c
@@ -107,14 +107,14 @@ run_test_bin(const char *bin, const char *pattern)
|
||||
* prevents delegation recursion. */
|
||||
static int
|
||||
exec_package_tests(int argc, char **argv, const char *target,
|
||||
const char *resolved, int add_dot)
|
||||
const char *resolved, const char *root_identity, int add_dot)
|
||||
{
|
||||
const char *override = getenv("WW_WWTEST");
|
||||
char fallback[1024];
|
||||
const char *prog = override && override[0] ? override : fallback;
|
||||
if (prog == fallback)
|
||||
snprintf(fallback, sizeof fallback, "%s/wwtest", self_dir);
|
||||
char **xargv = calloc((size_t)argc + 6, sizeof *xargv);
|
||||
char **xargv = calloc((size_t)argc + 8, sizeof *xargv);
|
||||
if (xargv == NULL) {
|
||||
fputs("ww test: cannot allocate package coordinator arguments\n",
|
||||
stderr);
|
||||
@@ -125,6 +125,10 @@ exec_package_tests(int argc, char **argv, const char *target,
|
||||
xargv[n++] = "package";
|
||||
xargv[n++] = "--ww-driver";
|
||||
xargv[n++] = (char *)self_path;
|
||||
if (root_identity != NULL) {
|
||||
xargv[n++] = "--ww-root-identity";
|
||||
xargv[n++] = (char *)root_identity;
|
||||
}
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (add_dot && !dotted && strcmp(argv[i], "--") == 0) {
|
||||
xargv[n++] = ".";
|
||||
@@ -148,6 +152,8 @@ struct ImportSet {
|
||||
int n, cap;
|
||||
};
|
||||
|
||||
#define SEP_LOCAL_IMPORT_PREFIX "__wwlocal"
|
||||
|
||||
static int
|
||||
import_seen(struct ImportSet *s, const char *path)
|
||||
{
|
||||
@@ -178,6 +184,14 @@ import_path_form(const char *name, char *out, size_t outsz)
|
||||
out[i] = '\0';
|
||||
}
|
||||
|
||||
static int
|
||||
reserved_import_path(const char *name)
|
||||
{
|
||||
size_t n = strlen(SEP_LOCAL_IMPORT_PREFIX);
|
||||
return strncmp(name, SEP_LOCAL_IMPORT_PREFIX, n) == 0
|
||||
&& (name[n] == '\0' || name[n] == '.');
|
||||
}
|
||||
|
||||
/* An import path names one directory package. There is deliberately no
|
||||
* <dir>/<path>.ww branch here: literal or searched single-file roots are a
|
||||
* CLI compatibility concern handled by locate_module, never an import edge. */
|
||||
@@ -554,6 +568,7 @@ struct sepgraph {
|
||||
struct sepproduct {
|
||||
const char *dir;
|
||||
const char *out;
|
||||
const char *identity; /* explicit canonical lookup identity, if any */
|
||||
const char *test_package;
|
||||
const char *status;
|
||||
char artifact[64];
|
||||
@@ -607,6 +622,22 @@ sep_diag_directory_identities(const char *entry, const char *a, const char *b)
|
||||
entry, a, b);
|
||||
}
|
||||
|
||||
static int sep_import_component(const char *, size_t);
|
||||
|
||||
static int
|
||||
sep_import_base_valid(const char *path)
|
||||
{
|
||||
const char *p = path;
|
||||
while (*p != '\0') {
|
||||
const char *dot = strchr(p, '.');
|
||||
size_t n = dot != NULL ? (size_t)(dot - p) : strlen(p);
|
||||
if (!sep_import_component(p, n)) return 0;
|
||||
if (dot == NULL) return 1;
|
||||
p = dot + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bind the canonical ordinary import identity of one provisional directory
|
||||
* action. The action's compiler path is derived from that base and its semantic
|
||||
* variant; neither requested-root state nor artifact naming participates. */
|
||||
@@ -620,6 +651,10 @@ sep_bind_import_base(struct sepgraph *g, int pi, const char *base)
|
||||
SEP_IMPORT_PATH_MAX - 1);
|
||||
return -1;
|
||||
}
|
||||
if (!reserved_import_path(base) && !sep_import_base_valid(base)) {
|
||||
fprintf(stderr, "ww: invalid package path %s\n", base);
|
||||
return -1;
|
||||
}
|
||||
if (p->import_base[0] != '\0') {
|
||||
if (strcmp(p->import_base, base) == 0) return 0;
|
||||
g->identity_failed = 1;
|
||||
@@ -996,12 +1031,6 @@ sep_external_production_name(const struct seppkg *pkg, const char *path,
|
||||
&& strcmp(pkg->test_package + n, "_test") == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sep_external_production_import(const struct seppkg *pkg, const char *path)
|
||||
{
|
||||
return sep_external_production_name(pkg, path, 0);
|
||||
}
|
||||
|
||||
/* Canonical bindings make a shared package independent of which selected
|
||||
* root reaches it first. Directory bindings and the raw-file inline
|
||||
* compatibility binding are part of the package action's source meaning. */
|
||||
@@ -1126,6 +1155,11 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
||||
const char *name = u->usepath ? u->usepath : u->str;
|
||||
if (previous && strcmp(previous, name) == 0) continue;
|
||||
previous = name;
|
||||
if (reserved_import_path(name)) {
|
||||
errorf(u->pos, "package path %s is reserved", name);
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
if (strlen(name) >= sizeof g->pkg[0].path) {
|
||||
errorf(u->pos, "import path is too long (limit %zu bytes)",
|
||||
sizeof g->pkg[0].path - 1);
|
||||
@@ -1140,16 +1174,21 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
||||
break;
|
||||
}
|
||||
char ipath[1024];
|
||||
int external_production = sep_external_production_import(
|
||||
&g->pkg[pi], name);
|
||||
int located = 0;
|
||||
if (external_production) {
|
||||
snprintf(ipath, sizeof ipath, "%s", g->pkg[pi].entry);
|
||||
located = 1;
|
||||
} else {
|
||||
int external_production = 0;
|
||||
/* A selected external logical root is already an exact resolved
|
||||
* path/directory pair. Its source import of that same full ordinary
|
||||
* identity reuses the colocated production action. No declaration leaf
|
||||
* or unrelated cached package can override normal context lookup. */
|
||||
const char *bound = g->pkg[pi].variant == SEP_VARIANT_EXTERNAL
|
||||
&& g->pkg[pi].import_base[0] != '\0'
|
||||
&& strcmp(g->pkg[pi].import_base, name) == 0
|
||||
? g->pkg[pi].canon : NULL;
|
||||
int located = bound != NULL;
|
||||
if (located)
|
||||
snprintf(ipath, sizeof ipath, "%s", bound);
|
||||
else
|
||||
located = locate_import(searchpath, path_form, ipath,
|
||||
sizeof ipath);
|
||||
}
|
||||
if (!located) {
|
||||
const char *dot = strrchr(name, '.');
|
||||
const char *leaf = dot ? dot + 1 : name;
|
||||
@@ -1188,7 +1227,7 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
||||
external_production = 1;
|
||||
if (self && !external_production) {
|
||||
const char *owner = g->pkg[pi].path[0]
|
||||
? g->pkg[pi].path : g->pkg[pi].name;
|
||||
? g->pkg[pi].path : g->pkg[pi].canon;
|
||||
errorf(u->pos, "self-import: package '%s' cannot import itself",
|
||||
owner[0] ? owner : "(root)");
|
||||
rc = -1;
|
||||
@@ -1399,7 +1438,7 @@ sep_load_pkg(struct sepgraph *g, int pi, int context)
|
||||
if (rc < 0) {
|
||||
fprintf(stderr,
|
||||
"ww: package %s resolves imports differently in %s and %s\n",
|
||||
g->pkg[pi].path[0] ? g->pkg[pi].path : g->pkg[pi].name,
|
||||
g->pkg[pi].path[0] ? g->pkg[pi].path : g->pkg[pi].canon,
|
||||
g->context[g->pkg[pi].emit_context].root,
|
||||
g->context[context].root);
|
||||
}
|
||||
@@ -1430,13 +1469,177 @@ sep_load_pkg(struct sepgraph *g, int pi, int context)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Finalize every provisional directory root after source discovery but before
|
||||
* generated-main construction or compilation. A source import binding wins;
|
||||
* otherwise a manifest-free literal root uses its validated declared package
|
||||
* name (external p_test roots bind the ordinary base p). */
|
||||
static int
|
||||
sep_import_component(const char *s, size_t n)
|
||||
{
|
||||
if (n == 0 || !((s[0] >= 'a' && s[0] <= 'z')
|
||||
|| (s[0] >= 'A' && s[0] <= 'Z') || s[0] == '_'))
|
||||
return 0;
|
||||
for (size_t i = 1; i < n; i++)
|
||||
if (!((s[i] >= 'a' && s[i] <= 'z')
|
||||
|| (s[i] >= 'A' && s[i] <= 'Z')
|
||||
|| (s[i] >= '0' && s[i] <= '9') || s[i] == '_'))
|
||||
return 0;
|
||||
return kwlookup(s, (u64)n) == TK_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
sep_import_path_from_relative(const char *rel, char *out, size_t outsz)
|
||||
{
|
||||
size_t off = 0;
|
||||
const char *p = rel;
|
||||
while (*p != '\0') {
|
||||
const char *slash = strchr(p, '/');
|
||||
size_t n = slash ? (size_t)(slash - p) : strlen(p);
|
||||
if (!sep_import_component(p, n)) return 0;
|
||||
if (off + n + (slash != NULL) + 1 > outsz) return -1;
|
||||
memcpy(out + off, p, n);
|
||||
off += n;
|
||||
if (slash == NULL) break;
|
||||
out[off++] = '.';
|
||||
p = slash + 1;
|
||||
}
|
||||
out[off] = '\0';
|
||||
return off != 0;
|
||||
}
|
||||
|
||||
/* A reverse candidate is authoritative only when the normal ordered forward
|
||||
* lookup selects this exact canonical directory. This prevents a later or
|
||||
* nested source root from manufacturing an alias shadowed by an earlier root. */
|
||||
static int
|
||||
sep_reverse_import_base(const struct sepgraph *g, const struct seppkg *pkg,
|
||||
int context, char *out, size_t outsz)
|
||||
{
|
||||
const char *searchpath = g->context[context].searchpath;
|
||||
const char *p = searchpath;
|
||||
while (*p != '\0') {
|
||||
const char *e = strchr(p, ':');
|
||||
size_t n = e ? (size_t)(e - p) : strlen(p);
|
||||
char *root = malloc(n + 1);
|
||||
if (root == NULL) return -1;
|
||||
memcpy(root, p, n);
|
||||
root[n] = '\0';
|
||||
char *canon = n == 0 ? NULL : realpath(root, NULL);
|
||||
free(root);
|
||||
if (canon != NULL) {
|
||||
size_t rn = strlen(canon);
|
||||
const char *rel = NULL;
|
||||
if (rn == 1 && canon[0] == '/' && pkg->canon[0] == '/'
|
||||
&& pkg->canon[1] != '\0')
|
||||
rel = pkg->canon + 1;
|
||||
else if (strncmp(pkg->canon, canon, rn) == 0
|
||||
&& pkg->canon[rn] == '/' && pkg->canon[rn + 1] != '\0')
|
||||
rel = pkg->canon + rn + 1;
|
||||
if (rel != NULL) {
|
||||
int ir = sep_import_path_from_relative(rel, out, outsz);
|
||||
if (ir < 0) {
|
||||
fprintf(stderr,
|
||||
"ww: package path is too long (limit %d bytes)\n",
|
||||
SEP_IMPORT_PATH_MAX - 1);
|
||||
free(canon);
|
||||
return -1;
|
||||
}
|
||||
if (ir > 0 && reserved_import_path(out)) ir = 0;
|
||||
if (ir > 0) {
|
||||
char located[1024];
|
||||
if (locate_import(searchpath, rel, located,
|
||||
sizeof located)) {
|
||||
char *selected = realpath(located, NULL);
|
||||
int same = selected != NULL
|
||||
&& strcmp(selected, pkg->canon) == 0;
|
||||
free(selected);
|
||||
if (same) { free(canon); return 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
free(canon);
|
||||
}
|
||||
if (!e) break;
|
||||
p = e + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sep_ordinary_declared_name(const struct seppkg *p, char *out, size_t outsz)
|
||||
{
|
||||
if (p->name[0] == '\0') return -1;
|
||||
size_t n = strlen(p->name);
|
||||
if (p->variant == SEP_VARIANT_EXTERNAL) {
|
||||
if (n <= 5 || strcmp(p->name + n - 5, "_test") != 0) {
|
||||
fprintf(stderr,
|
||||
"ww: package-test selector does not name an external package\n");
|
||||
return -1;
|
||||
}
|
||||
n -= 5;
|
||||
}
|
||||
if (n + 1 > outsz) return -1;
|
||||
memcpy(out, p->name, n);
|
||||
out[n] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The reserved local namespace is reversible, so filesystem identity never
|
||||
* depends on a hash, request order, output name, or another selected package. */
|
||||
static int
|
||||
sep_local_import_base(const struct seppkg *p, char *out, size_t outsz)
|
||||
{
|
||||
char leaf[sizeof p->name];
|
||||
if (sep_ordinary_declared_name(p, leaf, sizeof leaf) < 0) return -1;
|
||||
size_t off = 0;
|
||||
int n = snprintf(out, outsz, "%s.p", SEP_LOCAL_IMPORT_PREFIX);
|
||||
if (n < 0 || (size_t)n >= outsz) return -1;
|
||||
off = (size_t)n;
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
for (const unsigned char *s = (const unsigned char *)p->canon;
|
||||
*s != '\0'; s++) {
|
||||
unsigned char c = *s;
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||
|| (c >= '0' && c <= '9')) {
|
||||
if (off + 1 >= outsz) return -1;
|
||||
out[off++] = (char)c;
|
||||
} else if (c == '_' || c == '/') {
|
||||
if (off + 2 >= outsz) return -1;
|
||||
out[off++] = '_';
|
||||
out[off++] = c == '_' ? 'u' : 's';
|
||||
} else {
|
||||
if (off + 4 >= outsz) return -1;
|
||||
out[off++] = '_';
|
||||
out[off++] = 'x';
|
||||
out[off++] = hex[c >> 4];
|
||||
out[off++] = hex[c & 15];
|
||||
}
|
||||
}
|
||||
size_t ln = strlen(leaf);
|
||||
if (off + 1 + ln + 1 > outsz) return -1;
|
||||
out[off++] = '.';
|
||||
memcpy(out + off, leaf, ln + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Finalization verifies every reached context before generated-main creation.
|
||||
* Source bindings and explicit lookup identities remain authoritative; a
|
||||
* literal root either round-trips through an active root or receives the
|
||||
* reserved reversible local identity. */
|
||||
static int
|
||||
sep_finalize_directory_identities(struct sepgraph *g)
|
||||
{
|
||||
for (int pi = 0; pi < g->n; pi++) {
|
||||
struct seppkg *p = &g->pkg[pi];
|
||||
if (!p->is_dir || p->generated_main || p->failed || !p->loaded
|
||||
|| p->role == SEP_ROLE_TEST_SUPPORT
|
||||
|| p->import_base[0] != '\0')
|
||||
continue;
|
||||
for (int ci = 0; ci < g->ncontext; ci++) {
|
||||
if (p->context_state[ci] != 2) continue;
|
||||
char candidate[SEP_IMPORT_PATH_MAX];
|
||||
int found = sep_reverse_import_base(g, p, ci, candidate,
|
||||
sizeof candidate);
|
||||
if (found < 0) return -1;
|
||||
if (found > 0 && sep_bind_import_base(g, pi, candidate) < 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
for (int pi = 0; pi < g->n; pi++) {
|
||||
struct seppkg *p = &g->pkg[pi];
|
||||
if (!p->is_dir || p->generated_main || p->failed || !p->loaded
|
||||
@@ -1453,27 +1656,15 @@ sep_finalize_directory_identities(struct sepgraph *g)
|
||||
base = g->pkg[i].import_base;
|
||||
break;
|
||||
}
|
||||
char fallback[SEP_IMPORT_PATH_MAX];
|
||||
char local[SEP_IMPORT_PATH_MAX];
|
||||
if (base == NULL) {
|
||||
if (p->name[0] == '\0') {
|
||||
if (sep_local_import_base(p, local, sizeof local) < 0) {
|
||||
fprintf(stderr,
|
||||
"ww: package directory %s has no canonical import identity\n",
|
||||
p->entry);
|
||||
"ww: local package identity is too long (limit %d bytes)\n",
|
||||
SEP_IMPORT_PATH_MAX - 1);
|
||||
return -1;
|
||||
}
|
||||
if (p->variant == SEP_VARIANT_EXTERNAL) {
|
||||
size_t n = strlen(p->name);
|
||||
if (n <= 5 || strcmp(p->name + n - 5, "_test") != 0) {
|
||||
fprintf(stderr,
|
||||
"ww: package-test selector does not name an external package\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy(fallback, p->name, n - 5);
|
||||
fallback[n - 5] = '\0';
|
||||
} else {
|
||||
snprintf(fallback, sizeof fallback, "%s", p->name);
|
||||
}
|
||||
base = fallback;
|
||||
base = local;
|
||||
}
|
||||
if (sep_bind_import_base(g, pi, base) < 0) return -1;
|
||||
}
|
||||
@@ -1816,7 +2007,7 @@ static void
|
||||
workdir_stamp_text(char *buf, size_t bufsz, int is_test, int emit_asm)
|
||||
{
|
||||
snprintf(buf, bufsz, "ww workdir fmt %d mode %s asm %d\n",
|
||||
is_test ? 8 : 7, is_test ? "test" : "build", emit_asm);
|
||||
is_test ? 9 : 8, is_test ? "test" : "build", emit_asm);
|
||||
}
|
||||
|
||||
/* A stale global builder identity invalidates every committed unit voucher in
|
||||
@@ -1860,8 +2051,7 @@ invalidate_workdir_units(const char *scratch)
|
||||
* `-w` workdir with content-identity package reuse. */
|
||||
static int
|
||||
build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
const char *root_identity, const char *out,
|
||||
const char *objstem, const char *extra_includes,
|
||||
const char *out, const char *objstem, const char *extra_includes,
|
||||
const struct seplinkflags *linkflags, int package_only, int is_test,
|
||||
struct sepproduct *products, int nproducts, int emit_asm,
|
||||
const char *workdir, char *scratchout, size_t scratchoutsz,
|
||||
@@ -1974,8 +2164,6 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
if (g == NULL) return 1;
|
||||
g->support_context = -1;
|
||||
if (graphout) *graphout = g;
|
||||
const char *rootpath = package_only && root_identity
|
||||
? root_identity : "";
|
||||
int support_for[SEP_MAXPRODUCT];
|
||||
for (int i = 0; i < nproducts; i++) support_for[i] = -1;
|
||||
for (int i = 0; i < nproducts; i++) {
|
||||
@@ -2000,6 +2188,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
if (products[i].context < 0) return 1;
|
||||
const char *selector = products[i].variant == SEP_VARIANT_PRODUCTION
|
||||
? NULL : products[i].test_package;
|
||||
const char *rootpath = products[i].identity != NULL
|
||||
? products[i].identity : "";
|
||||
products[i].root = sep_find_or_add_variant(g, rootpath, entry,
|
||||
entry_is_dir, products[i].variant, selector,
|
||||
SEP_ROLE_NORMAL, products[i].artifact, 1);
|
||||
@@ -2477,6 +2667,7 @@ build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
|
||||
struct sepproduct product = {
|
||||
.dir = src,
|
||||
.out = out,
|
||||
.identity = root_identity,
|
||||
.test_package = test_package,
|
||||
.status = NULL,
|
||||
.artifact = {0},
|
||||
@@ -2486,7 +2677,7 @@ build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
|
||||
};
|
||||
if (!package_only && !entry_is_dir)
|
||||
snprintf(product.artifact, sizeof product.artifact, "__root");
|
||||
int r = build_one_sep_impl(src, entry_is_dir, root_identity, out, objstem,
|
||||
int r = build_one_sep_impl(src, entry_is_dir, out, objstem,
|
||||
extra_includes, linkflags, package_only, is_test,
|
||||
&product, 1, emit_asm, workdir, scratch,
|
||||
sizeof scratch, &g);
|
||||
@@ -2520,12 +2711,15 @@ build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
|
||||
* request. Its first output owns the shared cold sepwork tree; every product
|
||||
* remains an independent root compile and link inside that tree. */
|
||||
static int
|
||||
build_package_tests(const char *src, const char *extra_includes,
|
||||
const char *workdir, struct sepproduct *products, int nproducts)
|
||||
build_package_tests(const char *src, const char *root_identity,
|
||||
const char *extra_includes, const char *workdir,
|
||||
struct sepproduct *products, int nproducts)
|
||||
{
|
||||
char scratch[1100] = {0};
|
||||
struct sepgraph *g = NULL;
|
||||
int r = build_one_sep_impl(src, 1, NULL, products[0].out,
|
||||
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, 1,
|
||||
products, nproducts, 0, workdir, scratch, sizeof scratch, &g);
|
||||
sep_graph_free(g);
|
||||
@@ -2592,6 +2786,7 @@ resolve_module(const char *name, const char *incs, char *out, size_t outsz,
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (reserved_import_path(name)) return 0;
|
||||
char sp[4096];
|
||||
search_path(incs, sp, sizeof sp);
|
||||
char path_form[256];
|
||||
@@ -2763,7 +2958,7 @@ do_build(int argc, char **argv)
|
||||
} else {
|
||||
basename_no_ext(resolved, out, sizeof out);
|
||||
}
|
||||
const char *root_identity = package_only && !literal ? src : NULL;
|
||||
const char *root_identity = !literal && is_dir ? src : NULL;
|
||||
return build_one_sep(resolved, is_dir, root_identity, out, objstem, incs,
|
||||
&linkflags, package_only, 0, SEP_VARIANT_PRODUCTION, NULL, emit_asm,
|
||||
1, workdir);
|
||||
@@ -2781,6 +2976,8 @@ do_run(int argc, char **argv)
|
||||
outflag, sizeof outflag, NULL, 0, &src, NULL, NULL);
|
||||
if (next < 0) return 2;
|
||||
if (src == NULL) src = ".";
|
||||
struct stat requested;
|
||||
int literal = stat(src, &requested) == 0;
|
||||
char resolved[1024];
|
||||
int is_dir = 0;
|
||||
if (!resolve_module(src, incs, resolved, sizeof resolved, &is_dir)) {
|
||||
@@ -2796,7 +2993,9 @@ do_run(int argc, char **argv)
|
||||
snprintf(tmp, sizeof tmp, "%s/main", tmpdir);
|
||||
/* The freshly acquired directory owns both the executable and the
|
||||
* adjacent main.sepwork tree. Nothing outside it is adopted or removed. */
|
||||
if (build_one_sep(resolved, is_dir, NULL, tmp, tmp, incs, &linkflags,
|
||||
const char *root_identity = !literal && is_dir ? src : NULL;
|
||||
if (build_one_sep(resolved, is_dir, root_identity, tmp, tmp, incs,
|
||||
&linkflags,
|
||||
0, 0, SEP_VARIANT_PRODUCTION, NULL, 0, 0, NULL) != 0) {
|
||||
if (unlink(tmp) != 0 && errno != ENOENT)
|
||||
fputs("ww: cannot remove temporary output\n", stderr);
|
||||
@@ -2860,6 +3059,7 @@ do_test(int argc, char **argv)
|
||||
char workdir[1024] = {0};
|
||||
int packageopts = 0;
|
||||
int afterdash = 0;
|
||||
const char *request_identity = NULL;
|
||||
/* #17: an optional second positional after the target is a fnmatch
|
||||
* name-filter pattern, forwarded to the test binary as argv[1]. Only
|
||||
* meaningful for a single test file/module — rejected in dir mode. */
|
||||
@@ -2889,6 +3089,15 @@ do_test(int argc, char **argv)
|
||||
"%s%s", n ? ":" : "", dir);
|
||||
} else if (strcmp(argv[i], "-c") == 0) {
|
||||
compileonly = 1;
|
||||
} else if (strcmp(argv[i], "--ww-root-identity") == 0) {
|
||||
if (i + 1 >= argc || request_identity != NULL
|
||||
|| argv[i + 1][0] == '\0'
|
||||
|| reserved_import_path(argv[i + 1])) {
|
||||
fprintf(stderr,
|
||||
"ww test: invalid --ww-root-identity\n");
|
||||
return 2;
|
||||
}
|
||||
request_identity = argv[++i];
|
||||
} else if (strcmp(argv[i], "--ww-package-test") == 0) {
|
||||
if (i + 5 >= argc || nproducts >= SEP_MAXPRODUCT) {
|
||||
fprintf(stderr,
|
||||
@@ -3041,7 +3250,7 @@ do_test(int argc, char **argv)
|
||||
}
|
||||
/* -w forwards: the coordinator keys one persistent driver
|
||||
* workdir for the complete selected test request. */
|
||||
return exec_package_tests(argc, argv, src, NULL, 0);
|
||||
return exec_package_tests(argc, argv, src, NULL, NULL, 0);
|
||||
}
|
||||
struct stat st;
|
||||
if (stat(target, &st) != 0) {
|
||||
@@ -3076,10 +3285,12 @@ do_test(int argc, char **argv)
|
||||
"ww test: package-test products need -c\n");
|
||||
return 2;
|
||||
}
|
||||
return build_package_tests(resolved, incs, workdir,
|
||||
return build_package_tests(resolved, request_identity,
|
||||
incs, workdir,
|
||||
products, nproducts);
|
||||
}
|
||||
return exec_package_tests(argc, argv, src, resolved, 0);
|
||||
return exec_package_tests(argc, argv, src, resolved,
|
||||
request_identity != NULL ? request_identity : target, 0);
|
||||
}
|
||||
if (packageopts) {
|
||||
fprintf(stderr,
|
||||
@@ -3232,10 +3443,11 @@ do_test(int argc, char **argv)
|
||||
"ww test: package-test products need -c\n");
|
||||
return 2;
|
||||
}
|
||||
return build_package_tests(target, incs, workdir,
|
||||
return build_package_tests(target, request_identity, incs, workdir,
|
||||
products, nproducts);
|
||||
}
|
||||
return exec_package_tests(argc, argv, src, NULL, src == NULL);
|
||||
return exec_package_tests(argc, argv, src, NULL, request_identity,
|
||||
src == NULL);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
Reference in New Issue
Block a user