Boost C++ Linting With Clangd: A Practical Guide
Hey everyone! I've been working on a script to streamline clangd linting setup on Linux, and I wanted to share it with you all. For those who aren't familiar, clangd is an incredibly useful Language Server Protocol (LSP) implementation that provides features like code completion, diagnostics, and formatting for C++ in editors like Vim, Emacs, and others. It's a game-changer for code quality and productivity.
The Script: A Deep Dive
This script is currently a bash script, but I'm open to rewriting it in Python if there's enough interest. It's designed to automate the process of setting up clangd for a specific project structure. Let's break down what it does:
- Copy
compile_commands.json
: The script starts by copying thecompile_commands.json
file from the./build
directory. This file is crucial for clangd as it contains information about how your project's files are compiled, including include paths and compiler flags. Without it, clangd wouldn't know how to properly parse your code. - Copy Docker Headers: Next, the script copies the
/ext/include
directory from a Docker image to the./build
directory. This step is important if your project relies on headers that are only available within a Docker container, ensuring clangd has access to all the necessary headers for accurate analysis. This ensures that clangd has access to all the necessary headers, even those that might be specific to a Docker environment. This is particularly helpful when working on projects that have dependencies managed within containers. - Reset Include Paths in
compile_commands.json
: The script then modifies thecompile_commands.json
file to adjust the include paths. This is often necessary because the paths within the Docker environment might not match the paths on your host machine. By resetting these paths, the script ensures that clangd can correctly locate the header files on your system. This step is crucial for making sure clangd understands the project structure in your local development environment. - Set Include Paths in
.clangd
: Finally, the script creates or modifies a.clangd
file in the project root. This file allows you to specify additional include paths that might be missing from thecompile_commands.json
file. This is a great way to add project-specific include paths that clangd might not automatically detect, ensuring it has a complete picture of your project's dependencies. The.clangd
file acts as a configuration file for clangd, allowing you to fine-tune its behavior.
Here's the script:
#!/bin/bash
set -e
cd "$(dirname \"$0\")" || exit 1
if [ \"$1\" != \"tr1\" ] && [ \"$1\" != \"tr2\" ]; then
echo \"You did not specify tr1 or tr2\"
exit 1
fi
cd build || { echo \"build tr1 or tr2 first\"; exit 1; }
cd \"$1\" || { echo \"build $1 first\"; exit 1; }
cd linux || exit 1
cp compile_commands.json ../../../
cd ../..
if [ ! -d ext/include ]; then
echo \"Copying /ext/include from Docker image...\"
mkdir -p ext
docker create --name tmpcopy rrdash/trx-linux:latest
docker cp tmpcopy:/ext/include \"$(pwd)/ext/include\"
docker rm tmpcopy
fi
cd ..
# You have to change /home/noisecode3/TRX for now..
python - <<'PYCODE'
import json
import os
with open(\"compile_commands.json\", \"r\") as f:
data = json.load(f)
for entry in data:
if entry[\"directory\"].startswith(\"/app/build/\"):
entry[\"directory\"] = entry[\"directory\"].replace(
\"/app/build/\", \"/home/noisecode3/TRX/build/\", 1
)
entry[\"command\"] = entry[\"command\"].replace(
\"-I/ext\", \"-I/home/noisecode3/TRX/build/ext\"
)
entry[\"command\"] = entry[\"command\"].replace(
\"../../../src\", \"/home/noisecode3/TRX/src\"
)
entry[\"file\"] = entry[\"file\"].replace(
\"../../../src\", \"/home/noisecode3/TRX/src\"
)
with open(\"compile_commands.json\", \"w\") as f:
json.dump(data, f, indent=2)
PYCODE
FLAGS=\"CompileFlags:
Add: [-xc, -I$(pwd)/src/tr1/subprojects/libtrx, -I$(pwd)/src/tr1/subprojects/libtrx/include/libtrx, -I$(pwd)/build/ext/include, -I$(pwd)/build/ext/include/SDL2] \""
echo \"$FLAGS\" > .clangd
Key Improvements and Considerations
The script currently looks for dependencies like uthash
on the host machine, rather than relying on the Docker headers. This could be improved by incorporating the Docker headers more thoroughly. I'm also considering making the script more robust and user-friendly. Would a rewrite in Python be beneficial? Let me know your thoughts!
One important thing to note is that you'll need to adjust the paths in the Python script to match your specific project structure. Currently, it includes hardcoded paths like /home/noisecode3/TRX
, which you'll need to update.
Warning Suppression: A Necessary Evil?
After getting clangd up and running, I've noticed a few warnings that seem to be quite prevalent in our codebase. These include:
- `W #pragma once â– No copyright message found. You should have a line: