Remove sbt 1.x remote cache imple

This commit is contained in:
Eugene Yokota 2025-11-23 22:35:35 -05:00
parent 9bfc80c65c
commit 77e74871ad
14 changed files with 31 additions and 704 deletions

View File

@ -708,6 +708,33 @@ lazy val mainProj = (project in file("main"))
mimaSettings,
mimaBinaryIssueFilters ++= Vector(
exclude[DirectMissingMethodProblem]("sbt.Keys.scalaCompilerBridgeBinaryJar"),
exclude[DirectMissingMethodProblem]("sbt.Keys.packageCache"),
exclude[DirectMissingMethodProblem]("sbt.Keys.pullRemoteCache"),
exclude[DirectMissingMethodProblem]("sbt.Keys.pushRemoteCache"),
exclude[DirectMissingMethodProblem]("sbt.Keys.pushRemoteCacheArtifact"),
exclude[DirectMissingMethodProblem]("sbt.Keys.pushRemoteCacheConfiguration"),
exclude[DirectMissingMethodProblem]("sbt.Keys.pushRemoteCacheTo"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheArtifact"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheArtifacts"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheId"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheIdCandidates"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCachePom"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheProjectId"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheResolvers"),
exclude[DirectMissingMethodProblem]("sbt.Keys.packageCache"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheId"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheProjectId"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheIdCandidates"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheArtifacts"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheArtifact"),
exclude[DirectMissingMethodProblem]("sbt.Keys.pullRemoteCache"),
exclude[DirectMissingMethodProblem]("sbt.Keys.pushRemoteCache"),
exclude[DirectMissingMethodProblem]("sbt.Keys.pushRemoteCacheArtifact"),
exclude[DirectMissingMethodProblem]("sbt.Keys.pushRemoteCacheConfiguration"),
exclude[DirectMissingMethodProblem]("sbt.Keys.pushRemoteCacheTo"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheResolvers"),
exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCachePom"),
exclude[DirectMissingMethodProblem]("sbt.internal.RemoteCache.*"),
),
)
.dependsOn(lmCore, lmIvy, lmCoursierShadedPublishing)

View File

@ -2775,7 +2775,7 @@ object Classpaths {
packagedDefaultArtifacts.value
} else Map.empty[Artifact, HashedVirtualFileRef]
}
) ++ RemoteCache.projectSettings
)
/**
* Produces the Maven-compatible artifacts of an sbt plugin.

View File

@ -23,7 +23,6 @@ import sbt.internal.bsp.*
import sbt.internal.inc.ScalaInstance
import sbt.internal.librarymanagement.{ CompatibilityWarningOptions, IvySbt }
import sbt.internal.librarymanagement.ivy.{ IvyConfiguration, UpdateOptions }
import sbt.internal.remotecache.RemoteCacheArtifact
import sbt.internal.server.BuildServerProtocol.BspFullWorkspace
import sbt.internal.server.{ BspCompileTask, BuildServerReporter, ServerHandler }
import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition }
@ -299,7 +298,6 @@ object Keys {
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)
val packageCache = taskKey[HashedVirtualFileRef]("Produces the main artifact for caching.")
val packageOptions = taskKey[Seq[PackageOption]]("Options for packaging.").withRank(BTask)
val packageTimestamp = settingKey[Option[Long]]("Overwrites timestamps in JAR file to make the build reproducible; None keeps the existing timestamps (useful for web resources)").withRank(CSetting)
@ -429,19 +427,6 @@ object Keys {
val internalDependencyConfigurations = settingKey[Seq[(ProjectRef, Set[String])]]("The project configurations that this configuration depends on")
val closeClassLoaders = settingKey[Boolean]("Close classloaders in run and test when the task completes.").withRank(DSetting)
val allowZombieClassLoaders = settingKey[Boolean]("Allow a classloader that has previously been closed by `run` or `test` to continue loading classes.")
// val useRemoteCache = settingKey[Boolean]("Use remote cache.")
val remoteCacheId = taskKey[String]("Unique identifier for the remote cache.")
val remoteCacheProjectId = taskKey[ModuleID]("ModuleID used for remote cache JARs.")
val remoteCacheIdCandidates = taskKey[Seq[String]]("Remote cache ids to pull.")
val remoteCacheArtifacts = taskKey[Seq[RemoteCacheArtifact]]("Remote cache artifact definitions.")
val remoteCacheArtifact = taskKey[RemoteCacheArtifact]("The remote cache artifact definition.")
val pullRemoteCache = taskKey[Unit]("Retrieve remote cache.")
val pushRemoteCache = taskKey[Unit]("Push remote cache to the cache server.")
val pushRemoteCacheArtifact = settingKey[Boolean]("Enables publishing an artifact to remote cache.")
val pushRemoteCacheConfiguration = taskKey[PublishConfiguration]("")
val pushRemoteCacheTo = settingKey[Option[Resolver]]("The resolver to publish remote cache to.")
val remoteCacheResolvers = settingKey[Seq[Resolver]]("Resolvers for remote cache.")
val remoteCachePom = taskKey[HashedVirtualFileRef]("Generates a pom for publishing when publishing Maven-style.")
val localCacheDirectory = settingKey[File]("Operating system specific cache directory.")
val localDigestCacheByteSize = SettingKey[Long](BasicKeys.localDigestCacheByteSize).withRank(DSetting)
val usePipelining = settingKey[Boolean]("Use subproject pipelining for compilation.").withRank(BSetting)

View File

@ -12,47 +12,12 @@ package internal
import java.io.File
import java.nio.file.Path
import org.apache.ivy.core.module.descriptor.{ DefaultArtifact, Artifact as IArtifact }
import org.apache.ivy.core.report.DownloadStatus
import org.apache.ivy.core.resolve.DownloadOptions
import org.apache.ivy.plugins.resolver.DependencyResolver
import sbt.Defaults.prefix
import sbt.Keys.*
import sbt.Project.{ inConfig as _, * }
import sbt.ProjectExtra.*
import sbt.ScopeFilter.Make.*
import sbt.ScopeAxis.Select
import sbt.SlashSyntax0.*
import sbt.coursierint.LMCoursier
import sbt.internal.inc.{ HashUtil, JarUtils }
import sbt.internal.librarymanagement.*
import sbt.internal.remotecache.*
import sbt.io.IO
import sbt.io.syntax.*
import sbt.librarymanagement.*
import sbt.internal.librarymanagement.ivy.{ IvyCredentials, UpdateOptions }
import sbt.librarymanagement.syntax.*
import sbt.nio.FileStamp
import sbt.nio.Keys.{ inputFileStamps, outputFileStamps }
import sbt.std.TaskExtra.*
import sbt.util.InterfaceUtil.toOption
import sbt.util.{ CacheImplicits, DiskActionCacheStore, Logger }
import sbt.util.CacheImplicits.given
import sbt.util.{ CacheImplicits, DiskActionCacheStore }
import sjsonnew.JsonFormat
import xsbti.{ FileConverter, HashedVirtualFileRef, VirtualFileRef }
import xsbti.compile.CompileAnalysis
import scala.collection.mutable
object RemoteCache {
final val cachedCompileClassifier = "cached-compile"
final val cachedTestClassifier = "cached-test"
final val commitLength = 10
// TODO: cap with caffeine
private[sbt] val analysisStore: mutable.Map[HashedVirtualFileRef, CompileAnalysis] =
mutable.Map.empty
object RemoteCache:
private[sbt] def artifactToStr(art: Artifact): String = {
import LibraryManagementCodec.given
import sjsonnew.support.scalajson.unsafe.*
@ -60,31 +25,11 @@ object RemoteCache {
CompactPrinter(Converter.toJsonUnsafe(art)(using format))
}
def gitCommitId: String =
scala.sys.process.Process("git rev-parse HEAD").!!.trim.take(commitLength)
def gitCommitIds(n: Int): List[String] =
scala.sys.process
.Process("git log -n " + n.toString + " --format=%H")
.!!
.linesIterator
.toList
.map(_.take(commitLength))
lazy val defaultCacheLocation: File = SysProp.globalLocalCache
lazy val globalSettings: Seq[Def.Setting[?]] = Seq(
remoteCacheId := "",
remoteCacheIdCandidates :== Nil,
pushRemoteCacheTo :== None,
localCacheDirectory :== defaultCacheLocation,
localDigestCacheByteSize :== CacheImplicits.defaultLocalDigestCacheByteSize,
pushRemoteCache / ivyPaths := {
val app = appConfiguration.value
val base = app.baseDirectory.getCanonicalFile
// base is used only to resolve relative paths, which should never happen
IvyPaths(base.toString, localCacheDirectory.value.toString)
},
rootOutputDirectory := {
appConfiguration.value.baseDirectory
.toPath()
@ -103,408 +48,4 @@ object RemoteCache {
remoteCacheTlsClientKey := SysProp.remoteCacheTlsClientKey,
remoteCacheHeaders := SysProp.remoteCacheHeaders,
)
lazy val projectSettings: Seq[Def.Setting[?]] = (Seq(
pushRemoteCache := ((Def
.task {
val arts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value
val configs = arts flatMap { art =>
art.packaged.scopedKey.scope match {
case Scope(_, Select(c), _, _) => Some(c)
case _ => None
}
}
ScopeFilter(configurations = inConfigurationsByKeys(configs*))
})
.flatMapTask { case filter =>
Def.task {
val _ = pushRemoteCache.all(filter).value
()
}
})
.value,
pullRemoteCache := ((Def
.task {
val arts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value
val configs = arts flatMap { art =>
art.packaged.scopedKey.scope match {
case Scope(_, Select(c), _, _) => Some(c)
case _ => None
}
}
ScopeFilter(configurations = inConfigurationsByKeys(configs*))
})
.flatMapTask { case filter =>
Def.task {
val _ = pullRemoteCache.all(filter).value
()
}
})
.value,
pushRemoteCacheConfiguration / remoteCacheArtifacts := Def.uncached {
enabledOnly(remoteCacheArtifact.toSettingKey, defaultArtifactTasks).apply(_.join).value
},
pushRemoteCacheConfiguration / publishMavenStyle := true,
Compile / packageCache / pushRemoteCacheArtifact := true,
Compile / packageCache / artifact := Artifact(moduleName.value, cachedCompileClassifier),
remoteCachePom / pushRemoteCacheArtifact := true,
remoteCachePom := Def.uncached {
val s = streams.value
val converter = fileConverter.value
val config = (remoteCachePom / makePomConfiguration).value
val publisher = Keys.publisher.value
publisher.makePomFile((pushRemoteCache / ivyModule).value, config, s.log)
converter.toVirtualFile(config.file.get.toPath)
},
remoteCachePom / artifactPath := {
Defaults.prefixArtifactPathSetting(makePom / artifact, "remote-cache").value
},
remoteCachePom / makePomConfiguration := {
val converter = fileConverter.value
val config = makePomConfiguration.value
val out = converter.toPath((remoteCachePom / artifactPath).value)
config.withFile(out.toFile())
},
remoteCachePom / remoteCacheArtifact := Def.uncached {
PomRemoteCacheArtifact((makePom / artifact).value, remoteCachePom)
},
remoteCacheResolvers := pushRemoteCacheTo.value.toVector,
) ++ inTask(pushRemoteCache)(
Seq(
ivyPaths := (Scope.Global / pushRemoteCache / ivyPaths).value,
ivyConfiguration := Def.uncached {
val config0 = Classpaths.mkIvyConfiguration.value
config0
.withResolvers(remoteCacheResolvers.value.toVector)
.withOtherResolvers(pushRemoteCacheTo.value.toVector)
.withResolutionCacheDir(crossTarget.value / "alt-resolution")
.withPaths(ivyPaths.value)
.withUpdateOptions(UpdateOptions().withGigahorse(true))
},
ivySbt := Def.uncached {
IvyCredentials.register(credentials.value, streams.value.log)
val config0 = ivyConfiguration.value
new IvySbt(config0)
},
)
) ++ inTask(pullRemoteCache)(
Seq(
dependencyResolution := Def.uncached(Defaults.dependencyResolutionTask.value),
csrConfiguration := Def.uncached {
val rs = pushRemoteCacheTo.value.toVector ++ remoteCacheResolvers.value.toVector
LMCoursier.scalaCompilerBridgeConfigurationTask.value
.withResolvers(rs)
}
)
) ++ inConfig(Compile)(configCacheSettings(compileArtifact(Compile, cachedCompileClassifier))))
def getResourceFilePaths() = Def.task {
val syncDir = crossTarget.value / (prefix(configuration.value.name) + "sync")
val file = syncDir / "copy-resource"
file
}
def configCacheSettings[A <: RemoteCacheArtifact](
cacheArtifactTask: Def.Initialize[Task[A]]
): Seq[Def.Setting[?]] =
inTask(packageCache)(
Seq(
(Defaults.TaskZero / packageCache) := Def.uncached {
val converter = fileConverter.value
val original = (Defaults.TaskZero / packageBin).value
val originalFile = converter.toPath(original)
val artp = artifactPath.value
val artpFile = converter.toPath(artp)
val af = compileAnalysisFile.value
IO.copyFile(originalFile.toFile(), artpFile.toFile())
// skip zip manipulation if the artp is a blank file
if (af.exists && artpFile.toFile().length() > 0) {
JarUtils.includeInJar(artpFile.toFile(), Vector(af -> s"META-INF/inc_compile.zip"))
}
val rf = getResourceFilePaths().value
if (rf.exists) {
JarUtils.includeInJar(artpFile.toFile(), Vector(rf -> s"META-INF/copy-resources.txt"))
}
// val testStream = (test / streams).?.value
// testStream foreach { s =>
// val sf = Defaults.succeededFile(s.cacheDirectory)
// if (sf.exists) {
// JarUtils.includeInJar(artp, Vector(sf -> s"META-INF/succeeded_tests"))
// }
// }
converter.toVirtualFile(artpFile)
},
pushRemoteCacheArtifact := true,
remoteCacheArtifact := Def.uncached(cacheArtifactTask.value),
packagedArtifact := Def.uncached(artifact.value -> packageCache.value),
artifactPath := Defaults.artifactPathSetting(artifact).value
)
) ++ inTask(pushRemoteCache)(
Seq(
moduleSettings := Def.uncached {
val smi = scalaModuleInfo.value
ModuleDescriptorConfiguration(remoteCacheProjectId.value, projectInfo.value)
.withScalaModuleInfo(smi)
},
(Defaults.TaskZero / pushRemoteCache) := (Def
.task {
val s = streams.value
val config = pushRemoteCacheConfiguration.value
val is = (pushRemoteCache / ivySbt).value
val m = new is.Module(moduleSettings.value)
IvyActions.publish(m, config, s.log)
}
.tag(Tags.Publish, Tags.Network))
.value,
)
) ++ Seq(
remoteCacheIdCandidates := List(remoteCacheId.value),
remoteCacheProjectId := Def.uncached {
val o = organization.value
val m = moduleName.value
val id = remoteCacheId.value
val c = (projectID / crossVersion).value
val v = toVersion(id)
ModuleID(o, m, v).cross(c)
},
remoteCacheId := Def.uncached {
val inputs = (unmanagedSources / inputFileStamps).value
val cp = (externalDependencyClasspath / outputFileStamps).?.value.getOrElse(Nil)
val extraInc = (extraIncOptions.value) flatMap { (k, v) =>
Vector(k, v)
}
combineHash(extractHash(inputs) ++ extractHash(cp) ++ extraInc)
},
pushRemoteCacheConfiguration := Def.uncached {
val converter = fileConverter.value
val artifacts =
(pushRemoteCacheConfiguration / packagedArtifacts).value.toVector.map { (a, vf) =>
a -> converter.toPath(vf).toFile
}
Classpaths.publishConfig(
(pushRemoteCacheConfiguration / publishMavenStyle).value,
Classpaths.deliverPattern(crossTarget.value),
if (isSnapshot.value) "integration" else "release",
ivyConfigurations.value.map(c => ConfigRef(c.name)).toVector,
artifacts,
(pushRemoteCacheConfiguration / checksums).value.toVector,
Classpaths.getPublishTo(pushRemoteCacheTo.value).name,
ivyLoggingLevel.value,
isSnapshot.value
)
},
pushRemoteCacheConfiguration / packagedArtifacts := Def.uncached(
(Def
.task { (pushRemoteCacheConfiguration / remoteCacheArtifacts).value })
.flatMapTask { case artifacts =>
artifacts
.map(a => a.packaged.map(file => (a.artifact, file)))
.join
.apply(_.join.map(_.toMap))
}
.value
),
pushRemoteCacheConfiguration / remoteCacheArtifacts := Def.uncached {
List((packageCache / remoteCacheArtifact).value)
},
pullRemoteCache := Def.uncached {
import scala.jdk.CollectionConverters.*
val log = streams.value.log
val r = remoteCacheResolvers.value.head
val p = remoteCacheProjectId.value
val ids = remoteCacheIdCandidates.value
val is = (pushRemoteCache / ivySbt).value
val m = new is.Module((pushRemoteCache / moduleSettings).value)
val smi = scalaModuleInfo.value
val artifacts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value
val nonPom = artifacts.filterNot(isPomArtifact).toVector
val copyResources = getResourceFilePaths().value
m.withModule(log) { case (ivy, md, _) =>
val resolver = ivy.getSettings.getResolver(r.name)
if (resolver eq null) sys.error(s"undefined resolver '${r.name}'")
val cross = CrossVersion(p, smi)
val crossf: String => String = cross.getOrElse(identity[String](_))
var found = false
ids foreach { (id: String) =>
val v = toVersion(id)
val modId = p.withRevision(v).withName(crossf(p.name))
val ivyId = IvySbt.toID(modId)
if (found) ()
else {
val rawa = nonPom map { _.artifact }
val seqa = CrossVersion.substituteCross(rawa, cross)
val as = seqa map { a =>
val extra = a.classifier match {
case Some(c) => Map("e:classifier" -> c)
case None => Map.empty
}
new DefaultArtifact(ivyId, null, a.name, a.`type`, a.extension, extra.asJava)
}
pullFromMavenRepo0(as, resolver, log) match {
case Right(xs0) =>
val jars = xs0.distinct
nonPom.foreach { art =>
val classifier = art.artifact.classifier
findJar(classifier, v, jars) match {
case Some(jar) =>
extractJar(art, jar, copyResources)
log.info(s"remote cache artifact extracted for $p $classifier")
case None =>
log.info(s"remote cache artifact not found for $p $classifier")
}
}
found = true
case Left(e) =>
log.info(s"remote cache not found for ${v}")
log.debug(e.getMessage)
}
}
}
()
}
},
)
def isPomArtifact(artifact: RemoteCacheArtifact): Boolean =
artifact match {
case _: PomRemoteCacheArtifact => true
case _ => false
}
def compileArtifact(
configuration: Configuration,
classifier: String
): Def.Initialize[Task[CompileRemoteCacheArtifact]] = Def.task {
CompileRemoteCacheArtifact(
Artifact(moduleName.value, classifier),
configuration / packageCache,
(configuration / classDirectory).value,
(configuration / compileAnalysisFile).value
)
}
private def toVersion(v: String): String = s"0.0.0-$v"
private lazy val doption = new DownloadOptions
private def pullFromMavenRepo0(
artifacts: Vector[IArtifact],
r: DependencyResolver,
log: Logger
): Either[Throwable, Vector[File]] = {
try {
val files = r.download(artifacts.toArray, doption).getArtifactsReports.toVector map {
report =>
if (report == null) sys.error(s"failed to download $artifacts: " + r.toString)
else
report.getDownloadStatus match {
case DownloadStatus.NO =>
val o = report.getArtifactOrigin
if (o.isLocal) {
val localFile = new File(o.getLocation)
if (!localFile.exists) sys.error(s"$localFile doesn't exist")
else localFile
} else report.getLocalFile
case DownloadStatus.SUCCESSFUL =>
report.getLocalFile
case DownloadStatus.FAILED =>
sys.error(s"failed to download $artifacts: " + r.toString)
}
}
Right(files)
} catch {
case e: Throwable => Left(e)
}
}
private def findJar(classifier: Option[String], ver: String, jars: Vector[File]): Option[File] = {
val suffix = classifier.fold(ver)(c => s"$ver-$c.jar")
jars.find(_.toString.endsWith(suffix))
}
private def extractJar(
cacheArtifact: RemoteCacheArtifact,
jar: File,
copyResources: File
): Unit =
cacheArtifact match {
case a: CompileRemoteCacheArtifact =>
extractCache(jar, a.extractDirectory, preserveLastModified = true) { output =>
extractAnalysis(output, a.analysisFile)
extractResourceList(output, copyResources)
}
case a: TestRemoteCacheArtifact =>
extractCache(jar, a.extractDirectory, preserveLastModified = true) { output =>
extractAnalysis(output, a.analysisFile)
extractTestResult(output, a.testResult)
}
case a: CustomRemoteCacheArtifact =>
extractCache(jar, a.extractDirectory, a.preserveLastModified)(_ => ())
case _ =>
()
}
private def extractCache(jar: File, output: File, preserveLastModified: Boolean)(
processOutput: File => Unit
): Unit = {
IO.delete(output)
IO.unzip(jar, output, preserveLastModified = preserveLastModified)
processOutput(output)
// preserve semanticdb dir
// https://github.com/scalameta/scalameta/blob/a7927ee8e012cfff/semanticdb/scalac/library/src/main/scala/scala/meta/internal/semanticdb/scalac/SemanticdbPaths.scala#L9
Option((output / "META-INF").listFiles).foreach(
_.iterator.filterNot(_.getName == "semanticdb").foreach(IO.delete)
)
}
private def extractAnalysis(output: File, analysisFile: File): Unit = {
val metaDir = output / "META-INF"
val expandedAnalysis = metaDir / "inc_compile.zip"
if (expandedAnalysis.exists) {
IO.move(expandedAnalysis, analysisFile)
}
}
private def extractResourceList(output: File, copyResources: File): Unit = {
val metaDir = output / "META-INF"
val extractedCopyResources = metaDir / "copy-resources.txt"
if (extractedCopyResources.exists) {
IO.move(extractedCopyResources, copyResources)
}
}
private def extractTestResult(output: File, testResult: File): Unit = {
// val expandedTestResult = output / "META-INF" / "succeeded_tests"
// if (expandedTestResult.exists) {
// IO.move(expandedTestResult, testResult)
// }
}
private def defaultArtifactTasks: Seq[TaskKey[HashedVirtualFileRef]] =
Seq(Compile / packageCache, Test / packageCache)
private def enabledOnly[A](
key: SettingKey[A],
pkgTasks: Seq[TaskKey[HashedVirtualFileRef]]
): Def.Initialize[Seq[A]] =
Classpaths
.forallIn(key, pkgTasks)
.zipWith(Classpaths.forallIn(pushRemoteCacheArtifact, pkgTasks))
.apply(_.zip(_).collect { case (a, true) => a })
private def extractHash(inputs: Seq[(Path, FileStamp)]): Vector[String] =
inputs.toVector map { case (_, stamp0) =>
toOption(stamp0.stamp.getHash).getOrElse("cafe")
}
private def combineHash(vs: Vector[String]): String = {
val hashValue = HashUtil.farmHash(vs.sorted.mkString("").getBytes("UTF-8"))
java.lang.Long.toHexString(hashValue)
}
}
end RemoteCache

View File

@ -43,7 +43,6 @@ object LintUnused {
onLoadMessage,
onUnload,
pollInterval,
pushRemoteCacheArtifact,
sbt.nio.Keys.outputFileStamper,
sbt.nio.Keys.watchTriggers,
serverConnectionType,

View File

@ -1,18 +0,0 @@
name := "my-project"
scalaVersion := "2.12.20"
semanticdbIncludeInJar := true
semanticdbEnabled := true
pushRemoteCacheTo := Some(
MavenCache("local-cache", (ThisBuild / baseDirectory).value / "remote-cache-semanticdb")
)
Compile / remoteCacheId := "fixed-id"
Compile / remoteCacheIdCandidates := Seq((Compile / remoteCacheId).value)
Test / remoteCacheId := "fixed-id"
Test / remoteCacheIdCandidates := Seq((Test / remoteCacheId).value)
Compile / pushRemoteCacheConfiguration := (Compile / pushRemoteCacheConfiguration).value.withOverwrite(true)
Test / pushRemoteCacheConfiguration := (Test / pushRemoteCacheConfiguration).value.withOverwrite(true)

View File

@ -1,48 +0,0 @@
> pushRemoteCache
# Generated sources, compiled classes
$ exists target/scala-2.12/classes/MyClass.class
$ exists target/scala-2.12/classes/MyClass$.class
$ exists target/scala-2.12/classes/META-INF/semanticdb/src/main/scala/MyClass.scala.semanticdb
$ exists target/scala-2.12/zinc/inc_compile_2.12.zip
$ exists target/scala-2.12/test-classes/MyTest.class
$ exists target/scala-2.12/test-classes/MyTest$.class
$ exists target/scala-2.12/test-classes/META-INF/semanticdb/src/test/scala/MyTest.scala.semanticdb
$ exists target/scala-2.12/test-zinc/inc_compile_2.12.zip
# Compile
$ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar
$ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar.md5
$ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar.sha1
# Test
$ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar
$ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar.md5
$ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar.sha1
> clean
$ absent target/scala-2.12/classes/MyClass.class
$ absent target/scala-2.12/classes/MyClass$.class
$ absent target/scala-2.12/classes/META-INF
$ absent target/scala-2.12/zinc/inc_compile_2.12.zip
$ absent target/scala-2.12/test-classes/MyTest.class
$ absent target/scala-2.12/test-classes/MyTest$.class
$ absent target/scala-2.12/test-classes/META-INF
$ absent target/scala-2.12/test-zinc/inc_compile_2.12.zip
> pullRemoteCache
# Files extracted from the cache artifacts
$ exists target/scala-2.12/classes/MyClass.class
$ exists target/scala-2.12/classes/MyClass$.class
$ exists target/scala-2.12/classes/META-INF/semanticdb/src/main/scala/MyClass.scala.semanticdb
$ exists target/scala-2.12/zinc/inc_compile_2.12.zip
$ exists target/scala-2.12/test-classes/MyTest.class
$ exists target/scala-2.12/test-classes/MyTest$.class
$ exists target/scala-2.12/test-classes/META-INF/semanticdb/src/test/scala/MyTest.scala.semanticdb
$ exists target/scala-2.12/test-zinc/inc_compile_2.12.zip
# Artifacts can be pushed twice (enabled overriding)
> pushRemoteCache

View File

@ -1,95 +0,0 @@
import sbt.internal.remotecache.CustomRemoteCacheArtifact
import sbt.internal.inc.Analysis
import complete.DefaultParsers._
lazy val CustomArtifact = config("custom-artifact")
// Reset compiler iterations, necessary because tests run in batch mode
val recordPreviousIterations = taskKey[Unit]("Record previous iterations.")
val checkIterations = inputKey[Unit]("Verifies the accumulated number of iterations of incremental compilation.")
ThisBuild / scalaVersion := "2.12.20"
ThisBuild / pushRemoteCacheTo := Some(
MavenCache("local-cache", (ThisBuild / baseDirectory).value / "r")
)
lazy val root = (project in file("."))
.configs(CustomArtifact)
.settings(
name := "my-project",
customArtifactSettings,
pushRemoteCacheConfiguration / remoteCacheArtifacts += (CustomArtifact / packageCache / remoteCacheArtifact).value,
Compile / pushRemoteCacheConfiguration := (Compile / pushRemoteCacheConfiguration).value.withOverwrite(true),
Test / pushRemoteCacheConfiguration := (Test / pushRemoteCacheConfiguration).value.withOverwrite(true),
Compile / sourceGenerators += Def.task {
val extractDirectory = (CustomArtifact / sourceManaged).value
val output = extractDirectory / "HelloWorld.scala"
IO.write(output, "class HelloWorld")
Seq(output)
}.taskValue,
// bring back fixed-id because JDK 8/11 would be different intentionally
Compile / remoteCacheId := "fixed-id",
Compile / remoteCacheIdCandidates := Seq((Compile / remoteCacheId).value),
Test / remoteCacheId := "fixed-id",
Test / remoteCacheIdCandidates := Seq((Test / remoteCacheId).value),
CustomArtifact / remoteCacheId := "fixed-id",
CustomArtifact / remoteCacheIdCandidates := Seq((CustomArtifact / remoteCacheId).value),
// test tasks
recordPreviousIterations := {
val log = streams.value.log
CompileState.previousIterations = {
val previousAnalysis = (Compile / previousCompile).value.analysis.asScala
previousAnalysis match {
case None =>
log.info("No previous analysis detected")
0
case Some(a: Analysis) => a.compilations.allCompilations.size
}
}
},
checkIterations := {
val expected: Int = (Space ~> NatBasic).parsed
val actual: Int = ((Compile / compile).value match { case a: Analysis => a.compilations.allCompilations.size }) - CompileState.previousIterations
assert(expected == actual, s"Expected $expected compilations, got $actual")
}
)
def customArtifactSettings: Seq[Def.Setting[_]] = {
val classifier = "custom-artifact"
def cachedArtifactTask = Def.task {
val art = (CustomArtifact / artifact).value
val packaged = CustomArtifact / packageCache
val extractDirectory = (CustomArtifact / sourceManaged).value
CustomRemoteCacheArtifact(art, packaged, extractDirectory, preserveLastModified = false)
}
inConfig(CustomArtifact)(
sbt.internal.RemoteCache.configCacheSettings(cachedArtifactTask) ++
Seq(
packageOptions := {
val n = name.value + "-" + classifier
val ver = version.value
val orgName = organizationName.value
List(Package.addSpecManifestAttributes(n, ver, orgName))
},
sourceManaged := (Compile / target).value / "custom-artifact-gen",
mappings := {
val sourcesDir = sourceManaged.value
val sources = sourcesDir.allPaths.pair(Path.relativeTo(sourcesDir))
sources
},
packageConfiguration := Defaults.packageConfigurationTask.value,
packageCache := Defaults.packageTask.value,
artifact := Artifact(moduleName.value, classifier),
packagedArtifact := (artifact.value -> packageCache.value),
artifactPath := Defaults.artifactPathSetting(artifact).value,
artifactName := Artifact.artifactName
)
)
}

View File

@ -1,56 +0,0 @@
> recordPreviousIterations
> compile
> pushRemoteCache
# Generated sources, compiled classes
$ exists target/custom-artifact-gen/HelloWorld.scala
$ exists target/scala-2.12/classes/HelloWorld.class
$ exists target/scala-2.12/classes/MyClass.class
$ exists target/scala-2.12/classes/MyClass$.class
$ exists target/scala-2.12/zinc/inc_compile_2.12.zip
$ exists target/scala-2.12/test-classes/MyTest.class
$ exists target/scala-2.12/test-classes/MyTest$.class
$ exists target/scala-2.12/test-zinc/inc_compile_2.12.zip
# Compile
$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar
$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar.md5
$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar.sha1
# Test
$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar
$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar.md5
$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar.sha1
# Custom artifact
$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-custom-artifact.jar
$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-custom-artifact.jar.md5
$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-custom-artifact.jar.sha1
> clean
$ absent target/custom-artifact-gen/HelloWorld.scala
$ absent target/scala-2.12/classes/HelloWorld.class
$ absent target/scala-2.12/classes/MyClass.class
$ absent target/scala-2.12/classes/MyClass$.class
$ absent target/scala-2.12/zinc/inc_compile_2.12.zip
$ absent target/scala-2.12/test-classes/MyTest.class
$ absent target/scala-2.12/test-classes/MyTest$.class
$ absent target/scala-2.12/test-zinc/inc_compile_2.12.zip
> pullRemoteCache
# Files extracted from the cache artifacts
$ exists target/custom-artifact-gen/HelloWorld.scala
$ exists target/scala-2.12/classes/HelloWorld.class
$ exists target/scala-2.12/classes/MyClass.class
$ exists target/scala-2.12/classes/MyClass$.class
$ exists target/scala-2.12/zinc/inc_compile_2.12.zip
$ exists target/scala-2.12/test-classes/MyTest.class
$ exists target/scala-2.12/test-classes/MyTest$.class
$ exists target/scala-2.12/test-zinc/inc_compile_2.12.zip
> checkIterations 1
# Artifacts can be pushed twice (enabled overriding)
> pushRemoteCache

View File

@ -1,4 +0,0 @@
// This is necessary because tests are run in batch mode
object CompileState {
@volatile var previousIterations: Int = -1
}