第 18 章:开发环境与构建系统
第 18 章:开发环境与构建系统
“好的开发环境让你专注于代码,而不是环境问题。”
18.1 Docker 开发环境
18.1.1 快速启动
# 使用预构建的 LLVM Docker 镜像
docker pull ubuntu:22.04
# 创建 Dockerfile
cat > Dockerfile << 'EOF'
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
# 安装 LLVM 工具链
RUN apt-get update && apt-get install -y \
wget gnupg2 software-properties-common \
build-essential cmake ninja-build python3 \
git curl ca-certificates \
&& wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | \
tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc \
&& echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" | \
tee /etc/apt/sources.list.d/llvm.list \
&& apt-get update && apt-get install -y \
llvm-18-dev clang-18 lld-18 lldb-18 \
clang-tools-18 libclang-18-dev libc++-18-dev \
&& rm -rf /var/lib/apt/lists/*
ENV PATH="/usr/lib/llvm-18/bin:${PATH}"
ENV LLVM_DIR="/usr/lib/llvm-18/lib/cmake/llvm"
ENV CC=clang-18
ENV CXX=clang++-18
WORKDIR /workspace
CMD ["/bin/bash"]
EOF
# 构建并运行
docker build -t llvm-dev .
docker run -it -v $(pwd):/workspace llvm-dev
18.1.2 多版本 Docker 环境
# docker-compose.yml
version: '3'
services:
llvm17:
build:
context: .
dockerfile: Dockerfile
args:
LLVM_VERSION: 17
volumes:
- .:/workspace
environment:
- LLVM_VERSION=17
llvm18:
build:
context: .
dockerfile: Dockerfile
args:
LLVM_VERSION: 18
volumes:
- .:/workspace
environment:
- LLVM_VERSION=18
llvm19:
build:
context: .
dockerfile: Dockerfile
args:
LLVM_VERSION: 19
volumes:
- .:/workspace
environment:
- LLVM_VERSION=19
# Dockerfile — 参数化版本
ARG LLVM_VERSION=18
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
wget gnupg2 software-properties-common \
build-essential cmake ninja-build python3 git \
&& wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | \
tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc \
&& echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${LLVM_VERSION} main" | \
tee /etc/apt/sources.list.d/llvm.list \
&& apt-get update && apt-get install -y \
llvm-${LLVM_VERSION}-dev clang-${LLVM_VERSION} \
lld-${LLVM_VERSION} lldb-${LLVM_VERSION} \
&& rm -rf /var/lib/apt/lists/*
ENV PATH="/usr/lib/llvm-${LLVM_VERSION}/bin:${PATH}"
WORKDIR /workspace
18.2 CMake 集成
18.2.1 使用 find_package
# CMakeLists.txt — 使用系统安装的 LLVM
cmake_minimum_required(VERSION 3.20)
project(MyLLVMProject)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 查找 LLVM
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
# 添加 LLVM 头文件路径
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
# 查找 LLVM 库
llvm_map_components_to_libnames(LLVM_LIBS
core
support
analysis
instcombine
transformutils
scalaropts
)
# 构建可执行文件
add_executable(my-tool main.cpp)
target_link_libraries(my-tool ${LLVM_LIBS})
18.2.2 使用 Clang 库
# 查找 Clang
find_package(Clang REQUIRED CONFIG)
include_directories(${CLANG_INCLUDE_DIRS})
add_definitions(${CLANG_DEFINITIONS})
add_executable(my-clang-tool clang_tool.cpp)
target_link_libraries(my-clang-tool
clangTooling
clangASTMatchers
clangFrontend
clangSerialization
clangDriver
clangParse
clangSema
clangAnalysis
clangAST
clangBasic
clangEdit
clangLex
clangRewrite
)
18.2.3 查找 LLVM 和 Clang 的完整模板
# cmake/FindLLVM.cmake (自定义查找)
# 或使用系统自带的 Config 模式
# LLVM 查找优先级:
# 1. CMAKE_PREFIX_PATH 环境变量
# 2. LLVM_DIR CMake 变量
# 3. 系统默认路径
# 使用示例
cmake -DLLVM_DIR=/opt/llvm/lib/cmake/llvm \
-DClang_DIR=/opt/llvm/lib/cmake/clang \
-S . -B build
18.2.4 生成 LLVM IR 的 CMake 工具
# 添加自定义命令生成 LLVM IR
function(add_llvm_ir_target target source)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target}.ll
COMMAND ${CMAKE_C_COMPILER} -S -emit-llvm
-o ${CMAKE_CURRENT_BINARY_DIR}/${target}.ll
${CMAKE_CURRENT_SOURCE_DIR}/${source}
DEPENDS ${source}
COMMENT "Generating LLVM IR for ${source}"
)
add_custom_target(${target}_ir DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/${target}.ll)
endfunction()
18.3 CI/CD 配置
18.3.1 GitHub Actions
# .github/workflows/llvm-test.yml
name: LLVM Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test-matrix:
runs-on: ubuntu-latest
strategy:
matrix:
llvm-version: [17, 18, 19]
build-type: [Release, Debug]
steps:
- uses: actions/checkout@v4
- name: Install LLVM ${{ matrix.llvm-version }}
run: |
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | \
sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${{ matrix.llvm-version }} main" | \
sudo tee /etc/apt/sources.list.d/llvm.list
sudo apt-get update
sudo apt-get install -y \
llvm-${{ matrix.llvm-version }}-dev \
clang-${{ matrix.llvm-version }} \
lld-${{ matrix.llvm-version }}
- name: Configure
run: |
cmake -G Ninja -S . -B build \
-DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
-DLLVM_DIR=/usr/lib/llvm-${{ matrix.llvm-version }}/lib/cmake/llvm \
-DClang_DIR=/usr/lib/llvm-${{ matrix.llvm-version }}/lib/cmake/clang
- name: Build
run: ninja -C build
- name: Test
run: ninja -C build test
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install clang-tidy
run: sudo apt-get install -y clang-tidy-18
- name: Run clang-tidy
run: |
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -S . -B build
clang-tidy-18 -p build src/*.cpp
18.3.2 GitLab CI
# .gitlab-ci.yml
stages:
- build
- test
- lint
variables:
LLVM_VERSION: "18"
.build_template: &build_template
image: ubuntu:22.04
before_script:
- apt-get update
- apt-get install -y wget gnupg2 cmake ninja-build build-essential
- wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
- echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${LLVM_VERSION} main" > /etc/apt/sources.list.d/llvm.list
- apt-get update
- apt-get install -y llvm-${LLVM_VERSION}-dev clang-${LLVM_VERSION} lld-${LLVM_VERSION}
build-release:
<<: *build_template
stage: build
script:
- cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release
- ninja -C build
artifacts:
paths:
- build/
test:
<<: *build_template
stage: test
script:
- ninja -C build test
dependencies:
- build-release
18.4 项目结构模板
my-llvm-project/
├── CMakeLists.txt # 顶层 CMake
├── README.md
├── .github/workflows/ # CI/CD
├── .clang-format # 代码格式配置
├── .clang-tidy # 静态分析配置
├── cmake/
│ ├── FindLLVM.cmake # LLVM 查找
│ └── utils.cmake # 工具函数
├── include/
│ └── myproject/
│ ├── Pass.h # Pass 头文件
│ └── Utils.h
├── lib/
│ ├── Pass.cpp # Pass 实现
│ └── Utils.cpp
├── tools/
│ ├── my-opt/ # 自定义 opt
│ │ ├── CMakeLists.txt
│ │ └── main.cpp
│ └── my-tool/ # 其他工具
├── test/
│ ├── lit.cfg.py # lit 测试配置
│ └── passes/
│ ├── my-pass.ll # IR 测试
│ └── my-pass.test
├── docs/
└── benchmarks/
18.4.1 顶层 CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyLLVMProject VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(LLVM REQUIRED CONFIG)
find_package(Clang REQUIRED CONFIG)
include_directories(${LLVM_INCLUDE_DIRS} ${CLANG_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
add_subdirectory(lib)
add_subdirectory(tools)
add_subdirectory(test)
18.5 测试框架 (lit)
LLVM 使用 lit 作为集成测试框架:
# test/lit.cfg.py
import lit.formats
config.name = "MyProject"
config.test_format = lit.formats.ShTest(True)
config.suffixes = ['.ll', '.c', '.cpp', '.test']
config.test_source_root = os.path.dirname(__file__)
# 设置工具路径
config.substitutions.append(('%my-opt', 'my-opt'))
config.substitutions.append(('%clang', 'clang-18'))
; test/passes/my-pass.ll
; RUN: my-opt -passes='my-pass' %s | FileCheck %s
; CHECK-LABEL: @test_function
; CHECK: %result = add i32 %a, %b
define i32 @test_function(i32 %a, i32 %b) {
entry:
%result = add i32 %a, %b
ret i32 %result
}
# 运行测试
lit test/ -v
# 运行单个测试
lit test/passes/my-pass.ll -v
# 运行并显示输出
lit test/ -v --show-unsupported
18.6 代码格式和风格
# .clang-format
BasedOnStyle: LLVM
IndentWidth: 2
ColumnLimit: 80
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
PointerAlignment: Left
# .clang-tidy
Checks: >
-*,
bugprone-*,
performance-*,
readability-*,
modernize-*,
-modernize-use-trailing-return-type
WarningsAsErrors: ''
# 格式化代码
clang-format -i $(find . -name '*.cpp' -o -name '*.h')
# 检查代码
clang-tidy -p build src/*.cpp
18.7 本章小结
| 工具 | 用途 |
|---|---|
| Docker | 统一开发环境 |
| CMake | 构建系统 |
| GitHub Actions | CI/CD |
| lit | 集成测试 |
| clang-format | 代码格式化 |
| clang-tidy | 静态检查 |
扩展阅读
- LLVM CMake — CMake 构建参考
- LLVM lit — lit 测试框架
- LLVM Coding Standards — 编码规范
下一章: 第 19 章:故障排查 — 常见问题、IR 验证和 Pass 调试。