ESP32 uses either the RISC-V or Xtensa chips. I have the ESP32-DevKitC-VE which uses the Xtensa toolchain, so I will use that as the example here.
Setting up clangd
Install
clangd from your package manager of choice, and you
should be able to find it in $PATH. lsp-mode
can also download and install a copy under
$HOME/.emacs.d/cache/ but I generally prefer the system
package manager.
clangd requires a compiler driver with appropriate
flags. Instead of specifying all headers and flags manually, it can use
a compiler
command database which can be generated with cmake.
To do this with idf.py, simply add
CMAKE_EXPORT_COMPILE_COMMANDS=1 to your environment
variables:
$ CMAKE_EXPORT_COMPILE_COMMANDS=1 idf.py buildcompile_commands.json is expected to be in any of the
parent directories of the file you're editing. I put it in my project
root directory as a symbolic link, as the one generated by cmake above
is put under build/. In project root, run:
$ ln -s build/compile_commands.json compile_commands.jsonAs Xtensa uses gcc where some options may differ from
clang, I have added a .clangd file under
project root which is checked into vcs with the appropriate options.
CompileFlags:
Remove: [-mlongcalls, -fstrict-volatile-bitfields, -fno-tree-switch-conversion]The list of options was taken from my first run of lsp where
flycheck was complaining of unknown options.
Setting up lsp-mode
To setup Emacs to use lsp-mode
and its repertoire of helpers including company-mode and
flycheck-mode,
we need to install lsp-mode. I use use-package
here.
To have lsp-mode use the system clangd with
appropriate configurations, I have added lsp-clients-clangd-executable
and lsp-clients-clangd-args.
Since I have setup ESP32 toolchain
outside of default path, clangd will not use it as a query
driver to find system headers. Without the
--query-driver argument, clangd will first
find the appropriate compiler from the compile command database, but
will refuse it as it's not in default path. This is a security measure
to prevent running arbitrary executables and the user is expected to
"whitelist" the compiler driver to be used by clangd. The
driver can be any compiler compatible with gcc or
clangd. In this case, Xtensa uses the gcc
compiler.
Here's the Emacs lisp snippet to set up both lsp-mode
and lsp-ui (optional).
(use-package lsp-mode
:commands (lsp lsp-deferred)
:init
; to get lsp-mode going with xtensa
(setq lsp-clients-clangd-executable "clangd")
(setq lsp-clients-clangd-args '("--query-driver=/**/bin/xtensa-esp32-elf-*" "--background-index" "--header-insertion=iwyu" "-j=4" ))
:hook
(c-mode . lsp)
(lsp-mode . lsp-enable-which-key-integration))
(use-package lsp-ui
:commands lsp-ui-mode) Setup projectile for
running
You can also use projectile's lifecycle
commands to build and run the project with idf.py using
a per-directory
variable file. To do this, add a file named
.dir-locals.el to project root with the following
contents.
((nil . ((projectile-project-compilation-cmd . "idf.py build")
(projectile-project-run-cmd . "idf.py flash monitor"))))And that's pretty much it. To debug any issues that may arise, add
"--log=verbose" to lsp-client-clangd-args and
have a look at clangd buffers in Emacs.
This setup can theoretically be adapted for use with any
cross-compiler toolchain using either clang or
gcc as its compiler.