-
Notifications
You must be signed in to change notification settings - Fork 280
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
SemanticDB support #1467
SemanticDB support #1467
Conversation
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
@ricochet1k are you able to sign the Google CLA? Seems that's needed for them to accept your part of the PR. |
Updated to add Scala3 test (and fixed Scala3 bug that exposed). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @aishfenton for resurrecting @olafurpg's #1329!
We are in SemanticDB starvation with Scala and Bazel (Metals doesn't work etc), and letting scala_library
generate SemanticDB monorepo-wide, I think will get the whole thing moving in the right direction.
FTR I added SemanticDB support in sbt 1.3.0 with the following settings https://www.scala-sbt.org/1.x/docs/sbt-1.3-Release-Notes.html#SemanticDB+support:
ThisBuild / semanticdbEnabled := true
ThisBuild / semanticdbVersion := "4.1.9"
ThisBuild / semanticdbIncludeInJar := false
Regarding rule vs aspect discussion from the previous PR, I think this is a better approach than using aspect since SemanticDB is basically a compiler plugin that generates some binary on the side, so making some tool build with aspect would effectively be a duplicated compilation effort on CI (or more likely on someone's laptop trying to use Metals).
Strange, I thought I did already. Tried again. |
That did the trick @ricochet1k 👍 |
@@ -81,6 +81,10 @@ artifacts = { | |||
"@io_bazel_rules_scala_scala_library", | |||
], | |||
}, | |||
"org_scalameta_semanticdb_scalac": { | |||
"artifact": "org.scalameta:semanticdb-scalac_2.13.6:4.7.3", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that ScalaMeta version is repeated a few times in this file:
"org_scalameta_scalameta": {
"artifact": "org.scalameta:scalameta_2.13:4.3.24",
....
"org_scalameta_trees": {
"artifact": "org.scalameta:trees_2.13:4.3.24",
I suggest that it should be factored out as a variable similar to scala_version
at line 1:
scala_version = "2.13.6"
and change the above to
"artifact": "org.scalameta:semanticdb-scalac_{}:{}".format(scala_version, scalameta_version),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do in follow up PR. Seems to have a few gotchas, as it also requires ScalaFmt rules to be updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my own experience - keeping versions duplicated allows simple grep for artifact coords
@aishfenton I will review this next week, sorry for the delay |
@aishfenton thanks for taking this! Do you already use SemanticDB with Rules Scala? If yes, I would like to understand how tools consume such output. Regarding impl, have you consider implementing it as a phase? Eg. scalafmt phase. I'd like to understand if we can avoid adding additional logic to ScalacWorker and keep it within starlark files. At Wix we have macro which adds semantic db generation (authored by @martynasp-wix), which is used for refactoring automation. Here's the gist of it (most likely can be trimmed to much shorter impl by dropping configuration checks): def _append_parameters_to_list(kwargs, name, values_to_add):
values = kwargs.pop(name, [])
kwargs[name] = values + values_to_add
def _add_semanticdb_support(**kwargs):
semanticdb_plugins = ["@org_scalameta_semanticdb_scalac_2_12_15"]
semanticdb_scalacopts = [
"-P:semanticdb:synthetics:on",
]
is_main_repo = native.repository_name() == "@"
build_semanticdb = kwargs.pop("build_semanticdb", None)
if build_semanticdb:
scalacopts = semanticdb_scalacopts
plugins = semanticdb_plugins
elif build_semanticdb == False or not is_main_repo:
scalacopts = []
plugins = []
else:
plugins = select({
"@wix_tools//dependencies/rules_scala:with_semanticdb": semanticdb_plugins,
"//conditions:default": [],
})
scalacopts = select({
"@wix_tools//dependencies/rules_scala:with_semanticdb": semanticdb_scalacopts,
"//conditions:default": [],
})
_append_parameters_to_list(kwargs, "plugins", plugins)
_append_parameters_to_list(kwargs, "scalacopts", scalacopts)
return kwargs
def with_meta(scala_rule, *args, **kwargs):
scala_rule(*args, **_add_semanticdb_support(**kwargs)) |
Thanks @liucijus ! I went with modifying the ScalaWorker class, as Scala3 has SemanticDB built into the compiler (it now gets enabled as a standard scalac option, not a plugin). So I was thinking it'd make the most sense to treat that similar to other first-class scalac options. But that leaves the question for what to do for Scala2... And there I was somewhat hesitant to create a divergence between how 2.x and 3.x work. Another consideration is that often users will need to switch SemanticDB on/off in a single build (IDE/tools need it, but it adds a 30-50% compile overhead, so generally users will want it to default to off). At the moment I'm supporting that by
Is there a similar mechanism we could exploit for phases? (without having to create two targets for everything). |
Regarding how generally they're consumed by the tools. I'm only familiar with Metals. But my guess is that most tools would be similar in how they're using it. Metals does the following.
The last point is going to require a couple of changes on Metal's side to be compatible with Bazel. As we'll need to pass it a back collection of Jars, rather than a single exploded directory (working on that separately with them). I think the most important consideration is that enabling semanticdb can slow down the compile task quite a lot (the scalameta team mentioned 30-50% to me, but obviously it depends on complexity of the code). So it's going be to be important to be able to toggle it on/off depending on the caller. |
I think phase can make a decision which parameters to pass based on configured scala version. The fact that we had to split compiler code for 2 and 3 already complicates maintenance, I'd like to remove code from there where it is possible.
What works for us with dependency tracking is filters on toolchain. It does not have to be on the toolchain, but you need to have a way to enable it per target. |
@liucijus , can you explain a bit more on how filters could solve this? From what I can grok, filters seem to be about enabling something for a subset of targets, whereas the situation here is that we want it to enable/disable it for all targets (although doing it for a subset of targets could be nice too, but that hasn't come up as a use-case yet). And we need to be able to do that from the Bazel command line (without changing any BUILD files). |
FWIW. I think conceptually (i.e. so that IDEs can include |
Just to run the design change past you, for phases (to make sure I've got it right).
We'd include that phase as a standard phase (unlike Also want check an alternative design with you .. just in case.
Wondering if that'd be clearer, since the semanticdb phase above isn't really doing much more than a pass through. (and this'd be more consistent with diagnosticproto). |
Sorry for not being clear. What I had in mind is that rebuilding all the graph might be expensive, so filtering is a nice way to limit the impact. I'm not saying that it's needed in case of semantic db, but dropping some food for thought. |
I may be wrong but I think current phase implementation forces to have the following design:
Btw, pass through phases may look like they do almost nothing, but they are extension points which can be changed to a different logic by the users. For example filtering idea discussed above, can be implemented as a custom phase. |
@liucijus had a go at those changes. Is that what you had in mind ? (one test environment is failing for bazel-green-head ... but not sure if that's related to my changes, as seems to be about scala_proto? 🤔 ). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aishfenton my expectations are to have all the sematic db logic implemented in starlark without anything added to worker classes. If you think this is not doable for some reason - let me know.
@@ -243,6 +243,17 @@ private static String[] getPluginParamsFrom(CompileOptions ops) { | |||
"-P:dependency-analyzer:unused-deps-ignored-targets:" | |||
+ String.join(":", ignoredTargets)); | |||
} | |||
} | |||
|
|||
if (ops.enableSemanticDb) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aishfenton can this decision be made in one of the phases? My goal is to not have additional logic in worker code.
String[] params = { | ||
"-Yrangepos", | ||
"-P:semanticdb:failures:error", | ||
"-Xplugin-require:semanticdb" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these ops could be part of sematicdb phase. Eg.
def phase_semanticdb(ctx, p):
...
return struct(
enabled = enable_semanticdb,
scalacopts = [
"-Yrangepos",
"-P:semanticdb:failures:error",
"-Xplugin-require:semanticdb"
]
)
...
then this phase can be consumed by compile phase to augment other scalacopts
I tried out the changes in aishfenton’s pull request. I was able to get it to work with a Metals BSP prototype I’m experimenting with. Here’s some of my feedback:
|
This ticket can probably be closed now. SemanitcDB support was added in #1508 |
Description
Adds support for SemanticDB
Extends the work from the stale PR by @olafurpg and @ricochet1k.
enableSemanticDb
option, that is set on thescala_toolchain
method. Defaults to false. Needed, as semanticdb adds 20-50% overhead to compilation times (which not everyone will want/need).Motivation
SemanticDB is needed for many Scala tools, including Metals.