williamboman / nvim-lsp-installer

Further development has moved to https://github.com/williamboman/mason.nvim!
https://github.com/williamboman/mason.nvim
Apache License 2.0
2k stars 123 forks source link

rust-analyzer doesn't work on musl based systems #514

Closed Luxed closed 2 years ago

Luxed commented 2 years ago

Problem description

When using a musl based system (i.e. alpine or void-linux), the error message Spawning language server with cmd: rust-analyzer failed. The language server is either not installed, missing from PATH, or not executable.. If you replace the rust-analyzer executable in ~/.local/share/nvim/lsp_servers/rust/ by the correct musl based executable, everything works as expected.

Neovim version (>= 0.6)

NVIM v0.6.1
Build type: MinSizeRel
LuaJIT 2.1.0-beta3
Compiled by buildozer@build-edge-x86_64

Features: +acl +iconv +tui
See ":help feature-compile"

   system vimrc file: "$VIM/sysinit.vim"
  fall-back for $VIM: "/usr/share/nvim"

Run :checkhealth for more info

Operating system/version

Linux e34c990bcac2 5.16.11-1-default #1 SMP PREEMPT Thu Feb 24 05:07:05 UTC 2022 (90630c5) x86_64 Linux

NOTE: uname -a won't be useful since I tested this in a docker container (but I also had the issue in an Alpine VM).

I've recently downloaded the latest plugin version of both nvim-lsp-installer and nvim-lspconfig

Affected language servers

rust-analyzer

Steps to reproduce

  1. Start neovim in musl based system (you can use the included Dockerfile to facilitate testing).
  2. Run cargo new hello to create a simple project.
  3. cd hello
  4. nvim src/main.rs (if using the dockerfile with my config, the first launch installs the plugins, once done, you need to restart neovim).
  5. Install rust_analyzer using LspInstallInfo.
FROM alpine:edge

RUN apk update
RUN apk add neovim gcc g++ git rust rustfmt rust-src cargo

WORKDIR /root

RUN git clone https://github.com/Luxed/nvim-config .config/nvim

Actual behavior

Spawning language server with cmd: rust-analyzer failed. The language server is either not installed, missing from PATH, or not executable. error is shown.

Expected behavior

Plugin should download the right version depending on if the distro uses gnu or musl.

LspInfo

Language client log: /root/.cache/nvim/lsp.log
Detected filetype:   rust

0 client(s) attached to this buffer:

Other clients that match the filetype: rust

Config: rust_analyzer
  filetypes:         rust
  root directory:    /root/hello
  cmd:               rust-analyzer
  cmd is executable: Unable to find executable. Please check your path and ensure the server is installed
  autostart:         true
  custom handlers:   textDocument/hover, experimental/serverStatus

Configured servers list: rust_analyzer, rescriptls

Healthcheck

nvim: health#nvim#check
========================================================================
## Configuration
  - OK: no issues found

## Performance
  - OK: Build type: MinSizeRel

## Remote Plugins
  - OK: Up to date

nvim-lsp-installer: require("nvim-lsp-installer.health").check()
========================================================================
## nvim-lsp-installer report
  - OK: neovim version >= 0.6.0
  - WARNING: **Go**: not available
  - WARNING: **Ruby**: not available
  - WARNING: **RubyGem**: not available
  - WARNING: **Composer**: not available
  - WARNING: **PHP**: not available
  - ERROR: **npm**: not available
  - ERROR: **node**: not available
  - WARNING: **python3**: not available
  - WARNING: **pip3**: not available
  - WARNING: **javac**: not available
  - WARNING: **java**: not available
  - WARNING: **julia**: not available
  - WARNING: **curl**: not available
  - ERROR: **bash**: not available
  - OK: **sh**: `Ok`
  - OK: **tar**: `tar (busybox) 1.33.1`
  - ERROR: **gzip**: not available
  - ERROR: **wget**: not available

nvim-treesitter: require("nvim-treesitter.health").check()
========================================================================
## Installation
  - WARNING: `tree-sitter` executable not found (parser generator, only needed for :TSInstallFromGrammar, not required for :TSInstall)
  - WARNING: `node` executable not found (only needed for :TSInstallFromGrammar, not required for :TSInstall)
  - OK: `git` executable found.
  - OK: `cc` executable found. Selected from { vim.NIL, "cc", "gcc", "clang", "cl", "zig" }
    Version: cc (Alpine 11.2.1_git20220219) 11.2.1 20220219
  - OK: Neovim was compiled with tree-sitter runtime ABI version 14 (required >=13). Parsers must be compatible with runtime ABI.

## Parser/Features H L F I J
  - yaml           ✓ ✓ ✓ ✓ ✓ 
  - javascript     ✓ ✓ ✓ ✓ ✓ 
  - typescript     ✓ ✓ ✓ ✓ ✓ 
  - lua            ✓ ✓ ✓ ✓ ✓ 
  - jsdoc          ✓ . . . . 
  - query          ✓ ✓ ✓ ✓ ✓ 
  - html           ✓ ✓ ✓ ✓ ✓ 
  - css            ✓ . ✓ ✓ ✓ 
  - python         ✓ ✓ ✓ ✓ ✓ 
  - go             ✓ ✓ ✓ ✓ ✓ 
  - json           ✓ ✓ ✓ ✓ . 
  - rust           ✓ ✓ ✓ ✓ ✓ 
  - c_sharp        ✓ ✓ ✓ . ✓ 

  Legend: H[ighlight], L[ocals], F[olds], I[ndents], In[j]ections
         +) multiple parsers found, only one will be used
         x) errors found in the query, try to run :TSUpdate {lang}

provider: health#provider#check
========================================================================
## Clipboard (optional)
  - WARNING: No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.
    - ADVICE:
      - :help |clipboard|

## Python 2 provider (optional)
  - WARNING: No Python executable found that can `import neovim`. Using the first available executable for diagnostics.
  - ERROR: Python provider error:
    - ADVICE:
      - provider/pythonx: Could not load Python 2:
          python2 not found in search path or not executable.
          python2.7 not found in search path or not executable.
          python2.6 not found in search path or not executable.
          python not found in search path or not executable.
  - INFO: Executable: Not found

## Python 3 provider (optional)
  - WARNING: No Python executable found that can `import neovim`. Using the first available executable for diagnostics.
  - ERROR: Python provider error:
    - ADVICE:
      - provider/pythonx: Could not load Python 3:
          python3 not found in search path or not executable.
          python3.10 not found in search path or not executable.
          python3.9 not found in search path or not executable.
          python3.8 not found in search path or not executable.
          python3.7 not found in search path or not executable.
          python3.6 not found in search path or not executable.
          python not found in search path or not executable.
  - INFO: Executable: Not found

## Python virtualenv
  - OK: no $VIRTUAL_ENV

## Ruby provider (optional)
  - WARNING: `ruby` and `gem` must be in $PATH.
    - ADVICE:
      - Install Ruby and verify that `ruby` and `gem` commands work.

## Node.js provider (optional)
  - WARNING: `node` and `npm` (or `yarn`) must be in $PATH.
    - ADVICE:
      - Install Node.js and verify that `node` and `npm` (or `yarn`) commands work.

## Perl provider (optional)
  - WARNING: No usable perl executable found

targets: health#targets#check
========================================================================
  - OK: No conflicting mappings found

telescope: require("telescope.health").check()
========================================================================
## Checking for required plugins
  - OK: plenary installed.
  - OK: nvim-treesitter installed.

## Checking external dependencies
  - ERROR: rg: not found. `live-grep` finder will not function without [BurntSushi/ripgrep](https://github.com/BurntSushi/ripgrep) installed.
  - WARNING: fd: not found. Install [sharkdp/fd](https://github.com/sharkdp/fd) for extended capabilities

## ===== Installed extensions =====

vim.lsp: require("vim.lsp.health").check()
========================================================================
  - INFO: LSP log level : WARN
  - INFO: Log path: /root/.cache/nvim/lsp.log
  - INFO: Log size: 0 KB

vim.treesitter: require("vim.treesitter.health").check()
========================================================================
  - INFO: Runtime ABI version : 14
  - OK: Loaded parser for c_sharp: ABI version 13
  - OK: Loaded parser for css: ABI version 13
  - OK: Loaded parser for go: ABI version 13
  - OK: Loaded parser for html: ABI version 13
  - OK: Loaded parser for javascript: ABI version 13
  - OK: Loaded parser for jsdoc: ABI version 13
  - OK: Loaded parser for json: ABI version 13
  - OK: Loaded parser for lua: ABI version 13
  - OK: Loaded parser for python: ABI version 13
  - OK: Loaded parser for query: ABI version 13
  - OK: Loaded parser for rust: ABI version 13
  - OK: Loaded parser for typescript: ABI version 13
  - OK: Loaded parser for yaml: ABI version 13

Screenshots or recordings

No response

williamboman commented 2 years ago

Hello! I'm struggling a bit to figure out the nicest way to detect musl systems, any suggestions?

PriceHiller commented 2 years ago

Could try parsing the output of ldd --version. On a docker alpine container ldd --version gives me this output:

musl libc (aarch64)
Version 1.2.2
Dynamic Program Loader
Usage: /lib/ld-musl-aarch64.so.1 [options] [--] pathname

and Centos Stream ldd --version gives me this:

ldd (GNU libc) 2.28
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

But lddisn't a guarantee to be there. For instance, on my M1 Mac I don't have ldd in my path.

Another potential way of doing it (though has its own problems for stability) is parsing the nvim binary through strings and checking the first line since nvim is pretty much guaranteed to be installed if this project is being used.

On my Alpine Docker container strings $(which nvim) | head -n 1 yields:

/lib/ld-musl-aarch64.so.1

On my Centos stream install strings $(which nvim) | head -n 1 yields:

/lib64/ld-linux-x86-64.so.2

But we come to a problem with Mac M1 where strings $(which nvim) | head -n 1 yields:

R>UA

Which is pretty worthless.

Another thought was to pull in <headers.h> and take a look at the definitions there for __MUSL__ and whatnot, but some other libs can fake that definition for compatibility creating issues when parsing for that.

I'm sure there's a better way than any of the three I've given up there, but I'm unsure as to what that is.

Luxed commented 2 years ago

@treatybreaker Correct me if I'm wrong, but in the case of the Mac M1, you'd need to download the https://github.com/rust-analyzer/rust-analyzer/releases/download/2022-02-28/rust-analyzer-aarch64-apple-darwin.gz, no? If so, then finding out if the Mac is musl or not doesn't really change anything.

The first way with ldd --version seems to be a good idea actually. This is what dotnet uses for their dotnet-install.sh script which detects information about the system and installs the dotnet runtime and SDK automatically. Here's the line in question.

PriceHiller commented 2 years ago

You're correct on M1 Mac; I mentioned Mac because ldd is just not a command we can run for any system and get a consistent output for -- it's not quite as straight forward as what is in platform. Only reason I mentioned it. We could do a check if it's linux then do the ldd check then for musl, shouldn't be a problem.

Another issue though: rust-analyzer only provides a x86_64 compiled binary for musl, not aarch64 etc., see their latest stable release. Something I did on an Alpine aarch64 image is build rust-analyzer from source to get around this, and it works fine. IMO -- if they have rust installed correctly then we should be able to build from source and successfully install rust-analyzer that way. Admittedly the compilation process is a bit painful due to the time it takes to compile (could maybe use this as a fallback).

Luxed commented 2 years ago

Another issue though: rust-analyzer only provides a x86_64 compiled binary for musl.

Ah yes, didn't see that. But to be honest, that sounds more like an issue on their side.

if they have rust installed correctly then we should be able to build from source.

On that point, I'm not sure. I think this would be @williamboman's decision. If nothing has been done to support compiling language servers from source, then it's going to add a lot of complexity to the plugin. In the case of rust, compiling is trivial (provided things are installed, as you said), but that wouldn't be the case for every language.

Admittedly the compilation process is a bit painful due to the time it takes to compile (could maybe use this as a fallback).

It's not like you'd compile it every time you open your editor either. But that's still a good point. Especially since we're talking about arm, if I wanted to compile it on a raspberry pi it would take forever.

PriceHiller commented 2 years ago

I have a simple POC here which works for me, code is dirty but it does work: https://github.com/treatybreaker/nvim-lsp-installer

Does it work for y'all?

williamboman commented 2 years ago

Does it work for y'all?

Haven't tested it but it looks like a pretty straight forward option! It seems ldd --version prints to stderr, so I guess we need to redirect stderr ldd --version 2>&1 | grep musl. I think it'd be nice to defer the check until it's needed, to avoid a blocking call to the shell. I'm also not too familiar with Lua's os.execute(), but a quick Google suggests it works similar to system(3) (i.e. shells out to sh) and returns the exit code?

Luxed commented 2 years ago

Does it work for y'all?

Tested and working for me!

PriceHiller commented 2 years ago

I'm also not too familiar with Lua's os.execute()

I'm not too familiar with os.execute either; I know as much Lua as I need to know to configure Neovim. I use other languages for the most part. os.execute returns nil when it doesn't get a success return code and true when the command succeeds from what I can tell.

Tested and working for me!

Awesome! I'll defer the libc lookup into a function and make a pull request from there assuming I don't find anything else.