Skip to content

Instantly share code, notes, and snippets.

@Strus
Last active July 12, 2024 14:38
Show Gist options
  • Save Strus/042a92a00070a943053006bf46912ae9 to your computer and use it in GitHub Desktop.
Save Strus/042a92a00070a943053006bf46912ae9 to your computer and use it in GitHub Desktop.
How to use clangd C/C++ LSP in any project

How to use clangd C/C++ LSP in any project

tl;dr: If you want to just know the method, skip to How to section

Clangd is a state-of-the-art C/C++ LSP that can be used in every popular text editors like Neovim, Emacs or VS Code. Even CLion uses clangd under the hood. Unfortunately, clangd requires compile_commands.json to work, and the easiest way to painlessly generate it is to use CMake.

For simple projects you can try to use Bear - it will capture compile commands and generate compile_commands.json. Although I could never make it work in big projects with custom or complicated build systems.

But what if I tell you you can quickly hack your way around that, and generate compile_commands.json for any project, no matter how compilcated? I have used that way at work for years, originaly because I used CLion which supported only CMake projects - but now I use that method succesfully with clangd and Neovim.

Method summary

Basically what we need to achieve is to create a CMake file that will generate a compile_commands.json file with information about:

  1. All source files
  2. All include directories
  3. External libraries
  4. Precompiler definitions

We can do that easily without really caring about if the CMake-generate result will compile at all - we don't need to rewrite our existing build system, just hack a CMake file that will generate enough information for Clangd to work.

Prerequisities

  1. CMake
  2. clangd

How to

First, create a CMakeLists.txt file in the root folder of your projects, with content similar to this:

cmake_minimum_required(VERSION 3.8)
project(my_project)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Change path from /src if needed, or add more directories
file(GLOB_RECURSE sources
        "${CMAKE_SOURCE_DIR}/src/*.c"
        "${CMAKE_SOURCE_DIR}/src/*.cpp"
        )
# Add precompiler definitions like that:
add_definitions(-DSOME_DEFINITION)

add_executable(my_app ${sources})

# Add more include directories if needed
target_include_directories(my_app PUBLIC "{CMAKE_SOURCE_DIR}/include")

# If you have precompiled headers you can add them like this
target_precompiled_headers(my_app PRIVATE "${CMAKE_SOURCE_DIR}/src/pch.h")

(If your project already uses CMake, then you just need to add set(CMAKE_EXPORT_COMPILE_COMMANDS ON) to your main CMakeLists.txt file.)

Modify hacky CMakeLists.txt according to your project structure, and run:

cmake -S . -G "Unix Makefiles" -B cmake

which will generate the CMake output inside cmake directory. Check if compile_commands.json is there.

NOTE: You need to run that command every time you add/remove a source file in your project.

If you need more (ex. include external libraries like Boost), check out CMake documentation

Now you have two options:

  1. Symlink compile_commands.json to your root project folder:
ln -s cmake/compile_commands.json .

OR

  1. Create .clangd file in your root project folder, with the following contents:
CompileFlags:
  CompilationDatabase: "cmake"

Now open the project in you editor and everything should work (assuming clangd LSP is started).

@Strus
Copy link
Author

Strus commented Jun 26, 2024

@PowerUser64 Yeah, I know this tool - unfortunately I was never able to use it in practice. It works for small projects with simple build systems, but most big C/C++ projects that use Makefiles or some custom mambo-jumbo make it very hard to inject bear there. At work I always ended up with a hack CMake like this.

But I will add a mention that for simple projects one could try bear or similar tools.

BTW, in your example - if you use cmake there is no need to use bear. Just add set(CMAKE_EXPORT_COMPILE_COMMANDS ON) to your main CMakeLists.txt.

@PowerUser64
Copy link

if you use cmake [...], just add set(CMAKE_EXPORT_COMPILE_COMMANDS ON) to your main CMakeLists.txt.

Ooh, I didn't know that! Thought the other stuff in the example was somehow required too. Might be good to mention that on its own too somewhere. Thanks for the tip!

@Strus
Copy link
Author

Strus commented Jun 26, 2024

Good idea, I've added a mention about that.

@ktgon
Copy link

ktgon commented Jul 3, 2024

Thank you for this helpful post !!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment