1

Problem description

I am cross compiling with arm-none-eabi-g++ and I am using conan C/C++ binary package manager together with cmake build system.


My project consists of C, C++ and ARM assembly files. And it uses our customer's headers which are all located inside the /home/xenlauz/.conan/data (this is a standard directory where conan keeps binaries/libraries and header files).

My project is located elsewhere but it builds fine.


I want to make cross-referencing work inside "Neovim" so that I can "go to definition", "go to reference", "rename all symbols at once"... I know that this can be implemented by using clangd language server (LS).

In order to achieve this, I first had to configure my /home/xenlauz/.config/nvim/init.lua like shown below...

In "Neovim", I am using package manager "Packer" which is set up like this in order to be able to install the plugins nvim-lsp-installer and nvim-lspconfig:

local plugin_packer = require('packer')
plugin_packer.startup(
    function()
        use {
            "williamboman/nvim-lsp-installer",  -- LS manager.
            requires = {
                "neovim/nvim-lspconfig"         -- LS configuration engine.
            }
        }   
    end
)

Plugin nvim-lsp-installer is a LS manager which helps us to install and manage any kind of LS while plugin nvim-lspconfig is a native LS configurator that makes sure "Neovim" can talk with the installed LS. This plugin needs no configuration and we shouldn't configure it at any point because nvim-lsp-installer is used to configure the individual servers and then pass configuration to nvim-lspconfig like this:

local plugin_lspinstaller = require("nvim-lsp-installer")

--  NOTE: Store the project directory (Neovim has to be opened in project directory)
local myroot_dir = function()
    return vim.fn.getcwd()
end

plugin_lspinstaller.on_server_ready(
    function(server)

        -- NOTE: Create options.
        local server_opts = {}

        if server.name == "clangd" then
            server_opts = {
                cmd = {
                    "clangd"
                },
                filetypes = {
                    "c",
                    "cpp",
                    "objc",
                    "objcpp",
                    "cuda",
                    "proto"
                },
                root_dir = myroot_dir
            }
        end

        -- NOTE: Pass options to the `nvim-lspconfig` to apply them.
        server:setup(server_opts)

    end
)

With this settings I am able to open "Neovim" and install the clangd using a "Neovim" command :LspInstall clangd. This installs a local copy of clangd executable inside the /home/xenlauz/.local/share/nvim/lsp_servers/clangd/ and I can verify that it is working by running it:

~/.local/share/nvim/lsp_servers/clangd/clangd/bin/clangd --version
clangd version 15.0.6 (https://github.com/llvm/llvm-project 
088f33605d8a61ff519c580a71b1dd57d16a03f8)
Features: linux+grpc
Platform: x86_64-unknown-linux-gnu

This clangd binary automatically runs if I (a) open "Neovim" in my project's directory and then (b) open any .cpp file.


Unfortunately clangd does not recognize custom types defined in headers located in the /home/xenlauz/.conan/data so, e.g. "go to definition" does not work.

I know that clangd relies on the compile_commands.json file which contains array of objects, where each object typically contains the following fields:

  • directory (directory where compilation command command should be executed)
  • command (compilation command that transforms source file to binary)
  • file (source file being compiled)

In my case compile_commands.json configuration file is generated inside the project's subfodler ./build/compile_commands.json and is generated by cmake whose CMakeLists.txt includes two crucial lines:

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
include_directories(/home/xenlauz/.conan/data)

First line instructs cmake to generate compile_commands.json and second command adds -I /home/xenlauz/.conan/data flag in every command member inside the compile_commands.json. I verified that this flag is really added.

This still will not make clangd recognize headers inside the /home/xenlauz/.conan/data. The log shows that compile_commands.json file is found:

[ERROR][2023-03-10 09:12:26] .../vim/lsp/rpc.lua:733    "rpc"   "clangd"    "stderr"    "I[09:12:26.581] Loaded compilation database from /mnt/c/Users/xenlauz/Documents/002--projects/003--sick--workpackage--microengine/ksz8851-test-at-f429zi/worktrees/develop/build/compile_commands.json

but there is an error regarding the .s ARM assembly file (last line):

[ERROR][2023-03-10 09:12:27] .../vim/lsp/rpc.lua:733    "rpc"   "clangd"    "stderr"    "E[09:12:27.426] Indexing /mnt/c/Users/xenlauz/Documents/002--projects/003--sick--workpackage--microengine/ksz8851-test-at-f429zi/worktrees/develop/src/microengine_at_nucleo_stm32F429/Device/startup_stm32f429xx.s failed: Couldn't build compiler invocation\nI[09:12:27.426] --> $/progress\n"

Note that both lines start with [ERROR] but later in both lines we can see I[09:12:26.581] (for info) and E[09:12:27.426] for error. What a weird log!


I was thinking that maybe I have to configure clangd a bit. To configure it I have two options...

A: clangd offers a lot of parameters which I can see if I call it like this:

~/.local/share/nvim/lsp_servers/clangd/clangd/bin/clangd --help

These parameters can be included in my "Neovim" configuration file /home/xenlauz/.config/nvim/init.lua by slightly editing clangd configuration that I already referenced like this:

cmd = {
    "clangd",
    "--log=verbose"
}

Here I added a parameter that enables me a more verbose log which unfortunately I don't understand and I can not paste it here, because post size restrictions.

B: the other option is to add a .clangd configuration file (link) in the project's main folder. I did add this configuration file and clangd responds to the changes in this file as well. This is why I tried setting it like this:

CompileFlags:
    CompilationDatabase: ./build
    Add: -I/home/xenlauz/.conan/data

2nd line points clangd to the compile_commands.json file while 3rd line was suppose to add flag -I/home/xenlauz/.conan/data somehow to the compile commands... Well even after this "go to definition" still does not work...


Any suggestions how to configure clangd to work for me. It has already taken a lot of time...


ADD1:

After suggestion from user "HighCommander4" I tried editing the startup parameters of clangd like this:

cmd = {
    "clangd",
        "--log=verbose",
        "--compile-commands-dir=./build",
        "--query-driver=/usr/bin/arm-none-eabi-g++,/usr/local/bin/arm-none-eabi-g++,/usr/bin/arm-none-eabi-cpp,/usr/local/bin/arm-none-eabi-cpp,arm-none-eabi-gcc,/usr/bin/arm-none-eabi-gcc,/usr/local/bin/arm-none-eabi-gcc"
},

Unfortunately error: In the included file "stdexcept" file not found error from verbose log remains. I double checked the paths to compilers by using, e.g. whereis arm-none-.eabi-g++. I also tried different combinations of compilers in the list but error remains. Instroction about --query-driver= says:

clangd compilation flags options:

  --compile-commands-dir=<string>     - Specify a path to look for compile_commands.json. If path is invalid, clangd will look in the current directory and parent paths of each source file
  --query-driver=<string>             - Comma separated list of globs for white-listing gcc-compatible drivers that are safe to execute. Drivers matching any of these globs will be used to extract system includes. e.g. /usr/bin/**/clang-*,/path/to/repo/**/g++-*

ADD2:

After ADD1 I was getting really frustrated and decided to search for the string arm-none-eabi-b++ from within the ~/.conan/ folder. I read a bit and quickly found out that my programs are not being compiled by the system compilers, e.g. /usr/bin/arm-none-eabi-g++ but the ones from the ~/.conan/ directory:

/home/xenlauz/.conan/data/euler_toolchain_arm_rtos/1.1.0/sick/release/package/cb054d0b3e1ca595dc66bc2339d40f1f8f04ab31/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-g++

After I found this I changed my "Neovim" configuration like this:

            cmd = {
                "clangd",
                "--log=verbose",
                "--compile-commands-dir=./build",
                "--query-driver=/home/xenlauz/.conan/data/euler_toolchain_arm_rtos/1.1.0/sick/release/package/cb054d0b3e1ca595dc66bc2339d40f1f8f04ab31/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-g++"
            },

and it worked. The lesson I learned here is that I should use clangd with --query-driver= and always check the logs to find out which compiler is being used. I was a bit naive and believed that system compilers are being used.

71GA
  • 1,132
  • 6
  • 36
  • 69
  • To clarify, are you trying to invoke "go to definition" in the `.s` file, or in a C/C++ file? It would be helpful to see the entire clangd log including the lines logged at the time you invoke "go to definition". If the log is too large to include in your question itself, you could upload it as a Github Gist or on a Pastebin site and link to it. – HighCommander4 Mar 12 '23 at 06:54
  • @HighCommander4 I am trying to invoke *"go to definition"* in a `.c` file and it should take me to the `.c` or `.h` file, but it doesn't. – 71GA Mar 12 '23 at 21:31
  • 1
    Ok, so the error in the log about the `.s` file should be harmless. Full logs are needed for further diagnosis of your problem. – HighCommander4 Mar 13 '23 at 00:53
  • @HighCommander4 Here is the verbose log ([link](https://pastebin.com/uTaGGXJ9)). I tried to use *"go to definitiion"* to jump to `runTests()` function which is defined in the same file, i.e. `main.cpp` and this works. But it does not work if I use *"go to definition"* for a type `IBase_latest` which is defined somewhere inside the `~/.conan/data` folder. One more thing. I tried opening the same project in VS Code and at first it fails to *"go to definition"* for the same type... Then, VS Code offers me to autofix this by using my `compile_commands.json` and it works. Neovim does not work. – 71GA Mar 13 '23 at 10:30
  • In the verbose log almost every line except for the first one starts with `[ERROR]` but read through the lines and you will see that they have identifiers like this: `V[11:23:33.024]` so probably there are not errors everywhere... – 71GA Mar 13 '23 at 10:36
  • 1
    Regarding the log format: what clangd outputs is the part starting from `V[...]`, `I[...]`, etc. The first letter indicates the log severity (V = verbose, I = info, E = error etc.). All the preceding cruft is added by neovim. The format is annoying not only because the misleading `[ERROR]`, but also e.g. multiple lines being combined into one line with `\n` making it very unreadable. Kindly, **please** complain to the neovim developers and ask them to make this (accessing LSP server logs in a readable format) better. – HighCommander4 Mar 13 '23 at 20:30

1 Answers1

1

The logs show that the file being opened (Ksz8851snlDriverTests.cpp) contains errors:

  • In included file: 'cstddef' file not found"
    • with the "included file" being euler/abidefs/abi.h
  • then a bunch of Unknown type name 'IBase_latest'
  • and finally: Too many errors emitted, stopping now

Just to confirm, do you see these errors in your editor? If not, there may be an issue with your editor configuration where it's not showing diagnostics from clangd. If you do see them, well, they're the reason why go-to-definition is not working :)

The last error in particular, Too many errors emitted, stopping now, indicates that clangd gives up trying to build an AST for the file due to the previous errors. Without a correct AST, most clangd features including go-to-definition will not work correctly.

So, you need to start by fixing the above errors. Let's take a look at them more closely.


The first error, In included file: 'cstddef' file not found" indicates that clangd is having trouble finding standard library headers such as <cstddef>.

This is a somewhat common problem, addressed in this FAQ question on the clangd website.

Since you're using a cross-compiler, arm-none-eabi-g++, the last paragraph of that section is applicable:

If you’re using an unusual compiler (e.g. a cross-compiler for a different platform) you may want to pass --query-driver=/path/to/mygcc to allow clangd to extract the include paths from it directly.

I would recommend trying --query-driver as suggested (there are more details about its usage in clangd --help).


The remaining errors I suspect are downstream of the <cstddef> one, though I can't be sure. If you've fixed the <cstddef> error and are still seeing other errors, please feel free to share a new set of logs and I can take another look.

HighCommander4
  • 50,428
  • 24
  • 122
  • 194
  • Yes, I see these errors in my editor. I will take a look and try to fix what you suggested. Then I report back. – 71GA Mar 13 '23 at 20:47
  • Regarding the `--query-driver` I added the **ADD1** section at the end of my question. It failed to work. – 71GA Mar 14 '23 at 13:30
  • 1
    I managed to make it work. Thanks for pointing `--query-driver` parameter which led me in the right direction. In my case the problem was that I assumed a system compilers are being used, but they were not! Build system used compilers from the `~/conan` folder! So everybody! When you work with `conan` build system you should read the logs to identify which compiler is actually being used and then use it with the `--query-driver` parameter. Lesson learned! – 71GA Mar 14 '23 at 13:33