Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Support Bazel as a build tool #3233

Merged
merged 32 commits into from
Jan 16, 2024
Merged

Conversation

kpodsiad
Copy link
Member

@kpodsiad kpodsiad commented Oct 27, 2021

Add support for Bazel.
I created a simple project which uses Bazel and could be used for testing this integration.

Known todos:

@kpodsiad
Copy link
Member Author

kpodsiad commented Oct 28, 2021

I managed to generate SemanticDB in my test-project but it turns out that bazel doesn't have any folder where it stores compilation artifacts (at least I didn't find anything + metals decode command didn't point me to any useful directory), everything is loaded directly to the jar in bazel-out/bazel-bindirectory. Both classfiles and semanticDB files are stored in this JAR when metals treat this as a directory and tries to reach the content inside JAR :/

@ckipp01 ckipp01 added BSP Generic BSP related tickets bazel labels Jan 2, 2023
@tanishiking tanishiking mentioned this pull request Apr 12, 2023
7 tasks
tanishiking added a commit to tanishiking/metals that referenced this pull request Apr 13, 2023
While we can generate `.bsp/bazelbsp.json` by
`cs launch org.jetbrains.bsp;bazel-bsp:2.6.1 -M org.jetbrains.bsp.bazel.instal.Install`
https://github.com/JetBrains/bazel-bsp#easy-way-coursier

Metals couldn't connect to the bazel-bsp because it wan't autoConnectable.
This change enable Metals to see bazel-bsp as autoConnectable, and
Metals will spawn and connct to bazel bsp if there's `.bsp/bazelbsp.json`.

As @kpodsiad did in
scalameta#3233
we should

- reindex if the build configuration changed
  - I'm wondering we should check all *.bzl files and WORKSPACE and
    BUILD / BUILD.bazel files. And maybe we should honor the project
    view settings.
- Auto install bazel-bsp when we see `WORKSPACE` file.

scalameta#5138
partially fix for scalameta#5064
tanishiking added a commit that referenced this pull request Apr 14, 2023
While we can generate `.bsp/bazelbsp.json` by
`cs launch org.jetbrains.bsp;bazel-bsp:2.6.1 -M org.jetbrains.bsp.bazel.instal.Install`
https://github.com/JetBrains/bazel-bsp#easy-way-coursier

Metals couldn't connect to the bazel-bsp because it wan't autoConnectable.
This change enable Metals to see bazel-bsp as autoConnectable, and
Metals will spawn and connct to bazel bsp if there's `.bsp/bazelbsp.json`.

As @kpodsiad did in
#3233
we should

- reindex if the build configuration changed
  - I'm wondering we should check all *.bzl files and WORKSPACE and
    BUILD / BUILD.bazel files. And maybe we should honor the project
    view settings.
- Auto install bazel-bsp when we see `WORKSPACE` file.

#5138
partially fix for #5064
If `WORKSPACE` is found, and `.bsp/bazelbsp.json` is not found,
install bazel-bsp 2.7.1 and write the config file to `.bsp/bazelbsp.json`.
we should update metals-vscode so it fires textDocument/didSave
@tanishiking
Copy link
Member

tanishiking commented Jun 19, 2023

I added some changes so that

  • Metals will automatically install bazel-bsp 2.7.1 if WORKSPACE file is found && .bsp/bazelbsp.json isn't found.
  • Metals reloads workspace if Bazel build configuration has changed (though we need changes in metals-vscode too, so it fires textDocument/didSave on change Bazel related build configurations).

have to fix tests

@tgodzik
Copy link
Contributor

tgodzik commented Jun 19, 2023

I added some changes so that

  • Metals will automatically install bazel-bsp 2.7.1 if WORKSPACE file is found && .bsp/bazelbsp.json isn't found.
  • Metals reloads workspace if Bazel build configuration has changed (though we need changes in metals-vscode too, so it fires textDocument/didSave on change Bazel related build configurations).

have to fix tests

Do you need any help to finish? If you don't have time we can take over.

@tanishiking
Copy link
Member

tanishiking commented Jun 20, 2023

Thanks @tgodzik maybe it's better someone picks up to take over this and finishing it up :)

Scope

SemanticDB support shouldn't be a scope of this PR.

Future work

  • Documentation: how to use Metals on Bazel project.
  • It would be great if we can bump bazel-bsp's version by scala-steward or something.
    • Actually, bazel-bsp 2.7.2 is already out...

You should be able to test against a small Bazel project here: https://github.com/tanishiking/bazel-playground/tree/main/01-scala-tutorial By default, rules_scala will use Scala 2.13.6.

@tanishiking
Copy link
Member

@jkciesluk kindly took over the rest of work 💯

When aths where empty, FileWatcher was watching the entire file system
Adds two improvements:
1. After changing BUILD, WORKSPACE or *.bzl file, user will be prompted to import build.
2. If .bazelbsp is not present, user will be asked if they want to create it (the same way as with .bloop)
Previously cheksum was not persisted there, so first save of build file would result in unnecessary reload
Adds tests for connecting to build server using GenerateBspConfig and ImportBuild commands
Copy link
Contributor

@kasiaMarek kasiaMarek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few generic comments:

  1. It would be great to have some docs about bazel suport.
  2. It seems we don't have semanticdb and so on for bazel, and this results in multiple problems detected, it'd be great to let the user know, that this is expected for bazel not actual problem with their build. So maybe a different message?
  3. When I tired changing the scala version I got:
bazelbsp does not support `workspace/reload`, unable to reload

in logs, and those changes weren't actually imported. Am I missing something?

Comment on lines 1223 to 1225
!tables.buildServers
.selectedServer()
.contains(path.filename.stripSuffix(".json"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it ever actually switch between build servers? Or can we check tables.buildServers.isEmpty && no bloop instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it works good? If the currently chosen server is the same as the one that appeared in the .bsp, we don't want to call quickConnectToBuildServer, because it's most likely already been called in other place, but if some other build server appears we want to notify the user?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have an explicitly chosen build server (saved in tables.buildServers) and call quickConnectToBuildServer it will connect to that choice and ignore other possibilities. So you'll just reconnect to the same build server.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So maybe if the newly added build tool is different from the one in tables.buildServers, we can clean this table and then call quickConnectToBuildServer?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone adds sbt you'll have to call slowConnect for the bloop config to generate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can wait for someone invoking Switch build server command in that case I think. If they are already using a bsp server. This will be a rare enough scenario.

Comment on lines 231 to 232
_ <- server.fullServer
.didChangeWatchedFiles(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that needed? Shouldn't the file watcher detect the change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File watcher detects changes from the other didChangeWatchedFiles, but this change is from lsp workspace/didChangeWatchedFiles. I'm not sure why we need both and which notifications should belong where

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that we need both, since not all editors do workspace/didChangeWatchedFiles (at least not reliably), so workspace/didChangeWatchedFiles is sort of "additional". @tgodzik, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, at least from what I remember. For build tool files we use the LSP since this should be fairly reliable.

@@ -343,7 +343,8 @@ object MetalsEnrichments

def isDependencySource(workspace: AbsolutePath): Boolean = {
(isLocalFileSystem(workspace) &&
isInReadonlyDirectory(workspace)) || isJarFileSystem
(isInReadonlyDirectory(workspace) || isInTmpDirectory(workspace))) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When do we put sources into tmp?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ScalafixProvier.load(), but looks like this check is not needed anymore, I will remove it.

Comment on lines 1223 to 1225
!tables.buildServers
.selectedServer()
.contains(path.filename.stripSuffix(".json"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can wait for someone invoking Switch build server command in that case I think. If they are already using a bsp server. This will be a rare enough scenario.

@jkciesluk jkciesluk force-pushed the bazel-bsp branch 2 times, most recently from 1648f50 to e5ec695 Compare January 2, 2024 13:13
@tgodzik tgodzik changed the title [WIP] Support Bazel as a build tool Support Bazel as a build tool Jan 3, 2024
@tgodzik tgodzik changed the title Support Bazel as a build tool feature: Support Bazel as a build tool Jan 3, 2024
@tgodzik
Copy link
Contributor

tgodzik commented Jan 3, 2024

It also looks like the presentation compiler is not started with the right classpath, I can't see any hover or completions from dependency module (like Hello in the sample).

def multipleProblemsDetected: String =
s"Multiple problems detected in your build."

def bazelNavigation: String =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we suggest the flag to use to enable it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently there is no easy way to enable this, it would require changes in both metals and the users project. I think its best to work on it in a follow up.

@jkciesluk jkciesluk requested a review from tgodzik January 11, 2024 15:36
Copy link
Contributor

@tgodzik tgodzik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two minor things otherwise LGTM

@@ -176,16 +176,30 @@ class ProblemResolver(
futureMessage,
).flatten

def noSemanticDBMessages = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we instead create bazel message instead of the basic semanticdb ones if the current build server is bazel?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, we do (see Messages.CheckDoctor.bazelNavigation)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is that noSemanticDBMessages is used to figure out if there are no semanticdb messages, why do we need this as a fllback, instead of checking for semanticdb flags together with bazel?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, I will try to change that

Copy link
Contributor

@tgodzik tgodzik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@tgodzik
Copy link
Contributor

tgodzik commented Jan 15, 2024

Let's make sure we squash here, great work @jkciesluk ! 🚀

@tgodzik tgodzik merged commit 76caf3b into scalameta:main Jan 16, 2024
24 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bazel BSP Generic BSP related tickets
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants