From 01e3d9e9d86158a36855cc55b25bb0a17d24ee7e Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 21 Mar 2021 20:50:03 -0400 Subject: [PATCH] Use LOCALAPPDATA or $HOME/AppData/Local on Windows Fixes https://github.com/sbt/sbt/issues/5206 Problem -------- Coursier uses directories-jvm to determine its default cache directory. Currently directories-jvm shells out to Powershell to call the [Known Folders API](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid), which doesn't work in various environments, and instead of an error, it apparently returns `null/Coursier/cache` as the directory name. Solution -------- With due respect to the heroic effort directories-jvm is making to comply to the directory standards on all operating systems, including that of Microsoft, I don't think the majority of the sbt users care exactly where that directory is as long as it is well-documented, and calculated in a fast and predictable way. Instead of shelling out to Powershell, or using JNI, to hit the Known Folders API, I propose we first look at `LOCALAPPDATA` environment variable. When it is not found, it will fall back to `$HOME/AppData/Local`. Per discussion in https://github.com/dirs-dev/directories-jvm/issues/43, `LOCALAPPDATA` environment variable may NOT represent the one-true Known Folders API value of the AppData directory in case the user happened to have set the `LOCALAPPDATA` environmental variable. For the purpose of picking a directory for Coursier cache, I don't find that to be a problem because it will be faster, more reliable, and predictable. --- .../scala/sbt/coursierint/LMCoursier.scala | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/main/src/main/scala/sbt/coursierint/LMCoursier.scala b/main/src/main/scala/sbt/coursierint/LMCoursier.scala index 7ea805ce4..36d7cf524 100644 --- a/main/src/main/scala/sbt/coursierint/LMCoursier.scala +++ b/main/src/main/scala/sbt/coursierint/LMCoursier.scala @@ -22,6 +22,7 @@ import lmcoursier.definitions.{ import lmcoursier._ import lmcoursier.credentials.Credentials import Keys._ +import sbt.internal.util.Util import sbt.librarymanagement._ import sbt.librarymanagement.ivy.{ Credentials => IvyCredentials, @@ -37,12 +38,36 @@ object LMCoursier { private[this] val credentialRegistry: ConcurrentHashMap[(String, String), IvyCredentials] = new ConcurrentHashMap - def defaultCacheLocation: File = - sys.props.get("sbt.coursier.home") match { - case Some(home) => new File(home).getAbsoluteFile / "cache" - case _ => - CoursierDependencyResolution.defaultCacheLocation + def defaultCacheLocation: File = { + def absoluteFile(path: String): File = new File(path).getAbsoluteFile() + def windowsCacheDirectory: File = { + // Per discussion in https://github.com/dirs-dev/directories-jvm/issues/43, + // LOCALAPPDATA environment variable may NOT represent the one-true + // Known Folders API (https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid) + // in case the user happened to have set the LOCALAPPDATA environmental variable. + // Given that there's no reliable way of accessing this API from JVM, I think it's actually + // better to use the LOCALAPPDATA as the first place to look. + // When it is not found, it will fall back to $HOME/AppData/Local. + // For the purpose of picking the Coursier cache directory, it's better to be + // fast, reliable, and predictable rather than strict adherence to Microsoft. + val base = + sys.env + .get("LOCALAPPDATA") + .map(absoluteFile) + .getOrElse(absoluteFile(sys.props("user.home")) / "AppData" / "Local") + base / "Coursier" / "Cache" / "v1" } + sys.props + .get("sbt.coursier.home") + .map(home => absoluteFile(home) / "cache") + .orElse(sys.env.get("COURSIER_CACHE").map(absoluteFile)) + .orElse(sys.props.get("coursier.cache").map(absoluteFile)) match { + case Some(dir) => dir + case _ => + if (Util.isWindows) windowsCacheDirectory + else CoursierDependencyResolution.defaultCacheLocation + } + } def relaxedForAllModules: Seq[(ModuleMatchers, Reconciliation)] = Vector((ModuleMatchers.all, Reconciliation.Relaxed))