`update` held a process-global lock around coursier resolution and artifact
fetching whenever a logger was set OR coursier was not in fallback mode. That
lock exists only to serialize coursier's interactive progress bar, which is
rendered solely when no custom logger is supplied and coursier is not in
fallback mode. The `loggerOpt.nonEmpty` clause therefore over-serialized the
common non-interactive case (IntelliJ re-imports, CI, any non-TTY run, where
sbt supplies a quiet debug-only logger), making `update` scale with the number
of modules rather than the number of distinct artifacts.
Narrow the predicate to `!hasCustomLogger && !fallbackMode`
(Lock.progressBarActive), so resolution runs in parallel unless coursier is
actually drawing its progress bar. Interactive terminals stay serialized;
COURSIER_PROGRESS=false opts into parallelism there, and #5627 tracks fully
parallel resolution with live progress bars.
Safe: SbtCoursierCache is ConcurrentHashMap-backed, a CacheLogger is already
invoked concurrently within a single resolution (parallel downloads), and the
COURSIER_PROGRESS=false parallel path has been used in production for years.
Fixes#5508.
Co-authored-by: Brian Hotopp <brihoto@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deduplicate executable jar package entries. In the issue, the duplicate was something like:
(resource_managed/.../sbt/sbt.autoplugins, "sbt/sbt.autoplugins")
(classes/.../sbt/sbt.autoplugins, "sbt/sbt.autoplugins")
This is because copyResources copied autoplugin metadata to the classes directory.
Solution: when composing the packageBinMappings, differentiate between artifacts that come from class directory and from outside of it. If there is a duplicate, prefer resource that comes from outside of it.
Co-authored-by: Anatolii Kmetiuk <anatoliykmetyuk@gmail.com>
The original fixture pinned
scalaOrganization := "io.github.scala-wasm"
scalaVersion := "3.8.3-RC1-wasm-bin-SNAPSHOT"
and ran compile. The wasm fork never published a release of
scala3-library_3 -- only a snapshot to Sonatype's central-snapshots
repo, which has ~90-day retention. #8732 merged 2026-02-21; the
snapshot was GC'd ~2026-05-22 and develop CI began failing
deterministically.
Fixes#9259.
Co-authored-by: BrianHotopp <brihoto@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
**Problem**
When sbtn (native thin client) can't find a running sbt server, it prompts
to start one. There was no way to opt out of server auto-start from the
client side, which is needed for CI environments and scripting (sbt/sbt#7079).
**Solution**
Reuse the existing sbt.server.autostart system property in NetworkClient
to control whether sbtn should attempt to start a server. When no server is
running and sbt.server.autostart=false, sbtn exits with an error instead
of prompting.
Support setting this property via:
- sbtn --no-server compile (sets sbt.server.autostart=false)
- sbtn --autostart=false compile (new flag following sbt conventions)
- sbtn -Dsbt.server.autostart=false compile (direct system property)
- sbt --autostart=false compile (bash runner and sbtw)
This follows sbt's naming conventions for properties/options: positive
property names with =false opt-out (like --color=false, --supershell=false).
Fixessbt/sbt#7079
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
**Problem**
We need some tooling to debug caching issues.
**Solution**
This adds an exeprimental execution log support,
which shows input and output of cached tasks.
* Fix#4979: apply -debug (and other level options) at startup so loading shows debug log
- Parse log level from configuration.arguments in StandardMain.initialState
- Pass initialLevel to GlobalLogging.initial so console appender uses it from first log
- Set Keys.logLevel and BasicKeys.explicitGlobalLogLevels in initial state when level option present
- Add initialLevel parameter to GlobalLogging.initial (default Level.Info) for backward compatibility
- Add InitialLogLevelSpec tests for logLevelFromArguments
- Add docs/fix-4979-manual-verification.md for manual reproduction
* [2.x] feat: Add cacheVersion setting for global cache invalidation
**Problem**
There was no escape hatch to invalidate all task caches when needed.
**Solution**
Add `Global / cacheVersion` setting that incorporates into the cache key
hash. Changing it invalidates all caches. Defaults to reading system
property `sbt.cacheversion`, or else 0L. When 0L, the hash is identical
to the previous behavior (backward compatible).
Fixes#8992
* [2.x] refactor: Simplify BuildWideCacheConfiguration and add cacheVersion test
- Replace auxiliary constructors with default parameter values
- Add unit test verifying cacheVersion invalidates the cache
* [2.x] fix: Restore auxiliary constructors for binary compatibility
* [2.x] test: Improve cacheVersion scripted test and add release note
- Scripted test now verifies cache invalidation via a counter
that increments only when the task body actually executes
- Add release note documenting the cacheVersion setting
Bumps io to released 1.12.0 which contains the atomic-write changes
to IO.copyFile, IO.transfer(InputStream, File), and IO.jar/zip.
ActionCacheStore.putBlob now uses IO.transfer instead of duplicating
staging logic.
Co-authored-by: Anatolii Kmetiuk <anatoliykmetyuk@gmail.com>
**Problem**
Current MixedAnalyzingCompiler analysis cache caches using the last write,
assuming all writing happens via it. However, this doesn't work with
sbt 2.x caching system where the gz file under it can switch.
**Solution**
This switches to using caffeine caching, which includes file size
and file timestamp for local analysis caching.
**Problem**
Some tests are failing.
**Solution**
* Port EvictionErrorSpec to lm-coursier
* Port EvictionWarningSpec to lm-coursier
* Port InclExclSpec to lm-coursier
* Port ZincComponentCompilerSpec to lm-coursier
* Apparently we still need lmIvy
Rather than using the FileConverter to replace virtual paths, this uses the rootPaths directly. It only replaces a virtual path in a scalac option if the given segment of the option begins with the root path key.
**Problem**
When usePipelining := true in a multi-project build, sbt appends
-Ypickle-java and -Ypickle-write <path>/early.jar to scalacOptions
for fast parallel compilation. These flags were being leaked into the Scala
REPL, causing failure on console
**Solution**
Strip pipelining flags before forwarding to the REPL in Deafults.scala.
**Problem**
In sbt 2.x, forking still creates layered classloader in the worker process,
which doesn't work for some tests.
**Solution**
This provides an escape hatch to emulate the sbt 1.x semantics of
using the system classpath for testing.
**Problem**
Projects defined in subdirectory build.sbt files are
not correctly resolved to the base directory relative to the file.
**Solution**
Call resolveBase.
Bug 1: bgRun forks with inheritIO() instead of LoggedOutput
bgRunTask / bgRunMainTask resolve their fork options via (run / forkOptions), which inherits run / connectInput := true. That has two downstream consequences:
1. ForkRun.configLogged (in run/src/main/scala/sbt/Run.scala) skips installing OutputStrategy.LoggedOutput(log) whenever config.connectInput == true.
2. Fork.apply (in run/src/main/scala/sbt/Fork.scala) sees connectInput && outputStrategy == StdoutOutput and takes the interactiveFork path, which calls jpb.inheritIO().
Bug 2: Background-job log relay drops messages after the spawning task ends
Even after Bug 1 is fixed, LoggedOutput routes the forked process's stdout into the background ManagedLogger, whose appenders include a shared RelayAppender. The relay calls CommandExchange.logMessage(event), which uses isChannelOwner(c) to pick the target channel.
Closes#4399
- Add subscribeToAll to InitializeOption (protocol); default true for backward compatibility.
- CommandExchange: send broadcast notifyEvent/logMessage only to channels with subscribeToAll.
- TestServer: support subscribeToAll parameter for tests; AbstractServerTest: subscribeToAllForTest.
- ClientSubscriptionTest: assert default client receives build/logMessage when command runs.
- Scripted test server/client-subscription: run show name to exercise server client path.
Summary
- Adds support for passing JVM arguments inline to `run`, `runMain`, `bgRun`, `bgRunMain`, and `fgRun`/`fgRunMain` using `--` as a delimiter
- Syntax: `run <jvmArgs> -- <appArgs>` (e.g., `run -Xmx2G -Dapp.mode=debug -- arg1 arg2`)
- Fully backward compatible — without `--`, all arguments are treated as app args as before
- When `fork` is `false`, a warning is logged that JVM arguments will be ignored