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 =
"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"
" 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"
@@ -33,7 +33,7 @@ static const char *usage =
" 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"
" -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";
@@ -969,6 +969,17 @@ sep_command_declared_name(const struct seppkg *p)
? strcmp(p->name, "main_test") == 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
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);
}
/* 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
* product from a caller-owned binary left behind by an earlier invocation. */
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 -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). */
static void
workdir_stamp_text(char *buf, size_t bufsz, int is_test, int emit_asm)
{
snprintf(buf, bufsz, "ww workdir fmt %d mode %s asm %d\n",
is_test ? 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
@@ -2732,7 +2757,8 @@ invalidate_workdir_units(const char *scratch)
static int
build_one_sep_impl(const char *src, int entry_is_dir,
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,
const char *workdir, char *scratchout, size_t scratchoutsz,
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);
if (products[i].root < 0) return 1;
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";
/* -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 (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) {
for (int i = 0; i < nproducts; i++) {
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;
}
}
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)
return 1;
if (warm && sep_validate_workdir_owners(g, scratch) < 0)
return 1;
if (warm && stale_all && invalidate_workdir_units(scratch) != 0)
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 *stack = calloc((size_t)g->n, sizeof *stack);
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)
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 i = 0; i < nproducts; i++) {
int root = products[i].root;
@@ -3277,6 +3323,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
if (root_package) {
int root = products[0].root;
if (g->pkg[root].failed) { free(order); return 1; }
if (publish_package) {
char archive[SEP_ARTIFACT_MAX], iface[SEP_ARTIFACT_MAX];
char outiface[SEP_ARTIFACT_MAX];
sep_fname(g, root, scratch, ".a", archive, sizeof archive);
@@ -3289,10 +3336,12 @@ build_one_sep_impl(const char *src, int entry_is_dir,
}
if (copy_file_atomic(archive, out) != 0
|| copy_file_atomic(iface, outiface) != 0) {
fprintf(stderr, "ww: cannot write package artifact %s\n", out);
fprintf(stderr,
"ww: cannot write package artifact %s\n", out);
free(order);
return 1;
}
}
free(order);
return 0;
}
@@ -3414,7 +3463,8 @@ static int
build_one_sep(const char *src, int entry_is_dir, const char *root_identity,
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,
int root_variant, const char *test_package, int emit_asm,
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,
.support = -1,
};
if (!package_only && !entry_is_dir)
if (!entry_is_dir)
product.artifact = "__root";
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,
sizeof scratch, &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++)
products[i].identity = root_identity;
int r = build_one_sep_impl(src, 1, products[0].out,
products[0].out, extra_includes, NULL, 0, 1,
products[0].out, extra_includes, NULL, 0, 0, 1,
products, nproducts, 0, workdir, scratch, sizeof scratch, &g);
sep_graph_free(g);
return r;
@@ -3621,11 +3671,10 @@ parse_build_flags(const char *cmd, int argc, char **argv,
struct seplinkflags *linkflags,
char *outpath, size_t outsz,
char *workdir, size_t workdirsz,
const char **src_out, int *emit_asm_out, int *package_out)
const char **src_out, int *emit_asm_out)
{
*src_out = NULL;
if (emit_asm_out) *emit_asm_out = 0;
if (package_out) *package_out = 0;
int i = 0;
for (; i < argc; i++) {
if (strcmp(argv[i], "-S") == 0) {
@@ -3634,12 +3683,6 @@ parse_build_flags(const char *cmd, int argc, char **argv,
return -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) {
if (workdir == NULL) {
fprintf(stderr, "ww %s: unknown flag\n", cmd);
@@ -3738,20 +3781,14 @@ do_build(int argc, char **argv)
char outflag[PATH_MAX] = {0};
char workdir[PATH_MAX] = {0};
int emit_asm = 0;
int package_only = 0;
if (parse_build_flags("build", argc, argv, incs, incsz,
&linkflags,
outflag, sizeof outflag, workdir, sizeof workdir,
&src, &emit_asm, &package_only) < 0) {
&src, &emit_asm) < 0) {
free(incs);
return 2;
}
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;
int literal = stat(src, &requested) == 0;
char resolved[PATH_MAX];
@@ -3761,11 +3798,6 @@ do_build(int argc, char **argv)
free(incs);
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];
const char *objstem = NULL;
if (outflag[0]) {
@@ -3785,8 +3817,8 @@ do_build(int argc, char **argv)
}
const char *root_identity = !literal && is_dir ? src : NULL;
int rc = build_one_sep(resolved, is_dir, root_identity, out, objstem, incs,
&linkflags, package_only, 0, SEP_VARIANT_PRODUCTION, NULL, emit_asm,
1, workdir);
&linkflags, outflag[0] != '\0', 0, 0, SEP_VARIANT_PRODUCTION, NULL,
emit_asm, 1, workdir);
free(incs);
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 */
int next = parse_build_flags("run", argc, argv, incs, incsz,
&linkflags,
outflag, sizeof outflag, NULL, 0, &src, NULL, NULL);
outflag, sizeof outflag, NULL, 0, &src, NULL);
if (next < 0) { free(incs); return 2; }
if (src == NULL) src = ".";
struct stat requested;
@@ -3831,7 +3863,7 @@ do_run(int argc, char **argv)
const char *root_identity = !literal && is_dir ? src : NULL;
int buildrc = build_one_sep(resolved, is_dir, root_identity, tmp, tmp, incs,
&linkflags,
0, 0, SEP_VARIANT_PRODUCTION, NULL, 0, 0, NULL);
0, 1, 0, SEP_VARIANT_PRODUCTION, NULL, 0, 0, NULL);
free(incs);
if (buildrc != 0) {
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
* source. An explicit -o names the caller-owned artifact stem. */
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,
outstem[0] ? 1 : 0, workdir);
if (br != 0) {
@@ -4249,7 +4281,7 @@ do_test(int argc, char **argv)
}
/* See module-mode note: no-o scratch is redirected to /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);
if (br != 0) {
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
boundary is live in production Cstage and WWstage compilers and drivers.
An ordinary executable directory root is one normal package action. Its
finalized canonical import identity tags its owner-only unit; it receives only
direct exports, emits `.wwi`, `.o`, and a deterministic `.a`, and is compiled
exactly once. The declared package name validates the last component of that
identity for ordinary importable packages. A selected command root may declare
`package main`; `main` validates its executable role and never replaces or
truncates an identity such as `cmd.tool`. The narrow compiler `--entry` flag
both classifies that primary unit as a command and controls bare `main` codegen;
the package driver uses the parser-only `--command-package` marker for command
test variants that must remain ordinary archive code. Neither flag changes
export identity, and imported interfaces remain under strict leaf validation.
The linker receives that root archive first, then the complete reachable
package-archive closure and runtime archive; it never receives `.wwi`. The
linkers seed `main` before archive selection, so the existing WWAR member
protocol needs no special root object or format change.
A selected production root is one normal package action. Its finalized
canonical import identity tags its owner-only unit; it receives only direct
exports, emits `.wwi`, `.o`, and a deterministic `.a`, and is compiled exactly
once. The declared package name validates the last component of that identity
for ordinary importable packages. After loading and identity finalization, the
declaration also selects the terminal build action: `package main` is a command
and every other valid declaration is a compile-only library. A command root
receives the narrow compiler `--entry` flag, which controls bare `main` codegen,
then the linker receives that root archive first, the complete reachable
package-archive closure, and the runtime archive; it never receives `.wwi`. A
non-main root receives no `--entry` and never enters the linker. `main` validates
command kind but never replaces or truncates an identity such as `cmd.tool`.
The explicit package-less single-file compatibility path has no directory
package declaration to classify and remains a raw command unit; it does not
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
emits a deterministic archive at `lib.a` and its compiler interface at
`lib.a.wwi`, without invoking the linker. A logical target retains its full
identity (`ww build -p -I ROOT -o bar.a foo.bar` emits `foo.bar.*` symbols),
while a literal directory is 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. Package output requires a
directory and `-p` cannot be combined with assembly-only `-S`. 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).
Publication is separate from that semantic action choice. `ww build -o lib.a
DIR` or `ww build -I ROOT -o bar.a foo.bar` automatically publishes a non-main
root's deterministic archive and self-contained compiler export at
`FILE.wwi`, without invoking the linker; the latter retains `foo.bar.*` symbols.
Without `-o`, a non-main root and its dependencies are compiled in the selected
scratch or persistent work directory and no cwd product is invented. For a
command, `-o` continues to name the executable publication path. Output names,
request order, and whether publication was requested never enter package or
action identity. The historical action-selecting `-p` exception is removed and
rejected as an unknown build flag. Assembly-only `-S` still stops before object,
archive, publication, or link production. A literal directory is
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
@@ -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.
3. Bind the first precedence-valid candidate from each reaching context through
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
derivation applies only to still-unbound literal roots, so a nested active
root cannot rename an explicitly resolved package.

View File

@@ -1402,6 +1402,14 @@ fn sepcommanddeclaredname(p: *seppkg) bool = {
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 = {
let from: *seppkg = &g.pkg[importer];
let to: *seppkg = &g.pkg[dep];
@@ -3324,8 +3332,20 @@ fn copyfileatomic(src: *u8, dst: *u8) i32 = {
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 -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).
fn workdirstamptext(istest: i32, emitasm: i32) str = {
if (istest != 0) {
@@ -3335,9 +3355,9 @@ fn workdirstamptext(istest: i32, emitasm: i32) str = {
return "ww workdir fmt 11 mode test asm 0\n";
};
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 = {
@@ -3444,7 +3464,7 @@ fn cerrpath(head: str, path: *u8, tail: str) void = {
fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
out: *u8, objstem: *u8, incs: *u8, lf: *lflags,
packageonly: i32, istest: i32,
publishpackage: i32, requirecommand: i32, istest: i32,
products: *sepproduct, nproducts: i32, emitasm: i32,
workdir: *u8, scratchout: **u8,
graphout: **sepgraph) i32 = {
@@ -3663,9 +3683,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
SEP_ROLE_NORMAL, products[producti].artifact, true);
if (products[producti].root < 0) { return 1; };
products[producti].variantroot = products[producti].root;
if (istest == 0 && packageonly == 0) {
g.pkg[products[producti].root].linkentry = true;
};
producti += 1;
};
let testsupportmodule: str = "test";
@@ -3785,6 +3802,17 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
};
if (g.identityfailed) { 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) {
producti = 0;
for (producti < nproducts) {
@@ -3803,16 +3831,12 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
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 (warm && sepvalidateworkdirowners(g, 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 order: []i32;
let stack: []i32;
@@ -3839,6 +3863,21 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
};
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;
for (ci < g.n) { g.pkg[ci].color = 0; ci += 1; };
producti = 0;
@@ -4149,15 +4188,19 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
if (rootpackage) {
let root: i32 = products[0].root;
if (g.pkg[root].failed) { return 1; };
if (publishpackage != 0) {
let archive: *u8 = sepfname(g, root, scratch, ".a");
let iface: *u8 = sepfname(g, root, scratch, ".wwi");
let outiface: *u8 = sepappendlit(out, ".wwi");
if (archive == nil || iface == nil || outiface == nil) { return 1; };
if (archive == nil || iface == nil || outiface == nil) {
return 1;
};
if (copyfileatomic(archive, out) != 0
|| copyfileatomic(iface, outiface) != 0) {
cerrpath("ww: cannot write package artifact ", out, "\n");
return 1;
};
};
return 0;
};
@@ -4305,7 +4348,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
// exact tree. Twin of the cstage build_one_sep wrapper.
fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
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,
keepscratch: i32, workdir: *u8) i32 = {
let scratch: *u8 = nil;
@@ -4317,7 +4361,7 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
product.testpackage = testpackage;
product.status = nil;
product.artifact = nil;
if (packageonly == 0 && entryisdir == 0) {
if (entryisdir == 0) {
product.artifact = "__root\0".ptr;
};
product.variant = rootvariant;
@@ -4325,7 +4369,7 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
product.variantroot = -1;
product.support = -1;
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);
sepgraphfree(g);
if (keepscratch == 0 && scratch != nil) {
@@ -4367,7 +4411,7 @@ fn buildpackagetests(selfdir: *u8, src: *u8, rootidentity: *u8,
i += 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);
sepgraphfree(g);
return r;
@@ -4464,7 +4508,7 @@ fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
};
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);
};
@@ -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 workdir: *u8 = nil; // -w persistent package-artifact workdir
let emitasm: i32 = 0;
let packageonly: i32 = 0;
let inccap: u64 = 1u64;
let capi: i32 = start;
for (capi < argc) {
@@ -4535,8 +4578,6 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
if (p[0u64] == 45u8) { // '-'
if (cstreqlit(p, "-S")) {
emitasm = 1;
} else { if (cstreqlit(p, "-p")) {
packageonly = 1;
} else { if (p[1u64] == 73u8) { // '-I'
let dir: *u8 = nil;
if (p[2u64] != 0u8) {
@@ -4616,7 +4657,7 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
} else {
cerr("ww build: unknown flag\n");
return 2;
}; }; }; }; }; }; };
}; }; }; }; }; };
} else {
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];
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 requestedstat: os.filestat;
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");
return 1;
};
if (packageonly != 0 && isdir == 0) {
cerr("ww build: -p needs a package directory\n");
return 2;
};
let out: *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
// artifacts land beside the requested output (T3).
out = outflag;
@@ -4678,9 +4711,11 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
lf.nlibs = nlibs;
let rootidentity: *u8 = nil;
if (!requestedliteral && isdir != 0) { rootidentity = src; };
let publishpackage: i32 = 0;
if (outflag != nil && outflag[0u64] != 0u8) { publishpackage = 1; };
return buildonesep(selfdir, resolved, isdir, rootidentity,
out, objstem, incs.ptr, &lf,
packageonly, 0i32, SEP_VARIANT_PRODUCTION, nil,
publishpackage, 0i32, 0i32, SEP_VARIANT_PRODUCTION, nil,
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 (buildonesep(selfdir, resolved, isdir, rootidentity,
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));
if (cleanrc != 0 && cleanrc != -2i32) {
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;
if (outstem != nil) { keep = 1; };
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 (owntmp) {
let cleanrc: i32 = os.remove(pathstr(outp));

View File

@@ -984,7 +984,7 @@ fn mkdirall(path: str) void = {
" assert(pkg.second() == 9);\n};\n"));
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];
let out: commandout;
runcommand(root, "production-build", prodav,
@@ -2094,7 +2094,7 @@ fn mkdirall(path: str) void = {
if (rsi == 1) { uniondriver = wwtracedriver; };
let rootout: str = strings.concat(root, "/root-only-",
rootstages[rsi], ".a");
let rootav: []str = [driver(rootstages[rsi]), "build", "-p",
let rootav: []str = [driver(rootstages[rsi]), "build",
"-I", root, "-o", rootout, aaroot];
runcommand(root, strings.concat("root-only-", rootstages[rsi]), rootav,
(60i64 * (time.second: i64)): time.duration, &out);
@@ -3942,7 +3942,7 @@ fn mkdirall(path: str) void = {
let rootout: str = strings.concat(root, "/long-root-",
rootlabels[ri], "-", tags[si], ".a");
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]];
runcommandenv(root, strings.concat("long-root-", rootlabels[ri],
"-", tags[si]), rootav, env,
@@ -4232,7 +4232,7 @@ fn mkdirall(path: str) void = {
clean(trace);
writefile(trace, "");
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];
runcommandenv(root, strings.concat("command-import-", stages[si]),
importav, env, (60i64 * (time.second: i64)): time.duration, &out);
@@ -4351,7 +4351,7 @@ fn mkdirall(path: str) void = {
let identitydiagnostics: []str = ["", ""];
i = 0;
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"),
identityroot];
runcommandenv(root, strings.concat("duplicate-directory-identity-",
@@ -4372,7 +4372,7 @@ fn mkdirall(path: str) void = {
let reserveddiagnostics: []str = ["", ""];
i = 0;
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"),
reserved];
runcommandenv(root, strings.concat("reserved-local-import-", stages[i]),
@@ -4843,7 +4843,7 @@ fn runtimepath(relative: str) str = {
writefile(strings.concat(extra, "/extra.ww"),
"package extra;\nexport fn marker() i32 = { return 7; };\n");
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];
let out: commandout;
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];
i = 0;
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]];
expectcode(td, strings.concat("foo_root_", roottags[i]), rav, 0);
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"));
let ca: str = strings.concat(td, "/libonly-c.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];
let wav: []str = [testenv.driver("ww_ww"), "build", "-p", "-I", td,
let wav: []str = [testenv.driver("ww_ww"), "build", "-I", td,
"-o", wa, lib];
if (code(td, "build_package_c", cav) != 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")];
i = 0;
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];
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;
};
@@ -907,7 +907,7 @@ fn writediamond(td: str, reverse: bool) str = {
"/.wwtool.w6a")), testenv.readfile(assembler))
|| !testenv.same(testenv.readfile(strings.concat(work,
"/.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");
};
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"));
let ca: str = strings.concat(td, "/nested-c.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"];
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"];
if (code(td, "nested_package_c", cav) != 0
|| code(td, "nested_package_w", wav) != 0) {
@@ -1247,22 +1247,575 @@ fn writediamond(td: str, reverse: bool) str = {
let i: i32 = 0;
for (i < drivers.len) {
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"];
let co: testenv.commandout;
command(td, strings.concat("package_flag_contract_", drivers[i]),
av, &co);
if (co.termination != exec.termination.EXIT || co.code == 0
|| !testenv.has(co.stderr,
"ww build: -p and -S cannot be combined")) {
|| !testenv.has(co.stderr, "ww build: unknown flag")) {
fail("package-flags", strings.concat(drivers[i],
" accepted -p -S"));
" accepted the removed package-action flag"));
};
i += 1;
};
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 = {
let td: str = testenv.fresh();
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 src: str = strings.concat(td, "/inline.ww");
testenv.writefile(src, strings.concat(
"package main;\n",
"package aa;\n",
"export fn getv() i32 = { return 7; };\n",
"package main;\n",

View File

@@ -19,7 +19,7 @@ package rejects_test;
// paramshadow (#19/#16) — value names and module names are disjoint:
// a param/let/mlet/for-range/match-case binding named `shadowmod` in
// 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
// filters by the binding's own module — build+run exit 2). The
// carrier's neg_selfimp leg is DROPPED here:

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
//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).
package mod1;
export type stream = struct {

View File

@@ -1,4 +1,5 @@
//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.
package defmod;
export def defmod: i32 = 0i32;

View File

@@ -1,4 +1,5 @@
//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".
package fnmod;
export type flag = enum i32 {

View File

@@ -1,4 +1,5 @@
//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).
package typmod;
export type typmod = struct {

View File

@@ -1,4 +1,5 @@
//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.
package varmod;
export let varmod: i32 = 0i32;

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
//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.
package a;
export def J: i32 = 41;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
//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.
package e;
export type myerr = !i64;

View File

@@ -1,4 +1,5 @@
//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.
package e;
export type myerr = !i64;

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
//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).
package wcmodarr;
export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];

View File

@@ -1,4 +1,5 @@
//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.
package wcmodarr;
export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
//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.
package a;
export type more = void;

View File

@@ -1,4 +1,5 @@
//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.
package a;
export fn next(k: i32) (i32 | str | rune | u8) = {

View File

@@ -1,4 +1,5 @@
//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).
package a;
export type more = void;

View File

@@ -1,4 +1,5 @@
//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.
package a;
export type more = void;

View File

@@ -1,4 +1,5 @@
//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.
package a;
export type more = void;

View File

@@ -1,4 +1,5 @@
//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.
package a;
export type more = void;

View File

@@ -1,4 +1,5 @@
//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.
package cc;
export fn isa(c: rune) bool = { return c == 'a'; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ package direnum_test;
// Both stages must FAIL with "conflicting package names", and the
// 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,
// the binary exits 17, and the driver's local-identity owner unit
// 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");
assert(os.mkdir(rootok, 493) == 0);
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"));
testenv.writefile(strings.concat(rootok, "/a.ww"), strings.concat(
"// 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"));
let drvs: []str = ["ww", "ww_ww"];
let identity: str = testenv.localpackageidentity(rootok, "rootok");
let identity: str = testenv.localpackageidentity(rootok, "main");
let units: []str = ["", ""];
let s: i32 = 0;
for (s < 2) {