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';
|
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
|
/* Go's build -o directory branch follows an existing destination through
|
||||||
* stat, and a trailing platform separator declares a directory which the
|
* stat, and a trailing platform separator declares a directory which the
|
||||||
* request may need to create. WW's platform separator is '/'. */
|
* request may need to create. WW's platform separator is '/'. */
|
||||||
@@ -7442,6 +7474,11 @@ do_build(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
struct stat requested;
|
struct stat requested;
|
||||||
int literal = stat(src, &requested) == 0;
|
int literal = stat(src, &requested) == 0;
|
||||||
|
if (source_operand_ignored(src)) {
|
||||||
|
source_operand_no_sources(src);
|
||||||
|
free(incs);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
char resolved[PATH_MAX];
|
char resolved[PATH_MAX];
|
||||||
int is_dir = 0;
|
int is_dir = 0;
|
||||||
if (!resolve_module(src, incs, resolved, sizeof resolved, &is_dir)) {
|
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;
|
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) {
|
if (pattern != NULL) {
|
||||||
struct stat first;
|
struct stat first;
|
||||||
if (stat(target, &first) != 0 || !S_ISREG(first.st_mode)) {
|
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
|
Build workdir format remains `18`, test workdir format remains `19`, semantic
|
||||||
storage format remains `3`, and no test-result cache is introduced.
|
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
|
## 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.
|
||||||
|
|||||||
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
|
delegated graph-import scan or tools. An ordinary build compares production
|
||||||
names only; one test product compares its production, same-package test, and
|
names only; one test product compares its production, same-package test, and
|
||||||
external-test selections without merging their units.
|
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
|
- `import acme.codec;` loads the canonical package `acme.codec`. If that
|
||||||
package declares `package wire;`, the importing file sees its exported names
|
package declares `package wire;`, the importing file sees its exported names
|
||||||
as `wire.Name`; `codec.Name` is not an additional binding. An explicit alias
|
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:
|
run loop exactly. Measured on the 31-package `lib/...` walk:
|
||||||
7.0s sequential, 2.4s at `-j 4`.
|
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
|
List mode uses that same product process and initialization boundary but starts
|
||||||
no per-test child. The shared language harness emits only selected qualified
|
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
|
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;
|
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 = {
|
fn arenadupcstr(src: *u8, plen: u64) *u8 = {
|
||||||
let buf: []u8 = alloc([], plen + 1u64)!;
|
let buf: []u8 = alloc([], plen + 1u64)!;
|
||||||
let i: u64 = 0u64;
|
let i: u64 = 0u64;
|
||||||
@@ -8914,6 +8948,10 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
case void => requestedliteral = true;
|
case void => requestedliteral = true;
|
||||||
case let e: os.oserror => void;
|
case let e: os.oserror => void;
|
||||||
};
|
};
|
||||||
|
if (sourceoperandignored(src)) {
|
||||||
|
sourceoperandnosources(src);
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
let isdir: i32 = 0;
|
let isdir: i32 = 0;
|
||||||
let resolved: *u8 = resolvemodule(selfdir, src, incs.ptr, &isdir);
|
let resolved: *u8 = resolvemodule(selfdir, src, incs.ptr, &isdir);
|
||||||
if (resolved == nil) {
|
if (resolved == nil) {
|
||||||
@@ -9792,6 +9830,26 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
producti += 1;
|
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) {
|
if (patarg != nil) {
|
||||||
let first: os.filestat;
|
let first: os.filestat;
|
||||||
let firstregular: bool = false;
|
let firstregular: bool = false;
|
||||||
|
|||||||
@@ -987,6 +987,354 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
clean(root);
|
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
|
// A source file has one contiguous import section immediately after its
|
||||||
// package clause. The parser may continue after a late import for recovery,
|
// 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.
|
// but the imports-only loader and the full compiler parser both reject it.
|
||||||
|
|||||||
Reference in New Issue
Block a user