Skip to main content

Integrating a new build tool

Metals uses the Build Server Protocol (BSP) to communicate with build tools. Any build tool that implements the protocol should work with Metals.

There are two options for integrating Metals with a new build tool:

  • via Bloop: emit Bloop JSON configuration files and use the Bloop build server. The benefit of this approach is that it's simple to implement but has the downside that compilation happens outside of your build tool.
  • via custom build server: add Build Server Protocol support to your build tool. The benefit of this approach is that Metals integrates directly with your build tool, reproducing the same build environment as your current workflow. The downside of this approach is that it most likely requires more effort compared to emitting Bloop JSON files.

Enable SemanticDB#

If you use the Bloop build server there is nothing that needs to be added in order to enable SemanticDB. Metals itself will communicate with Bloop to make sure proper options are enabled.

Otherwise, if you implement a custom build server, the SemanticDB compiler plugin is required for Metals code navigation to work. Only limited Metals features like diagnostics and code formatting will work without the SemanticDB compiler plugin enabled.

Users get a warning from Metals when the build server does not enable the SemanticDB compiler plugin. Users have an option to suppress this warning by selecting a "Don't show again" option.

Make sure to declare the compiler option "-P:semanticdb:sourceroot:$WORKSPACE" where $WORKSPACE is the absolute path to the workspace root directory. By default, the sourceroot is inferred from the working directory of the compiler process, but it's better to explicitly declare it. If the sourceroot is misconfigured, then Metals is unable to find the SemanticDB files created by the compiler plugin.

Recommended compiler options#

We recommend you update the following compiler options when using Metals:

  • -Xlint: these warnings are helpful in the editor even if you don't have them enabled in your main build. Consult scalac -Xlint:help for a full list of available warnings.
  • -Xfatal-warnings: disable this setting even if you have it enabled in your main build. Fatal warnings prevent code navigation from working when there are warnings like 'unused import'.

Bloop build server#

Consult the Bloop configuration format to learn how to emit Bloop JSON files. Once the JSON files have been generated, use "Connect to build server" server command in Metals to establish a connection with Bloop.

Custom build server#

Consult the BSP specification to learn about the protocol in detail.

Server discovery#

Metals automatically connects to a BSP server if the workspace root directory contains a .bsp/$name.json file. For example, a build tool named "Bill" will have a .bsp/bill.json file with the following information.

{
"name": "Bill",
"version": "1.0.0",
"bspVersion": "2.0.0",
"languages": ["scala"],
"argv": ["bill", "bsp"]
}

When Metals detects a .bsp/bill.json file in the workspace:

  • it starts a new process bill bsp, the command is derived from the argv field in bill.json.
  • the working directory of the process is the workspace directory.
  • communication between Metals and the build server happens through standard input/output.

For a plug-and-play example of a build server, see Bill.scala in the Metals repository. Bill is a basic build server that is used for testing and demonstration purposes only.

Library bindings#

There are two available libraries to implement a BSP server:

  • ch.epfl.scala:bsp4j: A Java library built with Java CompletableFuture for async primitives and GSON for JSON serialization. This module is used by Metals and the IntelliJ Scala plugin.
  • ch.epfl.scala:bsp4s: A Scala library built with Monix Task/Observable for async primitives and Circe for JSON for serialization. This module is used by the Bloop build server.

Both libraries are compatible with each other. For example, it's OK to implement a server with bsp4j and a client in bsp4s.

Manual tests#

You can manually test your build server integration by running Metals with your editor of choice.

Create the following trace files to spy on incoming/outgoing JSON communication between Metals and your build tool.

# macOS
touch ~/Library/Caches/org.scalameta.metals/bsp.trace.json
# Linux
touch ~/.cache/metals/bsp.trace.json

Metals must re-start to pick up the trace file.

Automated tests#

The Metals repository has infrastructure for running end-to-end integration tests with build servers. To test your build server integration, you can to clone the repository and write custom tests under tests/unit/src/test/scala/tests.

If there is interest, we can publish a Metals testkit module to make it possible to write tests outside the Metals repository.

BSP endpoints#

Metals requires the following BSP endpoints to be implemented by the build server.

  • workspace/buildTargets: to list all the build targets in the workspace.
  • buildTarget/scalacOptions: to know the classpath and compiler options used to compile the project sources.
  • buildTarget/sources: to know what source files map to which build targets.
  • buildTarget/dependencySources: to support "Goto definition" for external libraries.
  • buildTarget/compile: to trigger compilation in a build target.

Additionally, Metals expects the server to send the following notifications:

  • buildTarget/publishDiagnostics: To report compile errors and warnings in the editor buffer.