* always create symlinks to the cache in the target locations, even if the digest matches #8445
* create a test (currently failing even on #develop) that fails because if `zipPath` in `sbt.util.ActionCache.packageDirectory` is a symlink to the CAS, in later calls, this path in the CAS gets overridden by the new sources.
- in this test, after "run 1" in line 15, the produced file "target/out/jvm/scala-3.7.4/a/classes.sbtdir.zip" is a symlink to the CAS, let's call it SH1.
- when "run 3" is executed, `IO.zip` saves the new value to `zipPath`, which is "target/out/jvm/scala-3.7.4/a/classes.sbtdir.zip -> SH1", so SH1 gets overridden.
- when the last "run 1" is executed, the cache retrieves SH1, but it contains the data from "run 3" (the test fails with "actual A.x is 3").
* when packaging a directory into a zip, use a temp directory to avoid overwriting the cache #8461
This adds a new setting that allows users to
configure Coursier's FileCache to cache local file:// artifacts. When enabled,
artifacts from local repositories are copied to the cache directory, which is
useful for scenarios like bundling compiler artifacts in a local repo for
offline use.
Fixes#7547
**Problem**
When cross-building sbt plugins with explicit `scalaVersion` set in `build.sbt`, the `updateSbtClassifiers` task fails because it uses the launcher's Scala version instead of the appropriate Scala version for the target sbt version.
For example, with this configuration:
```scala
lazy val root = (project in file("."))
.enablePlugins(SbtPlugin)
.settings(
scalaVersion := "2.10.7", // Explicit for IDE support
crossSbtVersions := Seq("0.13.17", "1.0.0"),
)
```
Running sbt "show updateSbtClassifiers" would fail with:
Error downloading org.scala-sbt:scripted-plugin_2.12:0.13.17
It should look for scripted-plugin_2.10:0.13.17 since sbt 0.13.x uses Scala 2.10.
**Solution**
Modified sbtClassifiersTasks in Defaults.scala to:
1. Check if the project is an sbt plugin (sbtPlugin.value)
2. For plugins, derive the Scala version from pluginCrossBuild / sbtBinaryVersion using PluginCross.scalaVersionFromSbtBinaryVersion
3. For non-plugins, continue using the launcher's Scala version (original behavior)
**Problem**
When project A's dependencies changed and A was compiled in one command, project B (depending on A) would not invalidate its update cache in a subsequent command. This caused stale classpaths.
The root cause was that `depsUpdated` only checked `!stats.cached`, which only detected fresh resolves within the same command. When a dependency was served from cache (even if resolved fresh in a previous command), `cached` was marked `true`, causing incorrect cache reuse.
Debug scenario from issue:
sbt:aaa> clean
sbt:aaa> a/compile
sbt:aaa> show itTests/depsUpdated
[info] * false <-- BUG: should be true
**Solution**
Added `stamp: String` field to `UpdateStats` that records when the update was resolved. This stamp persists across commands and enables accurate cross-command comparison:
- If any dependency's stamp > our cached stamp, we re-resolve
- Falls back to `!cached` check for backwards compatibility with old caches
- Using `String` type allows future transition from timestamps to content hashes
Eviction warnings and errors were not reported for Test dependencies because
`EvictionError` only checked the Compile configuration.
This fix runs the eviction check for both Compile and Test configurations
separately, with distinct error messages:
- Compile: `found version conflict(s) in library dependencies`
- Test: `found version conflict(s) in Test dependencies`
**Problem**
There's a disconnect between what is perceived to be the current
Scala version, and what sbt uses internally, and thus what it
chooses to be the default scalaVersion.
**Solution**
This displays a warning if scalaVersion setting is missing.
**Problem**
There's a disconnect between what is perceived to be the current
Scala version, and what sbt uses internally, and thus what it
chooses to be the default scalaVersion.
**Solution**
This displays a warning if scalaVersion setting is missing.
**Problem**
sbt 2.x uses crossTarget by default, but it contains Scala version
even when crossPaths is false.
**Solution**
This replaces it with the letter "u" for unspecified.
**Problem**
Compiler bridge resolution calls out to Coursier,
and it shows up on the profiler.
**Solution**
This uses sbt 2.x caching to cache the prebuilt compiler bridge binaries
to avoid calling Coursier from second time onwards.
**Problem**
sbt currently does not support JDK 25 Main class. JDK 25 supports:
1. non-public main method
2. doesn't need Array[String] arg
3. doesn't have to be a static method
**Solution**
This updates Zinc, which supports new Main class detection.
In addition, this implements in-process run emulation support.
**Problem**
For sbt 1.x, the user is forced to pick between having a stable ID for the root project,
or having the automatic aggregation of all subprojects.
The problem becomes more pronounced for large build that frequent add/remove subprojects.
**Solution**
This implements `.autoAggregate` method on `Project`, which is implemented as
`this.aggregate(LocalAggregate)`.
At the loading time, we can automatically expand `LocalAggregate` to a list of subproject references,
after we discover all subprojects.
The `autoAggregate` will use the base directory of the subproject to pick the parent-child
relationship. For example, a root project would aggregate all subprojects,
but `bar` might aggregate only `bar/bar1` and `bar/bar2`.
**Problem**
Forked tests apparently incorrectly returns success if an exception is thrown
on JDK 17 and up, due to exception failing to persist.
**Solution**
This adds custom codec for Throwable to workaround this issue.