espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.03k stars 7.13k forks source link

Support a language server (like ccls or clangd) (IDFGH-4929) #6721

Open grobx opened 3 years ago

grobx commented 3 years ago

Problem I have tried to index an example project (uart_echo_rs485) without success. Here are the steps to reproduce (${IDF_PATH} is where the release/v4.3 have been cloned and installed):

cd ${IDF_PATH}
source export.sh
cd examples/peripherals/uart/uart_echo_rs485
CMAKE_EXPORT_COMPILE_COMMAND=yes idf.py build
ln -s build/compile_commands.json
ccls -index=.
du -hs .ccls-cache

The last command should not print 0 .ccls-cache as the cache should not be empty.

Solution

I would love to use ccls to index my esp idf project, or any other language server.

Alternatives

I haven't tried clangd.

igrr commented 3 years ago

@roberti42 I'm afraid you will need to get some suggestions from ccls documentation or issue tracker on debugging this issue. When running idf.py build or idf.py reconfigure, build/compile_commands.json file will be generated by CMake. ESP-IDF doesn't affect this part. It is of course possible that something is wrong with the compile_commands.json file, but given that it is generated by CMake and not by our custom tools, it seems to be unlikely.

My guess is that ccls tries to pass the arguments found in compile_commands.json to clang, which fails because 1) some of the options are architecture-specific (e.g. -mlongcalls) and clang built for x86_64 doesn't recognize such xtensa-specific options, and 2) some of the options are specific to GCC, and not supported by clang. ccls has a configuration option (see https://github.com/MaskRay/ccls/wiki/FAQ#compiling-with-gcc) to filter out such options. But that is only my guess — inspecting ccls logs may point you in some other direction.

grobx commented 3 years ago

Thank you @igrr, I understand that this is related to ccls (or clangd) more than esp-idf. Is there any alternative to use features such as code completion and the like when developing using esp-idf?

jsmestad commented 3 years ago

Just wanted to say this is the only thing I am missing from esp-idf. I have tried clangd and ccls without getting a configuration that works.


I am stuck with an error I can't get passed (for anyone else like me who stumbles here):

In included file: argument to 'section' attribute is not valid for this target: mach-o section specifier requires a segment and section separated by a comma

So far the .clangd configuration I seem to have gotten the furthest with:

CompileFlags:
  Add: [
    -ferror-limit=0,
    -mlong-calls,
    -I/Users/justinsmestad/esp/esp-idf/components/**,
    -I/Users/justinsmestad/.espressif/tools/xtensa-esp32-elf/esp-2020r3-8.4.0/xtensa-esp32-elf/include,
    -I/Users/justinsmestad/.espressif/tools/xtensa-esp32-elf/esp-2020r3-8.4.0/xtensa-esp32-elf/xtensa-esp32-elf/include,
    -I/Users/justinsmestad/.espressif/tools/xtensa-esp32-elf/esp-2020r3-8.4.0/xtensa-esp32-elf/xtensa-esp32-elf/include/c++/8.4.0/,
  ]
  Remove: [-mlongcalls, -fstrict-volatile-bitfields]
mikebwilliams commented 3 years ago

I seem to have it working with neovim-0.5, the built in LSP, and clangd 12. I added this lua code to the configuration in init.vim

local lsp = require 'lspconfig'
require'lspconfig'.clangd.setup { 
        cmd = { "clangd", "--background-index --query-driver=/home/mike/.espressif/tools/xtensa-esp32-elf/esp-2020r3-8.4.0/**/bin/xtensa-esp32-elf-*" },
        root_dir = lsp.util.root_pattern('build/compile_commands.json', '.git'),
        }

I was able to remove all of the -I lines from the .clangd file. It seems clangd still wants to pick up stuff like from the system include dirs instead of those that the xtensa toolchain uses. Everything else is working though, I can quickly navigate through all the FreeRTOS and ESP-IDF source like I wanted.

dsalnikov commented 2 years ago

@mikebwilliams, could you clarify how you've configured Nvim? If I put following lines into init.vim:

 lua << EOF
 local lsp = require 'lspconfig'
 require'lspconfig'.clangd.setup {
         cmd = { "clangd", "--background-index --query-driver=/home/mike/.espressif/tools/xtensa-esp32-elf/esp-2020r3-8.4.0/**/bin/xtensa-esp32-elf-*" },
         root_dir = lsp.util.root_pattern('build/compile_commands.json', '.git'),
         }
 EOF

I get "Client 1 quit with exit code 1 and signal 0" message in a status bar and LSP is not working at all.

mikebwilliams commented 2 years ago

Did you set the --query-driver to the location of the toolchain on your computer? It's set to /home/mike/... in the command above

dsalnikov commented 2 years ago

Yes, my compiler path is /Users/dsalnikov/.espressif/tools/xtensa-esp32-elf/esp-2021r1-8.4.0/xtensa-esp32-elf/bin/xtensa-esp32-elf-gcc, so as a cmd I use "/Users/dsalnikov/.espressif/tools/xtensa-esp32-elf/esp-2021r1-8.4.0/*/bin/xtensa-esp32-elf-"

Looks like I have to specify commands separately:

local lsp = require 'lspconfig'
require'lspconfig'.clangd.setup {
        cmd = { "clangd", "--background-index", "--query-driver=/Users/dsalnikov/.espressif/tools/xtensa-esp32-elf/esp-2021r1-8.4.0/**/bin/xtensa-esp32-elf-*"},
        root_dir = lsp.util.root_pattern('build/compile_commands.json', '.git'),
        }
EOF

Now LSP starts fine.

PS To debug set vim.lsp.set_log_level("debug") in config and call :LspInfo to get log file path.

danngreen commented 2 years ago

Has anyone gotten rid of this error?

In included file: argument to 'section' attribute is not valid for this target: mach-o section specifier requires a segment and section separated by a comma

I get it when including certain esp-idf headers, such as . Simple example to demonstrate:

//test.c
#include <esp_wifi.h>

The lsp logs show that it's compiling a native target-triple: -triple x86_64-apple-macosx12.0.0, so I'm guessing that architecture doesn't support whatever 'section' attributes that the esp architecture does.

Perhaps there's a more compatible target-triple we can use?

tolgraven commented 2 years ago

So what's happening is clangd doesn't recognize xtensa-esp32-elf as a valid target/triple/arch, and falls back on empty (resulting in native), causing all kinds of trouble and weird errors, some of which can be circumvented by adding and removing args etc, but not all.

Getting everything working required cloning the xtensa llvm fork at https://github.com/espressif/llvm-project and building clangd from there:

cmake -S llvm -B build -G Ninja -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra"
cmake --build build

Then simply use the clangd in build/bin instead of system.

This is my .clangd, removing/adding some args is still necessary. And the automatic driver extraction sysroot makes no sense so I had to put that in as well.

CompileFlags:
  Add: [-mlong-calls, -isysroot=/Users/tol/.espressif/tools/xtensa-esp32-elf/esp-2021r2-8.4.0/xtensa-esp32-elf]
  Remove: [-fno-tree-switch-conversion, -mtext-section-literals, -mlongcalls, -fstrict-volatile-bitfields]

To easily make it work for everyone Espressif simply needs to build and distribute clangd in their existing xtensa-clang (that can be downloaded with idf_tools.py install xtensa-clang).

igrr commented 2 years ago

Thanks for the recommendation @tolgraven, I'll create a task to add clangd to the next xtensa-clang update.

danngreen commented 2 years ago

Thanks for this, it works! I'm sure it would be much appreciated to have clangd included in xtensa-clang, as compile time is pretty long.

tolgraven commented 2 years ago

It really makes such a difference having a proper LSP and not just tags and vim-fu. Really wish I'd gotten around to figuring this out sooner - solution far too obvious in retrospect.

Is it just me or is [presumably this version of] clangd/clang rather buggy? I'm getting inexplicable errors like

Renderer::Renderer(const std::string& id, uint16_t keyFrameHz, uint16_t targetHz, const RenderStage& target):
  RenderStage(id + " renderer", target.fieldSize(),
              target.fieldCount(), target.buffers().size()),
  core::Task(id.c_str(), 4096, taskPrio,
             milliseconds{1000/targetHz},
             0),
  Sub<PatchIn>(this),   
// ^ clang expected_either - Expected '(' or '{'  // which is utter nonsense and since it's not
  targetFps(targetHz) {                           // an actual error there's no way to make it go away...
...

Bunch of stuff like that (plus obviously the many quirks in gcc/clang in what will cause a warning or error, or simply silently compile. It also seems to generally post warnings as errors (despite my config saying otherwise) and misses a whole bunch of actual errors! So se makeprg=Ninja -C build still essential for me to know what's actually happening.

Anyone got a clue what might be going on?

listout commented 2 years ago

@tolgraven and @danngreen can you please share how you build clangd? I followed @tolgraven's suggestion and cloned the llvm-project repo and ran the cmake commands mentioned in that comment. But I'm getting FAILED: lib/libLTO.so.13 esp xtensa (to be more precise the error is collect2: fatal error: ld terminated with signal 9 [Killed]), which google suggests could be memory issue (I could be running out of memory). If you could just if you added any additional options. BTW I've 24 gigs of memory, don't know if that would be enough or not.

Thank you.

danngreen commented 2 years ago

@listout: Hmm.. I did exactly as specified in @tolgraven's post:

git clone https://github.com/espressif/llvm-project
cd llvm-project
cmake -S llvm -B build -G Ninja -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra"
cmake --build build

Maybe check which branch you're building? I notice the default branch is not master

It built for me on macOS 12 (intel hardware), with 16GB of memory, I'd be surprised if that's not enough.

@tolgraven Are you still getting weird errors like that? I'm not, but I have seen stuff like that on other projects. Setting the log level to debug, and then digging into the log will reveal the actual command being run by clangd. If you run that in the terminal, you should see the same errors. Then you can start deconstructing that command, seeing what flags are missing/added and go from there. I recall once for me some header files weren't compiling as c++20 because compile_commands.json didn't specify an entry for .h files, and clangd "thought" they should get compiled as c99. Might be a clue that the error you showed us occurs right before a token containing a '<', which c99 would not like.... just a guess.

listout commented 2 years ago

@danngreen thank you. I'll try again.

danngreen commented 2 years ago

Just to be clear, I was able to build commit 7b5afb55f5c7959a2903978a25774c75172e8741 (on the xtensa_release_13.0.0 branch). Also I was able to build it on apple M1 hardware, as well as x86_64. Both of those are using clang 13 as the compiler.

listout commented 2 years ago

I was finally able to build it with the additional argument -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=Xtensa. Thanks to gentoo linux for the idea.


@danngreen Nope the same issues, don't know whats happening but the linker is failing on me. Maybe I'll need to dig into it deeper.

My exact error message was: collect2: fatal error: ld terminated with signal 9 [Killed].

gdanov commented 2 years ago

So what's happening is clangd doesn't recognize xtensa-esp32-elf as a valid target/triple/arch, and falls back on empty (resulting in native), causing all kinds of trouble and weird errors, some of which can be circumvented by adding and removing args etc, but not all.

Getting everything working required cloning the xtensa llvm fork at https://github.com/espressif/llvm-project and building clangd from there:

cmake -S llvm -B build -G Ninja -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra"
cmake --build build

Then simply use the clangd in build/bin instead of system.

This is my .clangd, removing/adding some args is still necessary. And the automatic driver extraction sysroot makes no sense so I had to put that in as well.

CompileFlags:
  Add: [-mlong-calls, -isysroot=/Users/tol/.espressif/tools/xtensa-esp32-elf/esp-2021r2-8.4.0/xtensa-esp32-elf]
  Remove: [-fno-tree-switch-conversion, -mtext-section-literals, -mlongcalls, -fstrict-volatile-bitfields]

To easily make it work for everyone Espressif simply needs to build and distribute clangd in their existing xtensa-clang (that can be downloaded with idf_tools.py install xtensa-clang).

Thanks for this idea, did it on two different macs and now I've got lsp + emacs running great!

The alternative I used before that was ccls but the lsp flychecker disabled. The flycheck config was in .dir-local and used xtensa's g++ with command line copied from the generated .ccls file. Worked OK, but clangd is much better.

listout commented 2 years ago

~> Thanks for the recommendation @tolgraven, I'll create a task to add clangd to the next xtensa-clang update.~

~@igrr Any plan on adding clangd to xtensa-clang? Just saw a new update but it didn't include clangd~


Edit: Okay, I manually downloaded the latest release asset and clangd is included, so. How does one download the latest releases with idf_tools.py, it this releated to not using the master branch as I'm on stable branch (v4.4).

igrr commented 2 years ago

@listout could you please post the URL of the update you are referring to? I'll check.

listout commented 2 years ago

@igrr I was referring to this repo https://github.com/espressif/llvm-project.git. The latest release seems to be 12 days ago. My bad, I meant new release.


Looking at the download logs, it's most probably not pulling latest github release assets as it downloads xtensa-esp32-elf-llvm12_0_1-esp-12.0.1-20210914-linux-amd64.tar.xz instead of the new release, I was using the idf_tools.py install xtensa-clang to download.

Edit: Okay, I manually downloaded the latest release asset and clangd is indeed included. So, how does one download the latest releases with idf_tools.py? Is it releated to not using the master branch as I'm on stable branch (v4.4).

igrr commented 2 years ago

@listout I think we haven't updated the download link in tools.json, so the old version is still downloaded when you run idf_tools.py. We will update the link soon. In the meantime you can simply avoid running idf_tools.py install xtensa-clang, simply download the clang release you like, extract it somewhere and add the bin subdirectory to PATH. Be sure to do idf.py fullclean when switching to a new toolchain.

listout commented 2 years ago

@igrr Thanks. Did just that.

andychess commented 1 year ago

Has anyone else tried running the latest esp clangd under Mac OS13 Ventura? I get a security warning when I run binary either directly or via neovim.

Does anyone have a workaround?

Thanks in advance.

igrr commented 1 year ago

@andychess I've checked on one computer running macOS 13, but couldn't reproduce this issue.

What does the warning say, exactly? "clangd" can't be opened because Apple cannot check it for malicious software, or something else?

If that is the case, please check if you have quarantine attribute set on the clangd:

$ xattr -p com.apple.quarantine `which clangd`

if that prints a line which looks like 0000;abcdef01;Chrome;<GUID> then that means the binary is quarantined and will not run.

At that point you can remove the quarantine attribute using xattr -d com.apple.quarantine, if you trust the binary.

If instead you see No such xattr: com.apple.quarantine printed by xattr -p or if the warning message is different, then the issue might be somewhere else...

andychess commented 1 year ago

@igrr Thank you Ivan for answering so quickly.

The warning is indeed the one that you refer to (clangd" can't be opened because Apple cannot check it for malicious software).

I ran $ xattr... as you suggested above and received the reply: xattr: /usr/bin/clangd: No such xattr: com.apple.quarantine

I have read elsewhere that MacOS 13 implements a new version of Gatekeeper that is more difficult to circumvent.

Interesting that you did not encounter the problem. In my case, I downloaded the latest file from (llvm-esp-15.0.0-20221201-macos.tar.xz) to my Download directory, expanded the file and moved the esp-clang folder to my home directory. Running clangd from the command line and from the cmd: line in neovim both produce the warning.

It looks like I may have found the answer here: https://support.apple.com/de-de/guide/mac-help/mchleab3a043/mac (in German). The new way to run quarantined apps is to navigate to the required directory in Finder and hold CTRL and left click on the app. The select 'Open' from the menu. This produces a prompt that will allow the app to run. I had to repeat this step for several files in the folder and then clangd ran for me.

I hope this is useful to anyone who experiences the same issue and thanks again Ivan for pointing me in the right direction!

igrr commented 1 year ago

xattr: /usr/bin/clangd: No such xattr: com.apple.quarantine

This means that you are checking the OS-provided copy of clangd (in /usr/bin), not the ESP-specific one. When I wrote the command which clangd above, I have assumed that you had the esp-specific clangd already added to PATH, so which command would return the path to it. Good to know you have managed to solve the issue though, thanks for leaving the comment!

andychess commented 1 year ago

Yes, I only noticed that after I had posted my response. Nevertheless, your answer pointed me in the right direction and everything is working fine now. I'm really pleased with the result. Thank you :-).

xjzi commented 1 year ago

I was able to get clangd working without errors:

  1. idf_tools.py install esp-clang to download the ESP-architecture clang build including clangd.
  2. Configure your editor to use the new clangd binary at $HOME/.espressif/tools/esp-clang/15.0.0-23786128ae/esp-clang/bin/clangd
  3. Allow-list the gcc binary specified in compile_commands.json in the command: field by configuring your editor to run clangd with --query-driver=$HOME/.espressif/tools/xtensa-esp32s3-elf/esp-12.2.0_20230208/xtensa-esp32s3-elf/bin/xtensa-esp32s3-elf-gcc or whatever the binary is. Make sure it's an absolute path without "~".
  4. Remove compile flags that gcc has but clang doesn't using .clangd:
    CompileFlags:
    Remove: [-fno-tree-switch-conversion, -fstrict-volatile-bitfields]
joaotavora commented 1 year ago

@xjzi See also https://github.com/clangd/clangd/issues/537#issuecomment-1593697983

  1. I didn't need any special esp-clang installation.
  2. Just used normal clangd installed by my OS (Arch)
  3. You may be misinterpreting the meaning of --query-driver, which is understandable as it is a bit confusing. It is a glob for whitelisting what presumably is already a precise/correct compilation database in compile_commands.json. Check the linked issue closely.
  4. This was useful. In my case Iadded -mlongcalls to that list.
igrr commented 1 year ago

See the -mlong-calls argument there? I had to change it manually from -mlongcalls across the whole compile_commands.json

@joaotavora please note that these arguments aren't the same thing. -mlongcalls is an argument of Xtensa compiler, while -mlong-calls is an argument of an x86 compiler.

I am not sure the recommendation to use clang targeting x86-64 instead of Xtensa is helpful. If you do so, various macro expansions which depend on architecture (such as #if __XTENSA__) won't be done correctly. The other important problem with this approach is that type definitions (uint32_t, ptrdiff_t and others) also differ between compiler targets. I would recommend using the espressif builds of clang toolchain as they do support Xtensa architecture. The latest clang based toolchain releases also contain all the system headers, so you won't have unresolved header issues if you use it.

joaotavora commented 1 year ago

@joaotavora please note that these arguments aren't the same thing. -mlongcalls is an argument of Xtensa compiler, while -mlong-calls is an argument of an x86 compiler.

Right. Your pasting from the other post and a part which i myself noted was the wrong fix. Later on, if you read the full post, or just read above, you'll notice I just removed it from static analysis. As far as I can understand it only affects code generation and thus is useless for Clangd ally. So even the wrong -mlong-calls or some other completely different option would have no effect for LSP purposes.

The latest clang based toolchain releases also contain all the system headers, so you won't have unresolved header issues if you use it.

As far as I was able to tell from my experience, which so far has been positive --and admittedly limited -- Clangd actually calls the compiler with something like echo | foo-gcc -x c++ -E - to know what the system headers are. I wrote that from memory, so it might be off.

Anyway, that's what's its --query-driver option is for: instead of necessitating a full custom build of the whole toolchain for clang, ask any existing toolchain what makes it different in terms of defines and include paths. I think it's a neat and elegant idea. Time will tell how flawed it is, but so far so good.

I would recommend using the espressif builds of clang toolchain as they do support Xtensa architecture.

Building with xtensa GCC is fine, and so would building with xtensa Clang probably. But in this case i just need clangd, the language server, to understand the code and give me intellisense, etc.

Contrary to most other tools for this architecture (compiler, debugger, linker, disassembler) there shouldn't be a need for a special build of the code analysis toolkit.

igrr commented 1 year ago

@joaotavora Interesting, thanks for sharing this! Have you noticed any issues with resolution of types, e.g. due to different definitions of built-in types between the host and target architectures? For example, do you get correct resolution of something like static_assert(sizeof(void*) == 4); with the --query-driver approach? (assuming a 64-bit host and a 32-bit target.)

This is something we ran into with clang-tidy; even with all correct the system headers provided, the differences in evaluation of built-in types prevented clang-tidy from correctly analyzing Xtensa code. On the other hand, clang-tidy built from a version of Clang with Xtensa support had no issue with that.

joaotavora commented 1 year ago

@Igrr So far no problem -- though I've only been dabling around in relatively small ESP-IDF projects. But so far so good, completion and analysis match exactly (though with different compiler messages), what the gcc based compilation also reports.

To get around the types defined with __XTENSA and to teach clangd about the 32bit little endian nature of the system, this is the full contents my .clangd which lives at the root of my project (besides the usual .git folder):

CompileFlags:
    Add: [ -D__XTENSA__, --target=mipsel ]
    Remove: [-fno-tree-switch-conversion, -fstrict-volatile-bitfields, -mlongcalls]

Yes, I know mipsel isn't really the target, but it doesn't seem to matter (EDIT: and yes, your static_assert passes cleanly).

For reference here's a fragment of my compile_commands.json which I think is generated automatically by ESP-IDF's Cmake toolchain. Other then eliding some local filesystem details, I haven't touched it.

{
  "directory": "<ELIDED>/build/esp-idf/xtensa",
  "command": "<HOME>/.espressif/tools/xtensa-esp32-elf/esp-2022r1-11.2.0/xtensa-esp32-elf/bin/xtensa-esp32-elf-gcc -DSOC_MMU_PAGE_SIZE=CONFIG_MMU_PAGE_SIZE -I<ELIDED>/build/config -I/opt/esp-idf/components/xtensa/include -I/opt/esp-idf/components/xtensa/esp32/include -I/opt/esp-idf/components/newlib/platform_include -I/opt/esp-idf/components/freertos/FreeRTOS-Kernel/include -I/opt/esp-idf/components/freertos/esp_additions/include/freertos -I/opt/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/include -I/opt/esp-idf/components/freertos/esp_additions/include -I/opt/esp-idf/components/esp_hw_support/include -I/opt/esp-idf/components/esp_hw_support/include/soc -I/opt/esp-idf/components/esp_hw_support/include/soc/esp32 -I/opt/esp-idf/components/esp_hw_support/port/esp32/. -I/opt/esp-idf/components/esp_hw_support/port/esp32/private_include -I/opt/esp-idf/components/heap/include -I/opt/esp-idf/components/log/include -I/opt/esp-idf/components/soc/include -I/opt/esp-idf/components/soc/esp32/. -I/opt/esp-idf/components/soc/esp32/include -I/opt/esp-idf/components/hal/esp32/include -I/opt/esp-idf/components/hal/include -I/opt/esp-idf/components/hal/platform_port/include -I/opt/esp-idf/components/esp_rom/include -I/opt/esp-idf/components/esp_rom/include/esp32 -I/opt/esp-idf/components/esp_rom/esp32 -I/opt/esp-idf/components/esp_common/include -I/opt/esp-idf/components/esp_system/include -I/opt/esp-idf/components/esp_system/port/soc -I/opt/esp-idf/components/esp_system/port/include/private -mlongcalls -Wno-frame-address  -ffunction-sections -fdata-sections -Wall -Werror=all -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wextra -Wno-unused-parameter -Wno-sign-compare -Wno-enum-conversion -gdwarf-4 -ggdb -Og -fmacro-prefix-map=<ELIDED>=. -fmacro-prefix-map=/opt/esp-idf=/IDF -fstrict-volatile-bitfields -Wno-error=unused-but-set-variable -fno-jump-tables -fno-tree-switch-conversion -DconfigENABLE_FREERTOS_DEBUG_OCDAWARE=1 -std=gnu17 -Wno-old-style-declaration -D_GNU_SOURCE -DIDF_VER=\\\"HEAD-HASH-NOTFOUND\\\" -DESP_PLATFORM -D_POSIX_READER_WRITER_LOCKS -o CMakeFiles/__idf_xtensa.dir/eri.c.obj -c /opt/esp-idf/components/xtensa/eri.c",
  "file": "/opt/esp-idf/components/xtensa/eri.c"
},

EDIT: Sorry I forgot to remind you that --query-driver is still needed when starting the /usr/bin/clangd program. The somewhat lazy invocation on my end is clangd --query-driver=**, which tells clangd that it's OK to invoke any command in compile_commands.json to get information about sysroots and so on. If you're more paranoid about security, you could replace the ** which a glob that more closely matches the path to the xtensa-esp32-elf-gcc and xtensa-esp32-elf-g++ programs.

This is something we ran into with clang-tidy; even with all correct the system headers provided, the differences in evaluation of built-in types prevented clang-tidy from correctly analyzing Xtensa code. On the other hand, clang-tidy built from a version of Clang with Xtensa support had no issue with that.

The clangd language server I have installed is the stock version available for Archlinux. version 16 is already available but I haven't yet felt the need to upgrade.

clangd version 15.0.7
Features: linux
Platform: x86_64-pc-linux-gnu

clangd leverages clang-tidy automatically as long as .clang-tidy is in your project root. I don't see any problems, likely because clangd is feeding it a correct AST and types (because of the .clangd file).

I see the main advantage in this approach that apparently you can use the latest and greatest clangd (with latest LSP features and bugfixes) without needing to compile a full cross-architecture LLVM/clangd yourself.

andreichalapco commented 1 month ago

I also battled this week with the language server and here are my tips ...

  1. Be sure to use a new esp-idf version (remove .espressif folder and install again)
  2. idf_tools.py install esp-clang (install esp-idf)
  3. When developing with c++ be sure to put --query-driver= $HOME/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20240305/xtensa-esp-elf/bin/xtensa-esp32-elf-g++ as extra flag for clangd. (this is done for esp32 ... for other architectures the setting could vary ... explore the folder for all binaries)

.clangd conf:

CompileFlags:
  Remove: [-fstrict-volatile-bitfields, -fno-tree-switch-conversion, -fno-shrink-wrap]

Still using these settings I get some errors when in the header file esp_log.h : inttypes.h not found ... Probably some isysroot option in .clangd could help, but I haven't found the solution.

I think it is also beneficial to the community to make a documentation about the language server use. Using a mac myself I found that using the clangd compiled with xtensa makes for a much better developer experience. Clion (using clangd as backend) out of the box also does not manage very well espressif projects so the language server setting here is very important.

KaeLL commented 1 week ago

Still using these settings I get some errors when in the header file esp_log.h : inttypes.h not found

Same problem, but I solved that by specifying gcc instead of g++ to the query-driver option. Next person who reads this working on a mixed C and C++ project should test providing both options to see if it works.

Ironically enough, when compiling my project with clang, clangd can't find some system headers, like stdbool.h or time.h, among others.

j0of commented 1 week ago

Hi, I just got started with the ESP32 today and followed a quick tutorial by Low Level Learning to get started with esp-idf. I'm using Neovim with the clangd LSP as my dev environment, but I'm getting a few errors in my source file even though the project compiles normally. I think my issue is related, so I'm going to comment here rather than opening a new issue. My CMakeLists.txt is this:

cmake_minimum_required(VERSION 3.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

My source file is just the default, but also has some errors from the LSP:

#include <stdio.h>
Diagnostics:
1. Unknown argument '-mlongcalls'; did you mean '-mlong-calls'? [drv_unknown_argument_with_suggestion]
2. Unknown argument: '-fno-shrink-wrap' [drv_unknown_argument]
3. Unknown argument: '-fstrict-volatile-bitfields' [drv_unknown_argument]
4. Unknown argument: '-fno-tree-switch-conversion' [drv_unknown_argument]
5. Included header stdio.h is not used directly (fix available) [unused-includes]

void app_main(void)
{

}

Can someone help me out? Apologies if there is already a solution here that I just didn't see... this thread is quite long 😅

andychess commented 1 week ago

I'm running Neovim / Espidf as well. Have you got a .clangd file in your root directory?

Mine looks like this:

CompileFlags: Remove: [-fno-tree-switch-conversion, -fno-shrink-wrap, -mtext-section-literals, -mlong-calls, -fstrict-volatile-bitfields, -march=rv32imac_zicsr_zifencei]

The last one is necessary for the RISCV chips.

j0of commented 1 week ago

@andychess This is great, thanks! Have you also encountered errors with including FreeRTOS before? I'm getting In included file: 'machine/endian.h' file not found on the line #include <freertos/FreeRTOS.h>. I did some digging and found this, but it didn't work for me.

andychess commented 1 week ago

@j0of Glad it helped :-). Problems with the headers seem to be very common with clang. You probably need a section like this in the lsp config section of nvim. Mine looks like this at the moment.

1 require("mason").setup() 1 require("mason-lspconfig").setup() 2 3 -- vim.lsp.set_log_level 'error' 4 -- vim.lsp.set_log_level 'debug' 5 vim.lsp.set_log_level 'off' 6 7 require('vim.lsp.log').set_format_func(vim.inspect) 8 9 -- Setup language servers. 10

31 require('lspconfig').clangd.setup { 32 virtual_text = false, 33 cmd = { "/Users/andy/.espressif/tools/esp-clang/16.0.1-fe4f10a809/esp-clang/bin/clangd", 34 "--background-index", 35 }, 36 filetype = { "c", "cpp" }, 37 } 38 -- 39 require('lspconfig').pyright.setup{}

I've had to use a lot of trial and error with this as nvim seems to be very sensitive to syntax issues. In theory, it should be possible to use wildcards, but for some reason I often have to put in the full path. Perhaps someone can supply a bit more background to this?

The link you supplied was very interesting btw. I didn't know about the line in export.sh.

j0of commented 1 week ago

After a few more changes to the config, I finally got it to work, and it only took 2 days! 😂 Thanks again for the helpful replies @andychess . I'm gonna leave my solution here, partially to help any others who need help, but mainly to save future me from a massive headache if I somehow find myself in the same situation in the future.

Prerequisites

# Install & export idf stuffs
apt update && apt install -y clang-tidy
pip install -U pyclang # dunno if this is necessary; i just took this from another person's solution :v) )
idf_tools.py install esp-clang
. ${IDF_PATH}/export.sh

.clangd config

CompileFlags:
  Remove: [-fno-tree-switch-conversion, -fno-shrink-wrap, -mtext-section-literals, -mlongcalls, -fstrict-volatile-bitfields, -march=rv32imac_zicsr_zifencei]

Neovim Lspconfig

local lspconfig = require("lspconfig")
local capabilities = require('cmp_nvim_lsp').default_capabilities()
lspconfig.clangd.setup({
  handlers = handlers,
  capabilities = capabilities;
  cmd = { "/home/joof/.espressif/tools/esp-clang/esp-17.0.1_20240419/esp-clang/bin/clangd", "--background-index", "--query-driver=**", },
  root_dir = function()
      -- leave empty to stop nvim from cd'ing into ~/ due to global .clangd file
  end
})