diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala b/lm-core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala index 9614d6ba4..000f96354 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala @@ -112,6 +112,25 @@ private[librarymanagement] abstract class ArtifactFunctions { base + "-" + module.revision + classifierStr + "." + artifact.extension } + /** + * Like `artifactName`, but omits the module's version. + */ + def internalArtifactName( + scalaVersion: ScalaVersion, + module: ModuleID, + artifact: Artifact + ): String = + import artifact.* + val classifierStr = classifier match + case None => "" + case Some(c) => s"-${c}" + val cross = CrossVersion(module.crossVersion, scalaVersion.full, scalaVersion.binary) + val withPlatform = module.crossVersion match + case _: Disabled => artifact.name + case _ => CrossVersion.addPlatformSuffix(artifact.name, module.platformOpt, None) + val base = CrossVersion.applyCross(withPlatform, cross) + s"${base}${classifierStr}.${artifact.extension}" + val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType) @deprecated("Configuration should not be decided from the classifier.", "1.0") def classifierConf(classifier: String): Configuration = diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 7c91be7eb..8c83bdd8b 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1770,6 +1770,8 @@ object Defaults extends BuildCommon with DefExtra { packageTaskSettings(packageBin, packageBinMappings) ++ packageTaskSettings(packageSrc, packageSrcMappings) ++ packageTaskSettings(packageDoc, packageDocMappings) ++ + packageTaskSettings(packageInternal, packageBinMappings) ++ + inTask(packageInternal)(Seq(artifactName :== Artifact.internalArtifactName)) ++ Seq(Keys.`package` := packageBin.value) def packageBinMappings: Initialize[Task[Seq[(HashedVirtualFileRef, String)]]] = diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 0f16c5fca..267258dbe 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -324,6 +324,7 @@ object Keys { // package keys val packageBin = taskKey[HashedVirtualFileRef]("Produces a main artifact, such as a binary jar.").withRank(ATask) + val packageInternal = taskKey[HashedVirtualFileRef]("Produces a binary JAR for internal use (inter-project classpaths, BSP).").withRank(DTask) val `package` = taskKey[HashedVirtualFileRef]("Produces the main artifact, such as a binary jar. This is typically an alias for the task that actually does the packaging.").withRank(APlusTask) val packageDoc = taskKey[HashedVirtualFileRef]("Produces a documentation artifact, such as a jar containing API documentation.").withRank(AMinusTask) val packageSrc = taskKey[HashedVirtualFileRef]("Produces a source artifact, such as a jar containing sources and resources.").withRank(AMinusTask) diff --git a/main/src/main/scala/sbt/internal/ClasspathImpl.scala b/main/src/main/scala/sbt/internal/ClasspathImpl.scala index d5dbeeb0a..bc05394fb 100644 --- a/main/src/main/scala/sbt/internal/ClasspathImpl.scala +++ b/main/src/main/scala/sbt/internal/ClasspathImpl.scala @@ -132,7 +132,7 @@ private[sbt] object ClasspathImpl { (Def .task { val converter = fileConverter.value - val vf = (packageBin / artifactPath).value + val vf = (packageInternal / artifactPath).value val jar = converter.toPath(vf) (TrackLevel.intersection(track, exportToInternal.value), vf, jar) }) @@ -141,13 +141,13 @@ private[sbt] object ClasspathImpl { Def.task { val converter = fileConverter.value val analysisFile = converter.toVirtualFile(compileAnalysisFile.value.toPath) - Seq((packageBin.value, analysisFile)) + Seq((packageInternal.value, analysisFile)) } case (TrackLevel.TrackIfMissing, _, jar) if !jar.toFile().exists => Def.task { val converter = fileConverter.value val analysisFile = converter.toVirtualFile(compileAnalysisFile.value.toPath) - Seq((packageBin.value, analysisFile)) + Seq((packageInternal.value, analysisFile)) } case (_, vf, _) => Def.task { diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index 24d43b197..0d9effba8 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -824,7 +824,7 @@ object BuildServerProtocol { val internalDependencyClasspath = for { (ref, configs) <- bspInternalDependencyConfigurations.value config <- configs - } yield ref / config / Keys.packageBin + } yield ref / config / Keys.packageInternal ( target, scalacOptions, diff --git a/sbt-app/src/sbt-test/cache/export-jars-version-bump/app/App.scala b/sbt-app/src/sbt-test/cache/export-jars-version-bump/app/App.scala new file mode 100644 index 000000000..d1663b703 --- /dev/null +++ b/sbt-app/src/sbt-test/cache/export-jars-version-bump/app/App.scala @@ -0,0 +1,5 @@ +package example + +@main +def main(args: String*): Unit = + println(Foo.greeting) diff --git a/sbt-app/src/sbt-test/cache/export-jars-version-bump/build.sbt b/sbt-app/src/sbt-test/cache/export-jars-version-bump/build.sbt new file mode 100644 index 000000000..23f956a2d --- /dev/null +++ b/sbt-app/src/sbt-test/cache/export-jars-version-bump/build.sbt @@ -0,0 +1,27 @@ +import sbt.internal.util.CacheEventSummary +import complete.DefaultParsers.* + +Global / localCacheDirectory := baseDirectory.value / "diskcache" + +scalaVersion := "3.8.4" + +lazy val checkMiss = inputKey[Unit]("Assert the exact onsite/miss count of the previous run") + +lazy val foo = project + +lazy val app = project.dependsOn(foo) + +lazy val root = (project in file(".")) + .aggregate(foo, app) + .settings( + checkMiss := { + val expected: Int = (Space ~> NatBasic).parsed + val s = streams.value + val config = Def.cacheConfiguration.value + val prev = config.cacheEventLog.previous match + case d: CacheEventSummary.Data => d + case CacheEventSummary.Empty => sys.error("empty event log") + s.log.info(s"missCount = ${prev.missCount}") + assert(prev.missCount == expected, s"prev.missCount = ${prev.missCount} (expected $expected)") + } + ) diff --git a/sbt-app/src/sbt-test/cache/export-jars-version-bump/foo/Foo.scala b/sbt-app/src/sbt-test/cache/export-jars-version-bump/foo/Foo.scala new file mode 100644 index 000000000..b0be7dc85 --- /dev/null +++ b/sbt-app/src/sbt-test/cache/export-jars-version-bump/foo/Foo.scala @@ -0,0 +1,5 @@ +package example + +object Foo: + def greeting: String = "hello" +end Foo diff --git a/sbt-app/src/sbt-test/cache/export-jars-version-bump/test b/sbt-app/src/sbt-test/cache/export-jars-version-bump/test new file mode 100644 index 000000000..b2ca3abe6 --- /dev/null +++ b/sbt-app/src/sbt-test/cache/export-jars-version-bump/test @@ -0,0 +1,14 @@ +# warm the disk cache +> app/compile + +# after a clean, recompiling app (and foo) should fully restore from the disk cache +> clean +> app/compile +> checkMiss 0 + +# bumping foo's version alone (e.g. what sbt-dynver would do on a new commit) must not +# invalidate app's compile cache entry, since foo's jar content hasn't changed +> set foo / version := "0.2.0" +> clean +> app/compile +> checkMiss 0