* [2.x] test: Pin what a platform axis alone does
**Problem**
`jsPlatform` enables the Scala.js plugin; the axis does not. A row
built by `customRow` with `VirtualAxis.js` is a Scala.js row by every
name it carries and a JVM row by what runs.
**Solution**
Assert what such a row reports as its platform.
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
* [2.x] feat: Enable JS/Native plugins if the axis is used
Only jsPlatform and nativePlatform enabled the plugin their platform
needs, so using customRow directly would skip it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
* [2.x] feat: Configure the rows a matrix builds
A build could reach every row with configure, or one row with the call
that made it, and nothing in between, so settings for the rows of one
platform travelled with every call that creates them. Now
configureRows takes a function of a row's axes and answers with what
to apply, and a row is given it as it is built, so a row declared
after the call is covered too.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
**Problem**
The clean task no longer works as intended on sbt 2.x due to the disk cache.
**Solution**
This implements an in-memory state of invalidation for subproject and
configuration axes. So clean task would mark subprojects to be
invalidated regardless of the disk cache state.
The invalidation state would be lifted once any cached task executes,
so it's not technically same as sbt 1.x, but the UX should be similar.
LogExchange.unbindLoggerAppenders calls clearAppenders, which drops and closes every appender on the logger. So if you bind an appender to the global logger and then unbind it, sbt's own console and backing appenders go with it and nothing prints for the rest of the session.
LoggerContext now has removeAppender(loggerName, appender), which removes that one instance by identity and closes it. LogExchange gets an unbindLoggerAppenders(name, appenders) overload on top of it, so unbinding mirrors binding. The existing single-arg version behaves as before, it just says in the scaladoc what it does now.
Task-local filters used in sources.all(filter).value currently produce generic Scala macro scope errors. Report one actionable diagnostic at the offending reference, explaining how to move the definition outside the task or use Def.taskDyn.
**Problem**
Currently bad resources are silently dropped.
**Solution**
1. Fix a bad resource generator in the build.
2. Fail the build when bad resources are found.
* fix: restart the sbt server when -D options change
* fix: don't restart on completions, wait for the socket
* fix: only trust -D options recorded for this build
* fix: confirm the server left before reporting it gone
* Update protocol/src/main/contraband/portfile.contra
Co-authored-by: eugene yokota <[email protected]>
* fix: record the server -D options as a list
* fix: record the -D options without their values
The connection file keeps the name of each option and a salted digest of it
rather than the option itself, and the comparison follows the JVM in taking the
last definition of a name. A client that is not allowed to restart the server
says which options it cannot pick up instead of staying quiet about them.
* fix: compare the -D options given before a command
A trailing -D option used to switch the whole comparison off, which dropped the
options written before it, and a shutdown request that never reached the server
waited the full timeout before saying so.
* fix: leave a server no client started alone
The connection file says whether its recorded options are the whole story, so a
server an editor keeps is warned about instead of shut down, while one the client
started with no options is still replaced. A restart that cannot reach the server
warns and carries on rather than failing the invocation.
* build: filter NetworkClient clinit in MiMa
---------
Co-authored-by: eugene yokota <[email protected]>
Follow-up to #9643. The verification added there re-read every downloaded blob right after writing it, and it sat on top of an older cost: DiskActionCacheStore fully re-hashes CAS files on every findBlobs/syncBlobs/getBlobs call, even though the file name is the digest. On a cache hit that meant hashing a whole classpath worth of blobs just to conclude nothing changed.
- Remote blobs (grpc downloads and inline data) are hashed while the stream is written to a staged temp file, then moved into the CAS, so verification costs no extra I/O and tampered bytes never land under the CAS name. Local puts are copied without hashing and are not stamped, so a mismatched local file is still caught by the lookup hash.
- Verified entries are remembered per process by (digest, size, mtime, fileKey), so repeated lookups are a stat instead of a full hash. Any external change to a CAS file invalidates the stamp and forces a re-hash. This is the same trade-off CacheImplicits already makes for file stamps.
**Problem**
Channels.newInputStream/newOutputStream share a channel-wide lock,
which would deadlock for a duplex communication.
**Solution**
This implements an alternative DuplexChannels functions that's capable
of duplex communication expected of a "socket".
This also duplicates the Java implementation to the worker app,
so we can use JDK domain socket for forked test communication.
**Problem**
Serializing an UpdateReport re-reads every artifact through sjson-new's
File iso to recompute its sha256, so every ActionCache key computation
and Tracked write-back re-reads the whole dependency classpath on every
invocation, including no-ops.
**Solution**
Override the iso with a hash memoized by (path, size, lastModified),
with the same invalidation semantics as the digest cache in sbt/sbt#8363,
and mix it into the generated codec via contrabandCodecParents.
Generated-by: Claude Fable 5
**Problem**
Some custom LSP calls do not check initialize-handshake,
which over TCP includes token-based authentication.
**Solution**
This adds checkAuthenticated check around sbt/exec etc.
**Problem**
There's a race condition between per-byte readSystemIn notification
and one-byte-read thread lifecycle.
Note: One-byte-read thread was introduced as a solution to the problem
that switching the terminal between raw and canonical mode cannot happen
if it's blocked by read.
**Solution**
This eliminates the thread lifecycle issue by keeping the thread alive
throughout the lifecycle of sbtn itself.
read is still called on demand by the server readSystemIn notification.
The cached-task macro allocated one mutable slot per syntactic
Def.declareOutput / Def.declareOutputDirectory call site and snapshotted the
slots into the task's outputs after the body ran. A call inside a loop or .map
over a runtime-determined list is a single syntactic site executed many times,
so each iteration overwrote the same slot and only the last file was cached and
restored on a cache hit. There was also no way for a conditional call site that
did not execute to stay out of the outputs: its slot remained null.
Declared outputs now accumulate in a per-task ListBuffer: the macro emits one
buffer at the top of the cached body and rewrites each call site to
ActionCache.registerOutput(vf, buffer), which appends and returns the value.
Every execution registers, an unexecuted site contributes nothing, and the
static multi-site shape is unchanged.
Refs #9462 (the declareOutput-in-a-loop half)
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Co-authored-by: eugene yokota <[email protected]>
**Problem**
Global plugin loading doesn't work.
**Solution**
1. Use ModuleID from to supply the location of global-plugin module.
2. Update pluginData with the global plugin classpath.
**Problem**
Test is based on https://github.com/sbt/sbt/issues/9345#issuecomment-4718229113 which gives us the following sequence:
1. Metals sends buildTarget/compile.
2. sbt publishes real non-empty diagnostics.
3. Metals sends buildTarget/scalaMainClasses.
4. During that request, sbt emits build/publishDiagnostics with diagnostics: [] and reset: true.
5. The following build/taskFinish still reports errors: 1.
Previously, errors for diagnostics reporting via bsp were collected from a live compilation run. In the sequence above, that is triggered by buildTarget/compile. Then, buildTarget/scalaMainClasses does not trigger such a run for the second time, it uses the cached compilation result. Therefore, the diagnostics is not populated.
**Solution**
The proposed fix modifies sendFailureReport to accept an optional CompileFailed object that contains the diagnostics even in case the actual compiler did not run because the cached result was used. If no problems were found for a file via default means, this CompileFailed object is queried to see if it has any information about problems in a given file.
**Problem**
BatchUpdateBlobsRequest suffers from gRPC's message size limitation.
**Solution**
For larger files, we switch to using the ByteStream API, chunked to 1MB at a time.