Motivation
I’ve always wanted to get a language server working with the OpenCPI source code to make searching it a lot easier, but until I discovered a new tool this week I thought it was way too much effort to try to work on.
Introduction to LSP
For the unaware, a language server is the piece of software that lets your text editor do things like “Go to Definition”, or “Go to references”, or code actions like “Rename”. These let you avoid using more primitive mechanisms to achieve these results, like using grep
or rg
to find strings across the repo, or using sed
to do find and replace. These alternatives are inherently error prone, and can miss some occurrences or rename occurrences that aren’t related.
For many modern languages, these language servers are quite easy to configure due to the largely very predictable way the source code of the language is laid out (python and rust are really good examples of this). This is not the case for C or C++ projects that can layout their source in arbitrary ways, and can use a myriad of different build tools.
compile_commands.json
However, some tools can create files that help a language server parse a project. clang
based tooling can produce something called compile_commands.json
, which is a full list of all arguments given to all parts of the build process. From the perspective of a language server, this most usefully contains the -I
includes, allowing a dependency tree to be parsed for each file.
Unfortunately, as far as I could tell, there is no built in way for autotools
based flows (like OpenCPI) to generate this or a similar file.
Using bear
on the OpenCPI Runtime
Recently, I became aware of a tool called bear
. Very quick summary: bear
intercepts all processes related to C/C++ compilation that come out of an arbitrary build script and generates a compile_commands.json
. I thought I might as well try this out on OpenCPI, and it was incredibly easy to get working.
I decided to use the C++ LSP Server ccls
. My text editor is Neovim, so I used the setup available in nvim-lspconfig
. There is also an integration for VSCode. Other C++ LSP Servers are available, and I imagine can be integrated into other editors too.
To start, install your LSP Server and bear
. On Ubuntu 22.04:
sudo apt-get install -y bear ccls
You could run bear
on the whole scripts/install-opencpi.sh
script, but bear
slows down the build process it analyses so this may not be a good idea. Also, it will intercept the prerequisites
build processes as well, which could cause a very large compile_commands.json
file.
The main part of the OpenCPI build process that we want to intercept is the call to make framework
, but we would also like the make driver
part too. Honestly, the easiest way to do this is just to build OpenCPI once to completion and then rebuild the bits we need. There is probably a way to save time here but I already had a built copy, so:
# From OCPI_ROOT_DIR
./scripts/install-opencpi.sh --minimal # Only run this if you are in a fresh clone
source ./cdk/opencpi-setup.sh -r
make cleanframework cleandriver
bear -- make framework driver
This runs slower than the normal compile, as bear
is doing its magic in the background. If all goes well, compile_commands.json
will now exist in the OCPI_ROOT_DIR
. Now just open your editor with the compile_commands.json
compatible LSP Server installed and you should now have access to all of the LSP features.
I tested this with Neovim and VSCode configurations mentioned above and it worked!
My resulting compile_commands.json
file is 119,741 lines long! ccls
doesn’t seem to have a problem with this, and is very responsive in Neovim.
One extra thing. Obviously it’d be useful to have LSP working for all the RCC Workers that you care about. You could run your full build with bear
(bear -- ./scripts/install-opencpi.sh --minimal
) but this is going to take a while and intercept the prerequisites
files too.
Instead you can incrementally recompile just the stuff that you want LSP to work for, and use the --append
flag to bear
:
source ./cdk/opencpi-setup.sh -r
ocpidev clean -d projects/core/components/file_read.rcc
bear --append -- ocpidev build -d projects/core/components/file_read.rcc --no-doc
This adds to compile_commands.json
rather than overwriting it.
If anyone else tries this out and encounters difficulties, feel free to reply to this or direct message me for help