ww: select build action from package declaration

This commit is contained in:
2026-08-13 13:36:10 +09:00
parent 61a2ee5221
commit dab3cb4998
61 changed files with 872 additions and 157 deletions

View File

@@ -24,7 +24,7 @@
static const char *usage = static const char *usage =
"usage: ww [-V] <subcommand> [args...]\n" "usage: ww [-V] <subcommand> [args...]\n"
" -V print version and exit\n" " -V print version and exit\n"
" build [-p] [-S] [-w DIR] [-I DIR] [-o FILE] [path] build a local package graph\n" " build [-S] [-w DIR] [-I DIR] [-o FILE] [path] build a local package graph\n"
" run [path] ... build then exec, passing extra args to the program\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" " version print version and exit\n"
@@ -33,7 +33,7 @@ static const char *usage =
" foo.ww literal file\n" " foo.ww literal file\n"
" foo search cwd, -I dirs, then the source library for foo.ww or foo/\n" " foo search cwd, -I dirs, then the source library for foo.ww or foo/\n"
" lib/foo directory: build its package sources\n" " lib/foo directory: build its package sources\n"
" -p emits a non-main archive FILE + FILE.wwi\n" " -o publishes a non-main archive FILE + FILE.wwi\n"
" lib/... every package under lib, recursively (test only)\n" " lib/... every package under lib, recursively (test only)\n"
" . build the cwd's <basename>.ww\n"; " . build the cwd's <basename>.ww\n";
@@ -969,6 +969,17 @@ sep_command_declared_name(const struct seppkg *p)
? strcmp(p->name, "main_test") == 0 ? strcmp(p->name, "main_test") == 0
: strcmp(p->name, "main") == 0; : strcmp(p->name, "main") == 0;
} }
/* Directory-package action kind comes only from the loaded declaration.
* An explicit package-less file is the retained raw-unit compatibility path;
* it has no directory package declaration and remains a command unit. */
static int
sep_root_is_command(const struct seppkg *p)
{
if (p->name == NULL) return !p->is_dir;
return strcmp(p->name, "main") == 0;
}
static int static int
sep_forbidden_command_import(const struct sepgraph *g, int importer, int dep) sep_forbidden_command_import(const struct sepgraph *g, int importer, int dep)
{ {
@@ -2663,6 +2674,20 @@ copy_file_atomic(const char *src, const char *dst)
return rename(tmp, dst); return rename(tmp, dst);
} }
/* A package publication writes OUT, OUT.new, OUT.wwi, and OUT.wwi.new.
* Validate the longest spelling before any producer tool can run. */
static int
validate_package_output_path(const char *out)
{
size_t n = strlen(out);
if ((size_t)PATH_MAX < sizeof ".wwi.new"
|| n > (size_t)PATH_MAX - sizeof ".wwi.new") {
fprintf(stderr, "ww: package output path is too long\n");
return -1;
}
return 0;
}
/* A coordinator-private completion marker distinguishes a newly linked /* A coordinator-private completion marker distinguishes a newly linked
* product from a caller-owned binary left behind by an earlier invocation. */ * product from a caller-owned binary left behind by an earlier invocation. */
static int static int
@@ -2681,13 +2706,13 @@ record_product_status(const char *path)
} }
/* The stamp pins the non-content build inputs a unit compare cannot see: /* The stamp pins the non-content build inputs a unit compare cannot see:
* the -T/-S shape of the producer pass and the artifact protocol * the -T/-S/root-action shape of the producer pass and the artifact protocol
* revision (bump "fmt" when the unit/archive/commit format changes). */ * revision (bump "fmt" when the unit/archive/commit format changes). */
static void static void
workdir_stamp_text(char *buf, size_t bufsz, int is_test, int emit_asm) 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", snprintf(buf, bufsz, "ww workdir fmt %d mode %s asm %d\n",
is_test ? 11 : 10, is_test ? "test" : "build", emit_asm); is_test ? 11 : 12, is_test ? "test" : "build", emit_asm);
} }
/* A stale global builder identity invalidates every committed unit voucher in /* A stale global builder identity invalidates every committed unit voucher in
@@ -2732,7 +2757,8 @@ invalidate_workdir_units(const char *scratch)
static int static int
build_one_sep_impl(const char *src, int entry_is_dir, build_one_sep_impl(const char *src, int entry_is_dir,
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, const struct seplinkflags *linkflags, int publish_package,
int require_command, int is_test,
struct sepproduct *products, int nproducts, int emit_asm, struct sepproduct *products, int nproducts, int emit_asm,
const char *workdir, char *scratchout, size_t scratchoutsz, const char *workdir, char *scratchout, size_t scratchoutsz,
struct sepgraph **graphout) struct sepgraph **graphout)
@@ -2895,8 +2921,6 @@ build_one_sep_impl(const char *src, int entry_is_dir,
SEP_ROLE_NORMAL, products[i].artifact, 1); SEP_ROLE_NORMAL, products[i].artifact, 1);
if (products[i].root < 0) return 1; if (products[i].root < 0) return 1;
products[i].variant_root = products[i].root; products[i].variant_root = products[i].root;
if (!is_test && !package_only)
g->pkg[products[i].root].link_entry = 1;
} }
const char *test_support_module = "test"; const char *test_support_module = "test";
/* -T generates a dispatcher whose support qualifier is selected by the /* -T generates a dispatcher whose support qualifier is selected by the
@@ -3016,6 +3040,14 @@ build_one_sep_impl(const char *src, int entry_is_dir,
} }
if (g->identity_failed) return 1; if (g->identity_failed) return 1;
if (sep_finalize_directory_identities(g) < 0) return 1; if (sep_finalize_directory_identities(g) < 0) return 1;
if (!is_test) {
for (int i = 0; i < nproducts; i++) {
int root = products[i].variant_root;
if (!g->pkg[root].failed)
g->pkg[root].link_entry =
sep_root_is_command(&g->pkg[root]);
}
}
if (is_test && entry_is_dir) { if (is_test && entry_is_dir) {
for (int i = 0; i < nproducts; i++) { for (int i = 0; i < nproducts; i++) {
int variant = products[i].variant_root; int variant = products[i].variant_root;
@@ -3031,18 +3063,15 @@ build_one_sep_impl(const char *src, int entry_is_dir,
products[i].root = mainpkg; products[i].root = mainpkg;
} }
} }
int root_package = !is_test
&& !g->pkg[products[0].root].failed
&& !sep_root_is_command(&g->pkg[products[0].root]);
if (sep_validate_artifact_paths(g, scratch) < 0) if (sep_validate_artifact_paths(g, scratch) < 0)
return 1; return 1;
if (warm && sep_validate_workdir_owners(g, scratch) < 0) if (warm && sep_validate_workdir_owners(g, scratch) < 0)
return 1; return 1;
if (warm && stale_all && invalidate_workdir_units(scratch) != 0) if (warm && stale_all && invalidate_workdir_units(scratch) != 0)
return 1; return 1;
int root_package = package_only;
if (root_package && !g->pkg[products[0].root].failed
&& strcmp(g->pkg[products[0].root].name, "main") == 0) {
fprintf(stderr, "ww: -p requires a non-main package\n");
return 1;
}
int *order = calloc((size_t)g->n, sizeof *order); int *order = calloc((size_t)g->n, sizeof *order);
int *stack = calloc((size_t)g->n, sizeof *stack); int *stack = calloc((size_t)g->n, sizeof *stack);
int norder = 0; int norder = 0;
@@ -3065,6 +3094,23 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|| sep_validate_module_closure(g, order, ignored, 1) < 0) || sep_validate_module_closure(g, order, ignored, 1) < 0)
g->pkg[root].failed = 1; g->pkg[root].failed = 1;
} }
if (!is_test && require_command) {
int root = products[0].root;
if (!g->pkg[root].failed
&& !sep_root_is_command(&g->pkg[root])) {
fprintf(stderr, "ww: package %s is not a main package\n",
g->pkg[root].path[0] ? g->pkg[root].path
: g->pkg[root].canon);
free(stack); free(order);
return 1;
}
}
if (!g->pkg[products[0].root].failed
&& root_package && publish_package && !emit_asm
&& validate_package_output_path(out) < 0) {
free(stack); free(order);
return 1;
}
for (int pi = 0; pi < g->n; pi++) g->pkg[pi].color = 0; for (int pi = 0; pi < g->n; pi++) g->pkg[pi].color = 0;
for (int i = 0; i < nproducts; i++) { for (int i = 0; i < nproducts; i++) {
int root = products[i].root; int root = products[i].root;
@@ -3277,21 +3323,24 @@ build_one_sep_impl(const char *src, int entry_is_dir,
if (root_package) { if (root_package) {
int root = products[0].root; int root = products[0].root;
if (g->pkg[root].failed) { free(order); return 1; } if (g->pkg[root].failed) { free(order); return 1; }
char archive[SEP_ARTIFACT_MAX], iface[SEP_ARTIFACT_MAX]; if (publish_package) {
char outiface[SEP_ARTIFACT_MAX]; char archive[SEP_ARTIFACT_MAX], iface[SEP_ARTIFACT_MAX];
sep_fname(g, root, scratch, ".a", archive, sizeof archive); char outiface[SEP_ARTIFACT_MAX];
sep_fname(g, root, scratch, ".wwi", iface, sizeof iface); sep_fname(g, root, scratch, ".a", archive, sizeof archive);
int on = snprintf(outiface, sizeof outiface, "%s.wwi", out); sep_fname(g, root, scratch, ".wwi", iface, sizeof iface);
if (on < 0 || (size_t)on >= sizeof outiface) { int on = snprintf(outiface, sizeof outiface, "%s.wwi", out);
fprintf(stderr, "ww: package output path is too long\n"); if (on < 0 || (size_t)on >= sizeof outiface) {
free(order); fprintf(stderr, "ww: package output path is too long\n");
return 1; free(order);
} return 1;
if (copy_file_atomic(archive, out) != 0 }
|| copy_file_atomic(iface, outiface) != 0) { if (copy_file_atomic(archive, out) != 0
fprintf(stderr, "ww: cannot write package artifact %s\n", out); || copy_file_atomic(iface, outiface) != 0) {
free(order); fprintf(stderr,
return 1; "ww: cannot write package artifact %s\n", out);
free(order);
return 1;
}
} }
free(order); free(order);
return 0; return 0;
@@ -3414,7 +3463,8 @@ static int
build_one_sep(const char *src, int entry_is_dir, const char *root_identity, build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
const char *out, const char *out,
const char *objstem, const char *extra_includes, const char *objstem, const char *extra_includes,
const struct seplinkflags *linkflags, int package_only, int is_test, const struct seplinkflags *linkflags, int publish_package,
int require_command, int is_test,
int root_variant, const char *test_package, int emit_asm, int root_variant, const char *test_package, int emit_asm,
int keepscratch, const char *workdir) int keepscratch, const char *workdir)
{ {
@@ -3432,10 +3482,10 @@ build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
.variant_root = -1, .variant_root = -1,
.support = -1, .support = -1,
}; };
if (!package_only && !entry_is_dir) if (!entry_is_dir)
product.artifact = "__root"; product.artifact = "__root";
int r = build_one_sep_impl(src, entry_is_dir, out, objstem, int r = build_one_sep_impl(src, entry_is_dir, out, objstem,
extra_includes, linkflags, package_only, is_test, extra_includes, linkflags, publish_package, require_command, is_test,
&product, 1, emit_asm, workdir, scratch, &product, 1, emit_asm, workdir, scratch,
sizeof scratch, &g); sizeof scratch, &g);
sep_graph_free(g); sep_graph_free(g);
@@ -3477,7 +3527,7 @@ build_package_tests(const char *src, const char *root_identity,
for (int i = 0; i < nproducts; i++) for (int i = 0; i < nproducts; i++)
products[i].identity = root_identity; products[i].identity = root_identity;
int r = build_one_sep_impl(src, 1, products[0].out, int r = build_one_sep_impl(src, 1, products[0].out,
products[0].out, extra_includes, NULL, 0, 1, products[0].out, extra_includes, NULL, 0, 0, 1,
products, nproducts, 0, workdir, scratch, sizeof scratch, &g); products, nproducts, 0, workdir, scratch, sizeof scratch, &g);
sep_graph_free(g); sep_graph_free(g);
return r; return r;
@@ -3621,11 +3671,10 @@ parse_build_flags(const char *cmd, int argc, char **argv,
struct seplinkflags *linkflags, struct seplinkflags *linkflags,
char *outpath, size_t outsz, char *outpath, size_t outsz,
char *workdir, size_t workdirsz, char *workdir, size_t workdirsz,
const char **src_out, int *emit_asm_out, int *package_out) const char **src_out, int *emit_asm_out)
{ {
*src_out = NULL; *src_out = NULL;
if (emit_asm_out) *emit_asm_out = 0; if (emit_asm_out) *emit_asm_out = 0;
if (package_out) *package_out = 0;
int i = 0; int i = 0;
for (; i < argc; i++) { for (; i < argc; i++) {
if (strcmp(argv[i], "-S") == 0) { if (strcmp(argv[i], "-S") == 0) {
@@ -3634,12 +3683,6 @@ parse_build_flags(const char *cmd, int argc, char **argv,
return -1; return -1;
} }
*emit_asm_out = 1; *emit_asm_out = 1;
} else if (strcmp(argv[i], "-p") == 0) {
if (package_out == NULL) {
fprintf(stderr, "ww %s: unknown flag\n", cmd);
return -1;
}
*package_out = 1;
} else if (strcmp(argv[i], "-w") == 0) { } else if (strcmp(argv[i], "-w") == 0) {
if (workdir == NULL) { if (workdir == NULL) {
fprintf(stderr, "ww %s: unknown flag\n", cmd); fprintf(stderr, "ww %s: unknown flag\n", cmd);
@@ -3738,20 +3781,14 @@ do_build(int argc, char **argv)
char outflag[PATH_MAX] = {0}; char outflag[PATH_MAX] = {0};
char workdir[PATH_MAX] = {0}; char workdir[PATH_MAX] = {0};
int emit_asm = 0; int emit_asm = 0;
int package_only = 0;
if (parse_build_flags("build", argc, argv, incs, incsz, if (parse_build_flags("build", argc, argv, incs, incsz,
&linkflags, &linkflags,
outflag, sizeof outflag, workdir, sizeof workdir, outflag, sizeof outflag, workdir, sizeof workdir,
&src, &emit_asm, &package_only) < 0) { &src, &emit_asm) < 0) {
free(incs); free(incs);
return 2; return 2;
} }
if (src == NULL) src = "."; if (src == NULL) src = ".";
if (package_only && emit_asm) {
fprintf(stderr, "ww build: -p and -S cannot be combined\n");
free(incs);
return 2;
}
struct stat requested; struct stat requested;
int literal = stat(src, &requested) == 0; int literal = stat(src, &requested) == 0;
char resolved[PATH_MAX]; char resolved[PATH_MAX];
@@ -3761,11 +3798,6 @@ do_build(int argc, char **argv)
free(incs); free(incs);
return 1; return 1;
} }
if (package_only && !is_dir) {
fprintf(stderr, "ww build: -p needs a package directory\n");
free(incs);
return 2;
}
char out[PATH_MAX]; char out[PATH_MAX];
const char *objstem = NULL; const char *objstem = NULL;
if (outflag[0]) { if (outflag[0]) {
@@ -3785,8 +3817,8 @@ do_build(int argc, char **argv)
} }
const char *root_identity = !literal && is_dir ? src : NULL; const char *root_identity = !literal && is_dir ? src : NULL;
int rc = build_one_sep(resolved, is_dir, root_identity, out, objstem, incs, int rc = build_one_sep(resolved, is_dir, root_identity, out, objstem, incs,
&linkflags, package_only, 0, SEP_VARIANT_PRODUCTION, NULL, emit_asm, &linkflags, outflag[0] != '\0', 0, 0, SEP_VARIANT_PRODUCTION, NULL,
1, workdir); emit_asm, 1, workdir);
free(incs); free(incs);
return rc; return rc;
} }
@@ -3802,7 +3834,7 @@ do_run(int argc, char **argv)
char outflag[PATH_MAX] = {0}; /* -o accepted+ignored: run always uses the temp */ char outflag[PATH_MAX] = {0}; /* -o accepted+ignored: run always uses the temp */
int next = parse_build_flags("run", argc, argv, incs, incsz, int next = parse_build_flags("run", argc, argv, incs, incsz,
&linkflags, &linkflags,
outflag, sizeof outflag, NULL, 0, &src, NULL, NULL); outflag, sizeof outflag, NULL, 0, &src, NULL);
if (next < 0) { free(incs); return 2; } if (next < 0) { free(incs); return 2; }
if (src == NULL) src = "."; if (src == NULL) src = ".";
struct stat requested; struct stat requested;
@@ -3831,7 +3863,7 @@ do_run(int argc, char **argv)
const char *root_identity = !literal && is_dir ? src : NULL; const char *root_identity = !literal && is_dir ? src : NULL;
int buildrc = build_one_sep(resolved, is_dir, root_identity, tmp, tmp, incs, int buildrc = build_one_sep(resolved, is_dir, root_identity, tmp, tmp, incs,
&linkflags, &linkflags,
0, 0, SEP_VARIANT_PRODUCTION, NULL, 0, 0, NULL); 0, 1, 0, SEP_VARIANT_PRODUCTION, NULL, 0, 0, NULL);
free(incs); free(incs);
if (buildrc != 0) { if (buildrc != 0) {
if (unlink(tmp) != 0 && errno != ENOENT) if (unlink(tmp) != 0 && errno != ENOENT)
@@ -4181,7 +4213,7 @@ do_test(int argc, char **argv)
/* No-o redirects internal scratch to /tmp rather than beside the /* No-o redirects internal scratch to /tmp rather than beside the
* source. An explicit -o names the caller-owned artifact stem. */ * source. An explicit -o names the caller-owned artifact stem. */
int br = build_one_sep(resolved, is_dir, NULL, outp, int br = build_one_sep(resolved, is_dir, NULL, outp,
outstem[0] ? outstem : tmp, incs, NULL, 0, 1, outstem[0] ? outstem : tmp, incs, NULL, 0, 0, 1,
SEP_VARIANT_PRODUCTION, NULL, emit_asm, SEP_VARIANT_PRODUCTION, NULL, emit_asm,
outstem[0] ? 1 : 0, workdir); outstem[0] ? 1 : 0, workdir);
if (br != 0) { if (br != 0) {
@@ -4249,7 +4281,7 @@ do_test(int argc, char **argv)
} }
/* See module-mode note: no-o scratch is redirected to /tmp. */ /* See module-mode note: no-o scratch is redirected to /tmp. */
int br = build_one_sep(target, 0, NULL, outp, outstem[0] ? outstem : tmp, int br = build_one_sep(target, 0, NULL, outp, outstem[0] ? outstem : tmp,
incs, NULL, 0, 1, SEP_VARIANT_PRODUCTION, NULL, emit_asm, incs, NULL, 0, 0, 1, SEP_VARIANT_PRODUCTION, NULL, emit_asm,
outstem[0] ? 1 : 0, workdir); outstem[0] ? 1 : 0, workdir);
if (br != 0) { if (br != 0) {
if (owntmp && unlink(outp) != 0 && errno != ENOENT) if (owntmp && unlink(outp) != 0 && errno != ENOENT)

View File

@@ -2843,34 +2843,78 @@ source-like `.wwi` syntax remains a transitional export encoding pending the
binary `.wwe` format described above, but the separate direct-input ownership binary `.wwe` format described above, but the separate direct-input ownership
boundary is live in production Cstage and WWstage compilers and drivers. boundary is live in production Cstage and WWstage compilers and drivers.
An ordinary executable directory root is one normal package action. Its A selected production root is one normal package action. Its finalized
finalized canonical import identity tags its owner-only unit; it receives only canonical import identity tags its owner-only unit; it receives only direct
direct exports, emits `.wwi`, `.o`, and a deterministic `.a`, and is compiled exports, emits `.wwi`, `.o`, and a deterministic `.a`, and is compiled exactly
exactly once. The declared package name validates the last component of that once. The declared package name validates the last component of that identity
identity for ordinary importable packages. A selected command root may declare for ordinary importable packages. After loading and identity finalization, the
`package main`; `main` validates its executable role and never replaces or declaration also selects the terminal build action: `package main` is a command
truncates an identity such as `cmd.tool`. The narrow compiler `--entry` flag and every other valid declaration is a compile-only library. A command root
both classifies that primary unit as a command and controls bare `main` codegen; receives the narrow compiler `--entry` flag, which controls bare `main` codegen,
the package driver uses the parser-only `--command-package` marker for command then the linker receives that root archive first, the complete reachable
test variants that must remain ordinary archive code. Neither flag changes package-archive closure, and the runtime archive; it never receives `.wwi`. A
export identity, and imported interfaces remain under strict leaf validation. non-main root receives no `--entry` and never enters the linker. `main` validates
The linker receives that root archive first, then the complete reachable command kind but never replaces or truncates an identity such as `cmd.tool`.
package-archive closure and runtime archive; it never receives `.wwi`. The The explicit package-less single-file compatibility path has no directory
linkers seed `main` before archive selection, so the existing WWAR member package declaration to classify and remains a raw command unit; it does not
protocol needs no special root object or format change. participate in canonical directory-package interning.
The package driver still uses the parser-only `--command-package` marker for
command test variants that must remain ordinary archive code. Neither marker
changes export identity, and imported interfaces remain under strict leaf
validation. The linkers seed `main` before archive selection, so the existing
WWAR member protocol needs no special root object or format change.
`ww build -p -o lib.a DIR` explicitly requests a non-main package product: it Publication is separate from that semantic action choice. `ww build -o lib.a
emits a deterministic archive at `lib.a` and its compiler interface at DIR` or `ww build -I ROOT -o bar.a foo.bar` automatically publishes a non-main
`lib.a.wwi`, without invoking the linker. A logical target retains its full root's deterministic archive and self-contained compiler export at
identity (`ww build -p -I ROOT -o bar.a foo.bar` emits `foo.bar.*` symbols), `FILE.wwi`, without invoking the linker; the latter retains `foo.bar.*` symbols.
while a literal directory is reverse-resolved through the active source roots Without `-o`, a non-main root and its dependencies are compiled in the selected
or receives the deterministic local identity described below. Its declared scratch or persistent work directory and no cwd product is invented. For a
leaf can never invent or truncate that identity. Package output requires a command, `-o` continues to name the executable publication path. Output names,
directory and `-p` cannot be combined with assembly-only `-S`. Two cold builds request order, and whether publication was requested never enter package or
with identical inputs are required to produce byte-identical requested action identity. The historical action-selecting `-p` exception is removed and
products. Compiler intrinsics keep their package-mode runtime ABI independent rejected as an unknown build flag. Assembly-only `-S` still stops before object,
of transitive source interfaces (for example, `alloc` lowers to the runtime archive, publication, or link production. A literal directory is
allocator without requiring an `rt.wwi` compiler input). reverse-resolved through the active source roots or receives the deterministic
local identity described below; its declared leaf can never invent or truncate
that identity. Two cold builds with identical inputs are required to produce
byte-identical requested products. Compiler intrinsics keep their package-mode
runtime ABI independent of transitive source interfaces (for example, `alloc`
lowers to the runtime allocator without requiring an `rt.wwi` compiler input).
This rule follows the pinned official Go 1.26.5 source at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`. `go/build.Package` stores source
directory, declared name, and import path independently, and defines a command
solely as a package named `main`
([`go/build/build.go`, lines 436449](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L436-L449),
[`go/build/build.go`, lines 514519](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L514-L519)).
The Go builder's `AutoAction` links only `main` and returns the archive compile
action for every other package
([`cmd/go/internal/work/action.go`, lines 450456](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L450-L456)).
The build command invents a default output only for one `main`, applies an
explicit `-o` to either AutoAction result, and otherwise builds each requested
package without conflating publication and semantic kind
([`cmd/go/internal/work/build.go`, lines 473478](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L473-L478),
[`cmd/go/internal/work/build.go`, lines 508548](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L508-L548),
[`cmd/go/internal/work/build.go`, lines 551558](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L551-L558)).
Both WW stages represent the rule with the existing loaded declaration and
stable root action index; no package, dependency, ownership, locator, traversal,
or closure allocation is added. `ww run` adds only a command-kind requirement:
a successfully loaded, cycle-free non-main root produces the deterministic
`package PATH is not a main package` diagnostic before any compiler, assembler,
or linker invocation; package and graph failures retain precedence. Before a
non-main `-o` build invokes a producer, both stages validate the longest atomic
publication spelling, `FILE.wwi.new`, so an incomplete archive/export pair is
never caused by a late path-overflow failure. Package loading, cycle detection,
and closure validation retain diagnostic precedence over this publication-only
check, and `-S` does not validate a publication path it never consumes.
Build workdir format 12 invalidates older build-mode unit vouchers before reuse
because root compiler argv changed; test workdir format 11 is unchanged because
test variants still link only their explicit generated-main action. Thereafter
an equivalent warm library build invokes no tools, a private dependency change
stops at its unchanged export, and an export change recompiles its direct
importer under the existing propagation rule.
### 11.7 Implemented directory package-test slice ### 11.7 Implemented directory package-test slice
@@ -2939,7 +2983,7 @@ invocation, each still-unbound directory is finalized by this exact algorithm:
invalidates a name inferred from a later or nested root. invalidates a name inferred from a later or nested root.
3. Bind the first precedence-valid candidate from each reaching context through 3. Bind the first precedence-valid candidate from each reaching context through
the command-global bidirectional interner. An identity supplied by successful the command-global bidirectional interner. An identity supplied by successful
logical package lookup, such as `-p encoding.utf8`, is already bound and is logical package lookup, such as `encoding.utf8`, is already bound and is
preserved exactly. That forward-selected identity is authoritative: reverse preserved exactly. That forward-selected identity is authoritative: reverse
derivation applies only to still-unbound literal roots, so a nested active derivation applies only to still-unbound literal roots, so a nested active
root cannot rename an explicitly resolved package. root cannot rename an explicitly resolved package.

View File

@@ -1402,6 +1402,14 @@ fn sepcommanddeclaredname(p: *seppkg) bool = {
return cstreqlit(p.name, "main"); return cstreqlit(p.name, "main");
}; };
// Directory-package action kind comes only from the loaded declaration. An
// explicit package-less file is the retained raw-unit compatibility path and
// remains a command unit.
fn seprootiscommand(p: *seppkg) bool = {
if (p.name == nil) { return p.isdir == 0; };
return cstreqlit(p.name, "main");
};
fn sepforbiddencommandimport(g: *sepgraph, importer: i32, dep: i32) bool = { fn sepforbiddencommandimport(g: *sepgraph, importer: i32, dep: i32) bool = {
let from: *seppkg = &g.pkg[importer]; let from: *seppkg = &g.pkg[importer];
let to: *seppkg = &g.pkg[dep]; let to: *seppkg = &g.pkg[dep];
@@ -3324,8 +3332,20 @@ fn copyfileatomic(src: *u8, dst: *u8) i32 = {
return os.rename(pathstr(tmpp), pathstr(dst)); return os.rename(pathstr(tmpp), pathstr(dst));
}; };
// Package publication writes OUT, OUT.new, OUT.wwi, and OUT.wwi.new. Check
// the longest spelling before any producer tool can run.
fn validatepackageoutputpath(out: *u8) i32 = {
let suffix: u64 = ".wwi.new".len: u64 + 1u64;
let limit: u64 = os.PATH_MAX: u64;
if (suffix > limit || cstrlen(out) > limit - suffix) {
cerr("ww: package output path is too long\n");
return -1;
};
return 0;
};
// The stamp pins the non-content build inputs a unit compare cannot see: // The stamp pins the non-content build inputs a unit compare cannot see:
// the -T/-S shape of the producer pass and the artifact protocol // the -T/-S/root-action shape of the producer pass and the artifact protocol
// revision (bump "fmt" when the unit/archive/commit format changes). // revision (bump "fmt" when the unit/archive/commit format changes).
fn workdirstamptext(istest: i32, emitasm: i32) str = { fn workdirstamptext(istest: i32, emitasm: i32) str = {
if (istest != 0) { if (istest != 0) {
@@ -3335,9 +3355,9 @@ fn workdirstamptext(istest: i32, emitasm: i32) str = {
return "ww workdir fmt 11 mode test asm 0\n"; return "ww workdir fmt 11 mode test asm 0\n";
}; };
if (emitasm != 0) { if (emitasm != 0) {
return "ww workdir fmt 10 mode build asm 1\n"; return "ww workdir fmt 12 mode build asm 1\n";
}; };
return "ww workdir fmt 10 mode build asm 0\n"; return "ww workdir fmt 12 mode build asm 0\n";
}; };
fn stampmatches(path: *u8, want: str) bool = { fn stampmatches(path: *u8, want: str) bool = {
@@ -3444,7 +3464,7 @@ fn cerrpath(head: str, path: *u8, tail: str) void = {
fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32, fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
out: *u8, objstem: *u8, incs: *u8, lf: *lflags, out: *u8, objstem: *u8, incs: *u8, lf: *lflags,
packageonly: i32, istest: i32, publishpackage: i32, requirecommand: i32, istest: i32,
products: *sepproduct, nproducts: i32, emitasm: i32, products: *sepproduct, nproducts: i32, emitasm: i32,
workdir: *u8, scratchout: **u8, workdir: *u8, scratchout: **u8,
graphout: **sepgraph) i32 = { graphout: **sepgraph) i32 = {
@@ -3663,9 +3683,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
SEP_ROLE_NORMAL, products[producti].artifact, true); SEP_ROLE_NORMAL, products[producti].artifact, true);
if (products[producti].root < 0) { return 1; }; if (products[producti].root < 0) { return 1; };
products[producti].variantroot = products[producti].root; products[producti].variantroot = products[producti].root;
if (istest == 0 && packageonly == 0) {
g.pkg[products[producti].root].linkentry = true;
};
producti += 1; producti += 1;
}; };
let testsupportmodule: str = "test"; let testsupportmodule: str = "test";
@@ -3785,6 +3802,17 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
}; };
if (g.identityfailed) { return 1; }; if (g.identityfailed) { return 1; };
if (sepfinalizedirectoryidentities(g) < 0) { return 1; }; if (sepfinalizedirectoryidentities(g) < 0) { return 1; };
if (istest == 0) {
producti = 0;
for (producti < nproducts) {
let root: i32 = products[producti].variantroot;
if (!g.pkg[root].failed) {
g.pkg[root].linkentry =
seprootiscommand(&g.pkg[root]);
};
producti += 1;
};
};
if (istest != 0 && entryisdir != 0) { if (istest != 0 && entryisdir != 0) {
producti = 0; producti = 0;
for (producti < nproducts) { for (producti < nproducts) {
@@ -3803,16 +3831,12 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
producti += 1; producti += 1;
}; };
}; };
let rootpackage: bool = istest == 0
&& !g.pkg[products[0].root].failed
&& !seprootiscommand(&g.pkg[products[0].root]);
if (sepvalidateartifactpaths(g, scratch) < 0) { return 1; }; if (sepvalidateartifactpaths(g, scratch) < 0) { return 1; };
if (warm && sepvalidateworkdirowners(g, scratch) < 0) { return 1; }; if (warm && sepvalidateworkdirowners(g, scratch) < 0) { return 1; };
if (warm && staleall && invalidateworkdirunits(scratch) != 0) { return 1; }; if (warm && staleall && invalidateworkdirunits(scratch) != 0) { return 1; };
let rootpackage: bool = packageonly != 0;
if (rootpackage && !g.pkg[products[0].root].failed
&& cstreqlit(g.pkg[products[0].root].name, "main")) {
cerr("ww: -p requires a non-main package\n");
return 1;
};
let ci: i32 = 0; let ci: i32 = 0;
let order: []i32; let order: []i32;
let stack: []i32; let stack: []i32;
@@ -3839,6 +3863,21 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
}; };
producti += 1; producti += 1;
}; };
if (istest == 0 && requirecommand != 0) {
let root: i32 = products[0].root;
if (!g.pkg[root].failed && !seprootiscommand(&g.pkg[root])) {
let identity: *u8 = g.pkg[root].canon;
if (g.pkg[root].path[0u64] != 0u8) {
identity = g.pkg[root].path;
};
cerrpath("ww: package ", identity,
" is not a main package\n");
return 1;
};
};
if (!g.pkg[products[0].root].failed
&& rootpackage && publishpackage != 0 && emitasm == 0
&& validatepackageoutputpath(out) < 0) { return 1; };
ci = 0; ci = 0;
for (ci < g.n) { g.pkg[ci].color = 0; ci += 1; }; for (ci < g.n) { g.pkg[ci].color = 0; ci += 1; };
producti = 0; producti = 0;
@@ -4149,14 +4188,18 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
if (rootpackage) { if (rootpackage) {
let root: i32 = products[0].root; let root: i32 = products[0].root;
if (g.pkg[root].failed) { return 1; }; if (g.pkg[root].failed) { return 1; };
let archive: *u8 = sepfname(g, root, scratch, ".a"); if (publishpackage != 0) {
let iface: *u8 = sepfname(g, root, scratch, ".wwi"); let archive: *u8 = sepfname(g, root, scratch, ".a");
let outiface: *u8 = sepappendlit(out, ".wwi"); let iface: *u8 = sepfname(g, root, scratch, ".wwi");
if (archive == nil || iface == nil || outiface == nil) { return 1; }; let outiface: *u8 = sepappendlit(out, ".wwi");
if (copyfileatomic(archive, out) != 0 if (archive == nil || iface == nil || outiface == nil) {
|| copyfileatomic(iface, outiface) != 0) { return 1;
cerrpath("ww: cannot write package artifact ", out, "\n"); };
return 1; if (copyfileatomic(archive, out) != 0
|| copyfileatomic(iface, outiface) != 0) {
cerrpath("ww: cannot write package artifact ", out, "\n");
return 1;
};
}; };
return 0; return 0;
}; };
@@ -4305,7 +4348,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
// exact tree. Twin of the cstage build_one_sep wrapper. // exact tree. Twin of the cstage build_one_sep wrapper.
fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32, fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
rootidentity: *u8, out: *u8, rootidentity: *u8, out: *u8,
objstem: *u8, incs: *u8, lf: *lflags, packageonly: i32, istest: i32, objstem: *u8, incs: *u8, lf: *lflags, publishpackage: i32,
requirecommand: i32, istest: i32,
rootvariant: i32, testpackage: *u8, emitasm: i32, rootvariant: i32, testpackage: *u8, emitasm: i32,
keepscratch: i32, workdir: *u8) i32 = { keepscratch: i32, workdir: *u8) i32 = {
let scratch: *u8 = nil; let scratch: *u8 = nil;
@@ -4317,7 +4361,7 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
product.testpackage = testpackage; product.testpackage = testpackage;
product.status = nil; product.status = nil;
product.artifact = nil; product.artifact = nil;
if (packageonly == 0 && entryisdir == 0) { if (entryisdir == 0) {
product.artifact = "__root\0".ptr; product.artifact = "__root\0".ptr;
}; };
product.variant = rootvariant; product.variant = rootvariant;
@@ -4325,7 +4369,7 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
product.variantroot = -1; product.variantroot = -1;
product.support = -1; product.support = -1;
let r: i32 = buildonesepimpl(selfdir, src, entryisdir, out, objstem, let r: i32 = buildonesepimpl(selfdir, src, entryisdir, out, objstem,
incs, lf, packageonly, istest, &product, 1, incs, lf, publishpackage, requirecommand, istest, &product, 1,
emitasm, workdir, &scratch, &g); emitasm, workdir, &scratch, &g);
sepgraphfree(g); sepgraphfree(g);
if (keepscratch == 0 && scratch != nil) { if (keepscratch == 0 && scratch != nil) {
@@ -4367,7 +4411,7 @@ fn buildpackagetests(selfdir: *u8, src: *u8, rootidentity: *u8,
i += 1; i += 1;
}; };
let r: i32 = buildonesepimpl(selfdir, src, 1, let r: i32 = buildonesepimpl(selfdir, src, 1,
products[0].out, products[0].out, incs, &lf, 0, 1, products[0].out, products[0].out, incs, &lf, 0, 0, 1,
products, nproducts, 0, workdir, &scratch, &g); products, nproducts, 0, workdir, &scratch, &g);
sepgraphfree(g); sepgraphfree(g);
return r; return r;
@@ -4464,7 +4508,7 @@ fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
}; };
fn writeusage(fd: i32) void = { fn writeusage(fd: i32) void = {
let s: str = "usage: ww [-V] <subcommand> [args...]\n -V print version and exit\n build [-p] [-S] [-w DIR] [-I DIR] [-o FILE] [path] build a local package graph\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 version print version and exit\n\n path forms:\n foo.ww literal file\n foo search cwd, -I dirs, then the source library for foo.ww or foo/\n lib/foo directory: build its package sources\n -p emits a non-main archive FILE + FILE.wwi\n lib/... every package under lib, recursively (test only)\n . build the cwd's <basename>.ww\n"; let s: str = "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 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 version print version and exit\n\n path forms:\n foo.ww literal file\n 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 . build the cwd's <basename>.ww\n";
os.write(fd, s.ptr, s.len: u64); os.write(fd, s.ptr, s.len: u64);
}; };
@@ -4508,7 +4552,6 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
let outflag: *u8 = nil; // -o target (binary + intermediate stem); T3 let outflag: *u8 = nil; // -o target (binary + intermediate stem); T3
let workdir: *u8 = nil; // -w persistent package-artifact workdir let workdir: *u8 = nil; // -w persistent package-artifact workdir
let emitasm: i32 = 0; let emitasm: i32 = 0;
let packageonly: i32 = 0;
let inccap: u64 = 1u64; let inccap: u64 = 1u64;
let capi: i32 = start; let capi: i32 = start;
for (capi < argc) { for (capi < argc) {
@@ -4535,8 +4578,6 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
if (p[0u64] == 45u8) { // '-' if (p[0u64] == 45u8) { // '-'
if (cstreqlit(p, "-S")) { if (cstreqlit(p, "-S")) {
emitasm = 1; emitasm = 1;
} else { if (cstreqlit(p, "-p")) {
packageonly = 1;
} else { if (p[1u64] == 73u8) { // '-I' } else { if (p[1u64] == 73u8) { // '-I'
let dir: *u8 = nil; let dir: *u8 = nil;
if (p[2u64] != 0u8) { if (p[2u64] != 0u8) {
@@ -4616,7 +4657,7 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
} else { } else {
cerr("ww build: unknown flag\n"); cerr("ww build: unknown flag\n");
return 2; return 2;
}; }; }; }; }; }; }; }; }; }; }; }; };
} else { } else {
if (src == nil) { src = p; }; if (src == nil) { src = p; };
}; };
@@ -4627,10 +4668,6 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
let dot: [2]u8 = ['.': u8, 0u8]; let dot: [2]u8 = ['.': u8, 0u8];
src = &dot[0]; src = &dot[0];
}; };
if (packageonly != 0 && emitasm != 0) {
cerr("ww build: -p and -S cannot be combined\n");
return 2;
};
let requestedliteral: bool = false; let requestedliteral: bool = false;
let requestedstat: os.filestat; let requestedstat: os.filestat;
match (os.stat(&requestedstat, pathstr(src))) { match (os.stat(&requestedstat, pathstr(src))) {
@@ -4643,13 +4680,9 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
cerrpath("ww build: cannot find module ", src, "\n"); cerrpath("ww build: cannot find module ", src, "\n");
return 1; return 1;
}; };
if (packageonly != 0 && isdir == 0) {
cerr("ww build: -p needs a package directory\n");
return 2;
};
let out: *u8 = nil; let out: *u8 = nil;
let objstem: *u8 = nil; let objstem: *u8 = nil;
if (outflag != nil) { if (outflag != nil && outflag[0u64] != 0u8) {
// -o sets both the binary path and the intermediate stem so // -o sets both the binary path and the intermediate stem so
// artifacts land beside the requested output (T3). // artifacts land beside the requested output (T3).
out = outflag; out = outflag;
@@ -4678,9 +4711,11 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
lf.nlibs = nlibs; lf.nlibs = nlibs;
let rootidentity: *u8 = nil; let rootidentity: *u8 = nil;
if (!requestedliteral && isdir != 0) { rootidentity = src; }; if (!requestedliteral && isdir != 0) { rootidentity = src; };
let publishpackage: i32 = 0;
if (outflag != nil && outflag[0u64] != 0u8) { publishpackage = 1; };
return buildonesep(selfdir, resolved, isdir, rootidentity, return buildonesep(selfdir, resolved, isdir, rootidentity,
out, objstem, incs.ptr, &lf, out, objstem, incs.ptr, &lf,
packageonly, 0i32, SEP_VARIANT_PRODUCTION, nil, publishpackage, 0i32, 0i32, SEP_VARIANT_PRODUCTION, nil,
emitasm, 1i32, workdir); emitasm, 1i32, workdir);
}; };
@@ -4862,7 +4897,8 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
if (!requestedliteral && isdir != 0) { rootidentity = src; }; if (!requestedliteral && isdir != 0) { rootidentity = src; };
if (buildonesep(selfdir, resolved, isdir, rootidentity, if (buildonesep(selfdir, resolved, isdir, rootidentity,
outp, outp, incs.ptr, &lf, outp, outp, incs.ptr, &lf,
0i32, 0i32, SEP_VARIANT_PRODUCTION, nil, 0i32, 0i32, nil) != 0) { 0i32, 1i32, 0i32, SEP_VARIANT_PRODUCTION, nil,
0i32, 0i32, nil) != 0) {
let cleanrc: i32 = os.remove(pathstr(outp)); let cleanrc: i32 = os.remove(pathstr(outp));
if (cleanrc != 0 && cleanrc != -2i32) { if (cleanrc != 0 && cleanrc != -2i32) {
cerr("ww: cannot remove temporary output\n"); cerr("ww: cannot remove temporary output\n");
@@ -4952,7 +4988,8 @@ fn runsingletest(selfdir: *u8, src: *u8, incs: *u8, compileonly: i32,
let keep: i32 = 0; let keep: i32 = 0;
if (outstem != nil) { keep = 1; }; if (outstem != nil) { keep = 1; };
let bres: i32 = buildonesep(selfdir, src, 0, nil, outp, objstem, incs, &lf, let bres: i32 = buildonesep(selfdir, src, 0, nil, outp, objstem, incs, &lf,
0i32, 1i32, SEP_VARIANT_PRODUCTION, nil, emitasm, keep, workdir); 0i32, 0i32, 1i32, SEP_VARIANT_PRODUCTION, nil,
emitasm, keep, workdir);
if (bres != 0) { if (bres != 0) {
if (owntmp) { if (owntmp) {
let cleanrc: i32 = os.remove(pathstr(outp)); let cleanrc: i32 = os.remove(pathstr(outp));

View File

@@ -984,7 +984,7 @@ fn mkdirall(path: str) void = {
" assert(pkg.second() == 9);\n};\n")); " assert(pkg.second() == 9);\n};\n"));
let prodout: str = strings.concat(root, "/production.a"); let prodout: str = strings.concat(root, "/production.a");
let prodav: []str = [driver("ww"), "build", "-p", "-I", root, let prodav: []str = [driver("ww"), "build", "-I", root,
"-o", prodout, pkg]; "-o", prodout, pkg];
let out: commandout; let out: commandout;
runcommand(root, "production-build", prodav, runcommand(root, "production-build", prodav,
@@ -2094,7 +2094,7 @@ fn mkdirall(path: str) void = {
if (rsi == 1) { uniondriver = wwtracedriver; }; if (rsi == 1) { uniondriver = wwtracedriver; };
let rootout: str = strings.concat(root, "/root-only-", let rootout: str = strings.concat(root, "/root-only-",
rootstages[rsi], ".a"); rootstages[rsi], ".a");
let rootav: []str = [driver(rootstages[rsi]), "build", "-p", let rootav: []str = [driver(rootstages[rsi]), "build",
"-I", root, "-o", rootout, aaroot]; "-I", root, "-o", rootout, aaroot];
runcommand(root, strings.concat("root-only-", rootstages[rsi]), rootav, runcommand(root, strings.concat("root-only-", rootstages[rsi]), rootav,
(60i64 * (time.second: i64)): time.duration, &out); (60i64 * (time.second: i64)): time.duration, &out);
@@ -3942,7 +3942,7 @@ fn mkdirall(path: str) void = {
let rootout: str = strings.concat(root, "/long-root-", let rootout: str = strings.concat(root, "/long-root-",
rootlabels[ri], "-", tags[si], ".a"); rootlabels[ri], "-", tags[si], ".a");
clean(compilertrace); writefile(compilertrace, ""); clean(compilertrace); writefile(compilertrace, "");
let rootav: []str = [driver(stages[si]), "build", "-p", "-I", let rootav: []str = [driver(stages[si]), "build", "-I",
source, "-o", rootout, rootspellings[ri]]; source, "-o", rootout, rootspellings[ri]];
runcommandenv(root, strings.concat("long-root-", rootlabels[ri], runcommandenv(root, strings.concat("long-root-", rootlabels[ri],
"-", tags[si]), rootav, env, "-", tags[si]), rootav, env,
@@ -4232,7 +4232,7 @@ fn mkdirall(path: str) void = {
clean(trace); clean(trace);
writefile(trace, ""); writefile(trace, "");
let importout: str = strings.concat(root, "/importer-", stages[si], ".a"); let importout: str = strings.concat(root, "/importer-", stages[si], ".a");
let importav: []str = [driver(stages[si]), "build", "-p", "-I", source, let importav: []str = [driver(stages[si]), "build", "-I", source,
"-o", importout, importer]; "-o", importout, importer];
runcommandenv(root, strings.concat("command-import-", stages[si]), runcommandenv(root, strings.concat("command-import-", stages[si]),
importav, env, (60i64 * (time.second: i64)): time.duration, &out); importav, env, (60i64 * (time.second: i64)): time.duration, &out);
@@ -4351,7 +4351,7 @@ fn mkdirall(path: str) void = {
let identitydiagnostics: []str = ["", ""]; let identitydiagnostics: []str = ["", ""];
i = 0; i = 0;
for (i < stages.len) { for (i < stages.len) {
let av: []str = [driver(stages[i]), "build", "-p", "-I", root, let av: []str = [driver(stages[i]), "build", "-I", root,
"-o", strings.concat(root, "/bad-identity-", stages[i], ".a"), "-o", strings.concat(root, "/bad-identity-", stages[i], ".a"),
identityroot]; identityroot];
runcommandenv(root, strings.concat("duplicate-directory-identity-", runcommandenv(root, strings.concat("duplicate-directory-identity-",
@@ -4372,7 +4372,7 @@ fn mkdirall(path: str) void = {
let reserveddiagnostics: []str = ["", ""]; let reserveddiagnostics: []str = ["", ""];
i = 0; i = 0;
for (i < stages.len) { for (i < stages.len) {
let av: []str = [driver(stages[i]), "build", "-p", "-I", root, let av: []str = [driver(stages[i]), "build", "-I", root,
"-o", strings.concat(root, "/bad-reserved-", stages[i], ".a"), "-o", strings.concat(root, "/bad-reserved-", stages[i], ".a"),
reserved]; reserved];
runcommandenv(root, strings.concat("reserved-local-import-", stages[i]), runcommandenv(root, strings.concat("reserved-local-import-", stages[i]),
@@ -4843,7 +4843,7 @@ fn runtimepath(relative: str) str = {
writefile(strings.concat(extra, "/extra.ww"), writefile(strings.concat(extra, "/extra.ww"),
"package extra;\nexport fn marker() i32 = { return 7; };\n"); "package extra;\nexport fn marker() i32 = { return 7; };\n");
let extralib: str = strings.concat(root, "/libextra.a"); let extralib: str = strings.concat(root, "/libextra.a");
let libav: []str = [driver("ww"), "build", "-p", "-o", extralib, let libav: []str = [driver("ww"), "build", "-o", extralib,
extra]; extra];
let out: commandout; let out: commandout;
runcommand(root, "link-flags-library", libav, runcommand(root, "link-flags-library", libav,

View File

@@ -150,7 +150,7 @@ fn samefile(a: str, b: str, why: str) void = {
"late/example/foo", "late/example/foo", fooalias, fooalias]; "late/example/foo", "late/example/foo", fooalias, fooalias];
i = 0; i = 0;
for (i < rootouts.len) { for (i < rootouts.len) {
let rav: []str = [testenv.driver(rootdrivers[i]), "build", "-p", let rav: []str = [testenv.driver(rootdrivers[i]), "build",
"-I", early, "-I", late, "-o", rootouts[i], roottargets[i]]; "-I", early, "-I", late, "-o", rootouts[i], roottargets[i]];
expectcode(td, strings.concat("foo_root_", roottags[i]), rav, 0); expectcode(td, strings.concat("foo_root_", roottags[i]), rav, 0);
let rw: str = strings.concat(rootouts[i], ".sepwork/"); let rw: str = strings.concat(rootouts[i], ".sepwork/");

View File

@@ -166,9 +166,9 @@ fn rejectstable(dir: str, label: str, target: str, needle: str) void = {
"fn hidden() i32 = { return 99; };\n")); "fn hidden() i32 = { return 99; };\n"));
let ca: str = strings.concat(td, "/libonly-c.a"); let ca: str = strings.concat(td, "/libonly-c.a");
let wa: str = strings.concat(td, "/libonly-w.a"); let wa: str = strings.concat(td, "/libonly-w.a");
let cav: []str = [testenv.driver("ww"), "build", "-p", "-I", td, let cav: []str = [testenv.driver("ww"), "build", "-I", td,
"-o", ca, lib]; "-o", ca, lib];
let wav: []str = [testenv.driver("ww_ww"), "build", "-p", "-I", td, let wav: []str = [testenv.driver("ww_ww"), "build", "-I", td,
"-o", wa, lib]; "-o", wa, lib];
if (code(td, "build_package_c", cav) != 0 if (code(td, "build_package_c", cav) != 0
|| code(td, "build_package_w", wav) != 0) { || code(td, "build_package_w", wav) != 0) {
@@ -302,10 +302,10 @@ fn rejectstable(dir: str, label: str, target: str, needle: str) void = {
strings.concat(td, "/api-c2.a"), strings.concat(td, "/api-w.a")]; strings.concat(td, "/api-c2.a"), strings.concat(td, "/api-w.a")];
i = 0; i = 0;
for (i < packages.len) { for (i < packages.len) {
let pav: []str = [testenv.driver(drivers[i]), "build", "-p", "-I", td, let pav: []str = [testenv.driver(drivers[i]), "build", "-I", td,
"-o", packages[i], api]; "-o", packages[i], api];
if (code(td, strings.concat("package_", tags[i]), pav) != 0) { if (code(td, strings.concat("package_", tags[i]), pav) != 0) {
fail("self-contained", "ww build -p api failed"); fail("self-contained", "ww build api failed");
}; };
i += 1; i += 1;
}; };
@@ -907,7 +907,7 @@ fn writediamond(td: str, reverse: bool) str = {
"/.wwtool.w6a")), testenv.readfile(assembler)) "/.wwtool.w6a")), testenv.readfile(assembler))
|| !testenv.same(testenv.readfile(strings.concat(work, || !testenv.same(testenv.readfile(strings.concat(work,
"/.wwtool.stamp")), "/.wwtool.stamp")),
"ww workdir fmt 10 mode build asm 0\n")) { "ww workdir fmt 12 mode build asm 0\n")) {
fail("driver-identity", "persistent artifacts or identities are incomplete"); fail("driver-identity", "persistent artifacts or identities are incomplete");
}; };
let coldwwi: str = testenv.readfile(strings.concat(work, "/dep.wwi")); let coldwwi: str = testenv.readfile(strings.concat(work, "/dep.wwi"));
@@ -1224,9 +1224,9 @@ fn writediamond(td: str, reverse: bool) str = {
"export fn nestedvalue() i32 = { return 41; };\n")); "export fn nestedvalue() i32 = { return 41; };\n"));
let ca: str = strings.concat(td, "/nested-c.a"); let ca: str = strings.concat(td, "/nested-c.a");
let wa: str = strings.concat(td, "/nested-w.a"); let wa: str = strings.concat(td, "/nested-w.a");
let cav: []str = [testenv.driver("ww"), "build", "-p", "-I", root, let cav: []str = [testenv.driver("ww"), "build", "-I", root,
"-o", ca, "foo.bar"]; "-o", ca, "foo.bar"];
let wav: []str = [testenv.driver("ww_ww"), "build", "-p", "-I", root, let wav: []str = [testenv.driver("ww_ww"), "build", "-I", root,
"-o", wa, "foo.bar"]; "-o", wa, "foo.bar"];
if (code(td, "nested_package_c", cav) != 0 if (code(td, "nested_package_c", cav) != 0
|| code(td, "nested_package_w", wav) != 0) { || code(td, "nested_package_w", wav) != 0) {
@@ -1247,22 +1247,575 @@ fn writediamond(td: str, reverse: bool) str = {
let i: i32 = 0; let i: i32 = 0;
for (i < drivers.len) { for (i < drivers.len) {
let badout: str = strings.concat(td, "/bad-flags-", drivers[i]); let badout: str = strings.concat(td, "/bad-flags-", drivers[i]);
let av: []str = [testenv.driver(drivers[i]), "build", "-p", "-S", let av: []str = [testenv.driver(drivers[i]), "build", "-p",
"-I", root, "-o", badout, "foo.bar"]; "-I", root, "-o", badout, "foo.bar"];
let co: testenv.commandout; let co: testenv.commandout;
command(td, strings.concat("package_flag_contract_", drivers[i]), command(td, strings.concat("package_flag_contract_", drivers[i]),
av, &co); av, &co);
if (co.termination != exec.termination.EXIT || co.code == 0 if (co.termination != exec.termination.EXIT || co.code == 0
|| !testenv.has(co.stderr, || !testenv.has(co.stderr, "ww build: unknown flag")) {
"ww build: -p and -S cannot be combined")) {
fail("package-flags", strings.concat(drivers[i], fail("package-flags", strings.concat(drivers[i],
" accepted -p -S")); " accepted the removed package-action flag"));
}; };
i += 1; i += 1;
}; };
testenv.clean(td); testenv.clean(td);
}; };
@test fn package_kind_selects_build_action() void = {
let td: str = testenv.fresh();
let source: str = strings.concat(td, "/source");
let foo: str = strings.concat(source, "/foo");
let dep: str = strings.concat(foo, "/dep");
let bar: str = strings.concat(foo, "/bar");
let commanddir: str = strings.concat(foo, "/cmd");
let cyclea: str = strings.concat(foo, "/cyclea");
let cycleb: str = strings.concat(foo, "/cycleb");
let tools: str = strings.concat(td, "/tools");
mkdir(source); mkdir(foo); mkdir(dep); mkdir(bar); mkdir(commanddir);
mkdir(cyclea); mkdir(cycleb); mkdir(tools);
let depsrc: str = strings.concat(dep, "/dep.ww");
let depbase: str = strings.concat(
"package dep;\n",
"// DEP_SOURCE\n",
"fn hidden() i32 = { return 40; };\n",
"export fn value() i32 = { return hidden(); };\n");
let depprivate: str = strings.concat(
"package dep;\n",
"// DEP_SOURCE\n",
"fn hidden() i32 = { return 41; };\n",
"export fn value() i32 = { return hidden(); };\n");
let depexported: str = strings.concat(depprivate,
"export fn additional() i32 = { return 1; };\n");
testenv.writefile(depsrc, depbase);
testenv.writefile(strings.concat(bar, "/a.ww"), strings.concat(
"package bar;\nimport foo.dep;\n",
"// ROOT_A\n",
"export fn value() i32 = { return dep.value() + 2; };\n"));
testenv.writefile(strings.concat(bar, "/z.ww"), strings.concat(
"package bar;\nimport foo.dep;\n",
"// ROOT_Z\n",
"export fn other() i32 = { return 9; };\n"));
testenv.writefile(strings.concat(commanddir, "/main.ww"), strings.concat(
"package main;\nimport foo.bar;\n",
"// COMMAND_OWNER\n",
"fn main() i32 = { return bar.value(); };\n"));
testenv.writefile(strings.concat(cyclea, "/a.ww"), strings.concat(
"package cyclea;\nimport foo.cycleb;\n",
"export fn value() i32 = { return cycleb.value(); };\n"));
testenv.writefile(strings.concat(cycleb, "/b.ww"), strings.concat(
"package cycleb;\nimport foo.cyclea;\n",
"export fn value() i32 = { return cyclea.value(); };\n"));
let compiler: str = strings.concat(tools, "/w6c.sh");
let assembler: str = strings.concat(tools, "/w6a.sh");
let linker: str = strings.concat(tools, "/w6l.sh");
testenv.writeexecutable(compiler, strings.concat(
"#!/bin/sh\n",
"printf 'BEGIN' >> \"$WW_ROOT_COMPILER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_ROOT_COMPILER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_ROOT_COMPILER_TRACE\"\n",
"exec \"$WW_ROOT_REAL_COMPILER\" \"$@\"\n"));
testenv.writeexecutable(assembler, strings.concat(
"#!/bin/sh\n",
"printf 'BEGIN' >> \"$WW_ROOT_ASSEMBLER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_ROOT_ASSEMBLER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_ROOT_ASSEMBLER_TRACE\"\n",
"exec \"$WW_ROOT_REAL_ASSEMBLER\" \"$@\"\n"));
testenv.writeexecutable(linker, strings.concat(
"#!/bin/sh\n",
"printf 'BEGIN' >> \"$WW_ROOT_LINKER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_ROOT_LINKER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_ROOT_LINKER_TRACE\"\n",
"exec \"$WW_ROOT_REAL_LINKER\" \"$@\"\n"));
let stages: []str = ["ww", "ww_ww"];
let compilers: []str = ["w6c", "w6c_ww"];
let assemblers: []str = ["w6a", "w6a_ww"];
let linkers: []str = ["w6l", "w6l_ww"];
let tags: []str = ["c", "ww"];
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
let rootreference: []str = ["", "", "", "", ""];
let depreference: []str = ["", "", "", "", ""];
let commandreference: []str = ["", "", "", "", ""];
let nooutrootreference: []str = ["", "", "", "", ""];
let nooutdepreference: []str = ["", "", "", "", ""];
let publishreference: str = "";
let exportreference: str = "";
let binaryreference: str = "";
let diagnosticreference: str = "";
let cyclediagreference: str = "";
let cyclepathdiagreference: str = "";
let pathdiagreference: str = "";
let runtime: str = strings.concat(testenv.repo(), "/out/lib");
let si: i32 = 0;
for (si < stages.len) {
let work: str = strings.concat(td, "/work-", tags[si]);
let outpath: str = strings.concat(td, "/libbar-", tags[si], ".a");
let compilertrace: str = strings.concat(td, "/compiler-", tags[si]);
let assemblertrace: str = strings.concat(td, "/assembler-", tags[si]);
let linkertrace: str = strings.concat(td, "/linker-", tags[si]);
mkdir(work);
testenv.writefile(compilertrace, "");
testenv.writefile(assemblertrace, "");
testenv.writefile(linkertrace, "");
let env: []str = roottraceenv(source, runtime,
compiler, assembler, linker,
compilertrace, assemblertrace, linkertrace,
testenv.driver(compilers[si]),
testenv.driver(assemblers[si]),
testenv.driver(linkers[si]));
let av: []str = [testenv.driver(stages[si]), "build",
"-w", work, "-I", source, "-o", outpath, "foo.bar"];
let out: testenv.commandout;
testenv.runcommandenv(td, td, strings.concat("auto-cold-", tags[si]),
av, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0) {
fail("package-autoaction", strings.concat(tags[si],
" cold library build failed: ", out.stderr));
};
let ctrace: str = testenv.readfile(compilertrace);
let atrace: str = testenv.readfile(assemblertrace);
let wantcompiler: str = strings.concat(
"BEGIN<-c><-I><", work,
"/foo.dep.wwi.new><-o><", work,
"/foo.dep.s.new><", work, "/foo.dep.unit.new>\n",
"BEGIN<-c><--import><foo.dep><", work,
"/foo.dep.wwi><-I><", work,
"/foo.bar.wwi.new><-o><", work,
"/foo.bar.s.new><", work, "/foo.bar.unit.new>\n");
let wantassembler: str = strings.concat(
"BEGIN<-o><", work, "/foo.dep.o.new><", work,
"/foo.dep.s.new>\n",
"BEGIN<-o><", work, "/foo.bar.o.new><", work,
"/foo.bar.s.new>\n");
if (!testenv.same(ctrace, wantcompiler)
|| !testenv.same(atrace, wantassembler)
|| testenv.readfile(linkertrace).len != 0) {
fail("package-autoaction",
"library action tool arguments are not compile-only/direct");
};
let rootunit: str = testenv.readfile(strings.concat(work,
"/foo.bar.unit.ww"));
if (testenv.pos(rootunit, "// ROOT_A") < 0
|| testenv.pos(rootunit, "// ROOT_Z")
<= testenv.pos(rootunit, "// ROOT_A")
|| testenv.has(rootunit, "DEP_SOURCE")
|| testenv.occurrences(rootunit,
"//ww:module-reset foo.bar\n") != 2) {
fail("package-autoaction",
"library unit is not owner-only and byte-sorted");
};
if (!testenv.exists(outpath)
|| !testenv.exists(strings.concat(outpath, ".wwi"))
|| !testenv.same(testenv.readfile(outpath),
testenv.readfile(strings.concat(work, "/foo.bar.a")))
|| !testenv.same(testenv.readfile(strings.concat(outpath, ".wwi")),
testenv.readfile(strings.concat(work, "/foo.bar.wwi")))) {
fail("package-autoaction", "explicit package publication changed bytes");
};
let ai: i32 = 0;
for (ai < suffixes.len) {
let rootbytes: str = testenv.readfile(strings.concat(work,
"/foo.bar", suffixes[ai]));
let depbytes: str = testenv.readfile(strings.concat(work,
"/foo.dep", suffixes[ai]));
if (si == 0) {
rootreference[ai] = strings.dup(rootbytes);
depreference[ai] = strings.dup(depbytes);
} else { if (!testenv.same(rootreference[ai], rootbytes)
|| !testenv.same(depreference[ai], depbytes)) {
fail("package-autoaction",
"Cstage and WWstage package artifacts differ");
}; };
ai += 1;
};
if (si == 0) {
publishreference = strings.dup(testenv.readfile(outpath));
exportreference = strings.dup(testenv.readfile(strings.concat(
outpath, ".wwi")));
} else { if (!testenv.same(publishreference,
testenv.readfile(outpath)) || !testenv.same(exportreference,
testenv.readfile(strings.concat(outpath, ".wwi")))) {
fail("package-autoaction", "published stage artifacts differ");
}; };
// The other AutoAction half remains declaration-selected: package main
// alone receives --entry and its complete archive-only link closure.
testenv.clean(compilertrace); testenv.writefile(compilertrace, "");
testenv.clean(assemblertrace); testenv.writefile(assemblertrace, "");
testenv.clean(linkertrace); testenv.writefile(linkertrace, "");
let commandwork: str = strings.concat(td, "/command-work-", tags[si]);
let commandbin: str = strings.concat(td, "/command-", tags[si]);
mkdir(commandwork);
let commandav: []str = [testenv.driver(stages[si]), "build",
"-w", commandwork, "-I", source, "-o", commandbin, "foo.cmd"];
testenv.runcommandenv(td, td, strings.concat("auto-command-", tags[si]),
commandav, env, tmo(), &out);
ctrace = testenv.readfile(compilertrace);
atrace = testenv.readfile(assemblertrace);
let commandlink: str = testenv.readfile(linkertrace);
let wantcommandcompiler: str = strings.concat(
"BEGIN<-c><-I><", commandwork,
"/foo.dep.wwi.new><-o><", commandwork,
"/foo.dep.s.new><", commandwork, "/foo.dep.unit.new>\n",
"BEGIN<-c><--import><foo.dep><", commandwork,
"/foo.dep.wwi><-I><", commandwork,
"/foo.bar.wwi.new><-o><", commandwork,
"/foo.bar.s.new><", commandwork, "/foo.bar.unit.new>\n",
"BEGIN<--entry><-c><--import><foo.bar><", commandwork,
"/foo.bar.wwi><-I><", commandwork,
"/foo.cmd.wwi.new><-o><", commandwork,
"/foo.cmd.s.new><", commandwork, "/foo.cmd.unit.new>\n");
let wantcommandassembler: str = strings.concat(
"BEGIN<-o><", commandwork, "/foo.dep.o.new><", commandwork,
"/foo.dep.s.new>\n",
"BEGIN<-o><", commandwork, "/foo.bar.o.new><", commandwork,
"/foo.bar.s.new>\n",
"BEGIN<-o><", commandwork, "/foo.cmd.o.new><", commandwork,
"/foo.cmd.s.new>\n");
let wantcommandlink: str = strings.concat("BEGIN<-o><", commandbin,
"><", commandwork, "/foo.cmd.a><", commandwork,
"/foo.bar.a><", commandwork, "/foo.dep.a><", runtime,
"/libwwrt.a>\n");
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0
|| !testenv.same(ctrace, wantcommandcompiler)
|| !testenv.same(atrace, wantcommandassembler)
|| !testenv.same(commandlink, wantcommandlink)
|| testenv.has(commandlink, ".wwi>")) {
fail("package-autoaction",
"main action arguments or archive closure changed");
};
let commandunit: str = testenv.readfile(strings.concat(commandwork,
"/foo.cmd.unit.ww"));
if (!testenv.has(commandunit, "// COMMAND_OWNER")
|| testenv.has(commandunit, "// ROOT_A")
|| testenv.has(commandunit, "// DEP_SOURCE")) {
fail("package-autoaction", "command unit contains foreign sources");
};
ai = 0;
for (ai < suffixes.len) {
let commandbytes: str = testenv.readfile(strings.concat(commandwork,
"/foo.cmd", suffixes[ai]));
if (si == 0) { commandreference[ai] = strings.dup(commandbytes); }
else { if (!testenv.same(commandreference[ai], commandbytes)) {
fail("package-autoaction",
"Cstage and WWstage command artifacts differ");
}; };
ai += 1;
};
let commandrun: []str = [commandbin];
if (code(td, strings.concat("auto-command-run-", tags[si]),
commandrun) != 42) {
fail("package-autoaction", "main action binary returned wrong value");
};
// The public run command must make the same declaration-driven choice;
// its private temp paths make exact argv intentionally inapplicable here.
let drivercommandrun: []str = [testenv.driver(stages[si]), "run",
"-I", source, "foo.cmd"];
testenv.runcommand(td, td, strings.concat("auto-driver-command-run-",
tags[si]), drivercommandrun, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 42
|| out.stdout.len != 0 || out.stderr.len != 0) {
fail("package-autoaction", "ww run rejected or misran package main");
};
if (si == 0) {
binaryreference = strings.dup(testenv.readfile(commandbin));
} else { if (!testenv.same(binaryreference,
testenv.readfile(commandbin))) {
fail("package-autoaction", "linked stage binaries differ");
}; };
testenv.clean(compilertrace); testenv.writefile(compilertrace, "");
testenv.clean(assemblertrace); testenv.writefile(assemblertrace, "");
testenv.clean(linkertrace); testenv.writefile(linkertrace, "");
let nooutwork: str = strings.concat(td, "/no-output-work-", tags[si]);
mkdir(nooutwork);
let noout: []str = [testenv.driver(stages[si]), "build",
"-w", nooutwork, "-I", source, "foo.bar"];
testenv.runcommandenv(td, td, strings.concat("auto-no-output-", tags[si]),
noout, env, tmo(), &out);
ctrace = testenv.readfile(compilertrace);
atrace = testenv.readfile(assemblertrace);
let wantnooutcompiler: str = strings.concat(
"BEGIN<-c><-I><", nooutwork,
"/foo.dep.wwi.new><-o><", nooutwork,
"/foo.dep.s.new><", nooutwork, "/foo.dep.unit.new>\n",
"BEGIN<-c><--import><foo.dep><", nooutwork,
"/foo.dep.wwi><-I><", nooutwork,
"/foo.bar.wwi.new><-o><", nooutwork,
"/foo.bar.s.new><", nooutwork, "/foo.bar.unit.new>\n");
let wantnooutassembler: str = strings.concat(
"BEGIN<-o><", nooutwork, "/foo.dep.o.new><", nooutwork,
"/foo.dep.s.new>\n",
"BEGIN<-o><", nooutwork, "/foo.bar.o.new><", nooutwork,
"/foo.bar.s.new>\n");
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0 || testenv.exists(strings.concat(td, "/bar"))
|| !testenv.same(ctrace, wantnooutcompiler)
|| !testenv.same(atrace, wantnooutassembler)
|| testenv.readfile(linkertrace).len != 0) {
fail("package-autoaction",
"cold no-output library build did not remain compile-only");
};
ai = 0;
for (ai < suffixes.len) {
let nooutroot: str = testenv.readfile(strings.concat(nooutwork,
"/foo.bar", suffixes[ai]));
let nooutdep: str = testenv.readfile(strings.concat(nooutwork,
"/foo.dep", suffixes[ai]));
if (!testenv.same(nooutroot, rootreference[ai])
|| !testenv.same(nooutdep, depreference[ai])) {
fail("package-autoaction",
"publication request changed package artifact bytes");
};
if (si == 0) {
nooutrootreference[ai] = strings.dup(nooutroot);
nooutdepreference[ai] = strings.dup(nooutdep);
} else { if (!testenv.same(nooutrootreference[ai], nooutroot)
|| !testenv.same(nooutdepreference[ai], nooutdep)) {
fail("package-autoaction",
"cold no-output artifacts differ by stage");
}; };
ai += 1;
};
// An explicitly empty -o is observably the same no-publication request
// in both drivers and therefore reuses the same semantic actions.
testenv.clean(compilertrace); testenv.writefile(compilertrace, "");
testenv.clean(assemblertrace); testenv.writefile(assemblertrace, "");
testenv.clean(linkertrace); testenv.writefile(linkertrace, "");
let emptyout: []str = [testenv.driver(stages[si]), "build",
"-w", nooutwork, "-I", source, "-o", "", "foo.bar"];
testenv.runcommandenv(td, td, strings.concat("auto-empty-output-",
tags[si]), emptyout, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0
|| testenv.readfile(compilertrace).len != 0
|| testenv.readfile(assemblertrace).len != 0
|| testenv.readfile(linkertrace).len != 0) {
fail("package-autoaction",
"empty -o changed no-publication action semantics");
};
// Publication is not semantic action identity: an already-populated
// package workdir is reusable when the next request omits -o.
testenv.clean(compilertrace); testenv.writefile(compilertrace, "");
testenv.clean(assemblertrace); testenv.writefile(assemblertrace, "");
testenv.clean(linkertrace); testenv.writefile(linkertrace, "");
let warmnoout: []str = [testenv.driver(stages[si]), "build",
"-w", work, "-I", source, "foo.bar"];
testenv.runcommandenv(td, td, strings.concat("auto-warm-no-output-",
tags[si]), warmnoout, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0
|| testenv.readfile(compilertrace).len != 0
|| testenv.readfile(assemblertrace).len != 0
|| testenv.readfile(linkertrace).len != 0) {
fail("package-autoaction",
"publication request entered persistent action identity");
};
testenv.clean(compilertrace); testenv.writefile(compilertrace, "");
testenv.clean(assemblertrace); testenv.writefile(assemblertrace, "");
testenv.clean(linkertrace); testenv.writefile(linkertrace, "");
let pathwork: str = strings.concat(td, "/path-work-", tags[si]);
mkdir(pathwork);
let longout: str = strings.concat(td, "/");
for (longout.len < 4090) { longout = strings.concat(longout, "x"); };
let pathav: []str = [testenv.driver(stages[si]), "build",
"-w", pathwork, "-I", source, "-o", longout, "foo.bar"];
testenv.runcommandenv(td, td, strings.concat("auto-output-path-", tags[si]),
pathav, env, tmo(), &out);
let wantpathdiag: str = "ww: package output path is too long\n";
if (out.termination != exec.termination.EXIT || out.code != 1
|| !testenv.same(out.stderr, wantpathdiag)
|| testenv.readfile(compilertrace).len != 0
|| testenv.readfile(assemblertrace).len != 0
|| testenv.readfile(linkertrace).len != 0
|| testenv.exists(strings.concat(pathwork, "/foo.dep.unit.ww"))
|| testenv.exists(strings.concat(pathwork, "/foo.bar.unit.ww"))) {
fail("package-autoaction",
"package publication path failed after the tool boundary");
};
if (si == 0) { pathdiagreference = strings.dup(out.stderr); }
else { if (!testenv.same(pathdiagreference, out.stderr)) {
fail("package-autoaction", "output path diagnostics differ by stage");
}; };
// Output publication is unused under -S, so even this spelling must not
// participate in an assembly-only action or its failure behavior.
testenv.clean(compilertrace); testenv.writefile(compilertrace, "");
testenv.clean(assemblertrace); testenv.writefile(assemblertrace, "");
testenv.clean(linkertrace); testenv.writefile(linkertrace, "");
let asmwork: str = strings.concat(td, "/asm-work-", tags[si]);
mkdir(asmwork);
let asmav: []str = [testenv.driver(stages[si]), "build", "-S",
"-w", asmwork, "-I", source, "-o", longout, "foo.bar"];
testenv.runcommandenv(td, td, strings.concat("auto-asm-long-output-",
tags[si]), asmav, env, tmo(), &out);
let wantasmcompiler: str = strings.concat(
"BEGIN<-c><-I><", asmwork,
"/foo.dep.wwi.new><-o><", asmwork,
"/foo.dep.s.new><", asmwork, "/foo.dep.unit.new>\n",
"BEGIN<-c><--import><foo.dep><", asmwork,
"/foo.dep.wwi><-I><", asmwork,
"/foo.bar.wwi.new><-o><", asmwork,
"/foo.bar.s.new><", asmwork, "/foo.bar.unit.new>\n");
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0
|| !testenv.same(testenv.readfile(compilertrace), wantasmcompiler)
|| testenv.readfile(assemblertrace).len != 0
|| testenv.readfile(linkertrace).len != 0
|| testenv.exists(strings.concat(asmwork, "/foo.dep.o"))
|| testenv.exists(strings.concat(asmwork, "/foo.dep.a"))
|| testenv.exists(strings.concat(asmwork, "/foo.bar.o"))
|| testenv.exists(strings.concat(asmwork, "/foo.bar.a"))) {
fail("package-autoaction",
"assembly-only action consumed an unused publication path");
};
testenv.clean(compilertrace); testenv.writefile(compilertrace, "");
testenv.clean(assemblertrace); testenv.writefile(assemblertrace, "");
testenv.clean(linkertrace); testenv.writefile(linkertrace, "");
let runav: []str = [testenv.driver(stages[si]), "run",
"-I", source, "foo.bar"];
testenv.runcommandenv(td, td, strings.concat("auto-run-reject-", tags[si]),
runav, env, tmo(), &out);
let wantdiag: str = "ww: package foo.bar is not a main package\n";
if (out.termination != exec.termination.EXIT || out.code != 1
|| !testenv.same(out.stderr, wantdiag)
|| testenv.readfile(compilertrace).len != 0
|| testenv.readfile(assemblertrace).len != 0
|| testenv.readfile(linkertrace).len != 0) {
fail("package-autoaction", "run crossed the non-main tool boundary");
};
if (si == 0) { diagnosticreference = strings.dup(out.stderr); }
else { if (!testenv.same(diagnosticreference, out.stderr)) {
fail("package-autoaction", "run diagnostics differ by stage");
}; };
let cyclerun: []str = [testenv.driver(stages[si]), "run",
"-I", source, "foo.cyclea"];
testenv.runcommandenv(td, td, strings.concat("auto-run-cycle-", tags[si]),
cyclerun, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 1
|| !testenv.has(out.stderr,
"ww: dependency cycle: foo.cyclea -> foo.cycleb -> foo.cyclea\n")
|| testenv.has(out.stderr, "is not a main package")
|| testenv.readfile(compilertrace).len != 0
|| testenv.readfile(assemblertrace).len != 0
|| testenv.readfile(linkertrace).len != 0) {
fail("package-autoaction",
"run command-kind validation masked a graph failure");
};
if (si == 0) { cyclediagreference = strings.dup(out.stderr); }
else { if (!testenv.same(cyclediagreference, out.stderr)) {
fail("package-autoaction", "run cycle diagnostics differ by stage");
}; };
let cyclepathwork: str = strings.concat(td, "/cycle-path-work-",
tags[si]);
mkdir(cyclepathwork);
let cyclepathav: []str = [testenv.driver(stages[si]), "build",
"-w", cyclepathwork, "-I", source, "-o", longout,
"foo.cyclea"];
testenv.runcommandenv(td, td, strings.concat("auto-cycle-path-", tags[si]),
cyclepathav, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 1
|| !testenv.has(out.stderr, "ww: dependency cycle: ")
|| !testenv.has(out.stderr, "foo.cyclea")
|| !testenv.has(out.stderr, "foo.cycleb")
|| testenv.has(out.stderr, "package output path is too long")
|| testenv.readfile(compilertrace).len != 0
|| testenv.readfile(assemblertrace).len != 0
|| testenv.readfile(linkertrace).len != 0) {
fail("package-autoaction",
strings.concat("publication path validation masked a graph failure: ",
out.stderr));
};
if (si == 0) { cyclepathdiagreference = strings.dup(out.stderr); }
else { if (!testenv.same(cyclepathdiagreference, out.stderr)) {
fail("package-autoaction",
"cycle/output-path diagnostic differs by stage");
}; };
assert(os.remove(depsrc) == 0);
testenv.writefile(depsrc, depprivate);
testenv.runcommandenv(td, td, strings.concat("auto-private-", tags[si]),
av, env, tmo(), &out);
ctrace = testenv.readfile(compilertrace);
atrace = testenv.readfile(assemblertrace);
let wantprivatecompiler: str = strings.concat(
"BEGIN<-c><-I><", work,
"/foo.dep.wwi.new><-o><", work,
"/foo.dep.s.new><", work, "/foo.dep.unit.new>\n");
let wantprivateassembler: str = strings.concat(
"BEGIN<-o><", work, "/foo.dep.o.new><", work,
"/foo.dep.s.new>\n");
if (out.termination != exec.termination.EXIT || out.code != 0
|| !testenv.same(ctrace, wantprivatecompiler)
|| !testenv.same(atrace, wantprivateassembler)
|| testenv.readfile(linkertrace).len != 0
|| !testenv.same(rootreference[4], testenv.readfile(strings.concat(
work, "/foo.bar.a")))) {
fail("package-autoaction",
"private dependency change escaped its export boundary");
};
testenv.clean(compilertrace); testenv.writefile(compilertrace, "");
testenv.clean(assemblertrace); testenv.writefile(assemblertrace, "");
testenv.clean(linkertrace); testenv.writefile(linkertrace, "");
assert(os.remove(depsrc) == 0);
testenv.writefile(depsrc, depexported);
testenv.runcommandenv(td, td, strings.concat("auto-export-", tags[si]),
av, env, tmo(), &out);
ctrace = testenv.readfile(compilertrace);
atrace = testenv.readfile(assemblertrace);
let wantexportcompiler: str = strings.concat(
"BEGIN<-c><-I><", work,
"/foo.dep.wwi.new><-o><", work,
"/foo.dep.s.new><", work, "/foo.dep.unit.new>\n",
"BEGIN<-c><--import><foo.dep><", work,
"/foo.dep.wwi><-I><", work,
"/foo.bar.wwi.new><-o><", work,
"/foo.bar.s.new><", work, "/foo.bar.unit.new>\n");
let wantexportassembler: str = strings.concat(
"BEGIN<-o><", work, "/foo.dep.o.new><", work,
"/foo.dep.s.new>\n",
"BEGIN<-o><", work, "/foo.bar.o.new><", work,
"/foo.bar.s.new>\n");
if (out.termination != exec.termination.EXIT || out.code != 0
|| !testenv.same(ctrace, wantexportcompiler)
|| !testenv.same(atrace, wantexportassembler)
|| testenv.readfile(linkertrace).len != 0) {
fail("package-autoaction",
"exported dependency change did not reach its direct importer");
};
testenv.clean(compilertrace); testenv.writefile(compilertrace, "");
testenv.clean(assemblertrace); testenv.writefile(assemblertrace, "");
testenv.clean(linkertrace); testenv.writefile(linkertrace, "");
testenv.runcommandenv(td, td, strings.concat("auto-warm-", tags[si]),
av, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0
|| testenv.readfile(compilertrace).len != 0
|| testenv.readfile(assemblertrace).len != 0
|| testenv.readfile(linkertrace).len != 0) {
fail("package-autoaction", "warm library build did not fully reuse");
};
assert(os.remove(depsrc) == 0);
testenv.writefile(depsrc, depbase);
si += 1;
};
testenv.clean(td);
};
@test fn generated_test_hook_is_not_a_user_exemption() void = { @test fn generated_test_hook_is_not_a_user_exemption() void = {
let td: str = testenv.fresh(); let td: str = testenv.fresh();
let src: str = strings.concat(td, "/case.ww"); let src: str = strings.concat(td, "/case.ww");

View File

@@ -249,6 +249,7 @@ fn runrootargv(dir: str, root: str, name: str, drv: str,
let td: str = testenv.fresh(); let td: str = testenv.fresh();
let src: str = strings.concat(td, "/inline.ww"); let src: str = strings.concat(td, "/inline.ww");
testenv.writefile(src, strings.concat( testenv.writefile(src, strings.concat(
"package main;\n",
"package aa;\n", "package aa;\n",
"export fn getv() i32 = { return 7; };\n", "export fn getv() i32 = { return 7; };\n",
"package main;\n", "package main;\n",

View File

@@ -19,7 +19,7 @@ package rejects_test;
// paramshadow (#19/#16) — value names and module names are disjoint: // paramshadow (#19/#16) — value names and module names are disjoint:
// a param/let/mlet/for-range/match-case binding named `shadowmod` in // a param/let/mlet/for-range/match-case binding named `shadowmod` in
// a file importing module shadowmod is rejected at the decl site; the // a file importing module shadowmod is rejected at the decl site; the
// renamed binding builds+runs 42 (no over-trigger); a param named // renamed binding in a real `package main` builds+runs 42 (no over-trigger); a param named
// like a module a SIBLING module imports does not trip (src_imports // like a module a SIBLING module imports does not trip (src_imports
// filters by the binding's own module — build+run exit 2). The // filters by the binding's own module — build+run exit 2). The
// carrier's neg_selfimp leg is DROPPED here: // carrier's neg_selfimp leg is DROPPED here:

View File

@@ -12,7 +12,7 @@
// p2() = mod2.ping = 31 // p2() = mod2.ping = 31
// total = 112 // total = 112
package fnlabelmangle; package main;
import mod1; import mod1;
import mod2; import mod2;

View File

@@ -6,7 +6,7 @@
// own module — must NOT trip crossmod's param, even though a sibling // own module — must NOT trip crossmod's param, even though a sibling
// module in the same bundle imports that leaf. Build + run; exit = 2. // module in the same bundle imports that leaf. Build + run; exit = 2.
package paramshadowmod; package main;
import shadowmod; import shadowmod;
import crossmod; import crossmod;

View File

@@ -2,7 +2,7 @@
// imported module's bareword, so the rule doesn't fire and the body // imported module's bareword, so the rule doesn't fire and the body
// can call `shadowmod.say()` cleanly. Built + run; exit code = 42. // can call `shadowmod.say()` cleanly. Built + run; exit code = 42.
package paramshadowmod; package main;
import shadowmod; import shadowmod;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 26 //ww:run-exit 26
package main;
// migrated from test/wcc/696_modtype_leaf_collision.c: same-leaf cross-module `type stream` must stay mod-tagged (pre-fix sym smash cross-bound the fields). // migrated from test/wcc/696_modtype_leaf_collision.c: same-leaf cross-module `type stream` must stay mod-tagged (pre-fix sym smash cross-bound the fields).
package mod1; package mod1;
export type stream = struct { export type stream = struct {

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
// migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_DEF promotion must keep use_alias so `defmod.flag` resolves. // migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_DEF promotion must keep use_alias so `defmod.flag` resolves.
package defmod; package defmod;
export def defmod: i32 = 0i32; export def defmod: i32 = 0i32;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
// migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_FN promotion (the original bug) must keep use_alias or `fnmod.flag` fails "unknown type". // migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_FN promotion (the original bug) must keep use_alias or `fnmod.flag` fails "unknown type".
package fnmod; package fnmod;
export type flag = enum i32 { export type flag = enum i32 {

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
// migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_TYPE in-place promotion keeps use_alias so `typmod.flag` resolves (check.c self-import branch). // migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_TYPE in-place promotion keeps use_alias so `typmod.flag` resolves (check.c self-import branch).
package typmod; package typmod;
export type typmod = struct { export type typmod = struct {

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
// migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_VAR promotion must keep use_alias so `varmod.flag` resolves. // migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_VAR promotion must keep use_alias so `varmod.flag` resolves.
package varmod; package varmod;
export let varmod: i32 = 0i32; export let varmod: i32 = 0i32;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 107 //ww:run-exit 107
package main;
// Migrated from 700_e2e row 40. // Migrated from 700_e2e row 40.
package myos; package myos;
fn alloc(n: i64) i64 = { return n + 100; }; fn alloc(n: i64) i64 = { return n + 100; };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
// Migrated from 700_e2e row 123. // Migrated from 700_e2e row 123.
package pkg; package pkg;
export type base = enum i32 { DEC = 10, HEX = 16 }; export type base = enum i32 { DEC = 10, HEX = 16 };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 1 //ww:run-exit 1
package main;
// Migrated from 700_e2e row 118. // Migrated from 700_e2e row 118.
package pkg; package pkg;
type dir = enum { NORTH, SOUTH, EAST, WEST }; type dir = enum { NORTH, SOUTH, EAST, WEST };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
// migrated from test/wcc/732_def_const_fold.c: cross-module def-RHS fold (`def K = a.J + 1`) must emit the folded DATA row; run-exit links AND reads the value. // migrated from test/wcc/732_def_const_fold.c: cross-module def-RHS fold (`def K = a.J + 1`) must emit the folded DATA row; run-exit links AND reads the value.
package a; package a;
export def J: i32 = 41; export def J: i32 = 41;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 5 //ww:run-exit 5
package main;
package aa; package aa;
export def K: i32 = 5; export def K: i32 = 5;
export fn getk() i32 = { return K; }; export fn getk() i32 = { return K; };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 7 //ww:run-exit 7
package main;
package aa; package aa;
export let v: i32 = 7; export let v: i32 = 7;
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 7 //ww:run-exit 7
package main;
package aa; package aa;
export let v: i32 = 7; export let v: i32 = 7;
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 2 //ww:run-exit 2
package main;
package aa; package aa;
export let f: f64 = 2.5; export let f: f64 = 2.5;
export fn getf() f64 = { return f; }; export fn getf() f64 = { return f; };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 7 //ww:run-exit 7
package main;
package aa; package aa;
export let v: i32 = 7; export let v: i32 = 7;
export fn getv() i32 = { return v; }; export fn getv() i32 = { return v; };

View File

@@ -1,4 +1,5 @@
//ww:run //ww:run
package main;
// migrated from test/wcc/839_xmod_nominal_typeeqast.c: A6 — concrete e.myerr returned into e.res (bare-vs-qualified nominal forward), both stages accept. // migrated from test/wcc/839_xmod_nominal_typeeqast.c: A6 — concrete e.myerr returned into e.res (bare-vs-qualified nominal forward), both stages accept.
package e; package e;
export type myerr = !i64; export type myerr = !i64;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
// migrated from test/wcc/839_xmod_nominal_typeeqast.c: bare-spelled union variants matched via QUALIFIED case patterns (`case let x: e.myerr`) — cross-module nominal cover. // migrated from test/wcc/839_xmod_nominal_typeeqast.c: bare-spelled union variants matched via QUALIFIED case patterns (`case let x: e.myerr`) — cross-module nominal cover.
package e; package e;
export type myerr = !i64; export type myerr = !i64;

View File

@@ -1,3 +1,4 @@
//ww:run-exit 7 //ww:run-exit 7
package main;
package cmatrix; package cmatrix;
fn main() i32 = { return 7; }; fn main() i32 = { return 7; };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 37 //ww:run-exit 37
package main;
package pkg; package pkg;
export type point = struct { x: i64, y: i64 }; export type point = struct { x: i64, y: i64 };
export type rect = struct { lo: point, hi: point }; export type rect = struct { lo: point, hi: point };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 18 //ww:run-exit 18
package main;
package pkg; package pkg;
export type point = struct { x: i64, y: i64 }; export type point = struct { x: i64, y: i64 };
export type rect = struct { lo: point, hi: point }; export type rect = struct { lo: point, hi: point };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 27 //ww:run-exit 27
package main;
package pkg; package pkg;
export type point = struct { x: i64, y: i64 }; export type point = struct { x: i64, y: i64 };
export type rect = struct { lo: point, hi: point }; export type rect = struct { lo: point, hi: point };

View File

@@ -1,4 +1,5 @@
//ww:run //ww:run
package main;
// migrated from test/wcc/915_arr_module_index_run.c: cross-package mod.arr[0] — LEAQ base + u16 load width (#128b). // migrated from test/wcc/915_arr_module_index_run.c: cross-package mod.arr[0] — LEAQ base + u16 load width (#128b).
package wcmodarr; package wcmodarr;
export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16]; export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];

View File

@@ -1,4 +1,5 @@
//ww:run-exit 1 //ww:run-exit 1
package main;
// migrated from test/wcc/915_arr_module_index_run.c: nontrivial index — the pre-fix MOVQ-not-LEAQ segfault repro; 0x0801 mod 256 = 1. // migrated from test/wcc/915_arr_module_index_run.c: nontrivial index — the pre-fix MOVQ-not-LEAQ segfault repro; 0x0801 mod 256 = 1.
package wcmodarr; package wcmodarr;
export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16]; export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];

View File

@@ -1,4 +1,5 @@
//ww:run-exit 6 //ww:run-exit 6
package main;
// migrated from test/wcc/915_arr_module_index_run.c: per-element stride pin; 0x1006 mod 256 = 6. // migrated from test/wcc/915_arr_module_index_run.c: per-element stride pin; 0x1006 mod 256 = 6.
package wcmodarr; package wcmodarr;
export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16]; export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];

View File

@@ -1,4 +1,5 @@
//ww:run-exit 18 //ww:run-exit 18
package main;
package myf; package myf;
export type pt = struct { x: i32, y: i32 }; export type pt = struct { x: i32, y: i32 };
export def P: pt = pt{x=7, y=11}; export def P: pt = pt{x=7, y=11};

View File

@@ -1,4 +1,5 @@
//ww:run-exit 7 //ww:run-exit 7
package main;
package myf; package myf;
export fn helper() i32 = { return 42; }; export fn helper() i32 = { return 42; };
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 18 //ww:run-exit 18
package main;
package myf; package myf;
export type pt = struct { x: i32, y: i32 }; export type pt = struct { x: i32, y: i32 };
export let L: pt = pt{x=7, y=11}; export let L: pt = pt{x=7, y=11};

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
package myf; package myf;
export let S: i32 = 42; export let S: i32 = 42;
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run //ww:run
package main;
// migrated from test/wcc/929_match_4arm_cross_module_run.c: 3-arm boundary — arm 2 must reach its body under the shadowed callee. // migrated from test/wcc/929_match_4arm_cross_module_run.c: 3-arm boundary — arm 2 must reach its body under the shadowed callee.
package a; package a;
export type more = void; export type more = void;

View File

@@ -1,4 +1,5 @@
//ww:run //ww:run
package main;
// migrated from test/wcc/929_match_4arm_cross_module_run.c: mixed variant kinds (i32|str|rune|u8) — the shadowed-resolution fix is not shape-specific. // migrated from test/wcc/929_match_4arm_cross_module_run.c: mixed variant kinds (i32|str|rune|u8) — the shadowed-resolution fix is not shape-specific.
package a; package a;
export fn next(k: i32) (i32 | str | rune | u8) = { export fn next(k: i32) (i32 | str | rune | u8) = {

View File

@@ -1,4 +1,5 @@
//ww:run //ww:run
package main;
// migrated from test/wcc/929_match_4arm_cross_module_run.c: canonical 4-arm — caller fn `next` shadows callee a.next; every arm body must fire (#31 fnretlookupmod). // migrated from test/wcc/929_match_4arm_cross_module_run.c: canonical 4-arm — caller fn `next` shadows callee a.next; every arm body must fire (#31 fnretlookupmod).
package a; package a;
export type more = void; export type more = void;

View File

@@ -1,4 +1,5 @@
//ww:run //ww:run
package main;
// migrated from test/wcc/929_match_4arm_cross_module_run.c: reverse arm order — dispatch follows the callee's variant indices, not source order. // migrated from test/wcc/929_match_4arm_cross_module_run.c: reverse arm order — dispatch follows the callee's variant indices, not source order.
package a; package a;
export type more = void; export type more = void;

View File

@@ -1,4 +1,5 @@
//ww:run //ww:run
package main;
// migrated from test/wcc/929_match_4arm_cross_module_run.c: 5-arm scaling — arms 3 and 4 must each reach their body. // migrated from test/wcc/929_match_4arm_cross_module_run.c: 5-arm scaling — arms 3 and 4 must each reach their body.
package a; package a;
export type more = void; export type more = void;

View File

@@ -1,4 +1,5 @@
//ww:run //ww:run
package main;
// migrated from test/wcc/929_match_4arm_cross_module_run.c: 6-arm — every arm beyond the caller's variant count was broken, not just arm 2/3. // migrated from test/wcc/929_match_4arm_cross_module_run.c: 6-arm — every arm beyond the caller's variant count was broken, not just arm 2/3.
package a; package a;
export type more = void; export type more = void;

View File

@@ -1,4 +1,5 @@
//ww:run //ww:run
package main;
// migrated from test/wcc/949_xmod_fnptr_const_run.c: cross-module &cc.fn inside const table + scalar emits the &fn->DATAR reloc (#124); DISTINCT predicates catch a swapped reloc. // migrated from test/wcc/949_xmod_fnptr_const_run.c: cross-module &cc.fn inside const table + scalar emits the &fn->DATAR reloc (#124); DISTINCT predicates catch a swapped reloc.
package cc; package cc;
export fn isa(c: rune) bool = { return c == 'a'; }; export fn isa(c: rune) bool = { return c == 'a'; };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 249 //ww:run-exit 249
package main;
package myf; package myf;
export fn g() f64 = { return -7.0; }; export fn g() f64 = { return -7.0; };
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 253 //ww:run-exit 253
package main;
package myf; package myf;
export fn g() f64 = { return -3.9; }; export fn g() f64 = { return -3.9; };
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 3 //ww:run-exit 3
package main;
package myf; package myf;
export fn g() f64 = { return 3.9; }; export fn g() f64 = { return 3.9; };
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 242 //ww:run-exit 242
package main;
package myf; package myf;
export fn g() f64 = { return -7.0; }; export fn g() f64 = { return -7.0; };
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 8 //ww:run-exit 8
package main;
package myf; package myf;
export fn g() f64 = { return -7.0; }; export fn g() f64 = { return -7.0; };
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run //ww:run
package main;
package myf; package myf;
export fn frexp(n: f64) (f64, i64) = { export fn frexp(n: f64) (f64, i64) = {
if (n == 0.0) { return (0.0, 0i64); }; if (n == 0.0) { return (0.0, 0i64); };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 4 //ww:run-exit 4
package main;
package myf; package myf;
export fn fs(n: f64) (f64, str) = { export fn fs(n: f64) (f64, str) = {
if (n == 0.0) { return (n, "x"); }; if (n == 0.0) { return (n, "x"); };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 15 //ww:run-exit 15
package main;
package myf; package myf;
export fn addi64(a: i64, b: i64) (i64, bool) = { export fn addi64(a: i64, b: i64) (i64, bool) = {
let s: i64 = a + b; let s: i64 = a + b;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 10 //ww:run-exit 10
package main;
package beta; package beta;
export fn dup() (i64, i64) = { return (3i64, 7i64); }; export fn dup() (i64, i64) = { return (3i64, 7i64); };
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
package m; package m;
export fn f() i32 = { export fn f() i32 = {
assert(3 > 2, "m.f invariant"); assert(3 > 2, "m.f invariant");

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
package m; package m;
export fn assert(b: bool, msg: str) void = { }; export fn assert(b: bool, msg: str) void = { };
package main; package main;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
// Current-module &fn lookup must beat a later same-leaf declaration. // Current-module &fn lookup must beat a later same-leaf declaration.
package beta; package beta;
export type hfn = fn(x: i32) i32; export type hfn = fn(x: i32) i32;

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
package beta; package beta;
export type berr = !void; export type berr = !void;
export fn op() (i32 | berr) = { return 5; }; export fn op() (i32 | berr) = { return 5; };

View File

@@ -1,4 +1,5 @@
//ww:run-exit 42 //ww:run-exit 42
package main;
package beta; package beta;
export type berr = !void; export type berr = !void;
export fn op() (i32 | berr) = { return 5; }; export fn op() (i32 | berr) = { return 5; };

View File

@@ -18,7 +18,7 @@
// task; until then we exercise polymorphism via ctx pointers, which // task; until then we exercise polymorphism via ctx pointers, which
// is what the real port wants anyway. // is what the real port wants anyway.
package test; package main;
import os; import os;
import strconv; import strconv;

View File

@@ -15,7 +15,7 @@ package direnum_test;
// Both stages must FAIL with "conflicting package names", and the // Both stages must FAIL with "conflicting package names", and the
// two stages' diagnostics must be byte-identical. // two stages' diagnostics must be byte-identical.
// //
// rootorder — a private rootok/ dir built DIRECTLY (z.ww ORDER-Z // rootorder — a private rootok/ command dir built DIRECTLY (z.ww ORDER-Z
// value()=17 vs a.ww ORDER-A leading comment + main): build exit 0, // value()=17 vs a.ww ORDER-A leading comment + main): build exit 0,
// the binary exits 17, and the driver's local-identity owner unit // the binary exits 17, and the driver's local-identity owner unit
// stores ORDER-A strictly before ORDER-Z (byte-sorted deterministic // stores ORDER-A strictly before ORDER-Z (byte-sorted deterministic
@@ -121,14 +121,14 @@ fn rejectpair(label: str, td: str, target: str, needle: str) void = {
let rootok: str = strings.concat(td, "/rootok"); let rootok: str = strings.concat(td, "/rootok");
assert(os.mkdir(rootok, 493) == 0); assert(os.mkdir(rootok, 493) == 0);
testenv.writefile(strings.concat(rootok, "/z.ww"), strings.concat( testenv.writefile(strings.concat(rootok, "/z.ww"), strings.concat(
"package rootok;\n// ORDER-Z\n", "package main;\n// ORDER-Z\n",
"fn value() i32 = { return 17; };\n")); "fn value() i32 = { return 17; };\n"));
testenv.writefile(strings.concat(rootok, "/a.ww"), strings.concat( testenv.writefile(strings.concat(rootok, "/a.ww"), strings.concat(
"// leading comment is part of the loader grammar\n", "// leading comment is part of the loader grammar\n",
"package rootok;\n// ORDER-A\n", "package main;\n// ORDER-A\n",
"fn main() i32 = { return value(); };\n")); "fn main() i32 = { return value(); };\n"));
let drvs: []str = ["ww", "ww_ww"]; let drvs: []str = ["ww", "ww_ww"];
let identity: str = testenv.localpackageidentity(rootok, "rootok"); let identity: str = testenv.localpackageidentity(rootok, "main");
let units: []str = ["", ""]; let units: []str = ["", ""];
let s: i32 = 0; let s: i32 = 0;
for (s < 2) { for (s < 2) {