Use `-Dsbt.script` to start sbt server

In order to start the sbt server we must use the sbt script because
it is the only way to load the .sbtopts and the .jvmopts file properly.

To do so the sbt script can pass a -Dsbt.script prop to the java server.
It is used in the NetworkClient to start the server, and it is replicated
in the BuildServerConnection file (.bsp/sbt.json).
This commit is contained in:
Adrien Piquerez 2021-07-12 14:20:17 +02:00
parent 5f4798fa76
commit c9ca2d4afa
3 changed files with 61 additions and 25 deletions

View File

@ -1045,7 +1045,8 @@ object NetworkClient {
private[client] val noStdErr = "--no-stderr"
private[client] val sbtBase = "--sbt-base-directory"
private[client] def parseArgs(args: Array[String]): Arguments = {
var sbtScript = if (Properties.isWin) "sbt.bat" else "sbt"
val defaultSbtScript = if (Properties.isWin) "sbt.bat" else "sbt"
var sbtScript = Properties.propOrNone("sbt.script")
var launchJar: Option[String] = None
var bsp = false
val commandArgs = new mutable.ArrayBuffer[String]
@ -1067,11 +1068,10 @@ object NetworkClient {
sbtScript = a
.split("--sbt-script=")
.lastOption
.map(_.replaceAllLiterally("%20", " "))
.getOrElse(sbtScript)
.orElse(sbtScript)
case "--sbt-script" if i + 1 < sanitized.length =>
i += 1
sbtScript = sanitized(i).replaceAllLiterally("%20", " ")
sbtScript = Some(sanitized(i))
case a if a.startsWith("--sbt-launch-jar=") =>
launchJar = a
.split("--sbt-launch-jar=")
@ -1091,12 +1091,17 @@ object NetworkClient {
}
val base = new File("").getCanonicalFile
if (!sbtArguments.contains("-Dsbt.io.virtual=true")) sbtArguments += "-Dsbt.io.virtual=true"
if (!sbtArguments.exists(_.startsWith("-Dsbt.script"))) {
sbtScript.foreach { sbtScript =>
sbtArguments += s"-Dsbt.script=$sbtScript"
}
}
new Arguments(
base,
sbtArguments.toSeq,
commandArgs.toSeq,
completionArguments.toSeq,
sbtScript,
sbtScript.getOrElse(defaultSbtScript).replaceAllLiterally("%20", " "),
bsp,
launchJar
)

View File

@ -7,12 +7,13 @@
package sbt.internal.bsp
import java.io.File
import sbt.internal.bsp
import sbt.internal.bsp.codec.JsonProtocol.BspConnectionDetailsFormat
import sbt.io.IO
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
import java.io.File
import java.nio.file.{ Files, Paths }
object BuildServerConnection {
final val name = "sbt"
final val bspVersion = "2.0.0-M5"
@ -21,26 +22,45 @@ object BuildServerConnection {
private final val SbtLaunchJar = "sbt-launch(-.*)?\\.jar".r
private[sbt] def writeConnectionFile(sbtVersion: String, baseDir: File): Unit = {
import bsp.codec.JsonProtocol._
val bspConnectionFile = new File(baseDir, ".bsp/sbt.json")
val javaHome = System.getProperty("java.home")
val classPath = System.getProperty("java.class.path")
val sbtLaunchJar = classPath
.split(File.pathSeparator)
.find(jar => SbtLaunchJar.findFirstIn(jar).nonEmpty)
.map(_.replaceAllLiterally(" ", "%20"))
val argv =
Vector(
s"$javaHome/bin/java",
"-Xms100m",
"-Xmx100m",
"-classpath",
classPath,
"xsbt.boot.Boot",
"-bsp"
) ++ sbtLaunchJar.map(jar => s"--sbt-launch-jar=$jar")
val argv = Option(System.getProperty("sbt.script"))
.map(_.replaceAllLiterally("%20", " "))
.orElse(sbtScriptInPath) match {
case Some(sbtScript) =>
Vector(sbtScript, "-bsp", s"-Dsbt.script=${sbtScript.replaceAllLiterally(" ", "%20")}")
case None =>
// IntelliJ can start sbt even if the sbt script is not accessible from $PATH.
// To do so it uses its own bundled sbt-launch.jar.
// In that case, we must pass the path of the sbt-launch.jar to the BSP connection
// so that the server can be started.
// A known problem in that situation is that the .sbtopts and .jvmopts are not loaded.
val javaHome = System.getProperty("java.home")
val classPath = System.getProperty("java.class.path")
val sbtLaunchJar = classPath
.split(File.pathSeparator)
.find(jar => SbtLaunchJar.findFirstIn(jar).nonEmpty)
.map(_.replaceAllLiterally(" ", "%20"))
Vector(
s"$javaHome/bin/java",
"-Xms100m",
"-Xmx100m",
"-classpath",
classPath,
"xsbt.boot.Boot",
"-bsp"
) ++ sbtLaunchJar.map(jar => s"--sbt-launch-jar=$jar")
}
val details = BspConnectionDetails(name, sbtVersion, bspVersion, languages, argv)
val json = Converter.toJson(details).get
IO.write(bspConnectionFile, CompactPrinter(json), append = false)
}
private def sbtScriptInPath: Option[String] = {
// For those who use an old sbt script, the -Dsbt.script is not set
// As a fallback we try to find the sbt script in $PATH
val envPath = Option(System.getenv("PATH")).getOrElse("")
val allPaths = envPath.split(File.pathSeparator).map(Paths.get(_))
allPaths.map(_.resolve("sbt")).find(Files.exists(_)).map(_.toString)
}
}

11
sbt
View File

@ -299,6 +299,16 @@ addDefaultMemory() {
fi
}
addSbtScriptProperty () {
if [[ "${java_args[@]}" == -Dsbt.script=* ]]; then
:
else
sbt_script=$0
sbt_script=${sbt_script/ /%20}
addJava "-Dsbt.script=$sbt_script"
fi
}
require_arg () {
local type="$1"
local opt="$2"
@ -769,6 +779,7 @@ else
java_version="$(jdk_version)"
vlog "[process_args] java_version = '$java_version'"
addDefaultMemory
addSbtScriptProperty
set -- "${residual_args[@]}"
argumentCount=$#
run