Build Server Protocol

Build Server Protocol

  • Specification
  • GitHub

›Bindings

Overview

  • Specification
  • Implementations
  • Build Server Discovery
  • Frequently Asked Questions

Extensions

  • Scala
  • sbt

Bindings

  • Java
Edit

Java Bindings

Installation

The ch.epfl.scala:bsp4j module is a Java library that is available from Maven Central. The module has one external dependency on the eclipse/lsp4j library.

Gradle

compile group: 'ch.epfl.scala', name: 'bsp4j', version: '2.0.0-M4+13-b3992f30'

Maven

<dependency>
    <groupId>ch.epfl.scala</groupId>
    <artifactId>bsp4j</artifactId>
    <version>2.0.0-M4+13-b3992f30</version>
</dependency>

sbt

libraryDependencies += "ch.epfl.scala" % "bsp4j" % "2.0.0-M4+13-b3992f30"

Examples

Client

First, begin by obtaining an input and output stream to communicate with the build server.

val output: java.io.OutputStream = buildOutputStream()
val input: java.io.InputStream = buildInputStream()

Next, implement the BuildClient interface. Replace the ??? dummy implementations with the logic of your build client.

import java.util.concurrent._
import ch.epfl.scala.bsp4j._
import org.eclipse.lsp4j.jsonrpc.Launcher

class MyClient extends BuildClient {
  def onBuildLogMessage(params: LogMessageParams): Unit = ???
  def onBuildPublishDiagnostics(params: PublishDiagnosticsParams): Unit = ???
  def onBuildShowMessage(params: ShowMessageParams): Unit = ???
  def onBuildTargetDidChange(params: DidChangeBuildTarget): Unit = ???
  def onBuildTaskFinish(params: TaskFinishParams): Unit = ???
  def onBuildTaskProgress(params: TaskProgressParams): Unit = ???
  def onBuildTaskStart(params: TaskStartParams): Unit = ???
}
val localClient = new MyClient()

Optionally, create a custom ExecutorService to run client responses

import java.util.concurrent._
val es = Executors.newFixedThreadPool(1)
// es: ExecutorService = java.util.concurrent.ThreadPoolExecutor@6196c03d[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]

Next, wire the client implementation together with the remote build server.

val launcher = new Launcher.Builder[BuildServer]()
  .setOutput(output)
  .setInput(input)
  .setLocalService(localClient)
  .setExecutorService(es)
  .setRemoteInterface(classOf[BuildServer])
  .create()
// launcher: Launcher[BuildServer] = org.eclipse.lsp4j.jsonrpc.Launcher$Builder$1@2cb06e98

Next, obtain an instance of the remote BuildServer via getRemoteProxy().

val server = launcher.getRemoteProxy
// server: BuildServer = EndpointProxy for org.eclipse.lsp4j.jsonrpc.RemoteEndpoint@62b19302

Next, start listening to the remote build server on a separate thread. The .get() method call is blocking during the lifetime of BSP session.

new Thread {
  override def run() = launcher.startListening().get()
}
// res0: Thread = Thread[Thread-212,5,run-main-group-3]

Next, trigger the initialize handshake with the remote server.

val workspace = java.nio.file.Paths.get(".").toAbsolutePath().normalize()
// workspace: java.nio.file.Path = /Users/lgeirsson/dev/bsp
val initializeResult = server.buildInitialize(new InitializeBuildParams(
  "MyClient", // name of this client
  "1.0.0", // version of this client
  "2.0.0-M4+13-b3992f30", // BSP version
  workspace.toUri().toString(),
  new BuildClientCapabilities(java.util.Collections.singletonList("scala"))
))
// initializeResult: CompletableFuture[InitializeBuildResult] = org.eclipse.lsp4j.jsonrpc.RemoteEndpoint$1@21a852ee[Not completed, 1 dependents]

After receiving the initialize response, send the build/initialized notification.

initializeResult.thenAccept(_ => server.onBuildInitialized())
// res1: CompletableFuture[Void] = java.util.concurrent.CompletableFuture@169f9003[Not completed]

After sending the build/initialized notification, you can send any BSP requests and notications such as workspace/buildTargets, buildTarget/compile.

To close the BSP session, send the build/shutdown request followed by a build/exit notification.

server.buildShutdown().thenAccept(_ => server.onBuildExit())
// res2: CompletableFuture[Void] = java.util.concurrent.CompletableFuture@3974e8e6[Not completed]

Server

First, implement the BuildServer interface.

import java.util.concurrent._
import ch.epfl.scala.bsp4j._
import org.eclipse.lsp4j.jsonrpc.Launcher

class MyBuildServer extends BuildServer {
  var client: BuildClient = null // will be updated later
  def buildInitialize(params: InitializeBuildParams): CompletableFuture[InitializeBuildResult] = ???
  def buildShutdown(): CompletableFuture[Object] = ???
  def buildTargetCleanCache(params: CleanCacheParams): CompletableFuture[CleanCacheResult] = ???
  def buildTargetCompile(params: CompileParams): CompletableFuture[CompileResult] = ???
  def buildTargetDependencySources(params: DependencySourcesParams): CompletableFuture[DependencySourcesResult] = ???
  def buildTargetInverseSources(params: InverseSourcesParams): CompletableFuture[InverseSourcesResult] = ???
  def buildTargetResources(params: ResourcesParams): CompletableFuture[ResourcesResult] = ???
  def buildTargetRun(params: RunParams): CompletableFuture[RunResult] = ???
  def buildTargetSources(params: SourcesParams): CompletableFuture[SourcesResult] = ???
  def buildTargetTest(params: TestParams): CompletableFuture[TestResult] = ???
  def onBuildExit(): Unit = ???
  def onBuildInitialized(): Unit = ???
  def workspaceBuildTargets(): CompletableFuture[WorkspaceBuildTargetsResult] = ???
}
val localServer = new MyBuildServer()
// localServer: MyBuildServer = repl.Session$App4$MyBuildServer@40b72398

Next, construct a launcher for the remote build client.

val launcher = new Launcher.Builder[BuildClient]()
  .setOutput(System.out)
  .setInput(System.in)
  .setLocalService(localServer)
  .setRemoteInterface(classOf[BuildClient])
  .create()
// launcher: Launcher[BuildClient] = org.eclipse.lsp4j.jsonrpc.Launcher$Builder$1@d63afda

Next, update the remote build client reference in localServer.

localServer.client = launcher.getRemoteProxy()

Finally, in a main method wire everything together.

def main(args: Array[String]): Unit = {
  launcher.startListening().get() // listen until BSP session is over.
}
← sbt
  • Installation
    • Gradle
    • Maven
    • sbt
  • Examples
    • Client
    • Server
Build Server Protocol
Overview
SpecificationImplementations
Social
Copyright © 2019 Build Server Protocol