Use `Global / localCacheDirectory` for remote caching

Fixes https://github.com/sbt/sbt/issues/6102

https://github.com/sbt/sbt/pull/6026 changed the implementation of remote cache to NOT use dependency resolution (Coursier), and directly use Ivy resolver for efficiency. This was good, but when I made the change, I've changed the cache directory to be `crossTarget.value / "remote-cache"`. This was ok for local testing purpose, but not great for real usage since we don't want the cache to be wiped out either in the CI machines or on a local laptop.

This adds a new Global key called `localCacheDirectory`. Similar to Coursier cache, this is meant to be shared across all builds running on a machine. Also similar to Coursier cache this will try to follow the operating system specifc caching directory.

### localCacheDirectory location

- Environment variable: `SBT_LOCAL_CACHE`
- System property: `sbt.global.localcache`
- Windows: %LOCALAPPDATA%\sbt\v1
- macOS: $HOME/Library/Caches/sbt/v1
- Linux: $HOME/.cache/sbt/v1
This commit is contained in:
Eugene Yokota 2020-11-22 14:35:20 -05:00
parent 5b55ef1ba3
commit bd7a2e05bf
4 changed files with 54 additions and 3 deletions

View File

@ -47,6 +47,9 @@ object Util {
def ignoreResult[T](f: => T): Unit = macro Macro.ignore
lazy val isMac: Boolean =
System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("mac")
lazy val isWindows: Boolean =
System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows")

View File

@ -388,6 +388,7 @@ object Keys {
val pushRemoteCacheTo = settingKey[Option[Resolver]]("The resolver to publish remote cache to.")
val remoteCacheResolvers = settingKey[Seq[Resolver]]("Resolvers for remote cache.")
val remoteCachePom = taskKey[File]("Generates a pom for publishing when publishing Maven-style.")
val localCacheDirectory = settingKey[File]("Directory to pull the remote cache to.")
val usePipelining = settingKey[Boolean]("Use subproject pipelining for compilation.").withRank(BSetting)
val exportPipelining = settingKey[Boolean]("Product early output so downstream subprojects can do pipelining.").withRank(BSetting)

View File

@ -50,10 +50,19 @@ object RemoteCache {
.toList
.map(_.take(commitLength))
lazy val defaultCacheLocation: File = SysProp.globalLocalCache
lazy val globalSettings: Seq[Def.Setting[_]] = Seq(
remoteCacheId := "",
remoteCacheIdCandidates := Nil,
pushRemoteCacheTo :== None
pushRemoteCacheTo :== None,
localCacheDirectory :== defaultCacheLocation,
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, localCacheDirectory.value),
},
)
lazy val projectSettings: Seq[Def.Setting[_]] = (Seq(
@ -114,7 +123,7 @@ object RemoteCache {
remoteCacheResolvers := pushRemoteCacheTo.value.toVector,
) ++ inTask(pushRemoteCache)(
Seq(
ivyPaths := IvyPaths(baseDirectory.value, crossTarget.value / "remote-cache"),
ivyPaths := (Scope.Global / pushRemoteCache / ivyPaths).value,
ivyConfiguration := {
val config0 = Classpaths.mkIvyConfiguration.value
config0

View File

@ -8,13 +8,15 @@
package sbt
package internal
import java.io.File
import java.util.Locale
import scala.util.control.NonFatal
import scala.concurrent.duration._
import sbt.internal.util.{ Terminal => ITerminal }
import sbt.internal.util.{ Terminal => ITerminal, Util }
import sbt.internal.util.complete.SizeParser
import sbt.nio.Keys._
import sbt.io.syntax._
// See also BuildPaths.scala
// See also LineReader.scala
@ -176,4 +178,40 @@ object SysProp {
}
}
private[this] def file(value: String): File = new File(value)
private[this] def home: File = file(sys.props("user.home"))
/** Operating system specific cache directory, similar to Coursier cache.
*/
def globalLocalCache: File = {
val appName = "sbt"
def propCacheDir: Option[File] = sys.props.get("sbt.global.localcache").map(file)
def propCacheDir2: Option[File] =
sys.props.get(BuildPaths.GlobalBaseProperty) match {
case Some(base) => Some(file(base) / "cache")
case _ => None
}
def envCacheDir: Option[File] = sys.env.get("SBT_LOCAL_CACHE").map(file)
def windowsCacheDir: Option[File] =
sys.env.get("LOCALAPPDATA") match {
case Some(app) if Util.isWindows => Some(file(app) / appName)
case _ => None
}
def macCacheDir: Option[File] =
if (Util.isMac) Some(home / "Library" / "Caches" / appName)
else None
def linuxCache: File =
sys.env.get("XDG_CACHE_HOME") match {
case Some(cache) => file(cache) / appName
case _ => home / ".cache" / appName
}
def baseCache: File =
propCacheDir
.orElse(propCacheDir2)
.orElse(envCacheDir)
.orElse(windowsCacheDir)
.orElse(macCacheDir)
.getOrElse(linuxCache)
baseCache.getAbsoluteFile / "v1"
}
}