SerenityOS 的 clangd 语言服务器配置指南:compile_commands 数据库、跨编译器路径与 Include Cleaner 实战
【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity
clangd 是 SerenityOS(一个以 x86_64 / aarch64 / riscv64 为目标的类 Unix 操作系统)官方推荐的 C++ 语言服务器,它能基于 CMake 生成的compile_commands.json编译数据库理解整个仓库(AK、Userland、Kernel、Ladybird 等数十万行 C++ 代码)。本指南以仓库文档 Documentation/ClangdConfiguration.md 为骨架,结合 Toolchain/BuildClang.sh 与 Toolchain/CMake/LLVMConfig.cmake 等源码,完整讲解.clangd配置文件、编译数据库的生成时机、系统 clangd 与 Serenity 工具链 clangd 的取舍,以及跨编译器头文件路径的排查方法。读完本文,你将能在一小时内为任意编辑器配好可用的 SerenityOS C++ 开发环境,并看懂 clangd 报错背后的原因。
clangd 与 SerenityOS:为什么它是首选
clangd 是 LLVM 官方提供的 C++ 语言服务器(language server),通过 LSP 协议为编辑器提供补全、跳转、重命名、诊断等功能。官方建议在 SerenityOS 开发中优先选用 clangd,理由是它在各平台上“最可能开箱即用”(most likely to work on all platforms)。
- 如果你使用 CLion:CLion 自带一个经过特殊构建的 clangd,通常可以直接工作;
- 其他编辑器(VS Code、Vim/Neovim、Emacs、Helix 等):通过各自的 LSP 插件或专用扩展接入任意 clangd 二进制即可。
需要注意的是,SerenityOS 是一个交叉编译项目——它在宿主系统(如 Linux)上为 Serenity 目标(x86_64、aarch64、riscv64)构建内核与用户态程序。因此 clangd 需要两方面的信息才能正常工作:
- 编译数据库:每个源码文件用什么命令行编译(编译器路径、宏定义、头文件搜索路径);
- 交叉工具链的内建头文件:Serenity 专用 libc/libm 等头文件位于工具链 sysroot 中,系统 clangd 默认找不到。
这两点正是下文.clangd文件与--query-driver参数要解决的问题。
编译数据库(compile_commands.json):clangd 理解项目的根基
clangd 依赖compile_commands.json文件来理解项目结构。该文件由 CMake 在配置阶段自动生成,目录示例如下(对应不同架构/工具链组合):
Build/x86_64—— 使用系统 GCC 工具链的构建目录;Build/x86_64clang—— 使用 Serenity Clang 工具链的构建目录;Build/lagom—— Lagom(在宿主系统上构建并测试 Serenity 组件的子项目)的构建目录。
你日常使用哪种配置(架构 + 工具链),就把.clangd中的CompilationDatabase指向对应的构建目录。官方推荐优先使用Clang 工具链的构建目录(如Build/x86_64clang),因为 GCC 编译数据库与 clangd 存在已知兼容性问题(见下文“GCC 编译数据库的已知问题”)。
生成与更新时机:必须理解的关键约束
文档特别强调了一个容易踩坑的点——编译数据库不是实时更新的:
- 首次配置:至少运行一次
./Meta/serenity.sh run(该命令会触发配置与构建)来生成compile_commands.json; - 新增源文件或修改 CMake 编译选项后:必须重新运行
./Meta/serenity.sh build(或任何构建命令),CMake 才会重新生成编译数据库; - 在你重跑构建之前,clangd不会感知新源文件,或者会报告错误的编译错误。
也就是说,编辑 CMakeLists 或新增.cpp/.h后如果发现 clangd 行为异常,第一反应应该是重新构建,而不是怀疑配置。这与Meta/serenity.sh的构建流程(配置 → 编译 → 生成 compile commands)是一一对应的。
根目录.clangd文件:官方推荐配置逐行解读
在 Serenity 仓库根目录放置如下.clangd文件(注意:当前仓库不附带该文件,需开发者自行创建),官方称其“开箱即用”,可按需调整:
CompileFlags: # Add compilation flags to remove errors, or to make clangd behave like you’re compiling a specific system configuration. Add: [] # You can remove unwanted flags such as those that aren't supported by the current version of clang. Remove: [] # Build/x86_64 is also possible if you don’t have the Clang toolchain, but doesn’t work as well. CompilationDatabase: Build/x86_64clang Style: # clangd 20+: Use correct include style. AngledHeaders: ["AK/.*", "Userland/.*", "Kernel/.*", "Applications/.*", "Lib.*/.*"] Diagnostics: UnusedIncludes: Strict MissingIncludes: LooseCompileFlags:增删编译标志
| 配置项 | 含义与用法 |
|---|---|
Add: [] | 向每条编译命令追加标志,用于修复报错或模拟特定系统配置的编译行为(详见下节“实用技巧”) |
Remove: [] | 移除编译命令中不受当前 clang 版本支持的标志 |
CompilationDatabase: Build/x86_64clang | 指定编译数据库所在目录;换成Build/x86_64也可用(未安装 Clang 工具链时),但体验较差 |
Style.AngledHeaders:clangd 20+ 的包含风格控制
自 clangd 20 起,Style区块新增了AngledHeaders指令,用于让 clangd 在“插入缺失 include”时使用正确的尖括号风格。上例给出的正则列表:
"AK/.*"—— AK(Algorithms & data structures Kit)头文件,如AK/Vector.h;"Userland/.*"—— 用户态库与程序,如Userland/Libraries/LibCore/...;"Kernel/.*"—— 内核头文件;"Applications/.*"—— 应用程序目录;"Lib.*/.*"—— 所有Lib*前缀库的头文件,如LibGfx/...、LibWeb/...。
这些目录下的头文件都应使用尖括号形式#include <AK/Vector.h>而非引号形式。该指令取代了 clangd 19 及以下版本中必须使用的--header-insertion=never命令行参数(见下文),使 include 自动插入能按 Serenity 的目录风格正确工作。
Diagnostics:Include Cleaner(头文件清理器)控制
Diagnostics区块的两个标志用于禁用新版 clangd 自带的 Include Cleaner 特性:
UnusedIncludes: Strict—— 将“未使用的 include”提示级别设为严格(启用);MissingIncludes: Loose—— 将“缺失的 include”提示级别设为宽松(减弱)。
官方注释指出:这两个标志就是用来关闭 Include Cleaner 的;如果你不介意其产生的噪音 inlay 提示(inlay hints)与 problem 面板噪音,可以重新启用(例如改为UnusedIncludes: None或直接删除相关行)。“Strict”与“Loose”是对应诊断的严重度等级,Strict会把所有相关 case 都当作问题报告,Loose则只在比较确定时才报告。
CompileFlags 实用技巧:让 clangd 与真实编译行为对齐
文档给出四组非常实用的“加标志”技巧,用于修复 bug 与改善代码理解:
模拟 Serenity 目标而非宿主系统:如果你使用的不是 Serenity 工具链 clangd,请在
Add中加入-D__serenity__。这样 clangd 会把所有代码当作“为 Serenity 编译”来解析,而不是按你的宿主系统(如 Linux)来解析——否则大量#ifdef __serenity__分支会被错误裁剪,产生海量假报错。模拟内核/预内核编译:
Add: ["-DKERNEL"]让 clangd 按内核编译配置解析(Kernel/目录大量使用#ifdef KERNEL);Add: ["-DPREKERNEL"]则对应预内核(prekernel)编译配置。屏蔽 GCC 特有参数:当使用 GCC 编译数据库(如
Build/x86_64)时,clangd 常会在文件顶部抱怨 clang 不认识的命令行参数,典型报错如clang: Unknown argument: '-mpreferred-stack-boundary=3'。解决办法是给Add加上对应的否定形式-mno-preferred-stack-boundary(即-mno-<flag name>规则)。内核 GCC 数据库的额外已知项:对于内核的 GCC 编译数据库,
-mno-sse与-mno-8087两个否定参数通常能消除一批报错。
这些技巧的本质是:clangd 默认会尝试“吃掉”它不认识的参数,但对部分参数(尤其-m系列与-f系列)会直接报Unknown argument,用-mno-*/ 对应的否定形式即可优雅地中和掉。
clangd 命令行参数:系统 clangd 必须配置 --query-driver
--query-driver:找到交叉编译器的内建头文件
如果使用系统安装的 clangd(而非 Serenity 工具链自带的),必须让它能找到 Serenity 交叉编译器的内建 include 路径,否则永远会报形如file "<new>" not found的错误。命令行为:
--query-driver=SERENITY_PATH/Toolchain/Local/**/*其中SERENITY_PATH替换为 Serenity 源码目录的绝对路径。--query-driver的作用是:clangd 在遇到编译命令中的 GCC 风格编译器路径时,会按该 glob 匹配到的编译器实际执行一次查询(query),从而得知其内建头文件搜索路径与预定义宏。
编辑器通常提供路径占位符,例如 VS Code 的${workspaceFolder},可写成:
--query-driver=${workspaceFolder}/Toolchain/Local/**/*--header-insertion:clangd 19 及以下的必要开关
对于 clangd 19 及以下版本,强烈建议附加--header-insertion=never,以阻止 clangd 插入风格错误的 include(对应上游 clangd issue #1247)。因为旧版 clangd 无法识别 Serenity 这种“目录即命名空间”的头文件组织方式(#include <AK/String.h>),自动插入常生成带错误相对路径或引号形式的 include。
自 clangd 20 起,这个参数不再需要,取而代之的正是上文.clangd中的Style.AngledHeaders指令——它从配置层面告诉 clangd 哪些目录的头文件应使用尖括号风格,属于更正确、更持久的解决方案。
使用 Serenity 工具链自带 clangd(推荐)
Serenity 的 LLVM/Clang 工具链能构建一个了解 Serenity 目标及其配置的 clangd,官方总体上推荐使用它,原因有二:
- 它天然知道 x86_64/aarch64/riscv64 的 Serenity 目标配置,
__serenity__等宏与内建头文件路径无需任何额外配置; - 它“总是最新的”(always be up-to-date)——随仓库工具链版本同步演进。
代价是:必须先构建 Clang 工具链,构建步骤详见 Documentation/AdvancedBuildInstructions.md。
如何构建 Serenity-aware clangd
构建入口是 Toolchain/BuildClang.sh,按如下方式启用 clangd 组件:
cd Toolchain CLANG_ENABLE_CLANGD=ON ./BuildClang.shCLANG_ENABLE_CLANGD=ON环境变量是关键开关。仓库中的 Toolchain/CMake/LLVMConfig.cmake 明确实现了这一逻辑:
if(DEFINED ENV{CLANG_ENABLE_CLANGD} AND "$ENV{CLANG_ENABLE_CLANGD}" STREQUAL "ON") message(STATUS "Enabling clangd as a part of toolchain build") set(CLANG_ENABLE_CLANGD ON CACHE BOOL "" FORCE) else() message(STATUS "Disabling clangd as a part of toolchain build") set(CLANG_ENABLE_CLANGD OFF CACHE BOOL "" FORCE) endif()即:未设置该变量或值不是ON时,工具链构建默认不编译 clangd(与其他 libTooling 工具如 clang-format、clang-tidy 不同)。构建完成后,clangd 二进制位于Toolchain/Local/clang/bin/clangd,把编辑器指向该可执行文件即可。
构建注意事项(来自 AdvancedBuildInstructions 的警告):Clang 工具链构建期间机器可能严重变慢甚至短暂卡死,尤其是 CPU 核数多于可用内存(GB)时。可通过设置MAKEJOBS环境变量为小于 CPU 核数的数值来限制并行编译任务数,例如:
MAKEJOBS=4 CLANG_ENABLE_CLANGD=ON ./BuildClang.sh同目录下 Toolchain/CMake/LLVMConfig.cmake 还显示工具链同时为x86_64-serenity、aarch64-serenity、riscv64-serenity三个目标构建 runtime(foreach(target x86_64-serenity;aarch64-serenity;riscv64-serenity)),并为其设置 sysroot 与编译标志——这正是“Serenity-aware” clangd 能正确处理三个架构代码的原因。
已知问题与排查清单
文档列出两个官方确认的已知问题,排查时可按顺序核对:
1. 发行版 clangd 找不到交叉编译器内建头文件
部分发行版的 clangd 包在提供--query-driver后仍无法识别 Serenity 交叉编译器的内建 include 路径(至少在 Debian 上观察到)。当 inlay 提示显示<new>找不到时,官方建议按以下顺序“三重检查 + 四重检查”:
- 三重检查
.clangd配置是否与上文的推荐文件完全一致(尤其CompilationDatabase与CompileFlags); - 确认已通过
./Meta/serenity.sh run运行过系统(保证编译数据库与 sysroot 存在且最新); - 四重检查clangd 的命令行参数(
--query-driver的路径 glob 是否匹配Toolchain/Local/**/*,占位符是否被正确展开)。
若以上全部正确仍然失败,那么从 Serenity clang 工具链构建 clangd 是已知可行的最终方案(即上一节的CLANG_ENABLE_CLANGD=ON ./BuildClang.sh)。
2. clangd 在极端新特性下崩溃
clangd 有在“压榨编译器的前沿特性”时崩溃的倾向,官方举例:有时光是打开AK/Variant.h就足以触发崩溃。应对方法:
- 通常只需重启 clangd;
- 若重启无效,关闭当前打开的 C++ 文件再重启;
- 仍无效,可尝试切换 git 分支后再重启(有时有帮助)。
该问题与 Serenity 本身无关,而是 clangd 对复杂模板元编程(AK/Variant.h属于重度模板代码)的已知弱点,属于上游限制。
快速上手:最小可行配置清单
综合全文,一个全新的 SerenityOS 开发环境按以下顺序配置即可:
- 准备构建目录:执行
./Meta/serenity.sh run(至少一次),生成Build/x86_64clang/compile_commands.json;若使用 GCC 工具链则为Build/x86_64/; - 创建根目录
.clangd文件:采用上文官方推荐配置,将CompilationDatabase指向你最常用的构建目录;日常新增源码/改 CMake 后重跑./Meta/serenity.sh build刷新编译数据库; - 选择 clangd 来源:
- 快速方案:系统 clangd +
--query-driver=<SERENITY_PATH>/Toolchain/Local/**/*;clangd ≤ 19 追加--header-insertion=never; - 推荐方案:
CLANG_ENABLE_CLANGD=ON ./Toolchain/BuildClang.sh构建 Serenity-aware clangd,编辑器指向Toolchain/Local/clang/bin/clangd;
- 快速方案:系统 clangd +
- 按需微调:内核/预内核开发加
-DKERNEL/-DPREKERNEL;GCC 编译数据库报Unknown argument时用-mno-<flag>中和;非工具链 clangd 记得加-D__serenity__; - 遇到
<new>找不到等错误:按上文“已知问题”的检查顺序逐一核对。
按此流程,你就能获得接近真实编译行为的补全、跳转与诊断体验——这也是 SerenityOS 官方在 Documentation/ClangdConfiguration.md 中沉淀下来的最佳实践。
【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考