asdf
asdf copied to clipboard
bug: VS Code fails to initialize workspace with Cargo (Rust Alanyzer)
Describe the Bug
I am using the rust-analyzer extension for Visual Studio Code and when I open a Cargo project I see:
rust-analyzer failed to load workspace: "cargo" "--version" failed: No such file or directory (os error 2)
I have had nothing but problems trying to get other similar tools to find the shims from asdf. There has to be a method, as it is everything that I try to use with asdf-managed tools is simply inoperable.
I cannot find a setting in Code that allows me to change the path to the toolchain.
Steps to Reproduce
-
asdf plugin add rust
-
asdf install rust latest
-
asdf global rust latest
- Test VS Code, see it can't find Cargo.
-
asdf reshim
- Test again, error again.
-
rm -rf ~/.asdf/shims
-
asdf reshim
- Test again, error again.
- ???
Expected Behaviour
The IDEs and other tools should be able to find software managed by asdf. I have tried adding the initialization script to both .zshrc
and .profile
(ZSH is my login shell).
# Source the asdf version manager
[ -f '/opt/asdf-vm/asdf.sh' ] &&
source '/opt/asdf-vm/asdf.sh'
Everything works fine from the command line.
Actual Behaviour
No tooling I try (specifically VS code in this case) can ever find binaries from ~/.asdf
.
Environment
OS:
Linux jacob-thinkpad 5.17.0-2-MANJARO #1 SMP PREEMPT Fri Feb 25 11:28:23 UTC 2022 x86_64 GNU/Linux
SHELL:
zsh 5.8.1 (x86_64-pc-linux-gnu)
ASDF VERSION:
v0.9.0
ASDF ENVIRONMENT VARIABLES:
ASDF_DIR=/opt/asdf-vm
ASDF INSTALLED PLUGINS:
flutter https://github.com/oae/asdf-flutter.git master 1f5b9e2
java https://github.com/halcyon/asdf-java.git master b0341fd
nodejs https://github.com/asdf-vm/asdf-nodejs.git master 364c078
python https://github.com/danhper/asdf-python.git master 57a4d72
ruby https://github.com/asdf-vm/asdf-ruby.git master de49662
rust https://github.com/code-lever/asdf-rust.git master 0c88f99
asdf plugins affected (if relevant)
rust
I'm experiencing the same bug, but I'm not sure if this is the fault of asdf
or rust-analyzer
.
Hey, @spikespaz. I found a quick fix for this problem. Add the shims folder to the env vars of your .bash_profile
(or .zprofile
, if you are using Zsh):
# Import the default profile
if [ -f "/etc/profile" ]; then
source "/etc/profile"
fi
# Set the list of directories that Bash/Zsh searches for programs
path=(
$HOME/.asdf/shims
$path
)
I want to mention my gratitude to @baliestri, who helped me solve this problem.
path
only works with zsh, if you are using bash do it like this: export PATH=$HOME/.asdf/shims:$PATH
So in an Arch derivative, which zprofile
file am I looking for -- /etc/zsh/zprofile
, the /etc/profile
file it's importing via its single default line, or $ZDOTDIR/.zprofile
? (And if the latter doesn't exist for whatever reason, should I touch
that?)
@soukyomi also it's definitely asdf
-- I'm experiencing an identical issue, but with deno
.
Only if you want you can write to the .zshrc
file.
I use it as follows:
path=(
$HOME/.asdf/shims
$HOME/.local/{bin,sbin}
/usr/{bin,sbin}
/usr/local/{bin,sbin}
$path
)
This issue happens to me with some tools and not others. I have experienced this with Rust Analyzer myself, but didn't stick with Rust long enough to resolve or investigate in any meaningful way.
Given the unpredictable nature of this, I expect that this depends on how the plugin itself tries to find tools and what it does in the background to resolve missing tools. For instance, I use the shellcheck
VSCode plugin, but it can't find my asdf
installed Shellcheck version (I am unsure how it searches for it) and then proceeds to download and install a version to somewhere on my machine. Again, since this happens with some plugins and not others I expect it is because of the way the tool is located by the plugin. Happy to be corrected, because if this is an easy fix for us, then great.
The asdf.sh
script which is sourced in your shell (as part of step 3 of the setup guide) should source .asdf/shims
, so it should already be in your PATH and not needed to be added manually.
Is sourcing it in the "profile"
file different to VSCode? Why would this be?
Because the extension isn't looking at your PATH
, it's looking through files in /usr/bin
, /usr/sbin
, and /bin
. It never sees that the executables are in ~/.asdf/shims
. I would say it's a problem with the extensions that share this behavior, but considering that they are looking in standardized locations... ASDF is not Nix.
@spikespaz As you say in the initial issue:
The IDEs and other tools should be able to find software managed by asdf
They can if they look in the right place.
I would say it's a problem with the extensions that share this behavior, but considering that they are looking in standardized locations
Standardized locations for tools installed at the system level, non-version managed.
ASDF is not Nix.
Yes, it is not Nix. Our comments on that are here https://asdf-vm.com/guide/introduction.html
Can someone speak as to why @bitterteriyaki method supposedly works?
I am inclined to mark this as unfixable by asdf
core and close the Issue.
@jthegedus it's such a fundamental issue for users of asdf, like it's one of the first things I stumbled on. Right now the best solution for me is to run code
from the terminal session. It would be good if there was at least some kind of infrormation/doc pointing to the solution.
edit: I know it's not asdf issue per se but people think it is, for example look here: https://elixirforum.com/t/failed-to-run-elixir-command-error-in-vs-code/31047/6
edit2: so in my case when I have been sourcing $HOME/.asdf/asdf.sh
in ~/.bashrc
then it broke but when I added that source to ~/.bash_profile
then it worked. So my conclusion is that vscode will abide by the ~/.bash_profile
or .zprofile
(confirming what @bitterteriyaki have said already)
edit3: I've straced vscode and it doesn't look to me like it's directly sourcing the files, my assumption is that something launching it has already done that.
it's such a fundamental issue for users of asdf, like it's one of the first things I stumbled on
I am not sure what we can do about it though.
Copying shims to /usr/bin
or /usr/sbin
type locations might work, but we can't guess which location each VSCode plugin would decide to use, so we would have to copy to ALL of the possible locations, which does not feel like the right thing to do.
I was following this https://asdf-vm.com/guide/getting-started.html#_3-install-asdf and if there was a mention like "if you're facing issues with your code editor/3rd party tooling being unable to find binaries then add this to your ~/.bash_profile or whatever ..." then it would save a ton of my time (and others as well).
So my conclusion is that vscode will abide by the ~/.bash_profile or .zprofile
Is this captured in VSCode documentation anywhere? Before we recommend to all users in the docs I want to understand the issue completely. Is VSCode strictly tied to your Shell? A specific Shell, or the default Shell? If I use Fish will VSCode run that Shell's equivalent of profile?
Lots of questions to understand what is actually happening before we can recommend any solution. You have done some great digging, thanks @Rotwang If you have more time to further investigate that would be appreciated.
Is this captured in VSCode documentation anywhere?
Lots of questions to understand what is actually happening before we can recommend any solution.
This is key. If the behavior of VSCode is not documented anywhere, we cannot document a proper solution to this problem in the asdf docs. If VSCode doesn't clearly explain how it finds commands we can provide any official suggestions on how to work around the issue. Based on the conversation here there is nothing indicating any issues with asdf itself. If there aren't official docs on how VSCode handles this we should close this issue.
I have been wrestling with this for quite a while. I always launch vscode from the shell, which is generally helpful, vscode inherits the path and everything seems good.
My workflow for working on multiple projects is to open multiple vscode windows, and (no evidence for this) it seems like the directory that you open the initial window from is the environment that works. Actually, I just did a test locally - I exited vscode, loaded a project with an asdf config, and then opened another project. As I expected, I saw the bad behavior from a specific extension. My takeaway here is that having the shims dir on the path is not sufficient in all cases.
I completely agree that it's unclear how to really address this - it probably depends on extension implementations to some extent. My intuition says that a vscode extension would be the right answer, but I have no clue if vscode exposes an API that would a) be useful and b) work universally.
I had a thought a few minutes ago, that direnv
works in a vaguely similar way, so it might provide some clues. There are some direnv extensions for vscode, but it looks like they have limitations.
Not sure if any of this is helpful :man_shrugging:
This feels relevant: https://github.com/microsoft/vscode/issues/130493
But, I don't really know how asdf
works. I need to do some reading, But, feels like if the node extension host (which runs per-workspace) were launched with the right environment and/or ran the asdf
hook (?) first then everything would be happy.
Lots of questions to understand what is actually happening before we can recommend any solution. You have done some great digging, thanks @Rotwang If you have more time to further investigate that would be appreciated.
@jthegedus
There are two questions to be answered in my mind:
Question "A" why does the ~/.bash_profile
make vscode work while ~/.bashrc
doesn't (analogous to zshell's rc files).
Question "B" what environment do the graphical applications run with (ones that don't go near shell when being executed)
A) Actually vscode tends to launch it's children through a login shell, here is strace
output (my initial findings were not quite correct):
[pid 6886] execve("/bin/bash", ["/bin/bash", "-ilc", "'/usr/share/code/code' --ms-enable-electron-run-as-node -p '\"dc5562613985\" + JSON.stringify(process.env) + \"dc5562613985\"'"], ...
What you can see here is vscode starting a login
shell. Excerpt from bash manpage:
When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. [...] When an interactive shell that is not a login shell is started, bash reads and executes commands from /etc/bash.bashrc and ~/.bashrc, if these files exist.
So here is the reason, I had my . $HOME/.asdf/asdf.sh
in .bashrc
and the .bash_profile
was separate from it (i.e. it was empty) so vscode couldn't get the right path since it starts the login shell.
When I changed my user's shell (chsh -s /bin/sh myuser
) to sh
then the vscode would exec /bin/sh -ilc
instead (which would in turn source its own login files (/etc/profile
and .profile
if they exist).
B) It's difficult to tell on linux because there are so many different ways you can set up your graphical environment. There are many desktop managers (login things that make you enter password and they spin up your gnome/kde/fluxbox/etc. for you) that can be set up in a different way. I'm using ubuntu 20.04 with lightdm
and what it does once it authenticates you and runs your session is it runs a wrapper script (located in /usr/sbin/lightdm-session
) that sources ~/.profile
and ~/.xprofile
, GDM seems to be following suit with its /etc/gdm3/Xsession
.
You end up with variables from /etc/profile
and $HOME/.{profile,xsession}
A note on PAM we shouldn't forget that on linux the authentication is done by PAM which is going to provide its own variables from /etc/environment
and ~/.pam_environment
by default but it depends on PAM's configuration.
Having said all that what I did in the end was:
- Put the
. $HOME/.asdf/asdf.sh
into the~/.bashrc
- Source
.bashrc
in the~/.profile
(courtesy of ubuntu's defaults):
if [ -n "$BASH_VERSION" ]; then
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi
- Got rid of
~/.bash_profile
since the~/.profile
seems to be the common denominator here (both for graphical and shell sessions).
I hope that helps.
edit: typo and minor semantic mistake
AFAIK, .bash_profile
is always loaded, .bashrc
is only loaded for interactive sessions. Analagous for zsh.
AFAIK,
.bash_profile
is always loaded,.bashrc
is only loaded for interactive sessions. Analagous for zsh.
Not quite, if you look up the INVOCATION
section of the bash 5 manual you can find that for a login shell (login shell can be interactive but doesn't have to be) following files are being sourced: /etc/profile
, ~/.bash_profile
, ~/.bash_login
, and ~/.profile
(in that order). For an interactive bash session that is not a login shell /etc/bash.bashrc
and ~/.bashrc
, are being read.
Conclusion is that ~/.bash_profile
and ~/.bashrc
are independent of each other and being read at different times. A lot of people will source their ~/.bashrc
inside ~/.bash_profile
or ~/.profile
and that's why it looks like ~/.bashrc
is being always read (but it doesn't have to be true).
@keithlayne That VSCode Issue is interesting, thanks. You may be interested in https://github.com/asdf-community/asdf-direnv
@Rotwang That is a lot of very useful information, thanks for your time & effort!
So what I have learned is this:
- VSCode starts via a login shell
-
~/.profile
is the most common config file loaded by login shells, but it really depends - the dir that VSCode opens in the explorer tab determines the location for asdf resolution when commands are executed by VSCode Extensions (I tested this one myself)
What we need to do:
- make list of
~/.profile
-like files loaded by each of the Shells asdf explicitly supports (Bash, ZSH, Fish, Elvish) - write docs to guide users to source
$HOME/.asdf/asdf.sh
in file determined above or just.*rc
within that file - write docs/FAQ entry for this mess
Does this sound reasonable to everyone? @Stratus3D ?
I wonder how *Nix and Docker-based version-managed projects handle these issues, or is their workflow that the IDE Extensions just not use the version managed tooling?
the dir that VSCode opens in the explorer tab determines the location for asdf resolution when commands are executed by VSCode Extensions (I tested this one myself)
This is interesting, because it seems to conflict with my experience with multiple workspaces open. This isn't super easy to test, but I have an extension that only works when I load from the console (avoiding the .profile
question for now) and it's the first vscode window opened. If you already tested that specifically, then maybe an answer is closer for me than I thought.
Perhaps I got lucky. I had the below behaviour:
~/.tool-versions < empty
~/somedir/.tool-versions < deno version here
- Open VSCode, select directory to open from landing page UI, open in
~/somedir
. Correctly resolves Deno version - Open integrated terminal in
~/somedir
& runcode ~
- VSCode(window 2) opens in
~
and correctly errors trying to resolve Deno version. The Output tab shows the asdf error when the Extension tried to start the Deno Language Server:
No version is set for command deno
Consider adding one of the following versions in your config file at ~/.tool-versions
deno 1.22.1
So it is correctly trying to resolve the tool from the directory it was opened in and not from. At least for me, and with this particular Extension. The Deno extension might just be super resilient. As for my setup while testing this:
- Windows11 with WSL2 Ubuntu 20.22
- default shell is Nushell with asdf configured
- but no
nushell
was output in strace, it seemed to resolve/usr/bin/sh
, and the default Ubuntu~/.profile
does source~/.bashrc
however I do not haveasdf
configured with in any config other than Nushell.
Extension variance is definitely a factor in this, but not a predictable one and certainly not solvable by us.
User shell setup is also a nightmare, hence why we didn't want to get into the game of automatically modifying configs.
Additionally, I just got [email protected] which has unrelated, but further Shell integrations
This release we improved integration with bash-preexec
So VSCode are actively working in this area and can potentially introduce changes which would affect our advice. We must remain aware.
I would like to not get stuck in the weeds of comparing each others Shell setups if we agree on the "what we need to do" items I outlined above.
sudo ln -s ~/.asdf/shims/cargo /usr/bin/cargo
worked for me
Problem is also being discussed in rust-analyzer/issues/3154
It seems I've the same problem with Kotlin
https://github.com/fwcd/vscode-kotlin/issues/34#issuecomment-1580105492, Go
and Rust
, installed via asdf
. If I launch VSCode from the terminal, it's able to find these paths. But if it's launched from desktop icons, which basically runs /usr/bin/code --unity-launch %F
, it's unable to find these executables.
Edit:
On launching VSCode from desktop icon:
On launching VSCode from terminal (code .
):
My temporary ~/.zprofile
:
#!/bin/sh
# Import the default profile
if [ -f "/etc/profile" ]; then
# shellcheck disable=SC1091
. "/etc/profile"
fi
# append path
append_path () {
case ":$PATH:" in
*:"$1":*)
;;
*)
export PATH="${PATH:+$PATH:}$1"
esac
}
append_path "$HOME/.local/bin"
append_path "$HOME/.asdf/shims"
This seems like a VSCode issue rather than asdf
. Ref: https://github.com/microsoft/vscode/issues/187955