ww output: match Go exact -o parsing
This commit is contained in:
@@ -7375,7 +7375,8 @@ parse_build_flags(const char *cmd, int argc, char **argv,
|
|||||||
} else if (strncmp(argv[i], "-I", 2) == 0 && argv[i][2]) {
|
} else if (strncmp(argv[i], "-I", 2) == 0 && argv[i][2]) {
|
||||||
if (include_append(cmd, incs, incsz, argv[i] + 2) < 0)
|
if (include_append(cmd, incs, incsz, argv[i] + 2) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (strcmp(argv[i], "-o") == 0) {
|
} else if (strcmp(argv[i], "-o") == 0 ||
|
||||||
|
(strcmp(cmd, "build") == 0 && strcmp(argv[i], "--o") == 0)) {
|
||||||
if (i + 1 >= argc) {
|
if (i + 1 >= argc) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"ww %s: -o needs an argument\n", cmd);
|
"ww %s: -o needs an argument\n", cmd);
|
||||||
@@ -7383,7 +7384,16 @@ parse_build_flags(const char *cmd, int argc, char **argv,
|
|||||||
}
|
}
|
||||||
if (cli_copy(cmd, "-o", outpath, outsz, argv[++i]) < 0)
|
if (cli_copy(cmd, "-o", outpath, outsz, argv[++i]) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (strncmp(argv[i], "-o", 2) == 0 && argv[i][2]) {
|
} else if (strcmp(cmd, "build") == 0 &&
|
||||||
|
strncmp(argv[i], "-o=", 3) == 0) {
|
||||||
|
if (cli_copy(cmd, "-o", outpath, outsz, argv[i] + 3) < 0)
|
||||||
|
return -1;
|
||||||
|
} else if (strcmp(cmd, "build") == 0 &&
|
||||||
|
strncmp(argv[i], "--o=", 4) == 0) {
|
||||||
|
if (cli_copy(cmd, "-o", outpath, outsz, argv[i] + 4) < 0)
|
||||||
|
return -1;
|
||||||
|
} else if (strcmp(cmd, "run") == 0 &&
|
||||||
|
strncmp(argv[i], "-o", 2) == 0 && argv[i][2]) {
|
||||||
if (cli_copy(cmd, "-o", outpath, outsz, argv[i] + 2) < 0)
|
if (cli_copy(cmd, "-o", outpath, outsz, argv[i] + 2) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (argv[i][0] == '-') {
|
} else if (argv[i][0] == '-') {
|
||||||
@@ -7863,7 +7873,8 @@ do_test(int argc, char **argv)
|
|||||||
} else if (strncmp(argv[i], "-timeout-ms=", 12) == 0 &&
|
} else if (strncmp(argv[i], "-timeout-ms=", 12) == 0 &&
|
||||||
argv[i][12] != '\0') {
|
argv[i][12] != '\0') {
|
||||||
packageopts = 1;
|
packageopts = 1;
|
||||||
} else if (strcmp(argv[i], "-o") == 0) {
|
} else if (strcmp(argv[i], "-o") == 0 ||
|
||||||
|
strcmp(argv[i], "--o") == 0) {
|
||||||
if (i + 1 >= argc) {
|
if (i + 1 >= argc) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"ww test: -o needs an argument\n");
|
"ww test: -o needs an argument\n");
|
||||||
@@ -7871,9 +7882,12 @@ do_test(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
if (cli_copy("test", "-o", outstem, sizeof outstem,
|
if (cli_copy("test", "-o", outstem, sizeof outstem,
|
||||||
argv[++i]) < 0) return 2;
|
argv[++i]) < 0) return 2;
|
||||||
} else if (argv[i][1] == 'o' && argv[i][2]) {
|
} else if (strncmp(argv[i], "-o=", 3) == 0) {
|
||||||
if (cli_copy("test", "-o", outstem, sizeof outstem,
|
if (cli_copy("test", "-o", outstem, sizeof outstem,
|
||||||
argv[i] + 2) < 0) return 2;
|
argv[i] + 3) < 0) return 2;
|
||||||
|
} else if (strncmp(argv[i], "--o=", 4) == 0) {
|
||||||
|
if (cli_copy("test", "-o", outstem, sizeof outstem,
|
||||||
|
argv[i] + 4) < 0) return 2;
|
||||||
} else if (strcmp(argv[i], "-w") == 0) {
|
} else if (strcmp(argv[i], "-w") == 0) {
|
||||||
if (i + 1 >= argc) {
|
if (i + 1 >= argc) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
|||||||
@@ -8658,6 +8658,176 @@ executable, and retained-test-product bytes are unchanged. Build workdir format
|
|||||||
remains `18`, test workdir format remains `19`, and semantic storage format
|
remains `18`, test workdir format remains `19`, and semantic storage format
|
||||||
remains `3`.
|
remains `3`.
|
||||||
|
|
||||||
|
### 11.44 Implemented exact output-option name and value semantics
|
||||||
|
|
||||||
|
The `ww build` and `ww test` output option has the exact registered name `o`.
|
||||||
|
Its accepted forms are `-o VALUE`, `--o VALUE`, `-o=VALUE`, and
|
||||||
|
`--o=VALUE`. An equals form splits at the first `=` and preserves the complete
|
||||||
|
remaining value, including an empty value and additional `=` bytes. Repeated
|
||||||
|
occurrences are last-value-wins. A final empty value means no effective
|
||||||
|
explicit output: a single command uses its ordinary default, a multi-command
|
||||||
|
build performs its ordinary no-public-output build, a running test retains no
|
||||||
|
copy, and compile-only testing uses its ordinary default retained name.
|
||||||
|
Concatenated names such as `-oVALUE` and `--oVALUE` are unknown flags rather
|
||||||
|
than output requests. Build option parsing stops at the first root operand;
|
||||||
|
test parsing continues to recognize known test options, including exact `o`,
|
||||||
|
on either side of its package operands.
|
||||||
|
|
||||||
|
#### Pinned evidence and fact classification
|
||||||
|
|
||||||
|
The sole authority is official Go 1.26.5 at
|
||||||
|
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||||
|
|
||||||
|
- `init` registers exactly string flag name `o` for the build command in
|
||||||
|
[`cmd/go/internal/work/build.go`, lines 241–248](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L241-L248),
|
||||||
|
and `invoke` parses that flag set before passing only remaining operands to
|
||||||
|
`runBuild` in
|
||||||
|
[`cmd/go/main.go`, lines 290–322](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/main.go#L290-L322).
|
||||||
|
- `(*FlagSet).parseOne` accepts one or two leading dashes, splits the first
|
||||||
|
`=`, consumes the next argument only when there was no equals delimiter, and
|
||||||
|
rejects an unregistered concatenated name in
|
||||||
|
[`flag/flag.go`, lines 1074–1146](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/flag/flag.go#L1074-L1146).
|
||||||
|
`(*FlagSet).Parse` stops at the first non-flag operand at lines 1149–1176.
|
||||||
|
`stringValue.Set` and `(*FlagSet).Set` overwrite a repeated string value at
|
||||||
|
lines 240–250 and 494–528. `runBuild` derives effective explicit output from
|
||||||
|
the final value's nonzero length and otherwise selects its default behavior
|
||||||
|
in
|
||||||
|
[`cmd/go/internal/work/build.go`, lines 459–478](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L459-L478).
|
||||||
|
- Build command testdata uses an equals-delimited output successfully in
|
||||||
|
[`version_buildvcs_nested.txt`, line 57](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/version_buildvcs_nested.txt#L57).
|
||||||
|
`testParse` exercises exact names with two leading dashes and separate
|
||||||
|
values at
|
||||||
|
[`flag_test.go`, lines 164–215](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/flag/flag_test.go#L164-L215),
|
||||||
|
`TestUserDefined` asserts an equals-delimited string value at lines 251–267,
|
||||||
|
and `TestUsage` asserts unknown-flag failure at lines 153–161.
|
||||||
|
- Test command `init` registers exactly string flag name `o` in
|
||||||
|
[`cmd/go/internal/test/testflag.go`, lines 32–38](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/testflag.go#L32-L38).
|
||||||
|
`cmdflag.ParseOne` accepts one or two dashes, cuts the first `=`, preserves
|
||||||
|
empty and remaining-equals value bytes, and rejects unknown names in
|
||||||
|
[`cmd/go/internal/cmdflag/flag.go`, lines 53–118](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/cmdflag/flag.go#L53-L118).
|
||||||
|
`testFlags` recognizes known flags before and after the package list and
|
||||||
|
rejects an unknown flag with `-c` in
|
||||||
|
[`cmd/go/internal/test/testflag.go`, lines 219–349](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/testflag.go#L219-L349).
|
||||||
|
`testNeedBinary` and the output-classification branch treat final empty
|
||||||
|
`testO` as no explicit retained destination in
|
||||||
|
[`cmd/go/internal/test/test.go`, lines 631–646 and 771–781](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L631-L781).
|
||||||
|
- Official command testdata uses compile-only test output successfully in
|
||||||
|
[`devnull.txt`, lines 3–8](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/devnull.txt#L3-L8)
|
||||||
|
and
|
||||||
|
[`test_race_tag.txt`, lines 1–9](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_race_tag.txt#L1-L9).
|
||||||
|
[`test2json_interrupt.txt`, line 10](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test2json_interrupt.txt#L10)
|
||||||
|
places `-o` after a package operand, while
|
||||||
|
[`test_flag.txt`, lines 11–16](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_flag.txt#L11-L16)
|
||||||
|
asserts unknown-flag rejection with `-c` across supported placements.
|
||||||
|
|
||||||
|
Those registrations, parser branches, effective-output branches, and official
|
||||||
|
assertions are **behavior directly implemented or asserted by pinned Go**.
|
||||||
|
Applying their exact-name, first-equals, final-value, and command-placement
|
||||||
|
rules to WW's local command surface while retaining WW's diagnostic wording is
|
||||||
|
**behavior derived from the pinned implementation**. The measured WW matrix
|
||||||
|
below is **directly measured WW behavior**; no installed host Go result is used
|
||||||
|
as authority.
|
||||||
|
|
||||||
|
#### Direct pre-fix Cstage and WWstage matrix
|
||||||
|
|
||||||
|
Both stages had identical pre-fix behavior in every row:
|
||||||
|
|
||||||
|
| Route and spelling | Directly measured pre-fix result |
|
||||||
|
| --- | --- |
|
||||||
|
| single build, `-o=name` | exited 0 with empty streams, left `name` absent, and published executable `=name` |
|
||||||
|
| single build, `-oname` | exited 0 with empty streams and incorrectly accepted the concatenated name |
|
||||||
|
| single build, `--o name` or `--o=name` | exited 2 with empty stdout and byte-identical `ww build: unknown flag\n` stderr |
|
||||||
|
| single build, final `-o=` | exited 0 with empty streams and published literal file `=` rather than the default command name |
|
||||||
|
| build values containing `=` | retained an erroneous leading `=`; repeated separate forms were already last-value-wins |
|
||||||
|
| build option after the first root | remained package input rather than being reparsed, matching the required placement boundary |
|
||||||
|
| two-command coordinator, `-o=DIR/` | published both products beneath a literal leading-`=` directory |
|
||||||
|
| two-command coordinator, `-oDIR/` | incorrectly accepted the joined name and published beneath the requested directory |
|
||||||
|
| two-command coordinator, final `-o=` | exited 2 as `cannot use -o with multiple packages` instead of selecting no effective explicit output |
|
||||||
|
| compile-only directory test, `-o=name` | exited 0 with empty streams, left `name` absent, and published test binary `=name` |
|
||||||
|
| compile-only directory test, `-oname` | incorrectly accepted the concatenated name and published that retained binary |
|
||||||
|
| compile-only directory test, `--o=name` | exited 2 with empty stdout and byte-identical `ww test: unknown flag\n` stderr |
|
||||||
|
| compile-only directory test, final `-o=` or a value containing `=` | published literal `=` or an erroneous leading-`=` destination |
|
||||||
|
| test `-o` after its package operand | was already recognized, matching the required test placement boundary |
|
||||||
|
|
||||||
|
The wrong-path single-build executables were stage-byte-identical 4,268-byte
|
||||||
|
files with SHA-256
|
||||||
|
`866c1eb875dad271d37572f43fb9d9b0eb6a2344d2e61646e655bb09f7909bf6`.
|
||||||
|
The wrong-path retained test binaries were stage-byte-identical 112,829-byte
|
||||||
|
files with SHA-256
|
||||||
|
`5ea3ac9add844dc4cd98cc07fb64816415b5e3c03745dc4cc756ec093cf5cea7`.
|
||||||
|
The build and test unknown-flag diagnostics were respectively 23 and 22 bytes,
|
||||||
|
also byte-identical between Cstage and WWstage. These byte counts and hashes
|
||||||
|
describe only the direct pre-fix measurements.
|
||||||
|
|
||||||
|
#### Ownership and complete four-axis result
|
||||||
|
|
||||||
|
The Cstage command owners are `parse_build_flags` and `do_test` in
|
||||||
|
`cmd/ww/main.c`. Their WWstage semantic twins are `dobuild` and `dotest` in
|
||||||
|
`selfhost/cmd/ww/main.ww`. The shared multi-package owner is
|
||||||
|
`packagecommand` in `internal/wwpackage/package.ww`. Each recognizes exact
|
||||||
|
one-/two-dash separate/equals forms, replaces prior occurrences with the final
|
||||||
|
value, and derives effective explicit-output state from that final value's
|
||||||
|
non-emptiness. The build parser retains its first-root stop, while test and the
|
||||||
|
coordinator retain their established after-package recognition. `dorun` and
|
||||||
|
the shared `run` route are not changed. The compiler, assembler, archiver,
|
||||||
|
linker, runtime, package checker, and import resolver do not own this rule.
|
||||||
|
|
||||||
|
- **Go-like build:** exact accepted forms select the same established output
|
||||||
|
path as separate `-o VALUE`; a final empty value selects the existing
|
||||||
|
default/no-public-output branch. Invalid concatenated names reject before
|
||||||
|
loading, graph or action construction, producers, publication, or runtime.
|
||||||
|
- **Go-like test:** the same exact forms select retained destinations on both
|
||||||
|
sides of package operands. Final empty means no running-test retention or
|
||||||
|
the normal compile-only default. Discovery, variants, generated main,
|
||||||
|
filtering, execution, accounting, result annotation, and absence of a
|
||||||
|
test-result cache do not change.
|
||||||
|
- **Go-like package:** output bytes remain presentation metadata. Source
|
||||||
|
eligibility, package clauses, declared names, variants, command
|
||||||
|
classification, canonical package representatives, graph nodes, actions,
|
||||||
|
symbols, artifacts, and persistence keys are unchanged.
|
||||||
|
- **Go-like import:** output spelling creates no binding or edge and changes no
|
||||||
|
dotted import spelling, alias, search, local/vendor/internal rule,
|
||||||
|
visibility, cycle, initialization order, canonical identity, or `.wwi`
|
||||||
|
ownership.
|
||||||
|
|
||||||
|
#### Loading, lifecycle, parity, and proof
|
||||||
|
|
||||||
|
Accepted forms enter the same existing loading, graph, scheduling, compiler,
|
||||||
|
assembler, archiver, linker, runtime, publication, persistence, reuse, and
|
||||||
|
invalidation paths as `-o VALUE`. They add no action, process, transaction,
|
||||||
|
cache, key, artifact byte, or runtime state. Producer or runtime failure,
|
||||||
|
late output rejection, rollback, prior-state preservation, concurrent
|
||||||
|
publication, interruption, and process-group cleanup therefore retain their
|
||||||
|
established owners and results. Output installation keeps the existing
|
||||||
|
transaction, object-safety, mode, null-device, output-directory, and
|
||||||
|
running-retained-test guard rules.
|
||||||
|
|
||||||
|
Invalid concatenated names stop before all loading and work, create no
|
||||||
|
diagnostic competitor or product, and leave no unit, interface, assembly,
|
||||||
|
object, archive, executable, retained test binary, capture, output prefix,
|
||||||
|
`.new`, `.install`, `.wwtxn.*`, or scratch residue. A final empty value cannot
|
||||||
|
create literal `=`, `=.sepwork`, or transaction residue. Cold and warm
|
||||||
|
accepted requests use the ordinary publication and reuse paths; changing only
|
||||||
|
an accepted spelling does not rekey semantic work. Independent concurrent
|
||||||
|
requests own independent argument state, workdirs, stages, captures, and
|
||||||
|
outputs. Build starts no runtime; running tests keep their private executable
|
||||||
|
and publish only after successful execution.
|
||||||
|
|
||||||
|
The WW-native `output_flag_exact_name_and_value_semantics` observer is the
|
||||||
|
focused owner for both public stages and all three parser routes. It covers the
|
||||||
|
four accepted forms, extra and empty equals values, repetition, concatenated
|
||||||
|
name rejection, build/test placement controls, multi-command behavior,
|
||||||
|
diagnostic parity, runnable and retained artifact-byte parity, warm reuse, and
|
||||||
|
absence of literal-equals and transaction residue. Existing transaction,
|
||||||
|
producer/runtime failure, rollback, concurrency, interruption, output-mode,
|
||||||
|
and cleanup observers remain authoritative for the unchanged downstream
|
||||||
|
mechanisms. Post-fix byte counts and hashes are recorded only after direct
|
||||||
|
focused measurement; this section does not infer them from the implementation.
|
||||||
|
|
||||||
|
This is command parsing and output presentation only. No persisted-byte
|
||||||
|
contract changes: build workdir format remains `18`, test workdir format
|
||||||
|
remains `19`, and semantic storage format remains `3`.
|
||||||
|
|
||||||
## 12. Candidate architectures and hard-gate decision
|
## 12. Candidate architectures and hard-gate decision
|
||||||
|
|
||||||
Five candidates were developed as coherent systems, not as feature bins.
|
Five candidates were developed as coherent systems, not as feature bins.
|
||||||
|
|||||||
35
docs/spec.md
35
docs/spec.md
@@ -344,7 +344,19 @@ ImportPath = ident { "." ident } .
|
|||||||
arguments and result; this rule does not adopt Go's source signature. An
|
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
|
ordinary import of a package declared `main` is rejected, except for the
|
||||||
toolchain's colocated external-test wiring.
|
toolchain's colocated external-test wiring.
|
||||||
- For `ww build`, an explicit `-o` names an output directory when ordinary
|
- 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 `=`
|
||||||
|
characters; an empty value is valid. Repetition is last-value-wins, and a
|
||||||
|
final empty value means that there is no effective explicit output, so the
|
||||||
|
ordinary default-output or no-public-output rule applies. A concatenated
|
||||||
|
spelling such as `-oVALUE` or `--oVALUE` is an unknown flag, not an output
|
||||||
|
option. Build option parsing stops at the first package or source operand;
|
||||||
|
later flag-like arguments remain operands and are not reparsed as `-o`.
|
||||||
|
These spelling and placement rules select only caller-visible output
|
||||||
|
disposition and never supply package, import, graph, action, symbol,
|
||||||
|
artifact, `.wwi`, or persistence identity.
|
||||||
|
A nonempty effective `-o` names an output directory when ordinary
|
||||||
`stat` reports an existing directory (following symlinks) or its spelling
|
`stat` reports an existing directory (following symlinks) or its spelling
|
||||||
ends in `/`. This classification is independent of whether one or many
|
ends in `/`. This classification is independent of whether one or many
|
||||||
package roots were requested. Each selected command is published beneath
|
package roots were requested. Each selected command is published beneath
|
||||||
@@ -771,11 +783,22 @@ coordinator reports that successful validation exactly as
|
|||||||
`? <package> [no test files]\n`.
|
`? <package> [no test files]\n`.
|
||||||
|
|
||||||
Every test-bearing directory product links one request-private runnable.
|
Every test-bearing directory product links one request-private runnable.
|
||||||
`-c` retains an executable copy and suppresses its execution; `-o` retains a
|
The test output-option name is exactly `o`, with the accepted forms `-o VALUE`,
|
||||||
copy at the named destination and still executes unless `-c` is present. With
|
`--o VALUE`, `-o=VALUE`, and `--o=VALUE`. Equals forms split only at their
|
||||||
no explicit output, `-c` writes `<import-leaf>.test` in the invocation
|
first `=` and preserve an empty value or any additional `=` bytes. Repeated
|
||||||
directory. An output ending in `/` or naming an existing directory receives
|
occurrences are last-value-wins. A final empty value means no effective
|
||||||
that basename and may have missing parent directories created. One
|
explicit output and therefore requests no running-test retention; with `-c`,
|
||||||
|
the ordinary default retained name applies. A concatenated `-oVALUE` or
|
||||||
|
`--oVALUE` is unknown. Unlike build option parsing, known test options,
|
||||||
|
including these exact output forms, are recognized before or after package
|
||||||
|
operands. Invalid output-option names reject before package loading, product
|
||||||
|
construction, execution, or publication.
|
||||||
|
`-c` retains an executable copy and suppresses its execution; a nonempty
|
||||||
|
effective `-o` retains a copy at the named destination and still executes
|
||||||
|
unless `-c` is present. With no effective explicit output, `-c` writes
|
||||||
|
`<import-leaf>.test` in the invocation directory. An output ending in `/` or
|
||||||
|
naming an existing directory receives that basename and may have missing
|
||||||
|
parent directories created. One
|
||||||
non-directory output may name only one package. Multiple packages whose
|
non-directory output may name only one package. Multiple packages whose
|
||||||
visible import leaves would produce the same test-binary name reject before
|
visible import leaves would produce the same test-binary name reject before
|
||||||
tools or output creation; exact `/dev/null` is the discard exception. It keeps
|
tools or output creation; exact `/dev/null` is the discard exception. It keeps
|
||||||
|
|||||||
@@ -236,13 +236,23 @@ warning on stderr and accounting on stdout because no coordinator combines its
|
|||||||
descriptors or emits a package result.
|
descriptors or emits a package result.
|
||||||
|
|
||||||
Directory test binaries are always linked under the coordinator's temporary
|
Directory test binaries are always linked under the coordinator's temporary
|
||||||
product root. `-c` independently requests a caller-visible executable copy and
|
product root. The retained-output option has the exact registered name `o` and
|
||||||
suppresses execution. `-o` independently requests a copy and still runs the
|
accepts `-o VALUE`, `--o VALUE`, `-o=VALUE`, and `--o=VALUE`. An equals form
|
||||||
temporary binary unless `-c` is also present. Without `-o`, `-c` publishes
|
splits only at its first `=`, preserving an empty value and any later `=`
|
||||||
`<import-leaf>.test` in the invocation directory; an output ending in `/` or
|
bytes. Repeated occurrences are last-value-wins. A final empty value is no
|
||||||
naming an existing directory receives one such name per selected package and
|
effective explicit output: it requests no retained copy for a running test,
|
||||||
missing parents are created. A single non-directory output is legal for one
|
while `-c` falls back to the ordinary `<import-leaf>.test` name. Concatenated
|
||||||
package only. Multi-package non-directory output and duplicate visible binary
|
`-oVALUE` and `--oVALUE` spell unknown flags and reject before loading, product
|
||||||
|
construction, tools, execution, or publication. Known test flags, including
|
||||||
|
these exact output forms, remain recognized before or after package operands.
|
||||||
|
`-c` independently requests a caller-visible executable copy and suppresses
|
||||||
|
execution. A nonempty effective `-o` independently requests a copy and still
|
||||||
|
runs the temporary binary unless `-c` is also present. Without an effective
|
||||||
|
explicit output, `-c` publishes `<import-leaf>.test` in the invocation
|
||||||
|
directory; an output ending in `/` or naming an existing directory receives
|
||||||
|
one such name per selected package and missing parents are created. A single
|
||||||
|
non-directory output is legal for one package only. Multi-package
|
||||||
|
non-directory output and duplicate visible binary
|
||||||
names reject before tools; exact `/dev/null` discards every copy and permits
|
names reject before tools; exact `/dev/null` discards every copy and permits
|
||||||
duplicate names. The private test link still runs unless `-c` suppresses it;
|
duplicate names. The private test link still runs unless `-c` suppresses it;
|
||||||
the raw single-file compatibility route also links into driver-owned private
|
the raw single-file compatibility route also links into driver-owned private
|
||||||
|
|||||||
@@ -2055,16 +2055,20 @@ export fn packagecommand(args: []str) int = {
|
|||||||
i += 1;
|
i += 1;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if (strings.compare(a, "-o") == 0) {
|
if (strings.compare(a, "-o") == 0
|
||||||
|
|| strings.compare(a, "--o") == 0) {
|
||||||
if (i + 1 >= args.len) { pkgusage(); return 2; };
|
if (i + 1 >= args.len) { pkgusage(); return 2; };
|
||||||
outname = args[i + 1];
|
outname = args[i + 1];
|
||||||
explicitout = true;
|
explicitout = outname.len != 0;
|
||||||
i += 2;
|
i += 2;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if (strings.hasprefix(a, "-o") && a.len > 2) {
|
if (strings.hasprefix(a, "-o=")
|
||||||
outname = a[2:a.len];
|
|| strings.hasprefix(a, "--o=")) {
|
||||||
explicitout = true;
|
let valueoff: i32 = 3;
|
||||||
|
if (a[1] == '-') { valueoff = 4; };
|
||||||
|
outname = a[valueoff:a.len];
|
||||||
|
explicitout = outname.len != 0;
|
||||||
i += 1;
|
i += 1;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8853,19 +8853,25 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
};
|
};
|
||||||
libs[nlibs] = nm;
|
libs[nlibs] = nm;
|
||||||
nlibs += 1;
|
nlibs += 1;
|
||||||
} else { if (p[1u64] == 111u8) { // '-o'
|
} else { if (cstreqlit(p, "-o") || cstreqlit(p, "--o")
|
||||||
if (p[2u64] != 0u8) {
|
|| (p[1u64] == 111u8 && p[2u64] == 61u8)
|
||||||
outflag = p + 2u64;
|
|| (p[1u64] == 45u8 && p[2u64] == 111u8
|
||||||
|
&& p[3u64] == 61u8)) {
|
||||||
|
if (p[1u64] == 111u8 && p[2u64] == 61u8) {
|
||||||
|
outflag = p + 3u64;
|
||||||
|
} else { if (p[1u64] == 45u8
|
||||||
|
&& p[2u64] == 111u8 && p[3u64] == 61u8) {
|
||||||
|
outflag = p + 4u64;
|
||||||
} else {
|
} else {
|
||||||
if (i + 1 >= argc) {
|
if (i + 1 >= argc) {
|
||||||
cerr("ww build: -o needs an argument\n");
|
cerr("ww build: -o needs an argument\n");
|
||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
outflag = argv[i];
|
outflag = argv[i];
|
||||||
};
|
}; };
|
||||||
if (!clipathfits("build", "-o", outflag)) { return 2; };
|
if (!clipathfits("build", "-o", outflag)) { return 2; };
|
||||||
} else { if (p[1u64] == 119u8) { // '-w'
|
} else { if (p[1u64] == 119u8) { // '-w'
|
||||||
if (p[2u64] != 0u8) {
|
if (p[2u64] != 0u8) {
|
||||||
workdir = p + 2u64;
|
workdir = p + 2u64;
|
||||||
} else {
|
} else {
|
||||||
@@ -8892,6 +8898,7 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
|
if (outflag != nil && outflag[0u64] == 0u8) { outflag = nil; };
|
||||||
|
|
||||||
if (src == nil) {
|
if (src == nil) {
|
||||||
let dot: [2]u8 = ['.': u8, 0u8];
|
let dot: [2]u8 = ['.': u8, 0u8];
|
||||||
@@ -9656,19 +9663,26 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
&& ps.len > 12) {
|
&& ps.len > 12) {
|
||||||
packageopts = true; i += 1; continue;
|
packageopts = true; i += 1; continue;
|
||||||
};
|
};
|
||||||
if (p[1u64] == 111u8) { // '-o'
|
if (cstreqlit(p, "-o") || cstreqlit(p, "--o")
|
||||||
if (p[2u64] != 0u8) {
|
|| (p[1u64] == 111u8 && p[2u64] == 61u8)
|
||||||
outstem = p + 2u64;
|
|| (p[1u64] == 45u8 && p[2u64] == 111u8
|
||||||
|
&& p[3u64] == 61u8)) {
|
||||||
|
if (p[1u64] == 111u8 && p[2u64] == 61u8) {
|
||||||
|
outstem = p + 3u64;
|
||||||
|
} else { if (p[1u64] == 45u8
|
||||||
|
&& p[2u64] == 111u8 && p[3u64] == 61u8) {
|
||||||
|
outstem = p + 4u64;
|
||||||
} else {
|
} else {
|
||||||
if (i + 1 >= argc) {
|
if (i + 1 >= argc) {
|
||||||
cerr("ww test: -o needs an argument\n");
|
cerr("ww test: -o needs an argument\n");
|
||||||
return 2;
|
return 2;
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
outstem = argv[i];
|
outstem = argv[i];
|
||||||
};
|
};
|
||||||
if (!clipathfits("test", "-o", outstem)) { return 2; };
|
};
|
||||||
i += 1; continue;
|
if (!clipathfits("test", "-o", outstem)) { return 2; };
|
||||||
|
i += 1; continue;
|
||||||
};
|
};
|
||||||
if (p[1u64] == 119u8) { // '-w'
|
if (p[1u64] == 119u8) { // '-w'
|
||||||
if (p[2u64] != 0u8) {
|
if (p[2u64] != 0u8) {
|
||||||
@@ -9691,6 +9705,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
|
if (outstem != nil && outstem[0u64] == 0u8) { outstem = nil; };
|
||||||
if (target == nil) {
|
if (target == nil) {
|
||||||
let dot: [2]u8 = ['.': u8, 0u8];
|
let dot: [2]u8 = ['.': u8, 0u8];
|
||||||
target = &dot[0];
|
target = &dot[0];
|
||||||
|
|||||||
@@ -13816,6 +13816,435 @@ fn runtimepath(relative: str) str = {
|
|||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@test fn output_flag_exact_name_and_value_semantics() void = {
|
||||||
|
let root: str = fresh();
|
||||||
|
let source: str = strings.concat(root, "/source");
|
||||||
|
let multi_a: str = strings.concat(source, "/alpha");
|
||||||
|
let multi_b: str = strings.concat(source, "/beta");
|
||||||
|
let tests: str = strings.concat(source, "/tests");
|
||||||
|
let outputs: str = strings.concat(root, "/outputs");
|
||||||
|
mkdirall(source); mkdirall(multi_a); mkdirall(multi_b);
|
||||||
|
mkdirall(tests); mkdirall(outputs);
|
||||||
|
let single: str = strings.concat(source, "/single.ww");
|
||||||
|
writefile(single,
|
||||||
|
"package main;\nfn main() i32 = { return 29; };\n");
|
||||||
|
writefile(strings.concat(multi_a, "/main.ww"),
|
||||||
|
"package main;\nfn main() i32 = { return 31; };\n");
|
||||||
|
writefile(strings.concat(multi_b, "/main.ww"),
|
||||||
|
"package main;\nfn main() i32 = { return 37; };\n");
|
||||||
|
writefile(strings.concat(tests, "/tests.ww"),
|
||||||
|
"package exactflags;\nfn value() i32 = { return 41; };\n");
|
||||||
|
writefile(strings.concat(tests, "/tests_test.ww"), strings.concat(
|
||||||
|
"package exactflags;\n",
|
||||||
|
"@test fn exact() void = { assert(value() == 41); };\n"));
|
||||||
|
let rawtest: str = strings.concat(source, "/raw_test.ww");
|
||||||
|
writefile(rawtest, strings.concat(
|
||||||
|
"package exactraw;\n",
|
||||||
|
"@test fn raw_exact() void = { assert(true); };\n"));
|
||||||
|
|
||||||
|
let compilerwrapper: str = strings.concat(root, "/output-flag-w6c.sh");
|
||||||
|
writeexecutable(compilerwrapper, strings.concat(
|
||||||
|
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_OUTPUT_FLAG_TRACE\"\n",
|
||||||
|
"exec \"$WW_OUTPUT_FLAG_REAL_C\" \"$@\"\n"));
|
||||||
|
let stages: []str = ["ww", "ww_ww"];
|
||||||
|
let compilers: []str = ["w6c", "w6c_ww"];
|
||||||
|
let tags: []str = ["c", "ww"];
|
||||||
|
let forms: []str = ["one-separate", "one-equals", "two-separate",
|
||||||
|
"two-equals"];
|
||||||
|
let buildref: str = "";
|
||||||
|
let testref: str = "";
|
||||||
|
let alpharef: str = "";
|
||||||
|
let betaref: str = "";
|
||||||
|
let rawref: str = "";
|
||||||
|
let rejectrefs: []str = ["", "", "", "", ""];
|
||||||
|
let coordinatorrejectref: str = "";
|
||||||
|
let buildmissingref: str = "";
|
||||||
|
let testmissingref: str = "";
|
||||||
|
let stopref: str = "";
|
||||||
|
let baseenv: []str = os.getenvs();
|
||||||
|
let out: commandout;
|
||||||
|
let si: i32 = 0;
|
||||||
|
for (si < stages.len) {
|
||||||
|
let trace: str = strings.concat(root, "/output-flag-", tags[si],
|
||||||
|
".trace");
|
||||||
|
writefile(trace, "");
|
||||||
|
let env: []str = alloc([], (baseenv.len + 3): u64)!;
|
||||||
|
let ei: i32 = 0;
|
||||||
|
for (ei < baseenv.len) {
|
||||||
|
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei], "WW_OUTPUT_FLAG_")) {
|
||||||
|
append(env, baseenv[ei]);
|
||||||
|
};
|
||||||
|
ei += 1;
|
||||||
|
};
|
||||||
|
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||||||
|
append(env, strings.concat("WW_OUTPUT_FLAG_REAL_C=",
|
||||||
|
driver(compilers[si])));
|
||||||
|
append(env, strings.concat("WW_OUTPUT_FLAG_TRACE=", trace));
|
||||||
|
let ignoredrunout: str = strings.concat(outputs, "/ignored-run-",
|
||||||
|
tags[si]);
|
||||||
|
let runcontrol: []str = [driver(stages[si]), "run", "-I", source,
|
||||||
|
strings.concat("-o", ignoredrunout), single];
|
||||||
|
runcommand(root, strings.concat("output-flag-run-control-", tags[si]),
|
||||||
|
runcontrol, (60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 29);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||||
|
&& !os.exists(ignoredrunout));
|
||||||
|
|
||||||
|
// Exact output names change only publication. Distinct accepted
|
||||||
|
// spellings retain the same action and executable bytes.
|
||||||
|
let buildwork: str = strings.concat(root, "/build-work-", tags[si]);
|
||||||
|
mkdirall(buildwork);
|
||||||
|
let fi: i32 = 0;
|
||||||
|
for (fi < forms.len) {
|
||||||
|
let binary: str = strings.concat(outputs, "/build-", tags[si],
|
||||||
|
"-", forms[fi], "=tail");
|
||||||
|
let av: []str = [driver(stages[si]), "build", "-w", buildwork,
|
||||||
|
"-I", source];
|
||||||
|
if (fi == 0) { append(av, "-o"); append(av, binary); }
|
||||||
|
else if (fi == 1) { append(av, strings.concat("-o=", binary)); }
|
||||||
|
else if (fi == 2) { append(av, "--o"); append(av, binary); }
|
||||||
|
else { append(av, strings.concat("--o=", binary)); };
|
||||||
|
append(av, single);
|
||||||
|
rewritefile(trace, "");
|
||||||
|
runcommandenv(root, strings.concat("output-flag-build-", tags[si],
|
||||||
|
"-", forms[fi]), av, env,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||||
|
&& os.exists(binary));
|
||||||
|
if (fi == 0) { assert(readfile(trace).len != 0); }
|
||||||
|
else { assert(readfile(trace).len == 0); };
|
||||||
|
let bytes: str = readfile(binary);
|
||||||
|
if (buildref.len == 0) { buildref = strings.dup(bytes); }
|
||||||
|
else { assert(same(buildref, bytes)); };
|
||||||
|
let runav: []str = [binary];
|
||||||
|
runcommand(root, strings.concat("output-flag-build-run-", tags[si],
|
||||||
|
"-", forms[fi]), runav, time.second, &out);
|
||||||
|
expectexit(&out, 29);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||||
|
fi += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Repeated string flags are last-wins. A final empty value returns to
|
||||||
|
// default naming and cannot create a file or scratch tree named '='.
|
||||||
|
let stale: str = strings.concat(outputs, "/stale-build-", tags[si]);
|
||||||
|
let winner: str = strings.concat(outputs, "/winner-build-", tags[si],
|
||||||
|
"=last");
|
||||||
|
let lastav: []str = [driver(stages[si]), "build", "-w", buildwork,
|
||||||
|
"-I", source, "-o", stale, strings.concat("--o=", winner), single];
|
||||||
|
rewritefile(trace, "");
|
||||||
|
runcommandenv(root, strings.concat("output-flag-last-", tags[si]),
|
||||||
|
lastav, env, (60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||||
|
&& !os.exists(stale) && same(buildref, readfile(winner))
|
||||||
|
&& readfile(trace).len == 0);
|
||||||
|
let defaultbin: str = strings.concat(root, "/single");
|
||||||
|
let emptyav: []str = [driver(stages[si]), "build", "-w", buildwork,
|
||||||
|
"-I", source, "-o", stale, "-o=", single];
|
||||||
|
rewritefile(trace, "");
|
||||||
|
runcommandenvdir(root, strings.concat("output-flag-empty-", tags[si]),
|
||||||
|
emptyav, env, root,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||||
|
&& !os.exists(stale) && same(buildref, readfile(defaultbin))
|
||||||
|
&& readfile(trace).len == 0
|
||||||
|
&& !os.exists(strings.concat(root, "/="))
|
||||||
|
&& !os.exists(strings.concat(root, "/=.sepwork")));
|
||||||
|
assert(os.remove(defaultbin) == 0);
|
||||||
|
clean(strings.concat(defaultbin, ".sepwork"));
|
||||||
|
|
||||||
|
// Build parsing stops at its first operand: a later flag-like value is
|
||||||
|
// package input, not a second output option.
|
||||||
|
let early: str = strings.concat(outputs, "/early");
|
||||||
|
let late: str = strings.concat(outputs, "/late");
|
||||||
|
let stopav: []str = [driver(stages[si]), "build", "-I", source,
|
||||||
|
"-o", early, single, strings.concat("--o=", late)];
|
||||||
|
runcommand(root, strings.concat("output-flag-stop-", tags[si]), stopav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
assert(out.termination == exec.termination.EXIT && out.code != 0);
|
||||||
|
assert(out.stdout.len == 0 && !has(out.stderr, "unknown flag")
|
||||||
|
&& !os.exists(early) && !os.exists(late));
|
||||||
|
if (si == 0) { stopref = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(stopref, out.stderr)); };
|
||||||
|
|
||||||
|
// Joined names are unknown before loading, work creation, or output.
|
||||||
|
let ri: i32 = 0;
|
||||||
|
for (ri < 2) {
|
||||||
|
let badout: str = strings.concat(outputs, "/bad-build-", tags[si],
|
||||||
|
"-", forms[ri]);
|
||||||
|
let badwork: str = strings.concat(root, "/bad-build-work-", tags[si],
|
||||||
|
"-", forms[ri]);
|
||||||
|
let prefix: str = "-o";
|
||||||
|
if (ri != 0) { prefix = "--o"; };
|
||||||
|
let joined: str = strings.concat(prefix, badout);
|
||||||
|
let badav: []str = [driver(stages[si]), "build", "-w", badwork,
|
||||||
|
"-I", source, joined, single];
|
||||||
|
runcommand(root, strings.concat("output-flag-reject-build-", tags[si],
|
||||||
|
"-", forms[ri]), badav, time.second, &out);
|
||||||
|
expectexit(&out, 2);
|
||||||
|
assert(out.stdout.len == 0
|
||||||
|
&& same(out.stderr, "ww build: unknown flag\n")
|
||||||
|
&& !os.exists(badout) && !os.exists(badwork));
|
||||||
|
if (si == 0) { rejectrefs[ri] = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(rejectrefs[ri], out.stderr)); };
|
||||||
|
ri += 1;
|
||||||
|
};
|
||||||
|
let buildmissingwork: str = strings.concat(root,
|
||||||
|
"/missing-build-work-", tags[si]);
|
||||||
|
let buildmissing: []str = [driver(stages[si]), "build", "-w",
|
||||||
|
buildmissingwork, "-I", source, "--o"];
|
||||||
|
runcommand(root, strings.concat("output-flag-missing-build-", tags[si]),
|
||||||
|
buildmissing, time.second, &out);
|
||||||
|
expectexit(&out, 2);
|
||||||
|
assert(out.stdout.len == 0
|
||||||
|
&& same(out.stderr, "ww build: -o needs an argument\n")
|
||||||
|
&& !os.exists(buildmissingwork));
|
||||||
|
if (si == 0) { buildmissingref = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(buildmissingref, out.stderr)); };
|
||||||
|
|
||||||
|
// Two roots exercise coordinator output planning. The second spelling
|
||||||
|
// reuses the first request's compiled actions, and final empty performs
|
||||||
|
// the established no-public-output multi-build.
|
||||||
|
let multiwork: str = strings.concat(root, "/multi-work-", tags[si]);
|
||||||
|
mkdirall(multiwork);
|
||||||
|
let multione: str = strings.concat(root, "/multi-one-", tags[si], "/");
|
||||||
|
let multiav: []str = [driver(stages[si]), "build", "-w", multiwork,
|
||||||
|
"-I", source, strings.concat("--o=", multione), multi_a, multi_b];
|
||||||
|
rewritefile(trace, "");
|
||||||
|
runcommandenvdir(root, strings.concat("output-flag-multi-one-", tags[si]),
|
||||||
|
multiav, env, root,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||||
|
&& readfile(trace).len != 0);
|
||||||
|
let alphaone: str = strings.concat(multione, "alpha");
|
||||||
|
let betaone: str = strings.concat(multione, "beta");
|
||||||
|
if (alpharef.len == 0) {
|
||||||
|
alpharef = readfile(alphaone); betaref = readfile(betaone);
|
||||||
|
} else {
|
||||||
|
assert(same(alpharef, readfile(alphaone))
|
||||||
|
&& same(betaref, readfile(betaone)));
|
||||||
|
};
|
||||||
|
let alpharun: []str = [alphaone];
|
||||||
|
runcommand(root, strings.concat("output-flag-alpha-run-", tags[si]),
|
||||||
|
alpharun, time.second, &out);
|
||||||
|
expectexit(&out, 31);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||||
|
let betarun: []str = [betaone];
|
||||||
|
runcommand(root, strings.concat("output-flag-beta-run-", tags[si]),
|
||||||
|
betarun, time.second, &out);
|
||||||
|
expectexit(&out, 37);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||||
|
let multitwo: str = strings.concat(root, "/multi-two-", tags[si], "/");
|
||||||
|
let warmav: []str = [driver(stages[si]), "build", "-w", multiwork,
|
||||||
|
"-I", source, "-o", multitwo, multi_a, multi_b];
|
||||||
|
rewritefile(trace, "");
|
||||||
|
runcommandenvdir(root, strings.concat("output-flag-multi-warm-", tags[si]),
|
||||||
|
warmav, env, root,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||||
|
&& readfile(trace).len == 0
|
||||||
|
&& same(alpharef, readfile(strings.concat(multitwo, "alpha")))
|
||||||
|
&& same(betaref, readfile(strings.concat(multitwo, "beta"))));
|
||||||
|
let unusedmulti: str = strings.concat(root, "/multi-unused-", tags[si],
|
||||||
|
"/");
|
||||||
|
let multiempty: []str = [driver(stages[si]), "build", "-w", multiwork,
|
||||||
|
"-I", source, "-o", unusedmulti, "--o=", multi_a, multi_b];
|
||||||
|
rewritefile(trace, "");
|
||||||
|
runcommandenvdir(root, strings.concat("output-flag-multi-empty-", tags[si]),
|
||||||
|
multiempty, env, root,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||||
|
&& readfile(trace).len == 0 && !os.exists(unusedmulti)
|
||||||
|
&& !os.exists(strings.concat(root, "/alpha"))
|
||||||
|
&& !os.exists(strings.concat(root, "/beta")));
|
||||||
|
let multibadout: str = strings.concat(outputs, "/bad-multi-", tags[si]);
|
||||||
|
let multibadwork: str = strings.concat(root, "/bad-multi-work-", tags[si]);
|
||||||
|
let multibad: []str = [driver(stages[si]), "build", "-w", multibadwork,
|
||||||
|
"-I", source, strings.concat("--o", multibadout), multi_a, multi_b];
|
||||||
|
runcommand(root, strings.concat("output-flag-reject-multi-", tags[si]),
|
||||||
|
multibad, time.second, &out);
|
||||||
|
expectexit(&out, 2);
|
||||||
|
assert(out.stdout.len == 0
|
||||||
|
&& same(out.stderr, "ww build: unknown flag\n")
|
||||||
|
&& !os.exists(multibadout) && !os.exists(multibadwork));
|
||||||
|
if (si == 0) { rejectrefs[4] = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(rejectrefs[4], out.stderr)); };
|
||||||
|
let directbadout: str = strings.concat(outputs,
|
||||||
|
"/bad-direct-coordinator-", tags[si]);
|
||||||
|
let directbadwork: str = strings.concat(root,
|
||||||
|
"/bad-direct-coordinator-work-", tags[si]);
|
||||||
|
let directbad: []str = [driver("wwtest"), "package",
|
||||||
|
"--ww-operation", "build", "--ww-driver", driver(stages[si]),
|
||||||
|
"-w", directbadwork, "-I", source,
|
||||||
|
strings.concat("-o", directbadout), multi_a, multi_b];
|
||||||
|
runcommand(root, strings.concat("output-flag-reject-coordinator-",
|
||||||
|
tags[si]), directbad, time.second, &out);
|
||||||
|
expectexit(&out, 2);
|
||||||
|
assert(out.stdout.len == 0 && has(out.stderr, "usage: wwtest package")
|
||||||
|
&& !os.exists(directbadout) && !os.exists(directbadwork));
|
||||||
|
if (si == 0) { coordinatorrejectref = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(coordinatorrejectref, out.stderr)); };
|
||||||
|
|
||||||
|
// Compile-only directory testing accepts the same four spellings. Its
|
||||||
|
// final empty option may follow the package and restores the default.
|
||||||
|
let testwork: str = strings.concat(root, "/test-work-", tags[si]);
|
||||||
|
mkdirall(testwork);
|
||||||
|
fi = 0;
|
||||||
|
for (fi < forms.len) {
|
||||||
|
let binary: str = strings.concat(outputs, "/test-", tags[si], "-",
|
||||||
|
forms[fi], "=tail");
|
||||||
|
let av: []str = [driver(stages[si]), "test", "-c", "-w", testwork,
|
||||||
|
"-I", source];
|
||||||
|
if (fi == 0) { append(av, "-o"); append(av, binary); }
|
||||||
|
else if (fi == 1) { append(av, strings.concat("-o=", binary)); }
|
||||||
|
else if (fi == 2) { append(av, "--o"); append(av, binary); }
|
||||||
|
else { append(av, strings.concat("--o=", binary)); };
|
||||||
|
append(av, tests);
|
||||||
|
rewritefile(trace, "");
|
||||||
|
runcommandenv(root, strings.concat("output-flag-test-", tags[si], "-",
|
||||||
|
forms[fi]), av, env,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||||
|
&& os.exists(binary));
|
||||||
|
if (fi == 0) { assert(readfile(trace).len != 0); }
|
||||||
|
else { assert(readfile(trace).len == 0); };
|
||||||
|
let bytes: str = readfile(binary);
|
||||||
|
if (testref.len == 0) { testref = strings.dup(bytes); }
|
||||||
|
else { assert(same(testref, bytes)); };
|
||||||
|
let runav: []str = [binary];
|
||||||
|
runcommand(root, strings.concat("output-flag-test-run-", tags[si], "-",
|
||||||
|
forms[fi]), runav,
|
||||||
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stderr.len == 0 && has(out.stdout, "exact ... ok\n"));
|
||||||
|
fi += 1;
|
||||||
|
};
|
||||||
|
let staletest: str = strings.concat(outputs, "/stale-test-", tags[si]);
|
||||||
|
let runningretained: str = strings.concat(outputs,
|
||||||
|
"/running-test-", tags[si], "=tail");
|
||||||
|
let runningretainedav: []str = [driver(stages[si]), "test", "-w",
|
||||||
|
testwork, "-I", source, strings.concat("--o=", runningretained),
|
||||||
|
tests];
|
||||||
|
rewritefile(trace, "");
|
||||||
|
runcommandenvdir(root, strings.concat("output-flag-test-running-retained-",
|
||||||
|
tags[si]), runningretainedav, env, root,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stderr.len == 0 && has(out.stdout, "exact ... ok\n")
|
||||||
|
&& same(testref, readfile(runningretained))
|
||||||
|
&& readfile(trace).len == 0);
|
||||||
|
let defaulttest: str = strings.concat(root, "/tests.test");
|
||||||
|
let testempty: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
|
testwork, "-I", source, "-o", staletest, tests, "--o", ""];
|
||||||
|
rewritefile(trace, "");
|
||||||
|
runcommandenvdir(root, strings.concat("output-flag-test-empty-", tags[si]),
|
||||||
|
testempty, env, root,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||||
|
&& !os.exists(staletest) && same(testref, readfile(defaulttest))
|
||||||
|
&& readfile(trace).len == 0
|
||||||
|
&& !os.exists(strings.concat(root, "/=")));
|
||||||
|
assert(os.remove(defaulttest) == 0);
|
||||||
|
let runningstale: str = strings.concat(outputs,
|
||||||
|
"/stale-running-test-", tags[si]);
|
||||||
|
let runningempty: []str = [driver(stages[si]), "test", "-w",
|
||||||
|
testwork, "-I", source, "-o", runningstale, tests, "-o="];
|
||||||
|
rewritefile(trace, "");
|
||||||
|
runcommandenvdir(root, strings.concat("output-flag-test-running-empty-",
|
||||||
|
tags[si]), runningempty, env, root,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stderr.len == 0 && has(out.stdout, "exact ... ok\n")
|
||||||
|
&& !os.exists(runningstale) && readfile(trace).len == 0);
|
||||||
|
|
||||||
|
// The raw single-file route owns output state in the public driver, so
|
||||||
|
// final empty must normalize before its retain/copy decision too.
|
||||||
|
let rawwork: str = strings.concat(root, "/raw-work-", tags[si]);
|
||||||
|
mkdirall(rawwork);
|
||||||
|
let rawretained: str = strings.concat(outputs, "/raw-test-", tags[si],
|
||||||
|
"=tail");
|
||||||
|
let rawretainedav: []str = [driver(stages[si]), "test", "-w", rawwork,
|
||||||
|
strings.concat("--o=", rawretained), rawtest];
|
||||||
|
runcommandenvdir(root, strings.concat("output-flag-raw-retained-",
|
||||||
|
tags[si]), rawretainedav, env, root,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stderr.len == 0 && has(out.stdout, "raw_exact ... ok\n")
|
||||||
|
&& os.exists(rawretained));
|
||||||
|
let rawpublishedbytes: str = readfile(rawretained);
|
||||||
|
if (rawref.len == 0) { rawref = strings.dup(rawpublishedbytes); }
|
||||||
|
else { assert(same(rawref, rawpublishedbytes)); };
|
||||||
|
let rawstale: str = strings.concat(outputs, "/stale-raw-", tags[si]);
|
||||||
|
let rawempty: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
|
rawwork, "-o", rawstale, "--o=", rawtest];
|
||||||
|
runcommandenvdir(root, strings.concat("output-flag-raw-empty-", tags[si]),
|
||||||
|
rawempty, env, root,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
let rawbinary: str = strings.concat(rawwork, "/main");
|
||||||
|
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||||
|
&& !os.exists(rawstale) && os.exists(rawbinary)
|
||||||
|
&& !os.exists(strings.concat(root, "/=")));
|
||||||
|
let rawbytes: str = readfile(rawbinary);
|
||||||
|
assert(same(rawref, rawbytes));
|
||||||
|
ri = 0;
|
||||||
|
for (ri < 2) {
|
||||||
|
let badout: str = strings.concat(outputs, "/bad-test-", tags[si],
|
||||||
|
"-", forms[ri]);
|
||||||
|
let badwork: str = strings.concat(root, "/bad-test-work-", tags[si],
|
||||||
|
"-", forms[ri]);
|
||||||
|
let prefix: str = "-o";
|
||||||
|
if (ri != 0) { prefix = "--o"; };
|
||||||
|
let joined: str = strings.concat(prefix, badout);
|
||||||
|
let badav: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
|
badwork, "-I", source, joined, tests];
|
||||||
|
runcommand(root, strings.concat("output-flag-reject-test-", tags[si],
|
||||||
|
"-", forms[ri]), badav, time.second, &out);
|
||||||
|
expectexit(&out, 2);
|
||||||
|
assert(out.stdout.len == 0
|
||||||
|
&& same(out.stderr, "ww test: unknown flag\n")
|
||||||
|
&& !os.exists(badout) && !os.exists(badwork));
|
||||||
|
if (si == 0) { rejectrefs[2 + ri] = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(rejectrefs[2 + ri], out.stderr)); };
|
||||||
|
ri += 1;
|
||||||
|
};
|
||||||
|
let testmissingwork: str = strings.concat(root,
|
||||||
|
"/missing-test-work-", tags[si]);
|
||||||
|
let testmissing: []str = [driver(stages[si]), "test", "-c", "-w",
|
||||||
|
testmissingwork, "-I", source, "--o"];
|
||||||
|
runcommand(root, strings.concat("output-flag-missing-test-", tags[si]),
|
||||||
|
testmissing, time.second, &out);
|
||||||
|
expectexit(&out, 2);
|
||||||
|
assert(out.stdout.len == 0
|
||||||
|
&& same(out.stderr, "ww test: -o needs an argument\n")
|
||||||
|
&& !os.exists(testmissingwork));
|
||||||
|
if (si == 0) { testmissingref = strings.dup(out.stderr); }
|
||||||
|
else { assert(same(testmissingref, out.stderr)); };
|
||||||
|
|
||||||
|
let cleanpaths: []str = [root, outputs, source, multi_a, multi_b, tests,
|
||||||
|
buildwork, multiwork, testwork, rawwork];
|
||||||
|
let ci: i32 = 0;
|
||||||
|
for (ci < cleanpaths.len) {
|
||||||
|
assert(!directoryhasnew(cleanpaths[ci])
|
||||||
|
&& !directoryhasfragment(cleanpaths[ci], ".install")
|
||||||
|
&& !directoryhasfragment(cleanpaths[ci], ".wwtxn."));
|
||||||
|
ci += 1;
|
||||||
|
};
|
||||||
|
si += 1;
|
||||||
|
};
|
||||||
|
assert(!os.exists(strings.concat(root, "/="))
|
||||||
|
&& !os.exists(strings.concat(root, "/=.sepwork")));
|
||||||
|
clean(root);
|
||||||
|
};
|
||||||
|
|
||||||
@test fn compile_artifact_naming() void = {
|
@test fn compile_artifact_naming() void = {
|
||||||
let root: str = fresh();
|
let root: str = fresh();
|
||||||
let pdir: str = strings.concat(root, "/pkg");
|
let pdir: str = strings.concat(root, "/pkg");
|
||||||
|
|||||||
Reference in New Issue
Block a user