ww build: name current directory output

This commit is contained in:
2026-08-21 17:38:00 +09:00
parent 17b2879c3e
commit b03bbb1428
5 changed files with 436 additions and 23 deletions

View File

@@ -38,7 +38,7 @@ static const char *usage =
" -o FILE publishes a non-main archive FILE + FILE.wwi\n" " -o FILE publishes a non-main archive FILE + FILE.wwi\n"
" -o DIR publishes each selected command beneath DIR\n" " -o DIR publishes each selected command beneath DIR\n"
" lib/... every eligible package under lib, recursively\n" " lib/... every eligible package under lib, recursively\n"
" . build the cwd's <basename>.ww\n"; " . current directory package (default when no path is given)\n";
static char *self_dir; static char *self_dir;
static const char *self_path; static const char *self_path;
@@ -7158,6 +7158,36 @@ build_import_leaf(const char *identity, char *out, size_t outsz)
snprintf(out, outsz, "%s", leaf); snprintf(out, outsz, "%s", leaf);
} }
static int
build_current_dir_spelling(const char *path)
{
if (path[0] == '\0' || path[0] == '/') return 0;
const char *p = path;
int found = 0;
for (;;) {
while (*p == '/') p++;
if (*p == '\0') return found;
if (*p++ != '.' || (*p != '\0' && *p != '/')) return 0;
found = 1;
}
}
static void
build_local_dir_leaf(const char *resolved, char *out, size_t outsz)
{
char canonical[PATH_MAX];
const char *selected = resolved;
if (build_current_dir_spelling(resolved)
&& realpath(resolved, canonical) != NULL)
selected = canonical;
char tmp[PATH_MAX];
memcpy(tmp, selected, strlen(selected) + 1);
size_t n = strlen(tmp);
while (n > 1 && tmp[n - 1] == '/') tmp[--n] = '\0';
const char *base = strrchr(tmp, '/');
snprintf(out, outsz, "%s", base ? base + 1 : tmp);
}
static int static int
resolve_module(const char *name, const char *incs, char *out, size_t outsz, resolve_module(const char *name, const char *incs, char *out, size_t outsz,
int *is_dir) int *is_dir)
@@ -7428,15 +7458,12 @@ do_build(int argc, char **argv)
} else if (is_dir && root_identity != NULL) { } else if (is_dir && root_identity != NULL) {
build_import_leaf(root_identity, out, sizeof out); build_import_leaf(root_identity, out, sizeof out);
} else if (is_dir) { } else if (is_dir) {
char tmp[PATH_MAX]; build_local_dir_leaf(resolved, out, sizeof out);
memcpy(tmp, resolved, strlen(resolved) + 1);
size_t n = strlen(tmp);
while (n > 1 && tmp[n-1] == '/') tmp[--n] = '\0';
const char *b = strrchr(tmp, '/');
snprintf(out, sizeof out, "%s", b ? b + 1 : tmp);
} else { } else {
basename_no_ext(resolved, out, sizeof out); basename_no_ext(resolved, out, sizeof out);
} }
if (!outflag[0] && is_dir && build_current_dir_spelling(resolved))
objstem = out;
const char *default_output_dir = !outflag[0] && build_output_dir(out) const char *default_output_dir = !outflag[0] && build_output_dir(out)
? out : NULL; ? out : NULL;
if (discard_output) { if (discard_output) {

View File

@@ -8119,6 +8119,133 @@ This is runtime/coordinator presentation, not a persisted-byte contract. Build
workdir format remains `18`, test workdir format remains `19`, and semantic workdir format remains `18`, test workdir format remains `19`, and semantic
storage format remains `3`. storage format remains `3`.
### 11.40 Implemented current-directory default build names
An empty `ww build` package list selects the current directory just as an
explicit `.`, `./`, or equivalent sequence of single-dot components does. For
one command package and no explicit `-o`, the public executable is now named by
the selected directory's final component. The selector is loading syntax; it
is not the literal output pathname. Thus a command built while the current
directory is `tool` publishes `tool`, not `.`, and cold private build artifacts
use `tool.sepwork`, not `..sepwork`.
A non-main current-directory package uses the same corrected cold scratch stem,
but still has no link or public installation action. An explicit `-o`, exact
`-o /dev/null`, an ordinary non-current directory operand, and a contextual
dotted package request keep their established output rules.
#### Pinned Go evidence and fact classification
The sole semantic authority is official Go 1.26.5 at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- `search.CleanPatterns` turns an empty package-pattern list into exactly `.`;
`ImportPathsQuiet` then treats that local pattern as a directory selection
([`cmd/go/internal/search/search.go`, lines 441480](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/search/search.go#L441-L480)).
- `work.runBuild` loads packages and checks load errors before output planning.
With exactly one loaded `main` package and no `-o`, it selects
`DefaultExecName`
([`cmd/go/internal/work/build.go`, lines 459478](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L459-L478)).
- `load.(*Package).exeFromImportPath` takes the final loaded import-path
element, while `DefaultExecName` uses a source basename only for a
command-line-files package
([`cmd/go/internal/load/pkg.go`, lines 17271769](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1727-L1769)).
- Official command testdata distinguishes module naming from the manifest-free
GOPATH case and requires bare `go build` to create the directory-named `src`
executable
([`clean_binary.txt`, lines 1528](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/clean_binary.txt#L15-L28)).
A second manifest-free script changes to `m`, runs bare `go build`, and
requires executable `m`
([`gccgo_m.txt`, lines 414](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/gccgo_m.txt#L4-L14));
`build_static.txt` likewise builds and executes the default `hello`
([lines 1114](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/build_static.txt#L11-L14)).
Those selection, loading, default-name branches and script assertions are
**behavior directly implemented or asserted by pinned Go**. WW has no module
or manifest identity for a literal root, so using the selected local directory
leaf as its already-specified presentation fallback is **behavior derived from
the pinned implementation**. The directory remains loader metadata and does
not become canonical package or import identity.
#### Fresh four-axis audit and direct pre-fix measurements
The bounded audit examined every permanent axis before choosing this build
presentation difference. Both public stages were measured at the same source
paths:
- **Go-like build:** multi-command directory selection without `-o` was already
aligned. For a valid command in directory `a`, however, bare `build`,
`build .`, and `build ./` each exited 1 in both stages with empty stdout and
the same 55 stderr bytes (SHA-256
`2e8030a9eeb7187fbc1cb4ee9d794c352f1c31c92c884f8ae09a57e39bfe86db`):
`ww: build output "." already exists and is a directory`. Explicit `-o`
succeeded and produced byte-identical runnable binaries, proving that only
default presentation was wrong. This was the selected gap.
- **Go-like test:** an ordinary valid zero-match directory run exited 0 in both
stages with identical warning, accounting, and `[no tests to run]` result
bytes. The selected build branch does not enter test product naming,
filtering, capture, execution, result annotation, or retained test output.
- **Go-like package:** a directory containing two selected declared package
names was rejected in both stages with identical diagnostics. The selected
change occurs after package loading and does not alter source eligibility,
declaration checks, package kind, graph nodes, or actions.
- **Go-like import:** a dotted `cyclea -> cycleb -> cyclea` graph was rejected
in both stages with identical cycle diagnostics. The selected change does
not alter spelling, search, visibility, resolution, canonical identity, or
graph edges.
The pre-fix statuses, streams, hashes, diagnostics, artifacts, and runtime
results are **directly measured WW behavior**. The cited implementation and
testdata are **behavior directly implemented or asserted by pinned Go**. The
cross-axis non-effects follow from the bounded post-load output branch and are
**behavior derived from the pinned implementation**.
#### Ownership, final behavior, and preserved boundaries
`cmd/ww.do_build` and `selfhost/cmd/ww.dobuild` are semantic twins and the sole
owners of this rule. Their current-directory predicate accepts only relative
paths whose components are all exactly `.`. Only in that branch do they
canonicalize the selected directory and take its final component for output and
cold scratch presentation. Ordinary literal directory operands retain their
lexical leaf, and contextual roots retain their dotted identity leaf.
Direct post-fix probes through both stages show that bare, dot, and dot-slash
builds exit 0 with empty stdout/stderr, publish mode-executable binaries named
`a`, and produce `a.sepwork` with no `..sepwork`. All six binaries are
byte-identical (SHA-256
`866c1eb875dad271d37572f43fb9d9b0eb6a2344d2e61646e655bb09f7909bf6`).
Their unit, interface, assembly, object, archive, and init artifacts are also
byte-identical between stages and spellings. Current-directory library builds
still publish nothing and link nothing; their unit, interface, assembly,
object, and archive bytes remain stage-identical under `libcurrent.sepwork`.
Loading and source selection precede this branch. A missing dotted import
therefore retains its byte-identical diagnostic and creates neither output nor
scratch. Graph and action construction, compiler/assembler/archiver/linker
semantics, runtime behavior, and artifact content are unchanged. A genuine
directory occupying the derived output still rejects before tools and names
the derived leaf in its diagnostic.
Cold and warm persistent builds retain their existing keys and reuse rules. A
source invalidation reruns producers; an injected compiler failure preserves
the prior executable and committed persistent generation, publishes no stage,
and leaves no `.new`, `.install`, or `.wwtxn.*` residue. Restoring producer
success installs the changed executable. Existing concurrency, interruption,
process-group, rollback, publication, and cleanup owners gain no shared state or
new process path.
The WW-native `current_directory_build_default_output` observer proves both
stages, all three current-directory spellings, executable mode/runtime/byte
parity, explicit and null output controls, non-main non-publication, corrected
cold scratch, missing-import precedence, cold/warm reuse, invalidation,
producer-failure rollback, prior-output preservation, genuine collision, and
residue absence. Existing package/import graph, byte-identity, concurrent
transaction, and interruption observers remain authoritative for mechanisms
this presentation rule does not change.
No persisted-byte contract changed. 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.

View File

@@ -340,8 +340,17 @@ ImportPath = ident { "." ident } .
command's synthesized default basename already names a directory, loading command's synthesized default basename already names a directory, loading
and graph validation complete and the build rejects before tools without and graph validation complete and the build rejects before tools without
changing that directory; a non-main package synthesizes no default public changing that directory; a non-main package synthesizes no default public
output. Output paths and directory metadata never become package, import, output. With no path operand, the current directory is selected as if `.` had
graph, action, been supplied. For a lone literal current-directory command and no explicit
`-o`, `.`, `./`, and equivalent single-dot-component spellings derive the
default executable name from the canonical selected directory's final
component; cold adjacent scratch uses that same leaf plus `.sepwork`. The dot
spelling itself is never an output name. A current-directory non-main package
uses the corrected scratch presentation but still links and publishes
nothing. This physical leaf is fallback presentation metadata only: it never
displaces a requested contextual dotted identity or becomes package, import,
graph, action, artifact, symbol, `.wwi`, or persistence identity.
Output paths and directory metadata never become package, import, graph, action,
symbol, artifact, `.wwi`, or persistence identity. symbol, artifact, `.wwi`, or persistence identity.
Every caller-visible build installation checks its destination after all Every caller-visible build installation checks its destination after all

View File

@@ -8629,7 +8629,7 @@ fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
}; };
fn writeusage(fd: i32) void = { fn writeusage(fd: i32) void = {
let s: str = "usage: ww [-V] <subcommand> [args...]\n -V print version and exit\n build [-S] [-w DIR] [-I DIR] [-o FILE|DIR] [path ...] build local package graphs\n run [path] ... build then exec, passing extra args to the program\n test [-S -o STEM] [-w DIR] [options] [path ...] build/run tests; -S emits package asm\n version print version and exit\n\n path forms:\n foo.ww literal file\n foo search cwd, -I dirs, then the source library for foo.ww or foo/\n lib/foo directory: build its package sources\n -o FILE publishes a non-main archive FILE + FILE.wwi\n -o DIR publishes each selected command beneath DIR\n lib/... every eligible package under lib, recursively\n . build the cwd's <basename>.ww\n"; let s: str = "usage: ww [-V] <subcommand> [args...]\n -V print version and exit\n build [-S] [-w DIR] [-I DIR] [-o FILE|DIR] [path ...] build local package graphs\n run [path] ... build then exec, passing extra args to the program\n test [-S -o STEM] [-w DIR] [options] [path ...] build/run tests; -S emits package asm\n version print version and exit\n\n path forms:\n foo.ww literal file\n foo search cwd, -I dirs, then the source library for foo.ww or foo/\n lib/foo directory: build its package sources\n -o FILE publishes a non-main archive FILE + FILE.wwi\n -o DIR publishes each selected command beneath DIR\n lib/... every eligible package under lib, recursively\n . current directory package (default when no path is given)\n";
os.write(fd, s.ptr, s.len: u64); os.write(fd, s.ptr, s.len: u64);
}; };
@@ -8716,6 +8716,43 @@ fn defaultimportoutpath(identity: *u8) *u8 = {
return out.ptr; return out.ptr;
}; };
fn buildcurrentdirspelling(path: *u8) bool = {
if (path[0u64] == 0u8 || path[0u64] == '/': u8) { return false; };
let i: u64 = 0u64;
let found: bool = false;
for (true) {
for (path[i] == '/': u8) { i += 1u64; };
if (path[i] == 0u8) { return found; };
if (path[i] != '.': u8) { return false; };
i += 1u64;
if (path[i] != 0u8 && path[i] != '/': u8) { return false; };
found = true;
};
return false;
};
fn buildlocaldirleaf(resolved: *u8) *u8 = {
let selected: *u8 = resolved;
if (buildcurrentdirspelling(resolved)) {
let canonical: *u8 = canonicaldir(pathstr(resolved));
if (canonical != nil) { selected = canonical; };
};
let rlen: u64 = cstrlen(selected);
for (rlen > 1u64) {
if (selected[rlen - 1u64] != '/': u8) { break; };
rlen -= 1u64;
};
let bo: u64 = basenameoff(selected, rlen);
let outbuf: []u8 = alloc([], (os.PATH_MAX: u64))!;
outbuf.len = os.PATH_MAX;
let out: *u8 = outbuf.ptr;
let i: u64 = bo;
let off: u64 = 0u64;
for (i < rlen) { out[off] = selected[i]; off += 1u64; i += 1u64; };
cstrseal(out, off);
return out;
};
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = { fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
let src: *u8 = nil; let src: *u8 = nil;
let srcindex: i32 = -1; let srcindex: i32 = -1;
@@ -8897,22 +8934,14 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
} else { if (isdir != 0 && rootidentity != nil) { } else { if (isdir != 0 && rootidentity != nil) {
out = defaultimportoutpath(rootidentity); out = defaultimportoutpath(rootidentity);
} else { if (isdir != 0) { } else { if (isdir != 0) {
let rlen: u64 = cstrlen(resolved); out = buildlocaldirleaf(resolved);
for (rlen > 1u64) {
if (resolved[rlen - 1u64] != 47u8) { break; };
rlen -= 1u64;
};
let bo: u64 = basenameoff(resolved, rlen);
let outbuf: []u8 = alloc([], (os.PATH_MAX: u64))!;
outbuf.len = os.PATH_MAX;
out = outbuf.ptr;
let i: u64 = bo;
let off: u64 = 0u64;
for (i < rlen) { out[off] = resolved[i]; off += 1u64; i += 1u64; };
cstrseal(out, off);
} else { } else {
out = defaultoutpath(resolved); out = defaultoutpath(resolved);
}; }; }; }; }; };
if ((outflag == nil || outflag[0u64] == 0u8) && isdir != 0
&& buildcurrentdirspelling(resolved)) {
objstem = out;
};
let defaultoutputdir: *u8 = nil; let defaultoutputdir: *u8 = nil;
if ((outflag == nil || outflag[0u64] == 0u8) && buildoutputdir(out)) { if ((outflag == nil || outflag[0u64] == 0u8) && buildoutputdir(out)) {
defaultoutputdir = out; defaultoutputdir = out;

View File

@@ -14074,6 +14074,227 @@ fn runtimepath(relative: str) str = {
clean(root); clean(root);
}; };
// Empty build patterns select the current directory. A literal dot is
// loading syntax, not the caller-visible output name: command publication and
// cold scratch use the selected directory leaf without making that physical
// leaf package or import identity.
@test fn current_directory_build_default_output() void = {
let root: str = fresh();
let command: str = strings.concat(root, "/current-command");
let library: str = strings.concat(root, "/current-library");
let bad: str = strings.concat(root, "/bad-current");
mkdirall(command); mkdirall(library); mkdirall(bad);
let commandfile: str = strings.concat(command, "/main.ww");
let original: str = "package main;\nfn main() i32 = { return 23; };\n";
let changed: str = "package main;\nfn main() i32 = { return 24; };\n";
writefile(commandfile, original);
writefile(strings.concat(library, "/library.ww"), strings.concat(
"package declared_library;\n",
"export fn value() i32 = { return 7; };\n"));
writefile(strings.concat(bad, "/main.ww"), strings.concat(
"package main;\nimport missing.current;\n",
"fn main() i32 = { return missing.current.value(); };\n"));
let compilerwrapper: str = strings.concat(root, "/current-w6c.sh");
writeexecutable(compilerwrapper, strings.concat(
"#!/bin/sh\nprintf 'compile\\n' >> \"$WW_CURRENT_TRACE\"\n",
"if test \"$WW_CURRENT_FAIL\" = 1; then\n",
" printf 'injected current-directory compiler failure\\n' >&2\n",
" exit 97\nfi\nexec \"$WW_CURRENT_REAL_C\" \"$@\"\n"));
let stages: []str = ["ww", "ww_ww"];
let compilers: []str = ["w6c", "w6c_ww"];
let tags: []str = ["c", "ww"];
let spellings: []str = ["", ".", "./"];
let spellingtags: []str = ["bare", "dot", "slash"];
let baseenv: []str = os.getenvs();
let commandref: str = "";
let changedref: str = "";
let missingref: str = "";
let failref: str = "";
let collisionref: str = "";
let out: commandout;
let si: i32 = 0;
for (si < stages.len) {
rewritefile(commandfile, original);
let defaultbin: str = strings.concat(command, "/current-command");
let defaultscratch: str = strings.concat(defaultbin, ".sepwork");
let legacyscratch: str = strings.concat(command, "/..sepwork");
// Bare, dot, and dot-slash spellings select the same package and
// produce the same directory-named executable and cold artifacts.
let pi: i32 = 0;
for (pi < spellings.len) {
let av: []str = [driver(stages[si]), "build"];
if (spellings[pi].len != 0) { append(av, spellings[pi]); };
runcommanddir(root, strings.concat("current-", tags[si], "-",
spellingtags[pi]), command, av,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(os.exists(defaultbin) && os.exists(defaultscratch));
assert(!os.exists(legacyscratch));
assert((permissionmode(defaultbin) & 73u32) == 73u32);
let built: str = readfile(defaultbin);
if (commandref.len == 0) { commandref = strings.dup(built); }
else { assert(same(commandref, built)); };
let runav: []str = [defaultbin];
runcommand(root, strings.concat("current-run-", tags[si], "-",
spellingtags[pi]), runav, time.second, &out);
expectexit(&out, 23);
assert(!directoryhasnew(command)
&& !directoryhasfragment(command, ".install")
&& !directoryhasfragment(command, ".wwtxn."));
assert(os.remove(defaultbin) == 0);
clean(defaultscratch);
pi += 1;
};
// Explicit output and exact null output keep their established
// disposition and never create the default current-directory name.
let explicitout: str = strings.concat(root, "/explicit-", tags[si]);
let explicitav: []str = [driver(stages[si]), "build", "-o",
explicitout];
runcommanddir(root, strings.concat("current-explicit-", tags[si]),
command, explicitav,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(same(commandref, readfile(explicitout))
&& !os.exists(defaultbin) && !os.exists(defaultscratch));
assert(os.remove(explicitout) == 0);
clean(strings.concat(explicitout, ".sepwork"));
let nullav: []str = [driver(stages[si]), "build", "-o", "/dev/null"];
runcommanddir(root, strings.concat("current-null-", tags[si]), command,
nullav, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(!os.exists(defaultbin) && !os.exists(defaultscratch)
&& !os.exists(legacyscratch));
// A non-main current-directory build compiles and archives privately,
// but neither links nor publishes a directory-named file.
let librarybin: str = strings.concat(library, "/current-library");
let libraryscratch: str = strings.concat(librarybin, ".sepwork");
let librarylegacy: str = strings.concat(library, "/..sepwork");
let libraryav: []str = [driver(stages[si]), "build"];
runcommanddir(root, strings.concat("current-library-", tags[si]),
library, libraryav,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(!os.exists(librarybin) && os.exists(libraryscratch)
&& !os.exists(librarylegacy));
assert(!directoryhasnew(library)
&& !directoryhasfragment(library, ".wwtxn."));
clean(libraryscratch);
// Import loading retains precedence over output planning. Failure
// creates neither the corrected output nor either scratch spelling.
let badbin: str = strings.concat(bad, "/bad-current");
let badav: []str = [driver(stages[si]), "build"];
runcommanddir(root, strings.concat("current-missing-", tags[si]), bad,
badav, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0
&& has(out.stderr, "cannot find package missing.current\n"));
assert(!os.exists(badbin)
&& !os.exists(strings.concat(badbin, ".sepwork"))
&& !os.exists(strings.concat(bad, "/..sepwork")));
if (si == 0) { missingref = strings.dup(out.stderr); }
else { assert(same(missingref, out.stderr)); };
// Persistent cold/warm reuse keeps the same output. An invalidated
// producer failure preserves both that prior output and clean public
// state; a later successful invalidation publishes the changed binary.
let work: str = strings.concat(root, "/current-work-", tags[si]);
let trace: str = strings.concat(root, "/current-trace-", tags[si]);
mkdirall(work); writefile(trace, "");
let env: []str = alloc([], (baseenv.len + 4): u64)!;
let ei: i32 = 0;
for (ei < baseenv.len) {
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
&& !strings.hasprefix(baseenv[ei], "WW_CURRENT_")) {
append(env, baseenv[ei]);
};
ei += 1;
};
append(env, strings.concat("WW_W6C=", compilerwrapper));
append(env, strings.concat("WW_CURRENT_REAL_C=",
driver(compilers[si])));
append(env, strings.concat("WW_CURRENT_TRACE=", trace));
let failindex: i32 = env.len;
append(env, "WW_CURRENT_FAIL=");
let persistav: []str = [driver(stages[si]), "build", "-w", work];
runcommandenvdir(root, strings.concat("current-cold-", tags[si]),
persistav, env, command,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& readfile(trace).len != 0);
let prior: str = readfile(defaultbin);
assert(same(commandref, prior));
rewritefile(trace, "");
runcommandenvdir(root, strings.concat("current-warm-", tags[si]),
persistav, env, command,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& readfile(trace).len == 0 && same(prior, readfile(defaultbin)));
rewritefile(commandfile, changed); rewritefile(trace, "");
env[failindex] = "WW_CURRENT_FAIL=1";
runcommandenvdir(root, strings.concat("current-fail-", tags[si]),
persistav, env, command,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(readfile(trace).len != 0
&& has(out.stderr, "injected current-directory compiler failure\n")
&& has(out.stderr, "ww: w6c failed for ")
&& same(prior, readfile(defaultbin)));
assert(!directoryhasnew(command) && !directoryhasnew(work)
&& !directoryhasfragment(command, ".install")
&& !directoryhasfragment(command, ".wwtxn.")
&& !directoryhasfragment(work, ".wwtxn."));
if (si == 0) { failref = strings.dup(out.stderr); }
else { assert(same(failref, out.stderr)); };
env[failindex] = "WW_CURRENT_FAIL="; rewritefile(trace, "");
runcommandenvdir(root, strings.concat("current-changed-", tags[si]),
persistav, env, command,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& readfile(trace).len != 0);
let changedbytes: str = readfile(defaultbin);
assert(!same(prior, changedbytes));
if (changedref.len == 0) { changedref = strings.dup(changedbytes); }
else { assert(same(changedref, changedbytes)); };
let changedrun: []str = [defaultbin];
runcommand(root, strings.concat("current-changed-run-", tags[si]),
changedrun, time.second, &out);
expectexit(&out, 24);
assert(os.remove(defaultbin) == 0);
clean(work);
rewritefile(commandfile, original);
// A genuine destination-directory collision still rejects at output
// preflight, now naming the derived leaf rather than the dot selector.
mkdirall(defaultbin);
runcommanddir(root, strings.concat("current-collision-", tags[si]),
command, libraryav,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0 && same(out.stderr, strings.concat(
"ww: build output \"current-command\" already exists and is a directory\n")));
assert(!os.exists(defaultscratch) && !os.exists(legacyscratch));
if (si == 0) { collisionref = strings.dup(out.stderr); }
else { assert(same(collisionref, out.stderr)); };
clean(defaultbin);
si += 1;
};
assert(!directoryhasnew(root) && !directoryhasfragment(root, ".wwtxn."));
clean(root);
};
// A single directory root used to bypass the package coordinator and treat // A single directory root used to bypass the package coordinator and treat
// every non-null -o spelling as one file. Exercise the Go output-directory // every non-null -o spelling as one file. Exercise the Go output-directory
// branch at that dispatch boundary while the established graph transaction // branch at that dispatch boundary while the established graph transaction