Sigil 0.20.1
A sandbox and native-codegen correctness release. Builds no longer hang when a sandboxed environment exposes Sigil's shared store as readable but not writable, and a caught exception raised by eval-string no longer abandons the rest of a native WASM caller. Process spawning can also give a child its own working directory and environment overrides.
Existing projects build without changes. The cache fallback is automatic when the default store is read-only. An explicitly selected SIGIL_STORE remains a promise that the path is writable and now fails immediately when that promise is false.
A read-only build cache no longer looks like a lock held forever
In a sandboxed environment, $HOME/.cache/sigil/store can be read but not written. A warm build treated the write denial as another writer holding a lock and polled forever. The workaround was a fresh store under /tmp, which made the build proceed by throwing away the warm cache and recompiling the whole dependency graph.
A verified hit is now a read operation and takes no publication lock. When the default store really is read-only, sigil build also opens a stable writable upper store under /tmp/sigil-store-overlay-v1-<hash> and keeps the original store as a read-only fallback. Reads search both; new objects, locks and roots go only to the upper store. No sandbox setting or worker-specific environment variable is required, and the build prints both paths when the overlay is active.
A corrupt fallback object is left untouched and rebuilt into the writable layer. Permission failures now report immediately, and genuine lock contention has a 60-second bound. Measured in the environment that exposed this, the first sigil-stdlib build completed instead of hanging, and the next completed in about five seconds with every module reported CACHED.
A caught eval-string exception resumes native and WASM callers
A guard around eval-string could run its handler and still abandon everything after the guard in a native WASM build. The process exited 0, which made the failure especially quiet: Slate reached a ready state with a working eval channel, but never opened its startup view and never ran the rest of main.
Code after the guard now runs as expected. This covers eval, eval-string and other native calls that can raise indirectly, including applications that evaluate native code from native code.
The differential exception corpus now catches a failing eval-string, checks for caught:continued, then runs a later dynamic-wind case. Bytecode, native ELF and native WASM produce the same 14 lines and exit status.
Spawn a child in its own context
process-spawn accepts cwd: and environment:. The working directory changes only in the child, and environment entries such as "MODE=test" override inherited values without mutating the parent process:
(process-spawn "git" "status" cwd: checkout environment: (list "GIT_OPTIONAL_LOCKS=0"))
Both options use the native spawn path on Unix, macOS and Windows. Invalid environment entries are refused before the child starts.
Also in this release
- If a test file registered tests and then failed while loading, the runner reported only the load error and discarded the registered tests. It now runs them and counts the load error as one additional failure.
- Quotes, backslashes and control characters in package name or version metadata could produce invalid generated C for a native application. The generated string literals are now escaped.
Upgrade notes
Nothing is required. In a sandbox, run sigil build normally and Sigil will select the overlay when it needs one. If you set SIGIL_STORE yourself, point it at a writable directory.