mirror of https://github.com/sbt/sbt.git
[2.x] feat: XDG directory standard (#8769)
- Add SysProp.defaultGlobalBaseDirectory: uses SBT_CONFIG_HOME, XDG_CONFIG_HOME/sbt (Unix), LOCALAPPDATA/sbt (Windows), else ~/.sbt - BuildPaths.defaultGlobalBase delegates to SysProp for consistent default - sbt script: inject -Dsbt.global.base from XDG when not already set; getPreloaded() fallback uses SBT_CONFIG_HOME/XDG_CONFIG_HOME - sbt.bat: use LOCALAPPDATA/sbt when --sbt-dir not set - Tests: BuildPathsTest (property + absolute path), RunnerScriptTest (XDG)
This commit is contained in:
parent
2f14fd8bb1
commit
c6f67d706f
|
|
@ -122,6 +122,14 @@ object RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUtil:
|
|||
assert(out.contains[String]("-Dsbt.boot.directory=project/.boot"))
|
||||
assert(out.contains[String]("-Dsbt.ivy.home=project/.ivy"))
|
||||
|
||||
testOutput(
|
||||
"sbt with XDG_CONFIG_HOME set uses it for sbt.global.base (Closes #3681)",
|
||||
machineSbtoptsContents = "-v",
|
||||
windowsSupport = false,
|
||||
)("compile", "-v"): (out: List[String]) =>
|
||||
val hasGlobalBase = out.exists(s => s.contains("-Dsbt.global.base=") && s.contains("/sbt"))
|
||||
assert(hasGlobalBase, s"Expected -Dsbt.global.base=.../sbt in output (XDG): ${out.mkString(System.lineSeparator())}")
|
||||
|
||||
testOutput("accept `--ivy` in `SBT_OPTS`", sbtOpts = "--ivy /ivy/dir")("-v"):
|
||||
(out: List[String]) =>
|
||||
if (isWindows) cancel("Test not supported on windows")
|
||||
|
|
|
|||
|
|
@ -675,6 +675,8 @@ if defined sbt_args_sbt_version (
|
|||
|
||||
if defined sbt_args_sbt_dir (
|
||||
set _SBT_OPTS=-Dsbt.global.base=!sbt_args_sbt_dir! !_SBT_OPTS!
|
||||
) else if defined LOCALAPPDATA (
|
||||
set _SBT_OPTS=-Dsbt.global.base=!LOCALAPPDATA!\sbt !_SBT_OPTS!
|
||||
)
|
||||
|
||||
if defined sbt_args_sbt_boot (
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import java.io.File
|
|||
import java.util.Locale
|
||||
import KeyRanks.DSetting
|
||||
|
||||
import sbt.io.{ GlobFilter, Path }
|
||||
import sbt.io.GlobFilter
|
||||
import sbt.internal.util.AttributeKey
|
||||
|
||||
object BuildPaths {
|
||||
object BuildPaths:
|
||||
val globalBaseDirectory = AttributeKey[File](
|
||||
"global-base-directory",
|
||||
"The base directory for global sbt configuration and staging.",
|
||||
|
|
@ -106,7 +106,7 @@ object BuildPaths {
|
|||
}
|
||||
|
||||
def defaultVersionedGlobalBase(sbtVersion: String): File = defaultGlobalBase / sbtVersion
|
||||
def defaultGlobalBase = Path.userHome / ConfigDirectoryName
|
||||
def defaultGlobalBase: File = internal.SysProp.defaultGlobalBaseDirectory
|
||||
|
||||
private def binarySbtVersion(state: State): String =
|
||||
sbt.internal.librarymanagement.cross.CrossVersionUtil
|
||||
|
|
@ -145,4 +145,4 @@ object BuildPaths {
|
|||
|
||||
def crossPath(base: File, instance: xsbti.compile.ScalaInstance): File =
|
||||
base / ("scala_" + instance.version)
|
||||
}
|
||||
end BuildPaths
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import sbt.nio.Keys.*
|
|||
|
||||
// See also BuildPaths.scala
|
||||
// See also LineReader.scala
|
||||
object SysProp {
|
||||
object SysProp:
|
||||
def booleanOpt(name: String): Option[Boolean] =
|
||||
sys.props.get(name) match {
|
||||
case Some(x) => parseBoolean(x)
|
||||
|
|
@ -204,6 +204,25 @@ object SysProp {
|
|||
private def file(value: String): File = new File(value)
|
||||
private def home: File = file(sys.props("user.home"))
|
||||
|
||||
/**
|
||||
* Default directory for global sbt config (plugins, settings). Respects XDG Base Directory
|
||||
* and platform conventions: SBT_CONFIG_HOME, then XDG_CONFIG_HOME/sbt (Unix), then
|
||||
* LOCALAPPDATA/sbt (Windows), else user.home/.sbt.
|
||||
*/
|
||||
def defaultGlobalBaseDirectory: File =
|
||||
def fromEnv(name: String): Option[File] =
|
||||
sys.env.get(name).filter(_.nonEmpty).map(p => file(p.trim))
|
||||
val propBase =
|
||||
sys.props.get(BuildPaths.GlobalBaseProperty).filter(_.nonEmpty).map(p => file(p.trim))
|
||||
propBase
|
||||
.orElse(fromEnv("SBT_CONFIG_HOME"))
|
||||
.orElse(
|
||||
if Util.isWindows then fromEnv("LOCALAPPDATA").map(_ / "sbt")
|
||||
else fromEnv("XDG_CONFIG_HOME").map(_ / "sbt")
|
||||
)
|
||||
.getOrElse(home / BuildPaths.ConfigDirectoryName)
|
||||
.getAbsoluteFile
|
||||
|
||||
/**
|
||||
* Operating system specific cache directory, similar to Coursier cache.
|
||||
*/
|
||||
|
|
@ -289,4 +308,4 @@ object SysProp {
|
|||
.get(sys.env.getOrElse("XDG_RUNTIME_DIR", sys.props("java.io.tmpdir")))
|
||||
.resolve(s".sbt$halfhash")
|
||||
}
|
||||
}
|
||||
end SysProp
|
||||
|
|
|
|||
|
|
@ -8,43 +8,50 @@
|
|||
|
||||
package sbt
|
||||
|
||||
import BuildPaths.expandTildePrefix
|
||||
import java.io.File
|
||||
import BuildPaths.{ expandTildePrefix, defaultGlobalBase, GlobalBaseProperty }
|
||||
|
||||
object BuildPathsTest extends verify.BasicTestSuite {
|
||||
object BuildPathsTest extends verify.BasicTestSuite:
|
||||
|
||||
test("expandTildePrefix should expand empty path to itself") {
|
||||
test("defaultGlobalBase respects sbt.global.base system property"):
|
||||
val custom = new File(System.getProperty("java.io.tmpdir"), "sbt-test-3681").getAbsolutePath
|
||||
val prev = sys.props.get(GlobalBaseProperty)
|
||||
try
|
||||
sys.props(GlobalBaseProperty) = custom
|
||||
assert(defaultGlobalBase.getAbsolutePath == new File(custom).getAbsolutePath)
|
||||
finally
|
||||
prev match
|
||||
case Some(v) => sys.props(GlobalBaseProperty) = v
|
||||
case None => sys.props.remove(GlobalBaseProperty)
|
||||
|
||||
test("defaultGlobalBase returns absolute path"):
|
||||
assert(defaultGlobalBase.getAbsolutePath.nonEmpty)
|
||||
assert(defaultGlobalBase.isAbsolute)
|
||||
|
||||
test("expandTildePrefix should expand empty path to itself"):
|
||||
assertEquals("", expandTildePrefix(""))
|
||||
}
|
||||
|
||||
test("it should expand /home/user/path to itself") {
|
||||
test("it should expand /home/user/path to itself"):
|
||||
assertEquals("/home/user/path", expandTildePrefix("/home/user/path"))
|
||||
}
|
||||
|
||||
test("it should expand /~/foo/ to itself") {
|
||||
test("it should expand /~/foo/ to itself"):
|
||||
assertEquals("/~/foo/", expandTildePrefix("/~/foo/"))
|
||||
}
|
||||
|
||||
test("it should expand ~ to $HOME") {
|
||||
test("it should expand ~ to $HOME"):
|
||||
assertEquals(sys.env.getOrElse("HOME", ""), expandTildePrefix("~"))
|
||||
}
|
||||
|
||||
test("it should expand ~/foo/bar to $HOME/foo/bar") {
|
||||
test("it should expand ~/foo/bar to $HOME/foo/bar"):
|
||||
assertEquals(sys.env.getOrElse("HOME", "") + "/foo/bar", expandTildePrefix("~/foo/bar"))
|
||||
}
|
||||
|
||||
test("it should expand ~+ to $PWD") {
|
||||
test("it should expand ~+ to $PWD"):
|
||||
assertEquals(sys.env.getOrElse("PWD", ""), expandTildePrefix("~+"))
|
||||
}
|
||||
|
||||
test("it should expand ~+/foo/bar to $PWD/foo/bar") {
|
||||
test("it should expand ~+/foo/bar to $PWD/foo/bar"):
|
||||
assertEquals(sys.env.getOrElse("PWD", "") + "/foo/bar", expandTildePrefix("~+/foo/bar"))
|
||||
}
|
||||
|
||||
test("it should expand ~- to $OLDPWD") {
|
||||
test("it should expand ~- to $OLDPWD"):
|
||||
assertEquals(sys.env.getOrElse("OLDPWD", ""), expandTildePrefix("~-"))
|
||||
}
|
||||
|
||||
test("it should expand ~-/foo/bar to $OLDPWD/foo/bar") {
|
||||
test("it should expand ~-/foo/bar to $OLDPWD/foo/bar"):
|
||||
assertEquals(sys.env.getOrElse("OLDPWD", "") + "/foo/bar", expandTildePrefix("~-/foo/bar"))
|
||||
}
|
||||
}
|
||||
end BuildPathsTest
|
||||
|
|
|
|||
16
sbt
16
sbt
|
|
@ -448,8 +448,8 @@ findProperty() {
|
|||
done
|
||||
}
|
||||
|
||||
# Extracts the preloaded directory from either -Dsbt.preloaded, -Dsbt.global.base or -Duser.home
|
||||
# in that order.
|
||||
# Extracts the preloaded directory from -Dsbt.preloaded, -Dsbt.global.base, SBT_CONFIG_HOME,
|
||||
# XDG_CONFIG_HOME/sbt, or user.home/.sbt in that order.
|
||||
getPreloaded() {
|
||||
local preloaded && preloaded=$(findProperty sbt.preloaded)
|
||||
[ "$preloaded" ] && echo "$preloaded" && return
|
||||
|
|
@ -457,6 +457,9 @@ getPreloaded() {
|
|||
local global_base && global_base=$(findProperty sbt.global.base)
|
||||
[ "$global_base" ] && echo "$global_base/preloaded" && return
|
||||
|
||||
[ -n "${SBT_CONFIG_HOME}" ] && echo "${SBT_CONFIG_HOME}/preloaded" && return
|
||||
[ -n "${XDG_CONFIG_HOME}" ] && echo "${XDG_CONFIG_HOME}/sbt/preloaded" && return
|
||||
|
||||
local user_home && user_home=$(findProperty user.home)
|
||||
echo "${user_home:-$HOME}/.sbt/preloaded"
|
||||
}
|
||||
|
|
@ -642,7 +645,7 @@ Usage: `basename "$0"` [options]
|
|||
--jvm-client run JVM client
|
||||
--timings display task timings report on shutdown
|
||||
--allow-empty start sbt even if current directory contains no sbt project
|
||||
--sbt-dir <path> path to global settings/plugins directory (default: ~/.sbt)
|
||||
--sbt-dir <path> path to global settings/plugins directory (default: \$XDG_CONFIG_HOME/sbt or ~/.sbt)
|
||||
--sbt-boot <path> path to shared boot directory (default: ~/.sbt/boot in 0.11 series)
|
||||
--sbt-cache <path> path to global cache directory (default: operating system specific)
|
||||
--ivy <path> path to local Ivy repository (default: ~/.ivy2)
|
||||
|
|
@ -973,6 +976,13 @@ else
|
|||
vlog "[process_args] java_version = '$java_version'"
|
||||
addDefaultMemory
|
||||
addSbtScriptProperty
|
||||
if ! printf '%s\n' "${sbt_options[@]}" | grep -q '^-Dsbt\.global\.base='; then
|
||||
if [[ -n "${SBT_CONFIG_HOME}" ]]; then
|
||||
addSbt "-Dsbt.global.base=$SBT_CONFIG_HOME"
|
||||
elif [[ -n "${XDG_CONFIG_HOME}" ]]; then
|
||||
addSbt "-Dsbt.global.base=${XDG_CONFIG_HOME}/sbt"
|
||||
fi
|
||||
fi
|
||||
addJdkWorkaround
|
||||
set -- "${residual_args[@]}"
|
||||
argumentCount=$#
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
// Scripted test for #3681: default global base (XDG / sbt.global.base) is used correctly.
|
||||
lazy val checkGlobalBase = taskKey[Unit]("Verifies global base is absolute and non-empty")
|
||||
|
||||
lazy val root = (project in file(".")).settings(
|
||||
checkGlobalBase := {
|
||||
val g = BuildPaths.getGlobalBase(state.value)
|
||||
assert(g.isAbsolute, s"expected absolute path: $g")
|
||||
assert(g.getAbsolutePath.nonEmpty, "global base path must be non-empty")
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# Closes #3681: verify global base (XDG / sbt.global.base) is resolvable and absolute
|
||||
> checkGlobalBase
|
||||
Loading…
Reference in New Issue