mirror of https://github.com/sbt/sbt.git
Create BSP connection file at server startup
This commit is contained in:
parent
c80fe525c6
commit
a31747758c
|
|
@ -10,22 +10,24 @@ package internal
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import java.io.{ File, IOException }
|
import java.io.{ File, IOException }
|
||||||
import java.net.{ SocketTimeoutException, InetAddress, ServerSocket, Socket }
|
import java.net.{ InetAddress, ServerSocket, Socket, SocketTimeoutException }
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.nio.file.attribute.{ UserPrincipal, AclEntry, AclEntryPermission, AclEntryType }
|
import java.nio.file.attribute.{ AclEntry, AclEntryPermission, AclEntryType, UserPrincipal }
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
|
|
||||||
import scala.concurrent.{ Future, Promise }
|
import scala.concurrent.{ Future, Promise }
|
||||||
import scala.util.{ Try, Success, Failure }
|
import scala.util.{ Failure, Success, Try }
|
||||||
import sbt.internal.protocol.{ PortFile, TokenFile }
|
import sbt.internal.protocol.{ PortFile, TokenFile }
|
||||||
import sbt.util.Logger
|
import sbt.util.Logger
|
||||||
import sbt.io.IO
|
import sbt.io.IO
|
||||||
import sbt.io.syntax._
|
import sbt.io.syntax._
|
||||||
import sjsonnew.support.scalajson.unsafe.{ Converter, CompactPrinter }
|
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
|
||||||
import sbt.internal.protocol.codec._
|
import sbt.internal.protocol.codec._
|
||||||
import sbt.internal.util.ErrorHandling
|
import sbt.internal.util.ErrorHandling
|
||||||
import sbt.internal.util.Util.isWindows
|
import sbt.internal.util.Util.isWindows
|
||||||
import org.scalasbt.ipcsocket._
|
import org.scalasbt.ipcsocket._
|
||||||
|
import sbt.internal.bsp.BuildServerConnection
|
||||||
|
|
||||||
private[sbt] sealed trait ServerInstance {
|
private[sbt] sealed trait ServerInstance {
|
||||||
def shutdown(): Unit
|
def shutdown(): Unit
|
||||||
|
|
@ -85,6 +87,7 @@ private[sbt] object Server {
|
||||||
serverSocketOpt = Option(serverSocket)
|
serverSocketOpt = Option(serverSocket)
|
||||||
log.info(s"sbt server started at ${connection.shortName}")
|
log.info(s"sbt server started at ${connection.shortName}")
|
||||||
writePortfile()
|
writePortfile()
|
||||||
|
writeBspConnectionDetails()
|
||||||
running.set(true)
|
running.set(true)
|
||||||
p.success(())
|
p.success(())
|
||||||
while (running.get()) {
|
while (running.get()) {
|
||||||
|
|
@ -194,6 +197,13 @@ private[sbt] object Server {
|
||||||
IO.write(portfile, CompactPrinter(json))
|
IO.write(portfile, CompactPrinter(json))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private[this] def writeBspConnectionDetails(): Unit = {
|
||||||
|
import bsp.codec.JsonProtocol._
|
||||||
|
val details = BuildServerConnection.details(sbtVersion)
|
||||||
|
val json = Converter.toJson(details).get
|
||||||
|
IO.write(bspConnectionFile, CompactPrinter(json), append = false)
|
||||||
|
}
|
||||||
|
|
||||||
private[sbt] def prepareSocketfile(): Unit = {
|
private[sbt] def prepareSocketfile(): Unit = {
|
||||||
if (socketfile.exists) {
|
if (socketfile.exists) {
|
||||||
IO.delete(socketfile)
|
IO.delete(socketfile)
|
||||||
|
|
@ -211,7 +221,9 @@ private[sbt] case class ServerConnection(
|
||||||
portfile: File,
|
portfile: File,
|
||||||
tokenfile: File,
|
tokenfile: File,
|
||||||
socketfile: File,
|
socketfile: File,
|
||||||
pipeName: String
|
pipeName: String,
|
||||||
|
bspConnectionFile: File,
|
||||||
|
sbtVersion: String
|
||||||
) {
|
) {
|
||||||
def shortName: String = {
|
def shortName: String = {
|
||||||
connectionType match {
|
connectionType match {
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ private[sbt] final class CommandExchange {
|
||||||
private[sbt] def runServer(s: State): State = {
|
private[sbt] def runServer(s: State): State = {
|
||||||
lazy val port = s.get(serverPort).getOrElse(5001)
|
lazy val port = s.get(serverPort).getOrElse(5001)
|
||||||
lazy val host = s.get(serverHost).getOrElse("127.0.0.1")
|
lazy val host = s.get(serverHost).getOrElse("127.0.0.1")
|
||||||
|
lazy val sbtVersion = s.configuration.provider.id.version
|
||||||
lazy val auth: Set[ServerAuthentication] =
|
lazy val auth: Set[ServerAuthentication] =
|
||||||
s.get(serverAuthentication).getOrElse(Set(ServerAuthentication.Token))
|
s.get(serverAuthentication).getOrElse(Set(ServerAuthentication.Token))
|
||||||
lazy val connectionType = s.get(serverConnectionType).getOrElse(ConnectionType.Tcp)
|
lazy val connectionType = s.get(serverConnectionType).getOrElse(ConnectionType.Tcp)
|
||||||
|
|
@ -147,6 +148,7 @@ private[sbt] final class CommandExchange {
|
||||||
val tokenfile = serverDir / h / "token.json"
|
val tokenfile = serverDir / h / "token.json"
|
||||||
val socketfile = serverDir / h / "sock"
|
val socketfile = serverDir / h / "sock"
|
||||||
val pipeName = "sbt-server-" + h
|
val pipeName = "sbt-server-" + h
|
||||||
|
val bspConnectionFile = s.baseDir / ".bsp" / "sbt.json"
|
||||||
val connection = ServerConnection(
|
val connection = ServerConnection(
|
||||||
connectionType,
|
connectionType,
|
||||||
host,
|
host,
|
||||||
|
|
@ -156,6 +158,8 @@ private[sbt] final class CommandExchange {
|
||||||
tokenfile,
|
tokenfile,
|
||||||
socketfile,
|
socketfile,
|
||||||
pipeName,
|
pipeName,
|
||||||
|
bspConnectionFile,
|
||||||
|
sbtVersion
|
||||||
)
|
)
|
||||||
val serverInstance = Server.start(connection, onIncomingSocket, s.log)
|
val serverInstance = Server.start(connection, onIncomingSocket, s.log)
|
||||||
// don't throw exception when it times out
|
// don't throw exception when it times out
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,9 @@ import sjsonnew.support.scalajson.unsafe.Converter
|
||||||
|
|
||||||
object BuildServerProtocol {
|
object BuildServerProtocol {
|
||||||
import sbt.internal.bsp.codec.JsonProtocol._
|
import sbt.internal.bsp.codec.JsonProtocol._
|
||||||
private val bspVersion = "2.0.0-M5"
|
|
||||||
private val languageIds = Vector("scala")
|
|
||||||
private val bspTargetConfigs = Set("compile", "test")
|
private val bspTargetConfigs = Set("compile", "test")
|
||||||
private val capabilities = BuildServerCapabilities(
|
private val capabilities = BuildServerCapabilities(
|
||||||
CompileProvider(languageIds),
|
CompileProvider(BuildServerConnection.languages),
|
||||||
dependencySourcesProvider = true
|
dependencySourcesProvider = true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -141,7 +139,13 @@ object BuildServerProtocol {
|
||||||
{
|
{
|
||||||
case r: JsonRpcRequestMessage if r.method == "build/initialize" =>
|
case r: JsonRpcRequestMessage if r.method == "build/initialize" =>
|
||||||
val _ = Converter.fromJson[InitializeBuildParams](json(r)).get
|
val _ = Converter.fromJson[InitializeBuildParams](json(r)).get
|
||||||
val response = InitializeBuildResult("sbt", sbtVersion, bspVersion, capabilities, None)
|
val response = InitializeBuildResult(
|
||||||
|
"sbt",
|
||||||
|
sbtVersion,
|
||||||
|
BuildServerConnection.bspVersion,
|
||||||
|
capabilities,
|
||||||
|
None
|
||||||
|
)
|
||||||
callback.jsonRpcRespond(response, Some(r.id)); ()
|
callback.jsonRpcRespond(response, Some(r.id)); ()
|
||||||
|
|
||||||
case r: JsonRpcRequestMessage if r.method == "workspace/buildTargets" =>
|
case r: JsonRpcRequestMessage if r.method == "workspace/buildTargets" =>
|
||||||
|
|
@ -223,7 +227,7 @@ object BuildServerProtocol {
|
||||||
Some(baseDirectory),
|
Some(baseDirectory),
|
||||||
tags,
|
tags,
|
||||||
capabilities,
|
capabilities,
|
||||||
languageIds,
|
BuildServerConnection.languages,
|
||||||
projectDependencies.join.value.toVector,
|
projectDependencies.join.value.toVector,
|
||||||
dataKind = Some("scala"),
|
dataKind = Some("scala"),
|
||||||
data = Some(Converter.toJsonUnsafe(compileData)),
|
data = Some(Converter.toJsonUnsafe(compileData)),
|
||||||
|
|
|
||||||
56
protocol/src/main/contraband-scala/sbt/internal/bsp/BspConnectionDetails.scala
generated
Normal file
56
protocol/src/main/contraband-scala/sbt/internal/bsp/BspConnectionDetails.scala
generated
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
// DO NOT EDIT MANUALLY
|
||||||
|
package sbt.internal.bsp
|
||||||
|
/**
|
||||||
|
* https://build-server-protocol.github.io/docs/server-discovery.html
|
||||||
|
* @param name The name of the build tool
|
||||||
|
* @param version The version of the build tool
|
||||||
|
* @param bspVersion The bsp version of the build tool
|
||||||
|
* @param languages A collection of languages supported by this BSP server
|
||||||
|
* @param argv Command arguments runnable via system processes to start a BSP server
|
||||||
|
*/
|
||||||
|
final class BspConnectionDetails private (
|
||||||
|
val name: String,
|
||||||
|
val version: String,
|
||||||
|
val bspVersion: String,
|
||||||
|
val languages: Vector[String],
|
||||||
|
val argv: Vector[String]) extends Serializable {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override def equals(o: Any): Boolean = o match {
|
||||||
|
case x: BspConnectionDetails => (this.name == x.name) && (this.version == x.version) && (this.bspVersion == x.bspVersion) && (this.languages == x.languages) && (this.argv == x.argv)
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
override def hashCode: Int = {
|
||||||
|
37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.BspConnectionDetails".##) + name.##) + version.##) + bspVersion.##) + languages.##) + argv.##)
|
||||||
|
}
|
||||||
|
override def toString: String = {
|
||||||
|
"BspConnectionDetails(" + name + ", " + version + ", " + bspVersion + ", " + languages + ", " + argv + ")"
|
||||||
|
}
|
||||||
|
private[this] def copy(name: String = name, version: String = version, bspVersion: String = bspVersion, languages: Vector[String] = languages, argv: Vector[String] = argv): BspConnectionDetails = {
|
||||||
|
new BspConnectionDetails(name, version, bspVersion, languages, argv)
|
||||||
|
}
|
||||||
|
def withName(name: String): BspConnectionDetails = {
|
||||||
|
copy(name = name)
|
||||||
|
}
|
||||||
|
def withVersion(version: String): BspConnectionDetails = {
|
||||||
|
copy(version = version)
|
||||||
|
}
|
||||||
|
def withBspVersion(bspVersion: String): BspConnectionDetails = {
|
||||||
|
copy(bspVersion = bspVersion)
|
||||||
|
}
|
||||||
|
def withLanguages(languages: Vector[String]): BspConnectionDetails = {
|
||||||
|
copy(languages = languages)
|
||||||
|
}
|
||||||
|
def withArgv(argv: Vector[String]): BspConnectionDetails = {
|
||||||
|
copy(argv = argv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object BspConnectionDetails {
|
||||||
|
|
||||||
|
def apply(name: String, version: String, bspVersion: String, languages: Vector[String], argv: Vector[String]): BspConnectionDetails = new BspConnectionDetails(name, version, bspVersion, languages, argv)
|
||||||
|
}
|
||||||
35
protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BspConnectionDetailsFormats.scala
generated
Normal file
35
protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BspConnectionDetailsFormats.scala
generated
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
// DO NOT EDIT MANUALLY
|
||||||
|
package sbt.internal.bsp.codec
|
||||||
|
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||||
|
trait BspConnectionDetailsFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||||
|
implicit lazy val BspConnectionDetailsFormat: JsonFormat[sbt.internal.bsp.BspConnectionDetails] = new JsonFormat[sbt.internal.bsp.BspConnectionDetails] {
|
||||||
|
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BspConnectionDetails = {
|
||||||
|
__jsOpt match {
|
||||||
|
case Some(__js) =>
|
||||||
|
unbuilder.beginObject(__js)
|
||||||
|
val name = unbuilder.readField[String]("name")
|
||||||
|
val version = unbuilder.readField[String]("version")
|
||||||
|
val bspVersion = unbuilder.readField[String]("bspVersion")
|
||||||
|
val languages = unbuilder.readField[Vector[String]]("languages")
|
||||||
|
val argv = unbuilder.readField[Vector[String]]("argv")
|
||||||
|
unbuilder.endObject()
|
||||||
|
sbt.internal.bsp.BspConnectionDetails(name, version, bspVersion, languages, argv)
|
||||||
|
case None =>
|
||||||
|
deserializationError("Expected JsObject but found None")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override def write[J](obj: sbt.internal.bsp.BspConnectionDetails, builder: Builder[J]): Unit = {
|
||||||
|
builder.beginObject()
|
||||||
|
builder.addField("name", obj.name)
|
||||||
|
builder.addField("version", obj.version)
|
||||||
|
builder.addField("bspVersion", obj.bspVersion)
|
||||||
|
builder.addField("languages", obj.languages)
|
||||||
|
builder.addField("argv", obj.argv)
|
||||||
|
builder.endObject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,4 +37,5 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
||||||
with sbt.internal.bsp.codec.ScalacOptionsParamsFormats
|
with sbt.internal.bsp.codec.ScalacOptionsParamsFormats
|
||||||
with sbt.internal.bsp.codec.ScalacOptionsItemFormats
|
with sbt.internal.bsp.codec.ScalacOptionsItemFormats
|
||||||
with sbt.internal.bsp.codec.ScalacOptionsResultFormats
|
with sbt.internal.bsp.codec.ScalacOptionsResultFormats
|
||||||
|
with sbt.internal.bsp.codec.BspConnectionDetailsFormats
|
||||||
object JsonProtocol extends JsonProtocol
|
object JsonProtocol extends JsonProtocol
|
||||||
|
|
@ -416,3 +416,21 @@ type ScalacOptionsItem {
|
||||||
## The output directory for classfiles produced by this target
|
## The output directory for classfiles produced by this target
|
||||||
classDirectory: java.net.URI
|
classDirectory: java.net.URI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## https://build-server-protocol.github.io/docs/server-discovery.html
|
||||||
|
type BspConnectionDetails {
|
||||||
|
## The name of the build tool
|
||||||
|
name: String!
|
||||||
|
|
||||||
|
## The version of the build tool
|
||||||
|
version: String!
|
||||||
|
|
||||||
|
## The bsp version of the build tool
|
||||||
|
bspVersion: String!
|
||||||
|
|
||||||
|
## A collection of languages supported by this BSP server
|
||||||
|
languages: [String]
|
||||||
|
|
||||||
|
## Command arguments runnable via system processes to start a BSP server
|
||||||
|
argv: [String]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package sbt.internal.bsp
|
||||||
|
|
||||||
|
object BuildServerConnection {
|
||||||
|
final val name = "sbt"
|
||||||
|
final val bspVersion = "2.0.0-M5"
|
||||||
|
final val languages = Vector("scala")
|
||||||
|
final val argv = Vector("sbt", "-bsp")
|
||||||
|
def details(sbtVersion: String): BspConnectionDetails = {
|
||||||
|
BspConnectionDetails(name, sbtVersion, bspVersion, languages, argv)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue