**Problem**
Under CPU load, the thin client sometimes loses task output. The channel
started reading before it was subscribed, so an early sbt/attach was
dropped and the command ran on an unattached channel.
**Solution**
Start the channel threads after subscribing it.
Fixes#9834
Generated-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <[email protected]>
**Problem**
When a forked test JVM exits, WorkerProxy.watch calls notifyExit right
away, and on exit code 0 that completes the run. If the response reader
has not consumed the rest of the stream yet, the remaining events, logs and
the final response are dropped, and the build passes with failing tests
unreported. Slow listeners, or several forks at once, make this likely.
**Solution**
Track when the response stream reaches end of input (the socket reader for
Tcp/Ipc, stdout for Stdio), and have the watch thread wait for it, with a
bound, before calling notifyExit. This is what sbt 1.x did by joining the
acceptor thread. The new scripted test uses a listener that sleeps per
event, and fails without this change.
Fixes#9859
Generated-by: Claude Opus 5.5
Co-authored-by: Eugene <[email protected]>
PR #9790 added parser as qualified-private, anticipating use limited to
internal code; however, as PR #9781 will now be moved to a plugin, let's
relax the visibility limitation.
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
**Problem**
Scripted job 3 is near the 25 minute timeout, and tests/* runs twice.
**Solution**
Drop tests/* from job 2, and move groups from job 3 to jobs 4 and 7.
Generated-by: Claude Code
Co-authored-by: Claude Opus 5.5 <[email protected]>
**Problem**
The Scala 3 syntax reformat (f9688c146) turned braced catch blocks such
as
catch { case e: Exception => e.printStackTrace(); 1 }
into one-line catch cases. A one-line catch case body is a single
expression, so the semicolon ends the catch case and the value after it
becomes a separate statement that is always the result. ForkTest always
saw an exit code of 1 and both of its properties failed, and
extractLicenses0 in the build always returned Nil instead of the
license files referenced by NOTICE.
**Solution**
Put the catch case bodies on their own lines so the trailing value is
part of the catch case again.
Generated-by: Claude Opus 5.5
Co-authored-by: Claude Opus 5.5 <[email protected]>
**Problem**
CheckBuildSources.reset only refreshed the stored build source stamps
when the set of build source globs changed. A reload that did not go
through the command-level check (for example, scripted batch mode
reusing one sbt instance across tests) kept stale stamps, so the next
check reported unchanged build files as modified and reloaded sbt in
the middle of a watch. That reload closed the file tree repository the
watch was still using, failing the next iteration with "Tried to invoke
register on closed repository".
**Solution**
Take a fresh baseline of the build source stamps on every load.
Generated-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
**Problem**
Since 2.1.0-M1, the tilde command starts watching but never triggers a
build on a source change. The Scala 3 syntax reformat (f9688c146)
rewrote
catch { case _: InterruptedException => interrupted.set(true); Nil }
into a braceless one-liner. Without braces, the semicolon ends the catch
case, so the block became the try/catch expression followed by Nil, and
the events value was always Nil. Every file event was discarded.
**Solution**
Put the catch case body on its own lines so Nil is the value of the
catch case again.
Generated-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
**Problem**
Like #9848, some paths still swallow InterruptedException. The
NetworkTerminal request methods block on queue.take, so an
interrupted task thread gets a default value back and loses its
interrupt flag. removeChannel still uses put, and the client's boot
read thread ignores the interrupt meant to stop it.
**Solution**
Restore the flag in NetworkTerminal (in withThread only while the
terminal is open), use add in removeChannel, and stop the read
thread on interrupt. Add tests to NetworkChannelSpec.
Generated-by: Claude Opus 5.5
Co-authored-by: Claude Opus 5.5 <[email protected]>
**Problem**
In the default thin-client mode, writing to System.out or System.err
from an interrupted thread throws InterruptedException and clears the
interrupt flag. NetworkChannel enqueues output with `put` on unbounded
LinkedBlockingQueues, and `put` acquires its lock with
lockInterruptibly(). Logging libraries swallow the exception, so the
interrupt is silently lost and code relying on it can hang.
**Solution**
Use `add` instead of `put` on the unbounded queues. They never block,
so no other behavior changes. Add regression tests to
NetworkChannelSpec.
Fixes issue #9845
Generated-by: Claude Opus 5.5
**Problem**
Caffeine cache takes in account of how often the data was used,
so once 20 analysis fills up the cache, the last addition
may immediately get evicted.
**Solution**
Replace Caffeine cache with LRU cache.
**Problem**
The native thin client discards -J JVM options before it starts the
server, so sbt -J-Xmx2G starts the server with the runner's default
1 GB heap. sbt.bat does not handle -J on the command line: it passes
the option to sbt as a command, which exits with "Not a valid
command". Its default memory check also misses heap options given on
the command line or as -XX:MaxRAM and RAM percentages, so the
appended -Xmx1024m overrides them.
**Solution**
sbtn passes -J options to the server script, and strips -J when it
starts the launcher JAR through java. A -J split out of a quoted
command stays in that command, and a bare -J is dropped. sbt.bat
strips -J, adds the option to the JVM options, rejoins -J-D and -J-XX
values that cmd.exe splits at '=', and passes -J options on to sbtn.
Its default memory check ignores quotes and checks the same options
as the Unix runner.
Generated-by: OpenAI Codex
Generated-by: Claude Code
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
**Problem**
mkInput may fail for file locking reasons outside of the task engine
management.
**Solution**
This adds a simple retry. Note that we do not try to fall back
to organic task execution when mkInput fails.
**Problem**
Identity map of a task (x := expr.value) goes through the task macro
machinery requiring JsonFormat etc for cached task.
This is not necessary for dynamic task etc.
**Solution**
This implements a short-circuit for x := expr.value shaped tree.
**Problem**
Since Coursier 2.1.25 .m2/settings.xml takes over even when sbtResolvers exists.
**Solution**
Turn off .m2/settings.xml unless explicitly enabled.
Include project identity, portable project path, configuration and platform in test digests so sibling projects cannot reuse each other’s successful test results. Add regression coverage for isolation and cache reuse.
Problem: The Unix launcher ignores JAVA_HOME and selects Java from PATH when JAVACMD is absent.
Solution: Check JAVA_HOME/bin/java before falling back to PATH while preserving explicit --java-home and JAVACMD precedence.
Fixes#9794
Problem
The scripted pause handler assumes the JVM has a directly attached terminal. System.console() can return null when sbt runs in client-server mode or with redirected streams, causing pause to fail.
Solution
Use scala.io.StdIn.readLine() to read the configured input stream, which supports sbt client input forwarding. Add unit tests for waiting, line consumption, and EOF.
**Problem**
The in-memory Analysis cache is currently weighted by on-disk size,
but according to the user report it expands out 50x, so 100MB
size limit doesn't hit.
**Solution**
This set maxCount instead, default 20 and configurable by system property
sbt.local_cache.analysis_count.
**Problem**
Since #9471, Runtime/fullClasspath combines exportedProducts
(unversioned) with the Runtime internal dependency classpath
(packageBin, versioned), so the project's own JAR appears twice.
**Solution**
In Runtime, build fullClasspath and fullClasspathAsJar from
the versioned exported products.
Fixes#9813.
With overwrite = false, the default for non-snapshot versions, the ivyless
publisher kept an existing file and reported success, leaving stale bytes and
checksums. The HTTP paths took overwrite and never read it. sbt 1.x did the
opposite in both cases: file repositories warned and overwrote (22f47be29),
remote ones refused.
Restore that. File targets now warn and write through a temp file; HTTP targets
refuse after a HEAD probe that fails open. Also route a file: URLRepository,
Ivy or Maven layout, to the filesystem instead of httpPut.
Problem
compileIncremental declares the analysis file and the classes directory
as cache outputs, but not the early (pickle) jar or the early analysis.
A cache hit therefore leaves a subproject with a restored analysis and
no early jar. On the next incremental round Zinc has no jar to merge
scalac's pickles into, so the early jar ends up holding only that
round's TASTy/sig files, afterEarlyOutput(true) fires, and every
downstream subproject is compiled against it and fails with "Not found"
for each upstream type that was not just recompiled.
Solution
- Declare the early jar and the early analysis file as outputs of
compileIncremental when exportPipelining produced them, so a hit
restores them.
- Restored outputs are symlinks into the CAS, and Zinc rewrites the
early jar in place when it merges a round's pickles. Replace a
symlinked early jar by a real copy before Zinc runs so the cached
blob is never modified.
- When a previous analysis exists but the early jar does not (a cache
written before the jar was an output, exportPipelining switched on
for an existing build, a deleted early directory), recompile the
subproject from scratch, which writes a complete jar.
**Problem**
The non-Ivy Maven publisher derived the repository directory and every
filename from the first POM artifact. Custom-named artifacts could collide
with and replace the main JAR, while legacy sbt 1 plugin publishing depended
on artifact map order and collapsed crossed and uncrossed files.
**Solution**
Derive the directory from module coordinates and each filename from the
platform-aware cross-versioned artifact name. Reject target-path collisions
before publishing, de-duplicate remote snapshot metadata, and cover ordinary,
platform, snapshot, and legacy plugin layouts with unit, property, and
scripted tests.
Generated-by: OpenAI Codex