如何编译 llvm 20 用上 import std 并配置 clangd 跳转
前言
最近想写个新项目造点轮子练练手,打算用上C++20的modules能力,不过既然都用模块了,没有import std
也是差点意思。由于我用的是 openSUSE Tumbleweed,直接可以sudo zypper in gcc15
用上gcc-15.1,但是有个痛点是无论cmake还是xmake生成的compile_commands.json
,clangd 都无法跳转。
后来用zypper 装了个clang20,但是由于我的clangd是自己手动安装的,会提示一个分支不匹配的错误:
Module file '/usr/share/libc++/v1/std.pcm' built from a different branch () than the compiler ((https://github.com/llvm/llvm-project47addd4540b4c393e478ba92bea2589e330c57fb))clang(ast_file_different_branch)
所以决定从头编译llvm工具链,这篇文章做个记录。
以下内容大部分由 claude + cursor 完成
第一步:清理现有环境
1. 停止相关进程
sudo pkill clangd 2>/dev/null || true
1.2 删除手动安装的 LLVM(如果有)
sudo rm -rf /usr/local/bin/clang*
sudo rm -rf /usr/local/bin/llvm-*
sudo rm -rf /usr/local/bin/ld.lld
sudo rm -rf /usr/local/include/c++
sudo rm -rf /usr/local/lib/clang
sudo rm -rf /usr/local/lib/libc++*
sudo rm -rf /usr/local/lib/libclang*
sudo rm -rf /usr/local/lib/libLLVM*
sudo rm -rf /usr/local/lib/libLTO*
sudo rm -rf /usr/local/lib/libRemarks*
sudo rm -rf /usr/local/lib/x86_64-unknown-linux-gnu
sudo rm -rf /usr/local/share/clang
sudo rm -rf /usr/local/share/libc++
1.3 清理配置文件
sudo rm -f /etc/ld.so.conf.d/llvm-local.conf
sudo ldconfig
1.4 清理环境变量
# 从 shell 配置文件中移除相关配置
sed -i '/\/usr\/local.*clang\|\/usr\/local.*llvm\|LD_LIBRARY_PATH.*usr\/local/d' ~/.bashrc ~/.zshrc 2>/dev/null
1.5 卸载通过包管理器安装的 clang
sudo zypper remove clang clang-devel libc++ libc++-devel 2>/dev/null || true
1.6 删除旧的源码目录
rm -rf ~/llvm-project
第二步:安装编译依赖
sudo zypper install -t pattern devel_basis
sudo zypper install cmake ninja python3 git
第三步:下载 LLVM 源码
# 克隆 LLVM 项目
git clone https://github.com/llvm/llvm-project.git
# 切换到 release 分支
cd llvm-project
git checkout release/20.x
第四步:配置编译选项
mkdir build && cd build
# 配置 CMake
cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" \
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
-DCLANG_ENABLE_STATIC_ANALYZER=ON \
-DCLANG_ENABLE_ARCMT=ON \
-DCLANG_BUILD_EXAMPLES=OFF \
-DLIBCXX_ENABLE_MODULES=ON \
-DLIBCXX_ENABLE_STD_MODULES=ON \
-DLIBCXX_ENABLE_INCOMPLETE_FEATURES=ON \
-DLIBCXXABI_USE_LLVM_UNWINDER=ON \
-DCLANG_ENABLE_MODULES=ON \
-DCLANG_DEFAULT_CXX_STDLIB=libc++ \
-DLLVM_INSTALL_UTILS=ON \
../llvm
配置说明:
DLIBCXX_ENABLE_MODULES=ON
: 启用 libc++ 模块支持DLIBCXX_ENABLE_STD_MODULES=ON
: 启用 std 标准库模块DLIBCXXABI_USE_LLVM_UNWINDER=ON
: 使用 LLVM 的 unwinderDCLANG_DEFAULT_CXX_STDLIB=libc++
: 设置 libc++ 为默认标准库
第五步:编译
ninja -j$(nproc)
注意: 编译过程可能需要几个小时,确保有足够的磁盘空间(至少20GB)。
第六步:安装
sudo ninja install
第七步:配置环境
7.1 配置动态库路径
sudo tee /etc/ld.so.conf.d/llvm-local.conf << 'EOF'
/usr/local/lib
/usr/local/lib/x86_64-unknown-linux-gnu
EOF
sudo ldconfig
7.2 配置 PATH
echo 'export PATH=/usr/local/bin:$PATH' >> ~/.zshrc
source ~/.zshrc
第八步:验证基本安装
# 验证工具版本
clang++ --version
clangd --version
# 测试基本编译
cat > test_basic.cpp << 'EOF'
#include <iostream>
int main() {
std::cout << "Hello, clean LLVM!" << std::endl;
return 0;
}
EOF
clang++ -std=c++23 -stdlib=libc++ test_basic.cpp -o test_basic
./test_basic
第九步:预编译标准库模块
9.1 检查模块文件
# 检查模块配置
cat /usr/local/lib/x86_64-unknown-linux-gnu/libc++.modules.json
# 检查模块源文件
ls -la /usr/local/share/libc++/v1/
9.2 预编译 std 模块
cd ~
# 在用户目录预编译(避免权限问题导致的崩溃)
clang++ -std=c++23 -stdlib=libc++ \
-resource-dir=/usr/local/lib/clang/20 \
--precompile /usr/local/share/libc++/v1/std.cppm \
-o ./std.pcm
# 移动到系统目录
sudo mv ./std.pcm /usr/local/share/libc++/v1/
9.3 预编译 std.compat 模块
# 预编译 std.compat(需要已编译的 std 模块)
clang++ -std=c++23 -stdlib=libc++ \
-resource-dir=/usr/local/lib/clang/20 \
-fprebuilt-module-path=/usr/local/share/libc++/v1 \
--precompile /usr/local/share/libc++/v1/std.compat.cppm \
-o ./std.compat.pcm
# 移动到系统目录
sudo mv ./std.compat.pcm /usr/local/share/libc++/v1/
9.4 验证模块文件
ls -la /usr/local/share/libc++/v1/*.pcm
file /usr/local/share/libc++/v1/*.pcm
第十步:测试 C++23 模块功能
10.1 创建测试文件
cat > test_cpp23_modules.cpp << 'EOF'
import std;
import std.compat;
int main() {
std::println("=== C++23 Modules Test ===");
// 测试 std 模块
std::vector<int> numbers{1, 2, 3, 4, 5};
auto even_doubled = numbers
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * 2; });
std::print("Even numbers doubled: ");
for (auto n : even_doubled) {
std::print("{} ", n);
}
std::println("");
// 测试 std.compat 模块
double x = 3.14159 / 4;
std::println("sin(π/4) = {:.6f}", std::sin(x));
// 测试 C 兼容性功能
char buffer[50];
std::strcpy(buffer, "C++ modules work!");
std::println("C-style string: {}", buffer);
std::println("=== All tests passed! ===");
return 0;
}
EOF
10.2 编译和运行测试
# 编译测试程序
clang++ -std=c++23 -stdlib=libc++ \
-fprebuilt-module-path=/usr/local/share/libc++/v1 \
test_cpp23_modules.cpp -o test_cpp23_modules
# 运行测试
./test_cpp23_modules
第十一步 使用vscode+xmake开始写代码
10.1 安装xmake
curl-fsSL https://xmake.io/shget.text|bash
10.2 创建vscode项目
mkdir hello_world
# 使用vscode打开
code hello_world
10.3 项目结构
hello_world
├── src
│ └── main.cc
└── xmake.lua
10.4 编写代码和构建脚本
// main.cc
import std;
int main() {
std::println("Hello, World!");
return 0;
}
// xmake.lua
target("hello")
set_languages("c++23")
set_kind("binary")
set_toolchains("clang")
add_files("src/main.cc")
-- add_cxxflags("-stdlib=libc++")
-- add_ldflags("-stdlib=libc++")
add_cxxflags("-fprebuilt-module-path=/usr/local/share/libc++/v1")
10.5 配置clangd
先安装vscode的clangd插件,然后编写vscode配置文件
// .vscode/settings.json
{
"clangd.arguments": [
"--compile-commands-dir=${workspaceFolder}",
"--experimental-modules-support"
]
}
然后使用xmake生成compile_commands.json
xmake project -k compile_commands
10.6 编译和运行
xmake build
xmake run
更新工具链
要更新到新版本:
进入源码目录:
cd ~/llvm-build-clean/llvm-project
拉取最新代码:
git pull
重新编译:
cd build && ninja && sudo ninja install
重新预编译模块(步骤九)
附录
让cursor生成了一个morden c++的demo,编译和跳转都没问题
import std;
// C++23 概念:定义可处理的数据类型约束
template<typename T>
concept Processable = requires(T t) {
{ t + t } -> std::convertible_to<T>;
{ t * 2 } -> std::convertible_to<T>;
std::totally_ordered<T>;
};
// C++23 概念:异步生成器约束
template<typename T>
concept AsyncGeneratorType = requires(T t) {
typename T::promise_type;
{ t.begin() };
{ t.end() };
};
// 现代化的数据结构,使用结构化绑定
struct ProcessingResult {
std::size_t processed_count;
double average_value;
std::chrono::milliseconds processing_time;
// C++23 比较运算符自动生成
auto operator<=>(const ProcessingResult&) const = default;
};
// 协程:异步数据生成器
template<Processable T>
class AsyncDataGenerator {
public:
struct promise_type {
T current_value{};
auto get_return_object() {
return AsyncDataGenerator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
auto initial_suspend() { return std::suspend_always{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
auto yield_value(T value) {
current_value = value;
return std::suspend_always{};
}
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
using handle_type = std::coroutine_handle<promise_type>;
explicit AsyncDataGenerator(handle_type h) : coro_handle(h) {}
~AsyncDataGenerator() {
if (coro_handle) {
coro_handle.destroy();
}
}
// 移动语义支持
AsyncDataGenerator(AsyncDataGenerator&& other) noexcept
: coro_handle(std::exchange(other.coro_handle, {})) {}
AsyncDataGenerator& operator=(AsyncDataGenerator&& other) noexcept {
if (this != &other) {
if (coro_handle) {
coro_handle.destroy();
}
coro_handle = std::exchange(other.coro_handle, {});
}
return *this;
}
// 删除拷贝构造和赋值
AsyncDataGenerator(const AsyncDataGenerator&) = delete;
AsyncDataGenerator& operator=(const AsyncDataGenerator&) = delete;
// 迭代器支持
class iterator {
handle_type coro_;
public:
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
explicit iterator(handle_type h) : coro_(h) {}
iterator& operator++() {
coro_.resume();
if (coro_.done()) {
coro_ = {};
}
return *this;
}
T operator*() const {
return coro_.promise().current_value;
}
bool operator==(const iterator& other) const {
return coro_ == other.coro_;
}
};
iterator begin() {
if (coro_handle) {
coro_handle.resume();
if (coro_handle.done()) {
return iterator{handle_type{}};
}
}
return iterator{coro_handle};
}
iterator end() {
return iterator{handle_type{}};
}
private:
handle_type coro_handle;
};
// 协程生成器函数
template<Processable T>
AsyncDataGenerator<T> generate_fibonacci_sequence(std::size_t count) {
T a = 0, b = 1;
for (std::size_t i = 0; i < count; ++i) {
co_yield a;
auto next = a + b;
a = b;
b = next;
// 模拟异步延迟
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
// 现代化的异步处理器类
template<Processable T>
class AsyncDataProcessor {
private:
std::unique_ptr<std::vector<T>> buffer_;
mutable std::shared_mutex mutex_;
public:
AsyncDataProcessor() : buffer_(std::make_unique<std::vector<T>>()) {}
// 异步处理函数,使用协程
std::future<ProcessingResult> process_async(AsyncDataGenerator<T> generator) {
return std::async(std::launch::async, [this, gen = std::move(generator)]() mutable -> ProcessingResult {
auto start_time = std::chrono::high_resolution_clock::now();
// 使用范围库进行函数式处理
std::vector<T> data;
// 收集数据
for (auto value : gen) {
data.push_back(value);
}
// 使用C++23范围库进行数据转换和过滤
auto processed_data = data
| std::views::filter([](const T& x) { return x > 0; })
| std::views::transform([](const T& x) { return x * 2; })
| std::views::take(10);
// 计算统计信息
std::vector<T> result_vec;
for (auto value : processed_data) {
result_vec.push_back(value);
}
{
std::unique_lock lock(mutex_);
*buffer_ = result_vec;
}
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
double average = result_vec.empty() ? 0.0 :
std::accumulate(result_vec.begin(), result_vec.end(), 0.0) / result_vec.size();
return ProcessingResult{
.processed_count = result_vec.size(),
.average_value = average,
.processing_time = duration
};
});
}
// 获取处理后的数据(只读访问)
std::vector<T> get_processed_data() const {
std::shared_lock lock(mutex_);
return *buffer_;
}
};
// 现代化的格式化输出函数
template<Processable T>
void print_results(const ProcessingResult& result, const std::vector<T>& data) {
std::println("=== 处理结果 ===");
std::println("处理数量: {}", result.processed_count);
std::println("平均值: {:.2f}", result.average_value);
std::println("处理时间: {}ms", result.processing_time.count());
std::println("\n处理后的数据:");
for (std::size_t index = 0; index < data.size(); ++index) {
std::print("{:2}: {:6} ", index, data[index]);
if ((index + 1) % 5 == 0) std::println("");
}
if (!data.empty() && data.size() % 5 != 0) std::println("");
}
// 主异步演示函数
std::future<void> run_modern_cpp_demo() {
return std::async(std::launch::async, []() {
std::println("🚀 C++23 现代化特性演示");
std::println("展示特性: 模块、协程、概念、范围、格式化、智能指针、结构化绑定\n");
try {
// 创建异步数据处理器
auto processor = std::make_unique<AsyncDataProcessor<long long>>();
// 生成斐波那契数列
std::println("📊 生成斐波那契数列...");
auto generator = generate_fibonacci_sequence<long long>(15);
// 异步处理数据
std::println("⚡ 异步处理数据...");
auto future_result = processor->process_async(std::move(generator));
// 等待处理完成
auto result = future_result.get();
// 获取处理后的数据
auto processed_data = processor->get_processed_data();
// 使用结构化绑定解包结果
auto [count, average, time] = result;
// 格式化输出结果
print_results(result, processed_data);
// 展示更多现代C++特性
std::println("\n🔍 额外的现代特性演示:");
// 使用std::expected (C++23)进行错误处理
auto safe_divide = [](double a, double b) -> std::expected<double, std::string> {
if (b == 0.0) {
return std::unexpected("除零错误");
}
return a / b;
};
if (auto division_result = safe_divide(average, 2.0)) {
std::println("安全除法结果: {:.2f}", *division_result);
} else {
std::println("错误: {}", division_result.error());
}
// 使用std::format进行复杂格式化
auto summary = std::format(
"总结: 在{}ms内处理了{}个数据点,平均值为{:.2f}",
time.count(), count, average
);
std::println("\n📋 {}", summary);
} catch (const std::exception& e) {
std::println("❌ 异常: {}", e.what());
}
std::println("\n✅ 演示完成!");
});
}
// 主函数
auto main() -> int {
try {
// 运行现代C++演示
auto demo_future = run_modern_cpp_demo();
demo_future.wait();
return 0;
} catch (const std::exception& e) {
std::println("💥 程序异常: {}", e.what());
return 1;
} catch (...) {
std::println("💥 未知异常");
return 1;
}
}