driver: implement post-target run argv semantics
This commit is contained in:
@@ -7849,14 +7849,11 @@ parse_build_flags(const char *cmd, int argc, char **argv,
|
||||
return -1;
|
||||
} else if (*src_out == NULL) {
|
||||
*src_out = argv[i];
|
||||
/* Go's FlagSet stops at the first positional. For build,
|
||||
* everything after it is package-request input, even "--". */
|
||||
if (strcmp(cmd, "build") == 0) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break; /* leave remaining argv to caller (run-args) */
|
||||
/* Go's FlagSet stops at the first positional. Build leaves
|
||||
* the suffix as package-request input; run passes it verbatim
|
||||
* to the selected program, including flag-like operands. */
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
|
||||
@@ -2300,7 +2300,7 @@ The default workflow is deliberately short:
|
||||
```sh
|
||||
ww init example.org/hello
|
||||
ww build
|
||||
ww run -- argument
|
||||
ww run . argument
|
||||
ww test .
|
||||
```
|
||||
|
||||
@@ -2313,8 +2313,9 @@ shown in verbose output.
|
||||
`ww build [DIR|PRODUCT]` builds the default root product or one named product.
|
||||
The default profile is the fully specified `debug` profile; `--profile=release`
|
||||
selects the toolchain's immutable release profile. `ww run` first performs that
|
||||
same build, then runs only a product with `H = B`; arguments after `--` are never
|
||||
interpreted by the build.
|
||||
same build, then runs only a product with `H = B`. After one explicit run target
|
||||
is selected, every remaining operand is program input and is never interpreted
|
||||
as a build option. A leading `--` is not the target boundary.
|
||||
|
||||
There is no command that means “build and opportunistically download whatever is
|
||||
missing.” If a locked source or toolchain is absent, the diagnostic names its
|
||||
@@ -11168,6 +11169,165 @@ source packages, shared test-process state and failure topology, RE2-compatible
|
||||
flat `-run`, finite special-source handling, or external-driver interruption
|
||||
recovery.
|
||||
|
||||
### 11.57 Implemented post-target `ww run` argument boundary
|
||||
|
||||
After one explicit `ww run` target has been selected, every later operand is
|
||||
now program input. Neither driver reparses that suffix as build options. Known
|
||||
and unknown option spellings, would-be option values, a lone `--`, empty
|
||||
strings, later ordinary operands, and later `.ww` spellings retain their exact
|
||||
bytes and order in the child vector. WW still supports only one selected run
|
||||
target: this boundary does not turn a later `.ww` spelling into a second source
|
||||
file.
|
||||
|
||||
#### Pinned authority, official tests, and applicability
|
||||
|
||||
The sole semantic authority is official Go 1.26.5 at commit
|
||||
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||
|
||||
- **behavior directly implemented or asserted by pinned Go** — the Go command
|
||||
parses a run command's registered flags before entering `runRun` and passes
|
||||
only `Flag.Args()` to it
|
||||
([`cmd/go/main.go`, lines 312–322](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/main.go#L312-L322)).
|
||||
The standard flag parser stops at the first non-flag positional; it consumes
|
||||
`--` only when that spelling occurs before the positional boundary
|
||||
([`flag/flag.go`, lines 1074–1089 and 1149–1176](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/flag/flag.go#L1074-L1089)).
|
||||
- **behavior directly implemented or asserted by pinned Go** — `runRun`
|
||||
consumes either the contiguous named-file prefix or one selected package,
|
||||
leaves the suffix as `cmdArgs`, and attaches those exact arguments to the run
|
||||
action
|
||||
([`cmd/go/internal/run/run.go`, lines 96–140 and 170–173](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/run/run.go#L96-L140)).
|
||||
- **behavior directly implemented or asserted by pinned Go** — official
|
||||
regression test
|
||||
[`cmd/go/testdata/script/mod_run_flags_issue64738.txt`, lines 1–4](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/mod_run_flags_issue64738.txt#L1-L4)
|
||||
asserts that `-p ignored` after a requested package is program input, not a
|
||||
`cmd/go` flag. Official
|
||||
[`cmd/go/testdata/script/run_dirs.txt`, lines 1–20](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/run_dirs.txt#L1-L20)
|
||||
separately anchors Go's contiguous multi-file prefix; that source-set rule
|
||||
remains open in WW.
|
||||
- **behavior directly implemented or asserted by pinned Go** — Go deliberately
|
||||
does not preserve the compiled program's exact nonzero exit status
|
||||
([`cmd/go/internal/run/run.go`, lines 56 and 198–210](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/run/run.go#L198-L210));
|
||||
command error accounting owns the resulting Go-command status
|
||||
([`cmd/go/internal/base/base.go`, lines 218–246](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/base/base.go#L218-L246)).
|
||||
That independent exit-status difference is not credited to this slice.
|
||||
- **behavior derived from the pinned implementation** — WW's one explicit
|
||||
local run target supplies the same honest semantic boundary without modules,
|
||||
manifests, registries, network resolution, generalized imports, or source
|
||||
build expressions. WW's implicit default-current-directory extension has no
|
||||
explicit target token, so this slice leaves its leading option parsing
|
||||
unchanged. A leading or pre-target `--` therefore retains WW's existing
|
||||
exact unknown-flag rejection and is not claimed as Go `FlagSet` terminator
|
||||
parity.
|
||||
|
||||
Before this slice, **directly measured WW behavior** was identical in Cstage
|
||||
and WWstage but differed from the pinned boundary. Immediately target-following
|
||||
`-p ignored` and `-- -p ignored` returned status 2, empty stdout, and exact
|
||||
stderr `ww run: unknown flag\n`; `-o sentinel` and `-I path` were consumed by
|
||||
the driver; and only a suffix after a second nonflag was passed unchanged.
|
||||
Named-source and directory-package probes agreed on status, stdout, stderr,
|
||||
and diagnostic order. Accepted runs loaded one target and its ordinary import
|
||||
closure, constructed the private run action, compiled, assembled, archived,
|
||||
linked, executed, propagated WW's established child status, and removed the
|
||||
private executable and `.sepwork`. Rejected flag rows stopped before target
|
||||
resolution, graph/action construction, tools, runtime, or scratch creation.
|
||||
|
||||
#### Ownership, source selection, and the four permanent axes
|
||||
|
||||
- **directly measured WW behavior** — Cstage owns the boundary in
|
||||
`parse_build_flags` and its `do_run` consumer; WWstage owns the semantic twin
|
||||
in its `dorun` parser and executor. No loader, package coordinator, compiler,
|
||||
assembler, archiver, linker, test coordinator, checker, import binder, or
|
||||
runtime library can repair a driver option that was already consumed.
|
||||
- **behavior derived from the pinned implementation** — the primary axis is
|
||||
build/run execution semantics. Driver options are recognized before the
|
||||
first explicit target. That target alone controls resolution; the complete
|
||||
later suffix controls only child invocation.
|
||||
- **behavior derived from the pinned implementation** — the test axis is an
|
||||
explicit non-effect. `ww test` option parsing, target discovery, filters,
|
||||
source variants, generated harness, retained products, execution topology,
|
||||
and result accounting do not use this run boundary.
|
||||
- **behavior derived from the pinned implementation** — the package axis is an
|
||||
explicit non-effect. The suffix is never searched, statted, opened, or
|
||||
classified as source. Declared-name validation, source membership, command
|
||||
classification, initializer topology, and canonical package identity remain
|
||||
those of the one selected target.
|
||||
- **behavior derived from the pinned implementation** — the import axis is an
|
||||
explicit non-effect. Dotted spelling, aliases, local/vendor search, binding,
|
||||
visibility, cycles, graph edges, interface ownership, and initialization
|
||||
order are determined only by the selected source closure. Runtime argv is
|
||||
never package, import, graph, action, symbol, artifact, `.wwi`, publication,
|
||||
or persistence identity.
|
||||
|
||||
Direct roots retain `__root.*`; dotted directories retain dotted package,
|
||||
import, action, symbol, artifact, and semantic identities. Physical target
|
||||
spellings and paths remain loader or presentation observations. Post-target
|
||||
arguments add no root, edge, action, source, or invalidation input and cannot
|
||||
change compilation or comparable artifact bytes.
|
||||
|
||||
#### Phase order, diagnostics, and lifecycle
|
||||
|
||||
- **behavior derived from the pinned implementation** — target resolution,
|
||||
loading, import closure, graph construction, compilation, assembly,
|
||||
in-process archiving, and linking retain their existing order and inputs.
|
||||
After a successful private link, the child vector is the private executable
|
||||
at index 0 followed by the exact post-target suffix. The suffix reaches no
|
||||
earlier phase.
|
||||
- **directly measured WW behavior** — `os.args()` exposes that complete vector,
|
||||
including the PID-bearing private executable path at index 0. WW currently
|
||||
propagates an ordinary child's exact exit code. The argument repair changes
|
||||
only indices 1 onward; PID presentation and exact child-status propagation
|
||||
are preserved, including their independent difference from pinned Go.
|
||||
- **behavior derived from the pinned implementation** — known, unknown,
|
||||
incomplete, or `--` option spellings before the target retain their existing
|
||||
driver diagnostics and status. No spelling after the target can emit a
|
||||
driver-option diagnostic. Missing, invalid, non-main, or producer-failing
|
||||
targets diagnose before runtime; a valid target starts and thereafter owns
|
||||
output and failure caused by its arguments.
|
||||
- **behavior derived from the pinned implementation** — run products remain
|
||||
request-private. No public executable, retained test product, semantic fact,
|
||||
work record, transaction, result, or cache entry is published. Post-target
|
||||
argv enters no unit, interface, assembly, object, archive, initializer,
|
||||
executable, stamp, or persistence byte and creates no reuse or invalidation
|
||||
key. Ordinary build and every test route are byte-for-byte non-effects.
|
||||
- **behavior derived from the pinned implementation** — target or producer
|
||||
failure starts no program and follows ordinary rollback. Runtime nonzero
|
||||
follows the established WW status mapping after successful private linking.
|
||||
Existing unrelated public and committed semantic bytes remain untouched.
|
||||
Normal success, producer failure, runtime failure, and concurrent runs remove
|
||||
each request's owned private executable, `.sepwork`, stage, transaction,
|
||||
capture, result, request, descriptor, and child. Parser state and argv are
|
||||
invocation-local, so overlapping suffixes cannot cross between requests.
|
||||
- **behavior derived from the pinned implementation** — Cstage and WWstage
|
||||
must select the same boundary and retain exact status, stdout, stderr,
|
||||
diagnostic order, runtime argv, normal cleanup, and comparable build-artifact
|
||||
byte identity. The existing PID-bearing private path difference outside
|
||||
stable comparisons is not reclassified by this slice.
|
||||
|
||||
The WW-native observer `run_post_target_arguments_are_program_argv` covers both
|
||||
driver stages with literal named-source and directory-package targets. It
|
||||
checks known separate and joined option spellings, unknown options, would-be
|
||||
values, singleton value-taking spellings, `--`, later nonflags and `.ww`, an
|
||||
empty string, pre-target controls, no-operand default-dot selection, diagnostic
|
||||
precedence, exact child status and output, concurrent isolation, build/test
|
||||
controls, artifact-byte parity, and normal residue cleanup.
|
||||
|
||||
No signal or process-supervision owner changes. Direct external SIGTERM during
|
||||
blocked persistent compilation still can leave the owned compiler alive and
|
||||
exactly three fixed `.new` stages, poisoning the next request while preserving
|
||||
prior public and committed semantic bytes. That verified interruption gap
|
||||
remains open; blind stage deletion is not this argument-boundary repair.
|
||||
|
||||
No serialized representation changes. Build workdir format remains `18`, test
|
||||
workdir format remains `19`, and semantic storage format remains `3`; AST and
|
||||
`.wwi` schemas, action descriptors, request protocols, transaction markers,
|
||||
and stored facts are unchanged. This closes only the post-target argv slice.
|
||||
Regular or missing `_test.ww`, missing `.ww`, hidden named sources, multiple
|
||||
leading sources and their source-set boundary, shared test-package state,
|
||||
panic/exit/Fatal/FailNow topology, RE2-compatible flat `-run`, literal
|
||||
nonregular named-source behavior, three-way no-buildable-source diagnostics,
|
||||
Go-like run exit-status mapping, and external-driver interruption recovery
|
||||
remain open where applicable.
|
||||
|
||||
## 12. Candidate architectures and hard-gate decision
|
||||
|
||||
Five candidates were developed as coherent systems, not as feature bins.
|
||||
|
||||
26
docs/spec.md
26
docs/spec.md
@@ -703,6 +703,32 @@ ImportPath = ident { "." ident } .
|
||||
arguments and result; this rule does not adopt Go's source signature. An
|
||||
ordinary import of a package declared `main` is rejected, except for the
|
||||
toolchain's colocated external-test wiring.
|
||||
- For `ww run`, the first explicit non-option operand selects the one run
|
||||
target. Every later operand is program input and is passed unchanged and in
|
||||
order after the private executable's `argv[0]`; it is not reparsed as a
|
||||
driver option. This includes known or unknown option spellings, would-be
|
||||
option values, a lone `--`, empty arguments, ordinary nonflags, and later
|
||||
`.ww` spellings. A later `.ww` is therefore argv, not another source in a
|
||||
command-line package; multiple named sources remain unsupported. Registered
|
||||
driver options before the target retain their established spelling, value,
|
||||
repetition, and diagnostic rules. A leading or otherwise pre-target `--`
|
||||
retains WW's exact unknown-flag rejection rather than acting as a general
|
||||
terminator. With no explicit target, `ww run` retains its implicit current-
|
||||
directory selection and has no post-target suffix.
|
||||
Only the selected target and pre-target driver options affect resolution,
|
||||
source loading, package/import identity, graph construction, compiler,
|
||||
assembler, archiver, linker, private executable bytes, reuse, or
|
||||
invalidation. A missing, invalid, non-main, or producer-failing target
|
||||
diagnoses before runtime and no suffix spelling can mask that result. A
|
||||
successful private link executes with the exact suffix and retains WW's
|
||||
existing stdout, stderr, and exact child-status propagation. The private run
|
||||
product is removed normally and publishes no executable or persistent
|
||||
semantic state. `ww build`, every `ww test` route, package membership, and
|
||||
dotted import semantics are unchanged. Direct roots remain `__root.*`,
|
||||
dotted directories retain dotted identity, and argv never becomes package,
|
||||
import, action, symbol, artifact, `.wwi`, publication, or persistence
|
||||
identity. Build workdir format remains 18, test workdir format remains 19,
|
||||
and semantic storage format remains 3.
|
||||
- For `ww build`, the output-option name is exactly `o`. The accepted forms are
|
||||
`-o VALUE`, `--o VALUE`, `-o=VALUE`, and `--o=VALUE`. An equals form splits
|
||||
at its first `=` and preserves every later byte, including further `=`
|
||||
|
||||
@@ -479,6 +479,82 @@ supervision is unchanged: the known orphan compiler, fixed `.new` staging, and
|
||||
later persistent-request poisoning remain open and are not credited to this
|
||||
slice.
|
||||
|
||||
The run front has one separate post-target argument contract. The sole
|
||||
authority is official Go 1.26.5 at commit
|
||||
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||
|
||||
- **behavior directly implemented or asserted by pinned Go** — command flag
|
||||
parsing supplies only its positional suffix to `runRun`
|
||||
(`cmd/go/main.go:312–322`; `flag/flag.go:1074–1089,1149–1176`), and
|
||||
`runRun` consumes the named-file prefix or one package before attaching the
|
||||
untouched suffix to the run action
|
||||
(`cmd/go/internal/run/run.go:96–140,170–173`).
|
||||
- **behavior directly implemented or asserted by pinned Go** — official
|
||||
`cmd/go/testdata/script/mod_run_flags_issue64738.txt:1–4` proves that
|
||||
target-following `-p ignored` is program input rather than a Go-command
|
||||
option. `cmd/go/testdata/script/run_dirs.txt:1–20` anchors the independent
|
||||
contiguous named-file prefix, which WW does not claim here.
|
||||
- **behavior derived from the pinned implementation** — after WW's one
|
||||
explicit local run target, every remaining operand is runtime argv. The
|
||||
rule honestly applies without importing Go modules or manifests. WW's
|
||||
default-dot extension has no explicit target boundary, and leading or
|
||||
pre-target `--` remains its existing unknown flag rather than gaining
|
||||
general Go flag-terminator semantics.
|
||||
|
||||
Before the repair, **directly measured WW behavior** was stage-equal but
|
||||
different: both drivers rejected immediately target-following `-p ignored` and
|
||||
`-- -p ignored` at status 2 with empty stdout and exact unknown-flag stderr,
|
||||
consumed target-following `-o` and `-I` values as driver configuration, and
|
||||
passed the ordered suffix only after a second nonflag. Accepted controls loaded
|
||||
one target and its ordinary import closure and completed the private compiler,
|
||||
assembler, archiver, linker, and runtime route; rejected rows stopped before
|
||||
loading, actions, tools, runtime, or scratch.
|
||||
|
||||
The focused `run_post_target_arguments_are_program_argv` observer owns the
|
||||
completed boundary in `test/package/package_test.ww`. Across Cstage and WWstage
|
||||
it uses both a literal named source and a directory package and requires exact
|
||||
ordered delivery for separate and joined known-option spellings, unknown
|
||||
options, would-be values, a singleton value-taking spelling, `--`, later
|
||||
nonflags and `.ww`, and an empty string. It separately requires pre-target
|
||||
options and their missing/unknown diagnostics to remain driver-owned, leading
|
||||
`--` to keep its current rejection, no-operand run to keep default-dot
|
||||
selection, and target load/producer diagnostics to precede inert suffix bytes.
|
||||
Valid programs expose the private executable at `os.args()[0]`, receive the
|
||||
exact suffix at indices 1 onward, and retain WW's existing stdout, stderr, and
|
||||
exact child-status mapping. Pinned Go's distinct command-level nonzero status
|
||||
mapping (`cmd/go/internal/run/run.go:56,198–210` and
|
||||
`cmd/go/internal/base/base.go:218–246`) remains an open difference.
|
||||
|
||||
The observer also treats all four permanent axes as one contract. The build/run
|
||||
axis changes only the parser-to-child boundary. Build and test controls retain
|
||||
their status, streams, action inputs, and comparable artifact bytes. Package
|
||||
membership and command classification still come only from the selected
|
||||
target, while dotted-import resolution, edges, interfaces, visibility, and
|
||||
initialization still come only from that target's closure. Direct actions
|
||||
remain `__root.*`; dotted directories retain dotted identity; argv creates no
|
||||
package, import, graph, action, symbol, artifact, `.wwi`, publication,
|
||||
persistence, reuse, or invalidation identity.
|
||||
|
||||
Post-target bytes therefore cannot change compiler, assembler, archiver,
|
||||
linker, initializer, or private executable bytes. Run publishes no public
|
||||
product or semantic state. Normal success, target/producer failure, runtime
|
||||
nonzero, and overlapping requests must keep suffixes isolated and remove each
|
||||
owned private executable, `.sepwork`, stage, transaction, capture, result,
|
||||
request, descriptor, and child. Cstage and WWstage must agree on stable status,
|
||||
stdout, stderr, diagnostic order, runtime argv, cleanup, and comparable
|
||||
artifact bytes. No AST, interface, descriptor, request, transaction, or
|
||||
persistent schema changes; build workdir format remains 18, test workdir format
|
||||
remains 19, semantic storage format remains 3, and no test-result cache is
|
||||
introduced.
|
||||
|
||||
The observer makes no claim for regular or missing `_test.ww`, missing `.ww`,
|
||||
hidden named sources, multiple leading source operands and source-set
|
||||
boundaries, literal nonregular named sources, shared test-package state,
|
||||
panic/exit/Fatal/FailNow topology, Go-compatible RE2 `-run`, three-way
|
||||
no-buildable-source causes, or Go-like run exit-status mapping. External-driver
|
||||
SIGTERM supervision is also unchanged: the verified orphan compiler, three
|
||||
fixed `.new` stages, and later persistent-request poisoning remain open.
|
||||
|
||||
An existing local directory whose requested build basename ends `.ww`
|
||||
(including a visible `_test.ww` symlink to a directory) remains a directory
|
||||
package, not a raw named test source. WWstage `ww build` now uses the same
|
||||
|
||||
@@ -9832,12 +9832,11 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
}; }; }; };
|
||||
i += 1;
|
||||
} else {
|
||||
if (src == nil) {
|
||||
src = p;
|
||||
i += 1;
|
||||
} else {
|
||||
passstart = i;
|
||||
};
|
||||
src = p;
|
||||
i += 1;
|
||||
// The first target ends driver option parsing. The exact
|
||||
// suffix, including flag-like operands, belongs to the program.
|
||||
passstart = i;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -25131,3 +25131,307 @@ fn documentationclean(root: str) void = {
|
||||
&& !directoryhasfragment(root, ".request"));
|
||||
clean(root);
|
||||
};
|
||||
|
||||
// An explicit run target ends driver-option parsing. The child receives every
|
||||
// later string unchanged, including spellings that would have been options at
|
||||
// the driver front; argv[0] remains WW's private PID-bearing executable path.
|
||||
fn runargvexpect(out: *commandout, code: i32, stdout: str, stderr: str,
|
||||
message: str) void = {
|
||||
if (out.termination != exec.termination.EXIT || out.code != code
|
||||
|| !same(out.stdout, stdout) || !same(out.stderr, stderr)) {
|
||||
abort(message);
|
||||
};
|
||||
};
|
||||
|
||||
fn runargvrequire(ok: bool, message: str) void = {
|
||||
if (!ok) { abort(message); };
|
||||
};
|
||||
|
||||
fn runargvprivateclean(record: str, message: str) void = {
|
||||
let path: str = readfile(record);
|
||||
runargvrequire(path.len != 0 && !os.exists(path)
|
||||
&& !os.exists(strings.concat(path, ".sepwork")), message);
|
||||
};
|
||||
|
||||
fn runargvnormalizedstderr(stderr: str) str = {
|
||||
let prefix: str = "/tmp/ww_run_";
|
||||
let begin: i32 = pos(stderr, prefix);
|
||||
runargvrequire(begin >= 0, "run argv: missing private diagnostic path");
|
||||
let digits: i32 = begin + prefix.len;
|
||||
let end: i32 = digits;
|
||||
for (end < stderr.len && stderr[end] >= '0' && stderr[end] <= '9') {
|
||||
end += 1;
|
||||
};
|
||||
runargvrequire(end > digits,
|
||||
"run argv: malformed private diagnostic path");
|
||||
return strings.concat(strings.sub(stderr, 0, begin), "$RUN",
|
||||
strings.sub(stderr, end, stderr.len));
|
||||
};
|
||||
|
||||
@test fn run_post_target_arguments_are_program_argv() void = {
|
||||
let root: str = fresh();
|
||||
let source: str = strings.concat(root, "/source");
|
||||
let command: str = strings.concat(source, "/cmd/argv");
|
||||
let library: str = strings.concat(source, "/lib/argv");
|
||||
let tests: str = strings.concat(source, "/proof/tests");
|
||||
mkdirall(command); mkdirall(library); mkdirall(tests);
|
||||
let named: str = strings.concat(source, "/named.ww");
|
||||
let program: str = strings.concat(
|
||||
"package main;\nimport os;\nimport strings;\n",
|
||||
"fn main() i32 = {\n",
|
||||
" let av: []str = os.args(); let i: i32 = 1;\n",
|
||||
" if (av.len > 1) {\n",
|
||||
" let fd: i32 = os.open(av[1], os.flag.WRONLY | os.flag.CREATE |\n",
|
||||
" os.flag.TRUNC, 384i32); if (fd < 0) { return 96; };\n",
|
||||
" os.write(fd, av[0].ptr, av[0].len: u64); os.close(fd);\n",
|
||||
" };\n",
|
||||
" for (i < av.len) {\n",
|
||||
" os.write(os.STDOUT_FILENO, \"<\".ptr, 1u64);\n",
|
||||
" os.write(os.STDOUT_FILENO, av[i].ptr, av[i].len: u64);\n",
|
||||
" os.write(os.STDOUT_FILENO, \">\\n\".ptr, 2u64); i += 1;\n",
|
||||
" };\n",
|
||||
" if (av.len == 3 && strings.compare(av[2], \"exit-zero\") == 0) {\n",
|
||||
" return 0;\n",
|
||||
" }; return 47;\n};\n");
|
||||
writefile(named, program);
|
||||
writefile(strings.concat(command, "/main.ww"), program);
|
||||
writefile(strings.concat(library, "/library.ww"),
|
||||
"package argvlib;\nexport fn value() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(tests, "/tests.ww"),
|
||||
"package argvtests;\nfn value() i32 = { return 1; };\n");
|
||||
writefile(strings.concat(tests, "/tests_test.ww"), strings.concat(
|
||||
"package argvtests;\n",
|
||||
"@test fn ordinary() void = { assert(value() == 1); };\n"));
|
||||
|
||||
let expected: str = strings.concat(
|
||||
"<-I>\n<runtime-include>\n<--I=joined-include>\n",
|
||||
"<-o>\n<runtime-output>\n<--o=joined-output>\n",
|
||||
"<-L>\n<runtime-library-dir>\n<-l>\n<runtime-library>\n",
|
||||
"<-p>\n<ignored>\n<-->\n<later>\n<later.ww>\n<>\n<-I>\n");
|
||||
let suffix: []str = ["-I", "runtime-include", "--I=joined-include",
|
||||
"-o", "runtime-output", "--o=joined-output", "-L",
|
||||
"runtime-library-dir", "-l", "runtime-library", "-p", "ignored",
|
||||
"--", "later", "later.ww", "", "-I"];
|
||||
let stages: []str = ["ww", "ww_ww"];
|
||||
let tags: []str = ["c", "ww"];
|
||||
let targets: []str = [named, "cmd.argv", command];
|
||||
let targettags: []str = ["named", "dotted", "directory"];
|
||||
let out: commandout;
|
||||
let si: i32 = 0;
|
||||
for (si < stages.len) {
|
||||
let ti: i32 = 0;
|
||||
for (ti < targets.len) {
|
||||
let record: str = strings.concat(root, "/private-", tags[si],
|
||||
"-", targettags[ti]);
|
||||
let ignoredoutput: str = strings.concat(root, "/ignored-run-",
|
||||
tags[si], "-", targettags[ti]);
|
||||
let av: []str = [driver(stages[si]), "run", "-I", source,
|
||||
"-o", ignoredoutput, targets[ti]];
|
||||
append(av, record);
|
||||
let ai: i32 = 0;
|
||||
for (ai < suffix.len) { append(av, suffix[ai]); ai += 1; };
|
||||
runcommand(root, strings.concat("run-argv-", tags[si], "-",
|
||||
targettags[ti]), av,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
runargvexpect(&out, 47,
|
||||
strings.concat("<", record, ">\n", expected), "",
|
||||
"run argv: target suffix result");
|
||||
runargvrequire(!os.exists(ignoredoutput)
|
||||
&& !os.exists(strings.concat(ignoredoutput, ".sepwork")),
|
||||
"run argv: run published an output");
|
||||
runargvprivateclean(record, "run argv: runtime-nonzero cleanup");
|
||||
ti += 1;
|
||||
};
|
||||
|
||||
let successrecord: str = strings.concat(root, "/private-success-",
|
||||
tags[si]);
|
||||
let successav: []str = [driver(stages[si]), "run", named,
|
||||
successrecord, "exit-zero"];
|
||||
runcommand(root, strings.concat("run-argv-success-", tags[si]),
|
||||
successav, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
runargvexpect(&out, 0,
|
||||
strings.concat("<", successrecord, ">\n<exit-zero>\n"), "",
|
||||
"run argv: runtime-success result");
|
||||
runargvprivateclean(successrecord, "run argv: runtime-success cleanup");
|
||||
|
||||
// Before an explicit target, the existing option grammar remains in
|
||||
// force. No-operand run retains its implicit current-directory target.
|
||||
let unknownav: []str = [driver(stages[si]), "run", "-p", named];
|
||||
runcommand(root, strings.concat("run-argv-unknown-", tags[si]),
|
||||
unknownav, time.second, &out);
|
||||
runargvexpect(&out, 2, "", "ww run: unknown flag\n",
|
||||
"run argv: pre-target unknown flag");
|
||||
let missingflagav: []str = [driver(stages[si]), "run", "-I"];
|
||||
runcommand(root, strings.concat("run-argv-missing-flag-", tags[si]),
|
||||
missingflagav, time.second, &out);
|
||||
runargvexpect(&out, 2, "", "ww run: -I needs an argument\n",
|
||||
"run argv: pre-target missing option value");
|
||||
let leadingstopav: []str = [driver(stages[si]), "run", "--", named];
|
||||
runcommand(root, strings.concat("run-argv-leading-stop-", tags[si]),
|
||||
leadingstopav, time.second, &out);
|
||||
runargvexpect(&out, 2, "", "ww run: unknown flag\n",
|
||||
"run argv: leading terminator control");
|
||||
let defaultav: []str = [driver(stages[si]), "run"];
|
||||
runcommanddir(root, strings.concat("run-argv-default-", tags[si]),
|
||||
command, defaultav,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
runargvexpect(&out, 47, "", "", "run argv: default-dot control");
|
||||
|
||||
// Target selection and loading diagnose before inert runtime bytes.
|
||||
let missingtarget: []str = [driver(stages[si]), "run", "-I", source,
|
||||
"missing.argv", "-p", "ignored"];
|
||||
runcommand(root, strings.concat("run-argv-missing-target-", tags[si]),
|
||||
missingtarget, time.second, &out);
|
||||
runargvexpect(&out, 1, "",
|
||||
"ww run: cannot find module missing.argv\n",
|
||||
"run argv: missing target precedence");
|
||||
let nonmain: []str = [driver(stages[si]), "run", "-I", source,
|
||||
"lib.argv", "--", "later.ww"];
|
||||
runcommand(root, strings.concat("run-argv-non-main-", tags[si]),
|
||||
nonmain, time.second, &out);
|
||||
runargvexpect(&out, 1, "",
|
||||
"ww: package lib.argv is not a main package\n",
|
||||
"run argv: non-main target precedence");
|
||||
si += 1;
|
||||
};
|
||||
|
||||
// A selected main that reaches the compiler still diagnoses before any
|
||||
// flag-like suffix can be interpreted or any runtime side effect can occur.
|
||||
let broken: str = strings.concat(source, "/broken.ww");
|
||||
let brokenmarker: str = strings.concat(root, "/broken-ran");
|
||||
writefile(broken, strings.concat(
|
||||
"package main;\nimport os;\nfn main() i32 = {\n",
|
||||
" let fd: i32 = os.open(\"", brokenmarker, "\", os.flag.WRONLY |\n",
|
||||
" os.flag.CREATE | os.flag.TRUNC, 384i32);\n",
|
||||
" if (fd >= 0) { os.close(fd); }; return missing;\n};\n"));
|
||||
let producerstderr: str = "";
|
||||
let producerwant: str = strings.concat(
|
||||
"$RUN/main.sepwork/__root.unit.ww:7:41: error: undefined: missing\n",
|
||||
"ww: w6c failed for (root)\n");
|
||||
si = 0;
|
||||
for (si < stages.len) {
|
||||
let brokenav: []str = [driver(stages[si]), "run", broken,
|
||||
"-p", "ignored", "--", brokenmarker];
|
||||
runcommand(root, strings.concat("run-argv-producer-", tags[si]),
|
||||
brokenav, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
runargvrequire(out.termination == exec.termination.EXIT
|
||||
&& out.code == 1 && out.stdout.len == 0
|
||||
&& occurrences(out.stderr, "undefined: missing") == 1
|
||||
&& occurrences(out.stderr, "ww: w6c failed for (root)") == 1
|
||||
&& pos(out.stderr, "undefined: missing")
|
||||
< pos(out.stderr, "ww: w6c failed for (root)")
|
||||
&& !has(out.stderr, "unknown flag") && !os.exists(brokenmarker),
|
||||
"run argv: producer failure precedence");
|
||||
let normalized: str = runargvnormalizedstderr(out.stderr);
|
||||
runargvrequire(same(normalized, producerwant),
|
||||
"run argv: exact producer diagnostic");
|
||||
if (si == 0) { producerstderr = strings.dup(normalized); }
|
||||
else {
|
||||
runargvrequire(same(producerstderr, normalized),
|
||||
"run argv: producer diagnostic stage parity");
|
||||
};
|
||||
si += 1;
|
||||
};
|
||||
|
||||
// Runtime suffixes are request-local even while the two fronts execute
|
||||
// concurrently and share their source closure.
|
||||
let crecord: str = strings.concat(root, "/private-parallel-c");
|
||||
let wrecord: str = strings.concat(root, "/private-parallel-ww");
|
||||
let cav: []str = [driver("ww"), "run", named, crecord,
|
||||
"c-only", "", "-I"];
|
||||
let wav: []str = [driver("ww_ww"), "run", "-I", source, command,
|
||||
wrecord, "ww-only", "--", "tail.ww"];
|
||||
let cc: exec.command;
|
||||
cc.path = cav[0]; cc.argv = cav; cc.env = os.getenvs(); cc.dir = repo();
|
||||
cc.stdoutpath = strings.concat(root, "/run-argv-parallel-c.stdout");
|
||||
cc.stderrpath = strings.concat(root, "/run-argv-parallel-c.stderr");
|
||||
cc.deadline = time.add(time.now(time.clock.monotonic),
|
||||
(120i64 * (time.second: i64)): time.duration);
|
||||
cc.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||||
let wc: exec.command;
|
||||
wc.path = wav[0]; wc.argv = wav; wc.env = os.getenvs(); wc.dir = repo();
|
||||
wc.stdoutpath = strings.concat(root, "/run-argv-parallel-ww.stdout");
|
||||
wc.stderrpath = strings.concat(root, "/run-argv-parallel-ww.stderr");
|
||||
wc.deadline = time.add(time.now(time.clock.monotonic),
|
||||
(120i64 * (time.second: i64)): time.duration);
|
||||
wc.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||||
let cp: exec.process;
|
||||
let wp: exec.process;
|
||||
exec.start(&cp, &cc); exec.start(&wp, &wc);
|
||||
let cdone: bool = false;
|
||||
let wdone: bool = false;
|
||||
for (!cdone || !wdone) {
|
||||
if (!cdone) { cdone = exec.poll(&cp); };
|
||||
if (!wdone) { wdone = exec.poll(&wp); };
|
||||
if (!cdone || !wdone) {
|
||||
time.sleep(time.millisecond, time.clock.monotonic);
|
||||
};
|
||||
};
|
||||
runargvrequire(cp.result.errno == 0 && cp.result.cleanuperrno == 0
|
||||
&& cp.result.termination == exec.termination.EXIT && cp.result.code == 47
|
||||
&& wp.result.errno == 0 && wp.result.cleanuperrno == 0
|
||||
&& wp.result.termination == exec.termination.EXIT && wp.result.code == 47
|
||||
&& same(readfile(cc.stdoutpath), strings.concat("<", crecord,
|
||||
">\n<c-only>\n<>\n<-I>\n"))
|
||||
&& readfile(cc.stderrpath).len == 0
|
||||
&& same(readfile(wc.stdoutpath), strings.concat("<", wrecord,
|
||||
">\n<ww-only>\n<-->\n<tail.ww>\n"))
|
||||
&& readfile(wc.stderrpath).len == 0,
|
||||
"run argv: concurrent isolation");
|
||||
runargvprivateclean(crecord, "run argv: parallel C cleanup");
|
||||
runargvprivateclean(wrecord, "run argv: parallel WW cleanup");
|
||||
|
||||
// Run argv never changes build/test products or persistent action bytes.
|
||||
let cbuildwork: str = strings.concat(root, "/build-work-c");
|
||||
let wbuildwork: str = strings.concat(root, "/build-work-ww");
|
||||
let cbuildout: str = strings.concat(root, "/build-output-c");
|
||||
let wbuildout: str = strings.concat(root, "/build-output-ww");
|
||||
mkdirall(cbuildwork); mkdirall(wbuildwork);
|
||||
let cbuildav: []str = [driver("ww"), "build", "-w", cbuildwork,
|
||||
"-o", cbuildout, named];
|
||||
let wbuildav: []str = [driver("ww_ww"), "build", "-w", wbuildwork,
|
||||
"-o", wbuildout, named];
|
||||
runcommand(root, "run-argv-build-c", cbuildav,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
runargvexpect(&out, 0, "", "", "run argv: C build control");
|
||||
runcommand(root, "run-argv-build-ww", wbuildav,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
runargvexpect(&out, 0, "", "", "run argv: WW build control");
|
||||
runargvrequire(same(readfile(cbuildout), readfile(wbuildout))
|
||||
&& same(wrongsuffixsemantictreesnapshot(cbuildwork),
|
||||
wrongsuffixsemantictreesnapshot(wbuildwork)),
|
||||
"run argv: build artifact parity");
|
||||
let ctestwork: str = strings.concat(root, "/test-work-c");
|
||||
let wtestwork: str = strings.concat(root, "/test-work-ww");
|
||||
let ctestout: str = strings.concat(root, "/test-output-c");
|
||||
let wtestout: str = strings.concat(root, "/test-output-ww");
|
||||
mkdirall(ctestwork); mkdirall(wtestwork);
|
||||
let ctestav: []str = [driver("ww"), "test", "-c", "-w", ctestwork,
|
||||
"-I", source, "-o", ctestout, "proof.tests"];
|
||||
let wtestav: []str = [driver("ww_ww"), "test", "-c", "-w", wtestwork,
|
||||
"-I", source, "-o", wtestout, "proof.tests"];
|
||||
runcommand(root, "run-argv-test-c", ctestav,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
runargvexpect(&out, 0, "", "", "run argv: C test control");
|
||||
runcommand(root, "run-argv-test-ww", wtestav,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
runargvexpect(&out, 0, "", "", "run argv: WW test control");
|
||||
runargvrequire(same(readfile(ctestout), readfile(wtestout))
|
||||
&& same(wrongsuffixsemantictreesnapshot(ctestwork),
|
||||
wrongsuffixsemantictreesnapshot(wtestwork)),
|
||||
"run argv: test artifact parity");
|
||||
|
||||
runargvrequire(!wrongsuffixpathfragment(root, ".new")
|
||||
&& !wrongsuffixpathfragment(root, ".old")
|
||||
&& !wrongsuffixpathfragment(root, ".wwtxn.")
|
||||
&& !wrongsuffixpathfragment(root, ".install")
|
||||
&& !wrongsuffixpathfragment(root, ".sepwork")
|
||||
&& !wrongsuffixpathfragment(root, ".stage")
|
||||
&& !wrongsuffixpathfragment(root, ".capture")
|
||||
&& !wrongsuffixpathfragment(root, ".result")
|
||||
&& !wrongsuffixpathfragment(root, ".request")
|
||||
&& !wrongsuffixpathfragment(root, ".descriptor")
|
||||
&& !wrongsuffixpathfragment(root, ".child"),
|
||||
"run argv: normal residue");
|
||||
clean(root);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user