-
Notifications
You must be signed in to change notification settings - Fork 79
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
metals-vscode fails to use desired java-version. #1556
Comments
I'm sitting here writing a small VSCode plugin that tests different ways of accessing env. With What I did find was that it's possible to grab the workspace's env by forking a new process that calls env with workdir set to the project root. This is quite slow though, and opens up new questions, like how to pick the correct shell to run. A better way might be to forgo |
metals.javaVersion dictates what version metals should start with, though if you choose 17 and we find 21 then we will use that. Bloop should use the same version unless it was started separately, but we will try to restart it if the version changed (we might need to double check that one.) JAVA_HOME is a pretty standard way tools use to specify the java home to be used and I don't think we should forgo that. And there should not be an issue if the version of that java is correct, metals will work correctly. There was a lot of issues detecting Java and I'd rather keep what we have right now with possible fixes. The algorithm is:
The actual specific Java versions used by metals doesn't really matter unless it's lower than what you might need, We always add proper -release flags based on the version set in metals.javaHome, which is the java version to be used for the project. What problems do you actually encounter if the java is not the one you expect? |
There's a multitude of problems with using JAVA_HOME:
To be fair having Metals use JAVA_HOME to get at least something running is nice, but it is still quite hard to understand what you're actually using, especially as your terminal might well report a different JAVA_HOME than what's detected by Metals. However, working on different projects in as few as two separate vscode-windows using two different JDKs, I've found myself chasing environment variables around trying to understand why one of the project suddenly starts reporting that |
I think a much safer approach is just to use "-release" flags which means you will not ever publish a wrong bytecode version by default and it doesn't matter what JDK user has as long as it's higher than the release flag version. You don't have to make sure that the build tools picks up a correct java version either.
I would really want to know what problems you are encountering here. It should not be an issue especially since Bloop will report the correct errors according to So ideally, Java version should be set up by the build tools (some special variable or just release flag) and Bloop/Metals will take care of it having all the errors that it should. |
And also coming back to you description:
This is handled currently, there might be some issues to improve, but the errors and completions should be correct. |
Issues I've seen is that Bloop is running with the wrong JDK/flag so that a project for which say JDK 17 is used, is reported as using methods that do not exist, such as My more "shell-savvy" colleague tried helping me look into this yesterday, and it seemed like either VSCode or metals uses the path If metals picked up the JDK from the workspace root it'd probably work better, not sure how it would handle multi-projects though 🤔 |
That's not really possible if we use JDK 17+ by default, unless
As I said this should not be an issue at all since Metals just uses any correct version that it finds, it has nothing to do with the version that you might want to use for compilation. The build tool need to export the proper JDK or you need to modify
How it's defined? I am not aware of cases where JAVA_HOME or other env variables were different depending on directory. |
It's possible this has happened in BSP mode. Unfortunately it's hard for me to provide a setup to trigger the behaviour as most of the issues I've seen are transient and depends on what version of JDK vs code has picked up on (VSCode) startup.
Well it does affect compilation, at least in Bloop-mode. Right now I want to work on a java 21 project using virtual threads but Metals automatically locates Java 17 instead: (This is after a reload of the window with the command
With mise, the active version of java (and thus $PATH:java and $JAVA_HOME) is decided on based on the contents of a file called |
Did you change |
I did change that afterwards and it worked but it downloaded its own JDK instead of using the one available in Another problem is that this setting is a user setting, not available in the workspace settings, so there's no way to share the setting reliably between colleagues. |
Maybe we should also check metals.javaHome if it's set 🤔 Otherwise JDK 21 was not on PATH or in JAVA_HOME (or at least JAVA_HOME that VS Code sees)? |
This would work for one user, using user-settings so global, and would affect all projects the user works on. It won't work for workspace settings.
Yes, this is true. It seems like metals defaults to resolving JAVA_HOME from the system root, which in turn falls back to the JDK-version defined in $HOME/mise.yaml (if there is such a file). It would be more reasonable to default to the project directory. But even so, even if we could get this to work perfectly with all different variations of user toolchains, I still think the process is broken by design: it's pretty much global state and assumptions all the way through. Other plugins in the vs code space, like python for instance, prompts the user to specify which virtual environment to use. While this technically doesn't solve the issue of multiple users working on the same project, at least it's letting one user be aware of which platform they're running on. |
I don't really understand this, from what I know env variables are defined for environment/user, not for specific directories. So it should be the same irrelevant of where we start VS Code. This might differ in different shells but not really in your environment.
That's true, but that's also our attempt to make Metals work without users needing to understand the details of using different Java versions etc. Which might be sometimes at odds with more advanced users. We don't want to ask beginners about that, since it most likely is not relevant to them. I think we are dealing with two different issues here:
If we have the first one, we would be able to know which JDK version to start, or alternatively we should change the default to JDK 21 which would partly fix things. For 2. this would be a good fallback if Metals did choose something wrong by default. |
I agree with you in spirit, it's a real mess. Perhaps you already know this part but I'll go over it briefly just in case. For linux/unix (and therefore macOS, which I'm running), environment variables are set in slightly different ways depending on if you are logging onto your account, opening a terminal, and which terminal shell you are running. In my case, the last file touched by an interactive, non-login shell is ~/.zshrc, which in turn evaluates these two lines:
Similar tools, like jenv, sdkman etc. basically all work this way. It makes it so that an organisation can standardise on a "provider" tool, and commit these tool-configuration files to Git, to make sure that all users are running the same version of for instance java for a specific project. So when VS Code starts it also runs some sort of "rc-file", which initialises mise and makes it set some environment variables. Since metals relies on these variables to find a JDK, it is not all that apparent which one it chooses; you have to look in the Output/metals tab and have some intrinsic knowledge to understand what it's doing. And even when you do think you understand it, it's not certain that the JAVA_HOME metal sees is the one you think you have configured. In fact I'm still not sure if metals-vscode gets a new set of environment variables every time you reload the window, or if the env is static as long as you have at least one instance of VS Code running. It's an unfortunate practicality but these tools (mise, jenv, sdkman etc.) are meant to standardise on and simplify for new users—install mise, clone the repository, and once you've navigated to it your build dependencies are configured for you (with an additional
I sympathise with that. The problem for me is that even as a self-appointed "advanced user" I still can't get it to work like I think it should in some scenarios, and with less experienced users on the project the experience is even worse.
Yes, that's pretty much it. Number 1 is a problem with several dimensions in and of itself:
My reasoning being that while metals-vscode can't necessarily figure out which version of JDK to use for a given project, we do know what it itself needs to run successfully. These dependencies should not be intermingled with a project's dependencies. |
I should add that I think there are ways of making metals-vscode approachable for fresh users while at the same time allowing more specific configuration for projects that need it, but it needs to be thought over carefully. I also want to thank you for your patient discourse 😄 |
I added #1559 so we can actually override the version used by Metals. I wonder if the solution for the issue with directory based JAVA_HOME would not be just running a command to print it within the specified directory Something like spawn("env", [], { cwd: workspaceRoot }); and greping for JAVA_HOME or trying the same with |
What I've done to get this to work from other projects is to run an interactive shell in a process, so basically (oslib-lingo) The problematic part, then, is to figure out which shell the user uses, and how to invoke an interactive shell with it as different shells use different syntax. I'm not sure this is a workable solution due to differences between shells but also differences between platforms — I have zero idea how this should be done in Windows for instance. |
@tgodzik I understand this is more of a feature request, but it relates to what we've been discussing here and I'd like to see if you think it would be a good fit. |
O wow, that is a full fledged extension! And you managed to write it in scala JS, that is quite impressive. Not sure how to properly integrate this, but maybe you can actually switch metals.metalsJavaHome setting from https://github.com/scalameta/metals-vscode/pull/1559/files this way? We could have it as a separate extension or inside metals, though I guess we would need to rewrite to typescript? Or the other way, but that is much more work :(. I would actually love the extension in Scala JS, but I don't really have time to rewrite it. |
Thanks, once I got the hang of how to build my own façades it was a pretty nice experience! ScalablyTyped is a bit flaky and won't translate @types/node above version 15.x so those façades needed to be implemented by hand, but it's honestly not that much work so far.
One way to integrate would be to depend on the JDKManager (or whatever its name will be) extension by adding it to Speaking of integration, how do you want that part to work? From what you've told me so far I think the language server process should just start with whatever suitable JDK the user has configured, but the compilation must really happen with the workspace/workspace-folder JDK defined in the workspace settings. This means that the process that does the actual compilation might need to be restarted when working with multiple projects, or that several compiler processes should be allowed to run at the same time, perhaps with some internal time-to-live to shut down if not interacted with for some time. For what it's worth this seems to be what mill does:
I would also love it if metals-vscode was written in Scala though, perhaps I could look into that? It might take a while though, with holidays and other commitments. |
The additional extension could change the metals settings, right? So it would change java versions used by metals and ask to reload the window. That would make the dependency one way only. We could later depend from metals on it via extensionDependencies once everything is confirmed to work. For project java version we would need to update the workspace metals.javaHome and for global stuff we could update metals.metalsJAvaHome once it's merged
Only if it's something fun for you and I would not have an expectation for you to finish it if you didn't feel like it. |
Sounds good, I'll see what I can whip up! Need to figure out how to package this thing as well 😅
If i understand things correctly the new I'm a bit confused by the doc for
Why does it need to be <= the version the language server runs with? Is this because the version used by the language server is also the version Bloop uses? Would it be possible to "unlock" this dependency? |
Yes, but it needs to be at least 17 since metals is compiled with JDK 17
We automatically add -release flag to support older versions, but we cannot use a future version. |
Ok, I think I understand now. I think we can make the user experience quite pleasant. How about this:
This can all happen in JDKManager but I think looking ahead it makes sense to make some changes in the metals-vscode extension, for instance when it comes to asking the user to either allow the download of a newer JDK — that metals-vscode can't find the JDK I have configured in env:JAVA_HOME and just downloads something else has caught me by surprise a lot of times 😅 |
That should work yeah.
I would do it automatically unless turned off. It's better in that case to remove barriers
In that case I would also just download and only if failed involve the user |
metals-vscode does a bunch of work to find a version of java which to run:
java
onPATH
, it runs a small java-snippet in a shell process which captures the the java.home property of the java-process.metals.javaVersion
in the plugin. I'm not sure when this is used. It lets me select either 17 or 21.metals.javaHome
setting which in my installation is a blank free text field but says it defaults whatevermetals.javaVersion
is set to.Note that I set the
metals.javaVersion
to 21 but it still usedJAVA_HOME
and managed to locate java 17, which I did not want. At the same time my integrated shell reported no JAVA_HOME being set, whilejava -version
reported the desired version 21.Since the metals language server has a different lifecycle than bloop, the java version used for metals might differ from the java-version used by bloop, which exacerbates the problem.
Expected behavior
If using either JAVA_HOME or PATH:java is desired, the values used for these should be the ones available from the project root: different tools in this space sets (usually) both of these depending on some arbitrary logic, usually from a
.java-version
or.tool-versions
file which lists the version of java to use for the workspace, but the actual mechanism is not really important.What is important is that both compile server and language server are kept in sync, so to not create a situation where code that looks like it should compile doesn't or compiled code refers to symbols not available in the language server.
If the
metals.javaVersion
setting is meant to dictate which version of java to use for the whole of the project then JAVA_HOME should not be searched. If it is only meant to be used for a component which does not provide code introspection but only shuffles data between vscode and bloop, then I think it shouldn't be a controllable setting, as the plugin could manage this version by itself.As an aside other tools, for instance IntelliJ, keeps a list of JVMs that the user can choose from for his projects. There are no surprises, other than a JVM missing, which is easy to signal to the user with an error message. Perhaps this would be a better solution to the problem?
Search terms
JAVA_HOME
The text was updated successfully, but these errors were encountered: