fix: ignore hidden named source operands
This commit is contained in:
@@ -7136,6 +7136,38 @@ basename_no_ext(const char *path, char *out, size_t outsz)
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
}
|
||||
|
||||
/* Go's named-file package still applies matchFile's unconditional basename
|
||||
* exclusion before UseAllFiles, platform suffixes, source parsing, or test-file
|
||||
* classification. Keep this predicate on the public operand spelling: a
|
||||
* hidden parent does not hide a visible named file, while a hidden symlink
|
||||
* spelling remains hidden regardless of its non-directory target kind. */
|
||||
static int
|
||||
source_operand_ignored(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
size_t n = strlen(path);
|
||||
if (n < 3 || strcmp(path + n - 3, ".ww") != 0) return 0;
|
||||
if (stat(path, &st) != 0 || S_ISDIR(st.st_mode)) return 0;
|
||||
const char *base = strrchr(path, '/');
|
||||
base = base ? base + 1 : path;
|
||||
return base[0] == '.' || base[0] == '_';
|
||||
}
|
||||
|
||||
static void
|
||||
source_operand_no_sources(const char *path)
|
||||
{
|
||||
const char *slash = strrchr(path, '/');
|
||||
fputs("ww: ", stderr);
|
||||
if (slash == NULL) {
|
||||
fputc('.', stderr);
|
||||
} else if (slash == path) {
|
||||
fputc('/', stderr);
|
||||
} else {
|
||||
(void)fwrite(path, 1, (size_t)(slash - path), stderr);
|
||||
}
|
||||
fputs(": directory contains no WW package sources\n", stderr);
|
||||
}
|
||||
|
||||
/* Go's build -o directory branch follows an existing destination through
|
||||
* stat, and a trailing platform separator declares a directory which the
|
||||
* request may need to create. WW's platform separator is '/'. */
|
||||
@@ -7442,6 +7474,11 @@ do_build(int argc, char **argv)
|
||||
}
|
||||
struct stat requested;
|
||||
int literal = stat(src, &requested) == 0;
|
||||
if (source_operand_ignored(src)) {
|
||||
source_operand_no_sources(src);
|
||||
free(incs);
|
||||
return 1;
|
||||
}
|
||||
char resolved[PATH_MAX];
|
||||
int is_dir = 0;
|
||||
if (!resolve_module(src, incs, resolved, sizeof resolved, &is_dir)) {
|
||||
@@ -7988,6 +8025,29 @@ do_test(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
int discard_output = strcmp(outstem, "/dev/null") == 0;
|
||||
if (strstr(target, "...") == NULL && source_operand_ignored(target)) {
|
||||
if (nproducts != 0 && outstem[0]) {
|
||||
fprintf(stderr,
|
||||
"ww test: package-test products reject -o\n");
|
||||
return 2;
|
||||
}
|
||||
if (nproducts != 0) {
|
||||
fprintf(stderr,
|
||||
"ww test: package-test variant needs one directory\n");
|
||||
return 2;
|
||||
}
|
||||
if (packageopts) {
|
||||
fprintf(stderr,
|
||||
"ww test: package options need a directory\n");
|
||||
return 2;
|
||||
}
|
||||
source_operand_no_sources(target);
|
||||
if (src != NULL && !compileonly && !emit_asm)
|
||||
fputs("FAIL\n", stdout);
|
||||
free(products);
|
||||
free(incs);
|
||||
return 1;
|
||||
}
|
||||
if (pattern != NULL) {
|
||||
struct stat first;
|
||||
if (stat(target, &first) != 0 || !S_ISREG(first.st_mode)) {
|
||||
|
||||
@@ -9955,6 +9955,131 @@ artifact-layout, harness-protocol, cache, database, or publication field.
|
||||
Build workdir format remains `18`, test workdir format remains `19`, semantic
|
||||
storage format remains `3`, and no test-result cache is introduced.
|
||||
|
||||
### 11.50 Implemented named-source leading-dot/underscore eligibility
|
||||
|
||||
An explicitly named raw `.ww` source now observes the same unconditional
|
||||
basename exclusion as a named `.go` source in Go 1.26.5. This closes the gap in
|
||||
WW's existing single-source command route: a final requested basename beginning
|
||||
`.` or `_` is not a package source, even though named sources otherwise bypass
|
||||
directory-only target-suffix selection. Directory selection already enforced
|
||||
this rule and remains unchanged.
|
||||
|
||||
#### Pinned Go evidence and applicability
|
||||
|
||||
The reference is official Go 1.26.5 at commit
|
||||
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||
|
||||
- `PackagesAndErrors` recognizes an existing `.go` operand and routes the
|
||||
complete named list to `GoFilesPackage`
|
||||
([`cmd/go/internal/load/pkg.go`, lines 2903–2918](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L2903-L2918)).
|
||||
`GoFilesPackage` enables `UseAllFiles`, presents only the named `FileInfo`
|
||||
entries through a synthetic directory, and loads one command-line package
|
||||
([lines 3244–3315](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L3244-L3315)).
|
||||
- `go/build.Import` passes each synthetic entry through `Context.matchFile`
|
||||
([`go/build/build.go`, lines 886–914](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L886-L914)).
|
||||
`matchFile` rejects a name beginning `_` or `.` before extension, target
|
||||
suffix, open, imports, or build constraints; that branch precedes the
|
||||
`UseAllFiles` condition
|
||||
([lines 1438–1509](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1438-L1509)).
|
||||
A package with no remaining source category returns `NoGoError`
|
||||
([lines 1076–1082](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1076-L1082));
|
||||
the command loader presents the case as `no Go files`
|
||||
([`cmd/go/internal/load/pkg.go`, lines 250–270](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L250-L270)).
|
||||
- Build reports load errors before installation/action construction
|
||||
([`cmd/go/internal/work/build.go`, lines 697–728](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L697-L728)).
|
||||
Test uses the same loader
|
||||
([`cmd/go/internal/test/test.go`, lines 703–719](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L703-L719))
|
||||
and turns its error into package setup failure before constructing a runnable
|
||||
([lines 1015–1050](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1015-L1050)).
|
||||
- Official command testdata explicitly records that `_cgo_yy.go` named on the
|
||||
command line is ignored and permits the exact `no Go files` result
|
||||
([`cgo_bad_directives.txt`, lines 11–23](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/cgo_bad_directives.txt#L11-L23)).
|
||||
`import_ignore.txt` independently proves a dot file contributes no import
|
||||
([lines 1–11](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/import_ignore.txt#L1-L11)).
|
||||
|
||||
The rule honestly applies to WW's local literal `.ww` operand. It adds no
|
||||
module, manifest, registry, network, generalized import, or build-expression
|
||||
surface. This section is deliberately bounded to the existing single raw
|
||||
source target. Multiple named sources as one package and visible
|
||||
`*_test.ww` exclusion from `ww build` remain separate open semantics; when a
|
||||
future source-set loader admits multiple names, it must apply this same rule to
|
||||
each requested basename.
|
||||
|
||||
#### Source, package, and import ownership
|
||||
|
||||
The twin public drivers own one allocation-free operand predicate after
|
||||
existing CLI-shape checks and a successful requested-path `Stat`, but before
|
||||
logical resolution or graph entry. It requires an original spelling ending
|
||||
exactly `.ww`, then examines only the final requested basename. A hidden parent
|
||||
containing visible `main.ww` does not
|
||||
exclude the named source. A requested hidden symlink spelling is excluded even
|
||||
when its target is visible or non-regular, while a visible requested spelling
|
||||
remains eligible even when its target basename is hidden. This follows the
|
||||
`FileInfo.Name` seen
|
||||
by Go's synthetic named-file directory: Unix `Stat` follows the target but
|
||||
fills `Name` from the requested path
|
||||
([`os/stat_unix.go`, lines 28–38](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/stat_unix.go#L28-L38),
|
||||
[`os/stat_linux.go`, lines 13–30](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/stat_linux.go#L13-L30)).
|
||||
Physical target paths do not become identity.
|
||||
|
||||
Prefix exclusion precedes every later classifier. `_main_test.ww` and
|
||||
`.main_test.ww` are absent before production/test partitioning. A hidden
|
||||
`_main_windows.ww` is absent, but a visible `main_windows.ww` named directly
|
||||
remains eligible because the named-file analogue uses `UseAllFiles`; directory
|
||||
platform filtering is unchanged. Logical operands without a `.ww` suffix,
|
||||
directory and recursive requests, imports, and `ww run` retain their existing
|
||||
routes.
|
||||
|
||||
An excluded operand creates no package clause, declaration, production/test
|
||||
variant, or top-level state. Its bytes are not decoded or parsed: malformed
|
||||
UTF-8, NUL, BOM, missing/invalid package clauses, late imports, missing imports,
|
||||
unused imports, cycles, `internal`/vendor rules, and checker diagnostics cannot
|
||||
resurrect it or outrank selection. It contributes no qualifier, import-use
|
||||
obligation, edge, canonical dotted identity, initializer, or link closure. The
|
||||
lexical parent printed by the diagnostic is presentation metadata only and is
|
||||
never package, import, graph, action, symbol, `.wwi`, artifact, publication, or
|
||||
persistence identity.
|
||||
|
||||
#### Observable command and lifecycle behavior
|
||||
|
||||
`ww build HIDDEN.ww` exits 1 with empty standard output and
|
||||
`ww: PARENT: directory contains no WW package sources` on standard error.
|
||||
An ordinary explicit running `ww test HIDDEN.ww` adds its established exact
|
||||
command-owned `FAIL` line on standard output; compile-only and assembly-only
|
||||
test requests do not. With no slash, `PARENT` is `.`, a root child uses `/`,
|
||||
and otherwise it is the requested lexical bytes before the final slash. CLI
|
||||
flag/path-length errors and the existing raw-test directory-only package-option
|
||||
shape keep their precedence. A failed requested-path `Stat` retains ordinary
|
||||
target/logical resolution; for an existing non-directory raw operand,
|
||||
no-source selection precedes logical resolution, output-destination preflight,
|
||||
and all source, producer, publication, and runtime diagnostics. Exact
|
||||
`/dev/null` does not suppress the load failure.
|
||||
|
||||
No root/dependency action, test support, generated main, compiler, assembler,
|
||||
archiver, linker, harness, test child, or program process starts. Cold rejection
|
||||
creates no default or explicit output, workdir, adjacent `.sepwork`, unit,
|
||||
`.wwi`, assembly, object, archive, init artifact, binary, status, stage, capture,
|
||||
result, transaction, or private temporary directory. Warm rejection starts no
|
||||
transaction and preserves every prior work-artifact and public/retained byte.
|
||||
It leaves no `.new`, `.install`, `.wwtxn.*`, or recovery residue. Producer,
|
||||
runtime, publication-only, cleanup-only, signal, timeout, and descendant
|
||||
semantics for visible inputs are unchanged.
|
||||
|
||||
The predicate is request-local and creates no shared state, process group,
|
||||
lock, or interruption owner, so overlapping hidden and visible requests remain
|
||||
isolated. Cstage and WWstage must agree byte-for-byte on status, stdout, stderr,
|
||||
and complete artifact absence; representative visible named sources retain
|
||||
binary and semantic-artifact identity. The focused native
|
||||
`named_source_prefixes_are_ignored` observer owns both prefixes, build/test and
|
||||
compile-only paths, prefix-before-test/platform precedence, requested symlink
|
||||
spelling, hidden-parent/visible-basename and wrong-platform controls, unread
|
||||
malformed/import bytes, cold cleanup, warm preservation/restoration, residue,
|
||||
and stage parity.
|
||||
|
||||
No serialized format changes. Build workdir format remains `18`, test workdir
|
||||
format remains `19`, semantic storage format remains `3`, and there is no test
|
||||
result cache.
|
||||
|
||||
## 12. Candidate architectures and hard-gate decision
|
||||
|
||||
Five candidates were developed as coherent systems, not as feature bins.
|
||||
|
||||
16
docs/spec.md
16
docs/spec.md
@@ -319,6 +319,22 @@ ImportPath = ident { "." ident } .
|
||||
delegated graph-import scan or tools. An ordinary build compares production
|
||||
names only; one test product compares its production, same-package test, and
|
||||
external-test selections without merging their units.
|
||||
- A single existing raw `.ww` operand is also subject to the unconditional
|
||||
leading-name rule: if its final requested basename begins `.` or `_`, it is
|
||||
ignored before the source is opened. Named raw sources otherwise retain their
|
||||
existing all-files behavior, so a visible wrong-platform suffix remains
|
||||
eligible. Only the requested basename participates: a hidden parent does not
|
||||
hide visible `main.ww`, a hidden symlink spelling stays hidden for any
|
||||
existing non-directory target, and a visible symlink spelling stays eligible.
|
||||
The excluded operand creates no package, declaration, import binding/edge,
|
||||
action, artifact, initialization, test,
|
||||
publication, or persistent state. `ww build` reports the existing
|
||||
`directory contains no WW package sources` condition; an explicit running
|
||||
raw `ww test` also emits its command-owned `FAIL`, while `-c` and `-S` do not.
|
||||
This rule does not add multiple named-source package support, does not change
|
||||
visible `*_test.ww` handling, and does not apply to logical operands,
|
||||
directory/recursive requests, imports, or `ww run`. The diagnostic parent is
|
||||
presentation metadata and never canonical identity.
|
||||
- `import acme.codec;` loads the canonical package `acme.codec`. If that
|
||||
package declares `package wire;`, the importing file sees its exported names
|
||||
as `wire.Name`; `codec.Name` is not an additional binding. An explicit alias
|
||||
|
||||
@@ -219,6 +219,26 @@ every `-j` level, and `-j 1` — the default — matches the former sequential
|
||||
run loop exactly. Measured on the 31-package `lib/...` walk:
|
||||
7.0s sequential, 2.4s at `-j 4`.
|
||||
|
||||
The raw single-source compatibility route applies package-source eligibility
|
||||
before it invokes the compiler or harness. An existing operand ending `.ww`
|
||||
whose final requested basename begins `.` or `_` is ignored exactly as it is by
|
||||
directory discovery, even when the name also ends `_test.ww`; the file is not
|
||||
opened and cannot contribute a test descriptor, package declaration, import,
|
||||
variant, graph action, diagnostic, artifact, retained binary, or persistent
|
||||
state. A normal explicit running request exits 1 with exact `FAIL\n` stdout and
|
||||
the driver's `directory contains no WW package sources` stderr; `-c` and `-S`
|
||||
omit the final marker. Visible wrong-platform raw names remain eligible, and
|
||||
only the requested basename matters for symlinks (including non-regular
|
||||
targets) and hidden parent directories.
|
||||
CLI/target and the existing raw package-option shape retain precedence. Cold
|
||||
rejection creates no work or temporary product; warm rejection preserves prior
|
||||
work and public bytes without transaction residue. The focused
|
||||
`named_source_prefixes_are_ignored` package observer proves these rules in both
|
||||
driver stages, including malformed/import precedence, prefix-before-test and
|
||||
platform classification, symlink spelling, output rollback, residue, and
|
||||
diagnostic/artifact parity. Directory package coordination and test process,
|
||||
filter, timeout, signal, and descendant topology are unchanged.
|
||||
|
||||
List mode uses that same product process and initialization boundary but starts
|
||||
no per-test child. The shared language harness emits only selected qualified
|
||||
test names, one per line in descriptor order. A valid filter selecting no tests
|
||||
|
||||
@@ -8564,6 +8564,40 @@ fn basenameoff(p: *u8, plen: u64) u64 = {
|
||||
return start;
|
||||
};
|
||||
|
||||
// A named Go source still passes through matchFile's leading-dot/underscore
|
||||
// exclusion even when UseAllFiles bypasses platform suffixes and build tags.
|
||||
// Inspect the public operand spelling so a hidden parent is not source identity
|
||||
// and a hidden symlink spelling cannot be resurrected by a non-directory
|
||||
// target's name or file kind.
|
||||
fn sourceoperandignored(path: *u8) bool = {
|
||||
if (!cstrendswithlit(path, ".ww")) { return false; };
|
||||
let fi: os.filestat;
|
||||
match (os.stat(&fi, pathstr(path))) {
|
||||
case void => {
|
||||
let typ: u32 = (fi.mode: u32) & 61440u32;
|
||||
if (typ == os.mode.DIR: u32) { return false; };
|
||||
};
|
||||
case let e: os.oserror => return false;
|
||||
};
|
||||
let n: u64 = cstrlen(path);
|
||||
let base: u64 = basenameoff(path, n);
|
||||
return path[base] == '.' || path[base] == '_';
|
||||
};
|
||||
|
||||
fn sourceoperandnosources(path: *u8) void = {
|
||||
let n: u64 = cstrlen(path);
|
||||
let base: u64 = basenameoff(path, n);
|
||||
cerr("ww: ");
|
||||
if (base == 0u64) {
|
||||
cerr(".");
|
||||
} else { if (base == 1u64) {
|
||||
cerr("/");
|
||||
} else {
|
||||
os.write(os.STDERR_FILENO, path, base - 1u64);
|
||||
}; };
|
||||
cerr(": directory contains no WW package sources\n");
|
||||
};
|
||||
|
||||
fn arenadupcstr(src: *u8, plen: u64) *u8 = {
|
||||
let buf: []u8 = alloc([], plen + 1u64)!;
|
||||
let i: u64 = 0u64;
|
||||
@@ -8914,6 +8948,10 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
case void => requestedliteral = true;
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
if (sourceoperandignored(src)) {
|
||||
sourceoperandnosources(src);
|
||||
return 1;
|
||||
};
|
||||
let isdir: i32 = 0;
|
||||
let resolved: *u8 = resolvemodule(selfdir, src, incs.ptr, &isdir);
|
||||
if (resolved == nil) {
|
||||
@@ -9792,6 +9830,26 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
producti += 1;
|
||||
};
|
||||
};
|
||||
if (!strings.contains(pathstr(target), "...")
|
||||
&& sourceoperandignored(target)) {
|
||||
if (products.len != 0 && outstem != nil) {
|
||||
cerr("ww test: package-test products reject -o\n");
|
||||
return 2;
|
||||
};
|
||||
if (products.len != 0) {
|
||||
cerr("ww test: package-test variant needs one directory\n");
|
||||
return 2;
|
||||
};
|
||||
if (packageopts) {
|
||||
cerr("ww test: package options need a directory\n");
|
||||
return 2;
|
||||
};
|
||||
sourceoperandnosources(target);
|
||||
if (targetindex >= 0 && compileonly == 0 && emitasm == 0) {
|
||||
os.write(1, "FAIL\n".ptr, 5u64);
|
||||
};
|
||||
return 1;
|
||||
};
|
||||
if (patarg != nil) {
|
||||
let first: os.filestat;
|
||||
let firstregular: bool = false;
|
||||
|
||||
@@ -987,6 +987,354 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
clean(root);
|
||||
};
|
||||
|
||||
@test fn named_source_prefixes_are_ignored() void = {
|
||||
let root: str = fresh();
|
||||
let source: str = strings.concat(root, "/source");
|
||||
let hiddenparent: str = strings.concat(source, "/.parent");
|
||||
let symlinks: str = strings.concat(source, "/symlinks");
|
||||
let warm: str = strings.concat(source, "/warm");
|
||||
mkdirall(source); mkdirall(hiddenparent); mkdirall(symlinks); mkdirall(warm);
|
||||
|
||||
let maintext: str =
|
||||
"package main;\nfn main() i32 = { return 0; };\n";
|
||||
let testtext: str = strings.concat(
|
||||
"package raw_windows_test;\n",
|
||||
"@test fn selected() void = { assert(true); };\n");
|
||||
let missingmain: str = strings.concat(
|
||||
"package main;\nimport absent.pkg;\n",
|
||||
"fn main() i32 = { return pkg.value(); };\n");
|
||||
let missingtest: str = strings.concat(
|
||||
"package missing_test;\nimport absent.pkg;\n",
|
||||
"@test fn never() void = { abort(); };\n");
|
||||
let malformed: str = "this source must never be opened {\n";
|
||||
let hiddenbuild: str = strings.concat(source, "/_broken_windows_test.ww");
|
||||
let dotbuild: str = strings.concat(source, "/.missing.ww");
|
||||
let hiddentest: str = strings.concat(source,
|
||||
"/_broken_windows_test.ww");
|
||||
let dottest: str = strings.concat(source, "/.missing_test.ww");
|
||||
let visiblewrong: str = strings.concat(source, "/visible_windows.ww");
|
||||
let visiblewrongtest: str = strings.concat(source,
|
||||
"/visible_windows_test.ww");
|
||||
let hiddenparentmain: str = strings.concat(hiddenparent, "/main.ww");
|
||||
let visibletarget: str = strings.concat(symlinks, "/target.ww");
|
||||
let hiddentarget: str = strings.concat(symlinks, "/.target.ww");
|
||||
let hiddenlink: str = strings.concat(symlinks, "/_link.ww");
|
||||
let hiddendevice: str = strings.concat(symlinks, "/_device.ww");
|
||||
let visiblelink: str = strings.concat(symlinks, "/visible-link.ww");
|
||||
let warmvisible: str = strings.concat(warm, "/warm.ww");
|
||||
let warmhidden: str = strings.concat(warm, "/_warm.ww");
|
||||
writefile(hiddenbuild, malformed);
|
||||
writefile(dotbuild, missingmain);
|
||||
writefile(dottest, missingtest);
|
||||
writefile(visiblewrong, maintext);
|
||||
writefile(visiblewrongtest, testtext);
|
||||
writefile(hiddenparentmain, maintext);
|
||||
writefile(visibletarget, maintext);
|
||||
writefile(hiddentarget, maintext);
|
||||
assert(os.symlink(visibletarget, hiddenlink) == 0);
|
||||
assert(os.symlink("/dev/null", hiddendevice) == 0);
|
||||
assert(os.symlink(hiddentarget, visiblelink) == 0);
|
||||
writefile(warmvisible, maintext);
|
||||
|
||||
let stages: []str = ["ww", "ww_ww"];
|
||||
let tags: []str = ["c", "ww"];
|
||||
let rootartifacts: []str = ["/__root.unit.ww", "/__root.wwi",
|
||||
"/__root.s", "/__root.o", "/__root.a",
|
||||
"/__root.init.unit.ww", "/__root.init.s", "/__root.init.o"];
|
||||
let visiblebuildbin: str = "";
|
||||
let visiblebuildartifacts: []str = alloc([], rootartifacts.len: u64)!;
|
||||
let visibletestbin: str = "";
|
||||
let visibletestartifacts: []str = alloc([], rootartifacts.len: u64)!;
|
||||
let visibleteststdout: str = "";
|
||||
let hiddendiagnostics: []str = alloc([], 20u64)!;
|
||||
let si: i32 = 0;
|
||||
for (si < stages.len) {
|
||||
let prefix: str = strings.concat(tags[si], "-");
|
||||
let coldwork: str = strings.concat(root, "/cold-work-", tags[si]);
|
||||
let coldout: str = strings.concat(root, "/cold-output-", tags[si]);
|
||||
let out: commandout;
|
||||
|
||||
// A final requested basename, rather than any parent component, owns
|
||||
// Go's unconditional editor-temporary exclusion.
|
||||
let av: []str = [driver(stages[si]), "build", "-w", coldwork,
|
||||
"-o", coldout, "_broken_windows_test.ww"];
|
||||
runcommanddir(root, strings.concat(prefix, "relative-hidden-build"),
|
||||
source, av, (30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
let relativebuilddiag: str =
|
||||
"ww: .: directory contains no WW package sources\n";
|
||||
assert(out.stdout.len == 0 && same(out.stderr, relativebuilddiag)
|
||||
&& !os.exists(coldwork) && !os.exists(coldout)
|
||||
&& !os.exists(strings.concat(coldout, ".sepwork")));
|
||||
append(hiddendiagnostics, strings.dup(out.stderr));
|
||||
|
||||
let dotbuildav: []str = [driver(stages[si]), "build", "-o",
|
||||
strings.concat(root, "/dot-output-", tags[si]), dotbuild];
|
||||
runcommand(root, strings.concat(prefix, "dot-missing-build"), dotbuildav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
let sourcediag: str = strings.concat("ww: ", source,
|
||||
": directory contains no WW package sources\n");
|
||||
assert(out.stdout.len == 0 && same(out.stderr, sourcediag)
|
||||
&& !has(out.stderr, "absent.pkg")
|
||||
&& !os.exists(strings.concat(root, "/dot-output-", tags[si]))
|
||||
&& !os.exists(strings.concat(root, "/dot-output-", tags[si],
|
||||
".sepwork")));
|
||||
append(hiddendiagnostics, strings.dup(out.stderr));
|
||||
|
||||
let testcoldwork: str = strings.concat(root, "/cold-test-work-",
|
||||
tags[si]);
|
||||
let testcoldout: str = strings.concat(root, "/cold-test-output-",
|
||||
tags[si]);
|
||||
let hiddentestrunav: []str = [driver(stages[si]), "test", "-w",
|
||||
testcoldwork, "-o",
|
||||
testcoldout, hiddentest];
|
||||
runcommand(root, strings.concat(prefix, "hidden-test-run"),
|
||||
hiddentestrunav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(same(out.stdout, "FAIL\n") && same(out.stderr, sourcediag)
|
||||
&& !has(out.stderr, "invalid or missing package clause")
|
||||
&& !os.exists(testcoldwork) && !os.exists(testcoldout)
|
||||
&& !os.exists(strings.concat(testcoldout, ".sepwork")));
|
||||
append(hiddendiagnostics, strings.dup(out.stderr));
|
||||
|
||||
let compiled: str = strings.concat(root, "/hidden-compiled-", tags[si]);
|
||||
let hiddencompileav: []str = [driver(stages[si]), "test", "-c", "-o",
|
||||
compiled,
|
||||
hiddentest];
|
||||
runcommand(root, strings.concat(prefix, "hidden-test-compile"),
|
||||
hiddencompileav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(out.stdout.len == 0 && same(out.stderr, sourcediag)
|
||||
&& !os.exists(compiled)
|
||||
&& !os.exists(strings.concat(compiled, ".sepwork")));
|
||||
append(hiddendiagnostics, strings.dup(out.stderr));
|
||||
|
||||
let assembly: str = strings.concat(root, "/hidden-assembly-", tags[si]);
|
||||
let hiddenasmav: []str = [driver(stages[si]), "test", "-S", "-o",
|
||||
assembly,
|
||||
hiddentest];
|
||||
runcommand(root, strings.concat(prefix, "hidden-test-assembly"),
|
||||
hiddenasmav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(out.stdout.len == 0 && same(out.stderr, sourcediag)
|
||||
&& !os.exists(assembly)
|
||||
&& !os.exists(strings.concat(assembly, ".sepwork")));
|
||||
append(hiddendiagnostics, strings.dup(out.stderr));
|
||||
|
||||
let dottestav: []str = [driver(stages[si]), "test", dottest];
|
||||
runcommand(root, strings.concat(prefix, "dot-missing-test"), dottestav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(same(out.stdout, "FAIL\n") && same(out.stderr, sourcediag)
|
||||
&& !has(out.stderr, "absent.pkg"));
|
||||
append(hiddendiagnostics, strings.dup(out.stderr));
|
||||
|
||||
let visiblework: str = strings.concat(root, "/visible-work-", tags[si]);
|
||||
let visibleout: str = strings.concat(root, "/visible-output-", tags[si]);
|
||||
mkdirall(visiblework);
|
||||
let visiblebuildav: []str = [driver(stages[si]), "build", "-w",
|
||||
visiblework, "-o",
|
||||
visibleout, visiblewrong];
|
||||
runcommand(root, strings.concat(prefix, "visible-wrong-platform-build"),
|
||||
visiblebuildav, (60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||
&& os.exists(visibleout));
|
||||
let runav: []str = [visibleout];
|
||||
runcommand(root, strings.concat(prefix, "visible-wrong-platform-run"),
|
||||
runav, (10i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||
let visiblebytes: str = strings.dup(readfile(visibleout));
|
||||
let ai: i32 = 0;
|
||||
if (si == 0) { visiblebuildbin = strings.dup(visiblebytes); } else {
|
||||
assert(same(visiblebuildbin, visiblebytes));
|
||||
};
|
||||
for (ai < rootartifacts.len) {
|
||||
let bytes: str = strings.dup(readfile(strings.concat(visiblework,
|
||||
rootartifacts[ai])));
|
||||
if (si == 0) { append(visiblebuildartifacts, bytes); }
|
||||
else { assert(same(visiblebuildartifacts[ai], bytes)); };
|
||||
ai += 1;
|
||||
};
|
||||
|
||||
let testwork: str = strings.concat(root, "/visible-test-work-", tags[si]);
|
||||
mkdirall(testwork);
|
||||
let visibletestav: []str = [driver(stages[si]), "test", "-w", testwork,
|
||||
visiblewrongtest];
|
||||
runcommand(root, strings.concat(prefix, "visible-wrong-platform-test"),
|
||||
visibletestav, (60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stderr.len == 0
|
||||
&& occurrences(out.stdout, "selected ... ok\n") == 1
|
||||
&& occurrences(out.stdout,
|
||||
"1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1);
|
||||
let testbytes: str = strings.dup(readfile(strings.concat(testwork,
|
||||
"/main")));
|
||||
if (si == 0) {
|
||||
visibletestbin = strings.dup(testbytes);
|
||||
visibleteststdout = strings.dup(out.stdout);
|
||||
} else {
|
||||
assert(same(visibletestbin, testbytes)
|
||||
&& same(visibleteststdout, out.stdout));
|
||||
};
|
||||
ai = 0;
|
||||
for (ai < rootartifacts.len) {
|
||||
let bytes: str = strings.dup(readfile(strings.concat(testwork,
|
||||
rootartifacts[ai])));
|
||||
if (si == 0) { append(visibletestartifacts, bytes); }
|
||||
else { assert(same(visibletestartifacts[ai], bytes)); };
|
||||
ai += 1;
|
||||
};
|
||||
|
||||
let parentout: str = strings.concat(root, "/hidden-parent-", tags[si]);
|
||||
let parentwork: str = strings.concat(root, "/hidden-parent-work-",
|
||||
tags[si]);
|
||||
mkdirall(parentwork);
|
||||
let parentav: []str = [driver(stages[si]), "build", "-w", parentwork,
|
||||
"-o", parentout, hiddenparentmain];
|
||||
runcommand(root, strings.concat(prefix, "hidden-parent-visible-base"),
|
||||
parentav, (30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||
&& same(visiblebuildbin, readfile(parentout)));
|
||||
|
||||
let hiddenlinkout: str = strings.concat(root, "/hidden-link-", tags[si]);
|
||||
let hiddenlinkav: []str = [driver(stages[si]), "build", "-o",
|
||||
hiddenlinkout, hiddenlink];
|
||||
runcommand(root, strings.concat(prefix, "hidden-request-symlink"),
|
||||
hiddenlinkav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
let symlinkdiag: str = strings.concat("ww: ", symlinks,
|
||||
": directory contains no WW package sources\n");
|
||||
assert(out.stdout.len == 0 && same(out.stderr, symlinkdiag)
|
||||
&& !os.exists(hiddenlinkout)
|
||||
&& !os.exists(strings.concat(hiddenlinkout, ".sepwork")));
|
||||
append(hiddendiagnostics, strings.dup(out.stderr));
|
||||
|
||||
let deviceout: str = strings.concat(root, "/hidden-device-", tags[si]);
|
||||
let devicework: str = strings.concat(root, "/hidden-device-work-",
|
||||
tags[si]);
|
||||
let deviceav: []str = [driver(stages[si]), "build", "-w", devicework,
|
||||
"-o", deviceout, hiddendevice];
|
||||
runcommand(root, strings.concat(prefix, "hidden-device-build"), deviceav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(out.stdout.len == 0 && same(out.stderr, symlinkdiag)
|
||||
&& !os.exists(devicework) && !os.exists(deviceout)
|
||||
&& !os.exists(strings.concat(deviceout, ".sepwork")));
|
||||
append(hiddendiagnostics, strings.dup(out.stderr));
|
||||
|
||||
let devicetestwork: str = strings.concat(root,
|
||||
"/hidden-device-test-work-", tags[si]);
|
||||
let devicetestout: str = strings.concat(root,
|
||||
"/hidden-device-test-output-", tags[si]);
|
||||
let devicetestav: []str = [driver(stages[si]), "test", "-w",
|
||||
devicetestwork, "-o", devicetestout, hiddendevice, "selected"];
|
||||
runcommand(root, strings.concat(prefix, "hidden-device-test"),
|
||||
devicetestav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(same(out.stdout, "FAIL\n") && same(out.stderr, symlinkdiag)
|
||||
&& !os.exists(devicetestwork) && !os.exists(devicetestout)
|
||||
&& !os.exists(strings.concat(devicetestout, ".sepwork")));
|
||||
append(hiddendiagnostics, strings.dup(out.stderr));
|
||||
|
||||
let visiblelinkout: str = strings.concat(root, "/visible-link-", tags[si]);
|
||||
let visiblelinkwork: str = strings.concat(root, "/visible-link-work-",
|
||||
tags[si]);
|
||||
mkdirall(visiblelinkwork);
|
||||
let visiblelinkav: []str = [driver(stages[si]), "build", "-w",
|
||||
visiblelinkwork, "-o", visiblelinkout, visiblelink];
|
||||
runcommand(root, strings.concat(prefix, "visible-request-symlink"),
|
||||
visiblelinkav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||
&& same(visiblebuildbin, readfile(visiblelinkout)));
|
||||
|
||||
let warmwork: str = strings.concat(root, "/warm-work-", tags[si]);
|
||||
let warmout: str = strings.concat(root, "/warm-output-", tags[si]);
|
||||
mkdirall(warmwork);
|
||||
let warmvalidav: []str = [driver(stages[si]), "build", "-w",
|
||||
warmwork, "-o",
|
||||
warmout, warmvisible];
|
||||
runcommand(root, strings.concat(prefix, "warm-visible"), warmvalidav,
|
||||
(60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||
&& os.exists(warmout));
|
||||
let warmbin: str = strings.dup(readfile(warmout));
|
||||
let warmsnapshots: []str = alloc([], rootartifacts.len: u64)!;
|
||||
ai = 0;
|
||||
for (ai < rootartifacts.len) {
|
||||
append(warmsnapshots, strings.dup(readfile(strings.concat(warmwork,
|
||||
rootartifacts[ai]))));
|
||||
ai += 1;
|
||||
};
|
||||
assert(os.rename(warmvisible, warmhidden) == 0);
|
||||
let warmhiddenav: []str = [driver(stages[si]), "build", "-w",
|
||||
warmwork, "-o",
|
||||
warmout, warmhidden];
|
||||
runcommand(root, strings.concat(prefix, "warm-hidden"), warmhiddenav,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
let warmdiag: str = strings.concat("ww: ", warm,
|
||||
": directory contains no WW package sources\n");
|
||||
assert(out.stdout.len == 0 && same(out.stderr, warmdiag)
|
||||
&& same(warmbin, readfile(warmout))
|
||||
&& !os.exists(strings.concat(warmout, ".new"))
|
||||
&& !os.exists(strings.concat(warmout, ".sepwork")));
|
||||
append(hiddendiagnostics, strings.dup(out.stderr));
|
||||
ai = 0;
|
||||
for (ai < rootartifacts.len) {
|
||||
assert(same(warmsnapshots[ai], readfile(strings.concat(warmwork,
|
||||
rootartifacts[ai]))));
|
||||
ai += 1;
|
||||
};
|
||||
assert(!directoryhasnew(warmwork)
|
||||
&& !directoryhasfragment(warmwork, ".wwtxn.")
|
||||
&& !directoryhasfragment(warmwork, ".install")
|
||||
&& !directoryhasfragment(warmwork, ".sepwork"));
|
||||
assert(os.rename(warmhidden, warmvisible) == 0);
|
||||
runcommand(root, strings.concat(prefix, "warm-restored"), warmvalidav,
|
||||
(60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0
|
||||
&& same(warmbin, readfile(warmout)));
|
||||
ai = 0;
|
||||
for (ai < rootartifacts.len) {
|
||||
assert(same(warmsnapshots[ai], readfile(strings.concat(warmwork,
|
||||
rootartifacts[ai]))));
|
||||
ai += 1;
|
||||
};
|
||||
assert(!directoryhasnew(warmwork)
|
||||
&& !directoryhasfragment(warmwork, ".wwtxn.")
|
||||
&& !directoryhasfragment(warmwork, ".install")
|
||||
&& !directoryhasfragment(warmwork, ".sepwork"));
|
||||
si += 1;
|
||||
};
|
||||
let half: i32 = hiddendiagnostics.len / 2;
|
||||
let di: i32 = 0;
|
||||
for (di < half) {
|
||||
assert(same(hiddendiagnostics[di], hiddendiagnostics[di + half]));
|
||||
di += 1;
|
||||
};
|
||||
assert(!directoryhasnew(root)
|
||||
&& !directoryhasfragment(root, ".wwtxn.")
|
||||
&& !directoryhasfragment(root, ".install")
|
||||
&& !directoryhasfragment(root, ".sepwork")
|
||||
&& !directoryhasfragment(root, ".capture")
|
||||
&& !directoryhasfragment(root, ".result")
|
||||
&& !directoryhasfragment(root, ".request"));
|
||||
clean(root);
|
||||
};
|
||||
|
||||
// A source file has one contiguous import section immediately after its
|
||||
// package clause. The parser may continue after a late import for recovery,
|
||||
// but the imports-only loader and the full compiler parser both reject it.
|
||||
|
||||
Reference in New Issue
Block a user