**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
The launcher enumerates the boot directory, so `AppProvider.mainClasspath` and `ScalaProvider.jars` come back
in filesystem order, and sbt 2 folds that classpath into the metabuild's `CompileInputs2`. Two machines with
identical inputs therefore compute different keys: measured on one project at the same commit, same sbt and
same JDK, 439 classpath entries on both sides, the same set, 83 of them at a different index and all under
${SBT_BOOT}.
Sorting happens after conversion to the rooted virtual path, so the order is independent of the filesystem
and of where the boot directory lives. The order being replaced is a directory listing, not one anybody
chose. `Main.rawEval` builds the same list but never reaches a cache key, so it is left alone.
Generated-by: Claude Opus 5 (Claude Code)
**Problem**
sbt 1 plugin variants published by sbt 2 omitted the sbtVersion,
scalaVersion, and extraDependencyAttributes POM properties. Without this
metadata, sbt 1 consumers could resolve direct and transitive versions of
the same plugin as separate modules.
**Solution**
Restore module extra properties and Ivy-compatible dependency metadata in
generic POM generation. Preserve multiple dependency records through XML
formatting and cover publication and eviction with unit and scripted tests.
Fixes#9805
Generated-by: OpenAI Codex
**Problem**
sbt 1 plugin variants published by sbt 2 used cross-suffixed Maven paths and files, but their generated POMs retained uncrossed module and plugin-dependency artifact IDs.
**Solution**
Restore the former Ivy cross-version transformation for the module and plugin dependencies before generic POM generation. Re-enable and modernize the scripted publication test.
Fixes#9803
Generated-by: OpenAI Codex
Every artifact went up under the plain -SNAPSHOT name with no maven-metadata.xml, so a repo with unique snapshot naming stamped each upload on its own. Remote Maven snapshot deploys now pick one timestamp and build number for the whole publication and send the metadata.
**Problem**
sbt compile through the thin client prints only elapsed time: 2 s, where the
same command in the shell or under sbt --server prints
elapsed time: 2 s, cache 94%, 13 disk cache hits, 24 remote cache hits. The
summary is computed server-side and thrown away: ManagedLogger.success is gated
on Terminal.isSuccessEnabled, and a NetworkChannel's terminal answered
interactive.get || isInWatch, which is false whenever a command is passed on
the command line. The line the user sees is the client's own, built from
wall-clock alone.
**Solution**
Report the line from the server, and have the client print only when the server
will not.
NetworkChannel's terminal reports isSuccessEnabled = true, so the server
writes the line on every channel, and it advertises successLog in its
capabilities. A server from before this sends no such key, which reads as false,
so an older client-server pair behaves as it does today; an unattached client
also always prints, since it renders build/logMessage rather than the proxied
systemOut bytes. Older sbtn keeps printing its own line and so reports
success twice until it updates.
Generated-by: Claude Opus 5
Claude-Session: https://claude.ai/code/session_015BqrfmHbJUph6Kx7gJn3G8
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
**Problem**
The client sent the handshake and ignored the answer: responsePlan had
no case for it, so an invalid token was dropped. The channel then stayed
unauthenticated and every later request was refused, with nothing saying
why.
**Solution**
Match the handshake response. On a refusal, read the token file again
and present what it names now, up to handshakeAttemptLimit times, then
report the refusal.
---------
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
The flag and the axes both say whether a row is built for Scala, and
where they disagree the flag wins: a row given versions and
autoScalaLibrary = false is built for none of them, and the versions
are dropped. Nothing said so.
ProjectMatrix: validate axes against autoScalaLibrary
---------
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
Problem: taskValue outside a task or setting macro reports an error naming value.
Solution: use a dedicated taskValue marker, preserve macro processing, and add diagnostic and behavioral regression tests.
**Problem**
With HTTP/2, Apache HttpClient can begin the authenticated replay while the rejected stream is still consuming the same repeatable file producer.
The overlapping consumption can send corrupted artifacts, according to mkurz.
**Solution**
This hopefully fixes the issue by updateing to Gigahorse 0.9.6, which fixes the preauth support.
* [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]>
* test: Pin the axes of a row that carries no Scala version
`jvmPlatform(autoScalaLibrary = false)` passes `VirtualAxis.jvm` to
`customRow`, which appends it again, so the row holds the platform axis
twice and its generated directories are named `scalajvm-jvm` and
`javajvm-jvm`. Nothing covered that path.
* fix: Keep one platform axis on a row that names its own
`jvmPlatform(autoScalaLibrary = false)` hands `customRow` the platform
axis, and `customRow` appended a second one, so the row's generated
directories were named `scalajvm-jvm` and `javajvm-jvm` and a cell
whose sources sat in the usual places compiled nothing. Now the axis is
appended only when the caller named no platform.
**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.
Two invocations that both find different -D options could interleave, the second
one connecting to the server the first had just started and shutting that down
mid-build. The restart now holds the connection file and reads it again once it
has it, so a replacement that already carries the right options is left alone.
**Problem**
Tags are enforced per task node.
This means that a chain like setup-test-cleanup
would release the tags midway, and allow interleaving
executions even for exclusive tests.
**Solution**
This implements a special Span tag, which lets us
keep the tags across the task chains.
Since this requires the flatMapped tasks to not carry their
own tags to prevent a deadlock, the feature needs to be opt-in /
internal implementation.
**Problem**
sbt fails to start on openSUSE, since OSTYPE=linux, not linux-gnu.
The startup script tries to add .ZIP extension, and fails to untar native client.
**Solution**
Modified startup script only check that OSTYPE starts with linux, instead of linux-gnu.
compileOrder and usePipelining were missing from CompileInputs2, so two builds differing only in those settings shared one cache entry and replayed each other's results. Adds both to the key, with a unit test on the hash and a scripted test that fails without the change.