Meson:现代 C/C++ 构建系统的革新者
引言:C/C++ 构建的痛点与革新
想象一下,你正在开始一个新的 C++ 项目。你面临的选择是:
- 复杂的 CMake 语法,学习曲线陡峭
- 过时的 Autotools,难以维护
- 平台差异,需要为每个系统编写不同的构建逻辑
这时,一个名为Meson的构建系统出现了,它承诺:简单、快速、用户友好。Meson 不仅仅是一个工具,更是对传统构建系统哲学的彻底反思。
本文介绍了 Meson 构建系统,它是专为 C/C++ 等编译型语言设计的现代化开源工具。相比 CMake 和 Autotools,Meson 采用 Python 风格声明式语法,默认使用 Ninja 后端以实现极速构建。文章涵盖了 Meson 的发展历史、核心设计哲学(如声明式优于命令式、错误友好)、功能特性(依赖管理、跨平台支持、模块化)、安装配置及完整工作流程。通过对比 CMake 和 Autotools,展示了 Meson 在语法简洁性、构建速度和错误提示上的优势。此外,还提供了实际案例(GTK、Systemd 迁移)和最佳实践建议,包括项目结构组织、依赖管理策略及 IDE 集成方案。对于追求高效开发体验的 C/C++ 开发者,Meson 是一个值得尝试的选择。
想象一下,你正在开始一个新的 C++ 项目。你面临的选择是:
这时,一个名为Meson的构建系统出现了,它承诺:简单、快速、用户友好。Meson 不仅仅是一个工具,更是对传统构建系统哲学的彻底反思。
Meson是一个开源的、现代化的构建系统,专门为 C、C++、Fortran 等编译型语言设计。它的核心设计理念是:
传统方式:源代码 → Makefile → 可执行文件
Meson 方式:源代码 → meson.build(声明式) → Ninja → 可执行文件
关键洞察:Meson 不直接执行构建,而是生成 Ninja 构建文件,利用 Ninja 的极致速度。
背景:
核心观察:
'构建系统应该帮助开发者,而不是阻碍他们。'
2014 年 1 月:Meson 0.1.0 首次发布
设计原则:
关键里程碑:
采用增长:
当前状态:
生态系统:
# CMake(命令式风格)
add_executable(myapp)
target_sources(myapp PRIVATE main.cpp)
target_include_directories(myapp PRIVATE include)
target_link_libraries(myapp PRIVATE mylib)
# Meson(声明式风格)
executable('myapp', 'main.cpp', include_directories: 'include', dependencies: mylib_dep)
# 自动处理常见情况
# - 自动检测编译器
# - 合理的默认编译选项
# - 自动处理平台差异
# CMake 的错误信息
CMake Error at CMakeLists.txt:10 (target_link_libraries): Cannot specify link libraries for target "myapp" which is not built by this project.
# Meson 的错误信息
meson.build:10:0: ERROR: Tried to link target 'myapp' to non-existent target 'mylib'
# 生成 Ninja 文件,利用其极致速度
$ meson setup builddir
$ ninja -C builddir
# 极速构建
# 项目声明
project('myapp', 'cpp', version: '1.0.0', default_options: ['cpp_std=c++17'])
# 可执行文件
executable('myapp', 'src/main.cpp', 'src/utils.cpp', include_directories: 'include', dependencies: [thread_dep, fmt_dep])
# 库文件
mylib = library('mylib', 'src/lib.cpp', version: '1.0.0', soversion: '1')
# 测试
test('basic test', myapp, args: ['--test'], timeout: 60)
# 基本类型
project_name = 'myapp'
sources = ['main.cpp', 'utils.cpp']
version_array = [1, 0, 0]
# 数组
features = {'threading': true, 'gui': false}
# 字典
# 条件判断
if get_option('enable_gui')
sources += ['gui.cpp']
endif
# 循环
foreach file : sources
# 处理每个文件
endforeach
# 查找系统库
thread_dep = dependency('threads')
# 自动处理平台差异
opengl_dep = dependency('gl')
zlib_dep = dependency('zlib')
# 带版本要求
boost_dep = dependency('boost', version: '>=1.70', modules: ['thread', 'system'])
# 组件化依赖
gtk_dep = dependency('gtk4', version: '>=4.0.0', required: get_option('gtk4'))
# 找不到时的备选方案
cairo_dep = dependency('cairo', required: false, fallback: ['cairo', 'cairo_dep'])
# 从源码构建依赖
spdlog_proj = subproject('spdlog')
spdlog_dep = spdlog_proj.get_variable('spdlog_dep')
# 使用 WrapDB(在线依赖库)
# wrap 文件示例
[wrap-file]
directory = fmt-8.0.1
source_url = https://github.com/fmtlib/fmt/archive/8.0.1.zip
# 操作系统检测
if host_machine.system() == 'windows'
sources += ['win32.cpp']
elif host_machine.system() == 'darwin'
sources += ['macos.cpp']
else
sources += ['linux.cpp']
endif
# 架构检测
if host_machine.cpu_family() == 'x86_64'
add_project_arguments('-march=native', language: 'cpp')
endif
# 交叉编译支持
if meson.is_cross_build()
# 交叉编译特定配置
endif
# 检查编译器支持
if meson.get_compiler('cpp').has_function('std::make_unique', prefix: '#include <memory>')
add_project_arguments('-DHAVE_MAKE_UNIQUE', language: 'cpp')
endif
# 检查 C++ 标准支持
cpp = meson.get_compiler('cpp')
if cpp.has_header('filesystem')
# 使用 std::filesystem
elif cpp.has_header('experimental/filesystem')
# 使用 experimental 版本
endif
# 使用模块
gnome = import('gnome')
pkgconfig = import('pkgconfig')
# GNOME 资源编译
gresource = gnome.compile_resources(
'myapp-resources', 'data/myapp.gresource.xml',
source_dir: 'data', c_name: 'myapp'
)
# 生成 pkg-config 文件
pkgconfig.generate(
libraries: mylib, version: '1.0', name: 'mylib', description: 'My awesome library'
)
# 创建自定义模块
# meson.build 中:
mod = import('my_module')
result = mod.do_something()
# modules/my_module.py:
def do_something():
return 'result'
# 各平台安装
# Ubuntu/Debian
sudo apt install meson ninja-build
# Fedora/RHEL
sudo dnf install meson ninja-build
# macOS
brew install meson ninja
# Windows
pip install meson ninja
# 或使用独立安装器
# 从源码安装
pip install meson
# 1. 创建项目结构
myproject/
├── meson.build
├── src/
│ ├── meson.build
│ └── main.cpp
└── include/
└── myproject.h
# 2. 配置构建目录
meson setup builddir
# 或指定选项
meson setup builddir --buildtype=debug
# 3. 构建项目
cd builddir && ninja
# 或使用 meson compile
meson compile -C builddir
# 4. 运行测试
meson test -C builddir
# 5. 安装
meson install -C builddir
# 顶层 meson.build
project('myapp', 'cpp', version: '1.0.0', default_options: [
'cpp_std=c++17',
'warning_level=3',
'werror=true'
])
# 子目录
subdir('src')
# 安装配置
install_data('README.md', install_dir: 'share/doc/myapp')
# 测试配置
if get_option('tests')
subdir('tests')
endif
# src/meson.build
# 可执行文件
sources = [
'main.cpp',
'utils.cpp',
'parser.cpp'
]
incdir = include_directories('../include')
# 依赖
thread_dep = dependency('threads')
fmt_dep = dependency('fmt')
executable('myapp', sources, include_directories: incdir, dependencies: [thread_dep, fmt_dep], install: true)
# 库项目配置
project('mylib', 'c', 'cpp', version: '1.2.3', license: 'MIT')
# 版本信息
version_array = meson.project_version().split('.')
version_major = version_array[0].to_int()
version_minor = version_array[1].to_int()
version_micro = version_array[2].to_int()
# 库源文件
lib_sources = files(
'src/core.cpp',
'src/utils.cpp',
'src/api.cpp'
)
# 头文件
public_headers = files(
'include/mylib/core.h',
'include/mylib/utils.h'
)
# 构建库
mylib = library('mylib', lib_sources, version: meson.project_version(), soversion: version_major, install: true, include_directories: include_directories('include'), dependencies: [
dependency('threads'),
dependency('zlib')
])
# 安装头文件
install_headers(public_headers, subdir: 'mylib')
# pkg-config 文件
import('pkgconfig').generate(mylib, name: 'mylib', description: 'My awesome library', version: meson.project_version(), url: 'https://github.com/me/mylib')
# 创建交叉编译文件
# cross_file.txt
[binaries]
c = '/usr/bin/arm-linux-gnueabihf-gcc'
cpp = '/usr/bin/arm-linux-gnueabihf-g++'
ar = '/usr/bin/arm-linux-gnueabihf-ar'
strip = '/usr/bin/arm-linux-gnueabihf-strip'
[host_machine]
system = 'linux'
cpu_family = 'arm'
cpu = 'armv7hl'
endian = 'little'
# 使用交叉编译文件
meson setup builddir --cross-file cross_file.txt
# meson_options.txt
option('buildtype', type: 'combo', choices: ['plain', 'debug', 'debugoptimized', 'release', 'minsize'], value: 'debugoptimized', description: 'Build type')
option('warning_level', type: 'combo', choices: ['0', '1', '2', '3'], value: '3', description: 'Warning level')
option('enable_gui', type: 'boolean', value: true, description: 'Enable GUI support')
option('optimization_level', type: 'integer', min: 0, max: 3, value: 2, description: 'Optimization level')
# 在 meson.build 中使用
if get_option('enable_gui')
gui_dep = dependency('gtk4')
# GUI 相关代码
endif
# 自定义构建步骤
custom_target('generate-docs', input: 'README.md', output: 'documentation.html', command: [pandoc, '@INPUT@', '-o', '@OUTPUT@'], install: true, install_dir: 'share/doc')
# 运行脚本
run_command('generate-headers.py', check: true)
# 配置头文件
configure_file(
input: 'config.h.in',
output: 'config.h',
configuration: {
'VERSION': meson.project_version(),
'ENABLE_FEATURE': get_option('enable_feature')
}
)
# 测试配置
test('basic functionality', myapp, args: ['--test'], timeout: 30)
# 基准测试
benchmark('performance test', benchmark_exe, args: ['--bench'], timeout: 120, priority: -1)
# 低优先级,最后运行
# 测试套件
test_suite('all', 'basic functionality', 'edge cases', 'performance test', is_parallel: false)
# 顺序执行
| 特性 | Meson | CMake |
|---|---|---|
| 语法 | 声明式,Python 风格 | 命令式,自定义语言 |
| 学习曲线 | 平缓 | 陡峭 |
| 默认构建后端 | Ninja(极快) | Make(较慢) |
| 错误信息 | 清晰,友好 | 经常晦涩 |
| 跨平台 | 优秀 | 优秀 |
| 依赖管理 | WrapDB + pkg-config | find_package + FetchContent |
| 社区规模 | 成长中,活跃 | 庞大,成熟 |
| 特性 | Meson | Autotools |
|---|---|---|
| 配置语言 | Python 风格 | M4 宏 + shell |
| 构建速度 | 极快(Ninja 后端) | 慢(shell 脚本) |
| Windows 支持 | 原生支持 | 需要 Cygwin/MSYS |
| 现代特性 | 有 | 无 |
| 维护难度 | 简单 | 复杂 |
# LLVM 项目构建时间对比(16 核机器)
# 项目:LLVM 13.0.0,约 200 万行代码
# CMake + Make: 45 分钟
# CMake + Ninja: 32 分钟 # 快 29%
# Meson + Ninja: 28 分钟 # 快 38%
# 配置生成时间
# CMake: 12 秒
# Meson: 3 秒 # 快 4 倍
# 内存占用
# CMake 配置: ~250MB
# Meson 配置: ~50MB # 减少 80%
myproject/
├── meson.build # 根配置
├── meson_options.txt # 项目选项
├── include/ # 公共头文件
│ └── myproject/
│ ├── core.h
│ └── utils.h
├── src/ # 源代码
│ ├── meson.build
│ ├── core.cpp
│ └── utils.cpp
├── subprojects/ # 子项目依赖
│ └── package.wrap
├── tests/ # 测试
│ ├── meson.build
│ └── test_core.cpp
└── docs/ # 文档
└── meson.build
# 依赖查找策略
deps = []
# 1. 首先尝试系统包
system_dep = dependency('zlib', required: false)
if system_dep.found()
deps += system_dep
else
# 2. 使用 subproject(源码构建)
zlib_proj = subproject('zlib', default_options: ['default_library=static'])
deps += zlib_proj.get_variable('zlib_dep')
endif
# 3. WrapDB 回退
dependency('fmt', fallback: ['fmt', 'fmt_dep'], default_options: ['default_library=static'])
# 从 git 获取版本信息
r = run_command('git', 'describe', '--tags', '--always', check: false)
if r.returncode() == 0
version = r.stdout().strip()
else
version = meson.project_version()
endif
# 配置头文件
conf_data = configuration_data({
'VERSION': version,
'GIT_COMMIT': run_command('git', 'rev-parse', '--short', 'HEAD').stdout().strip()
})
configure_file(
input: 'version.h.in',
output: 'version.h',
configuration: conf_data
)
# 避免重复的 platform.cpp
platform_sources = []
if host_machine.system() == 'windows'
platform_sources += 'platform_win32.cpp'
elif host_machine.system() == 'darwin'
platform_sources += 'platform_macos.cpp'
else
platform_sources += 'platform_linux.cpp'
endif
executable('myapp', 'main.cpp', platform_sources, # 只有相关平台文件被编译
dependencies: deps)
# 1. 使用 files() 函数减少字符串处理
sources = files(
'src/file1.cpp',
'src/file2.cpp',
'src/file3.cpp'
)
# 2. 避免在循环中重复获取选项
enable_gui = get_option('enable_gui')
foreach source : sources
if enable_gui
# 处理 GUI 相关
endif
endforeach
# 3. 合理使用 subdir vs files
# 小项目:直接使用 files()
# 大项目:使用 subdir() 组织
# subprojects/fmt.wrap
[wrap-file]
directory = fmt-8.0.1
source_url = https://github.com/fmtlib/fmt/releases/download/8.0.1/fmt-8.0.1.zip
source_filename = fmt-8.0.1.zip
source_hash = 5d98c504d0205c3101...
patch_url = https://wrapdb.mesonbuild.com/v2/fmt_8.0.1-1/get_patch
patch_filename = fmt-8.0.1-1-wrap.zip
patch_hash = 1234567890abcdef...
[provide]
dependency_names = fmt
// .vscode/settings.json
{
"mesonbuild.buildFolder": "${workspaceFolder}/build",
"mesonbuild.configureOnOpen": true,
"C_Cpp.default.configurationProvider": "mesonbuild.mesonbuild"
}
# GitHub Actions 示例
name: Meson Build
on:
push:
pull_request:
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Meson
run: pip install meson ninja
- name: Configure
run: meson setup build
- name: Build
run: meson compile -C build
- name: Test
run: meson test -C build --verbose
背景:
迁移结果:
# 构建时间对比
# Autotools: 8 分钟
# Meson: 2 分钟 # 快 4 倍
# 代码行数对比
# Autotools 配置:约 5000 行
# Meson 配置:约 800 行 # 减少 84%
# 开发者反馈:
# "配置更清晰,错误更容易调试"
# "Windows 构建现在可以正常工作"
挑战:
Meson 解决方案:
# 条件构建各种组件
if get_option('networkd')
subdir('src/network')
endif
if get_option('timesyncd')
subdir('src/timesync')
endif
# 系统特定的配置
if host_machine.system() == 'linux'
# Linux 特定功能
elif host_machine.system() == 'freebsd'
# FreeBSD 适配
endif
成果:
# 清晰的语法
executable('myapp', sources, dependencies: deps, install: true)
# 对比 CMake 的等效代码更简洁
# Ninja 后端 + 智能的增量构建
$ time meson compile -C build
real 0m1.234s
# 即使是大项目也很快
# 清晰指出问题所在
meson.build:42:0: ERROR: Unknown variable "my_sources". Did you mean "sources"?
# Meson 的哲学:做正确的事
# 有时可能限制过度定制
# 复杂的定制逻辑可能需要 Python 模块
# 计划中的 Rust 支持
rust = import('rust')
rust_lib = rust.static_library('mylib', 'src/lib.rs')
# 探索分布式构建
meson setup --distributed-build
# 与 vcpkg、conan 等包管理器更好集成
meson wrap promote vcpkg
Meson 代表了一种新的构建系统哲学:开发者体验至上。它证明了:
对于新项目,Meson 是一个极佳的选择。它的学习曲线平缓,生产力提升明显。即使对于现有项目,如果构建配置成为瓶颈,迁移到 Meson 也是值得考虑的。
开始使用 Meson:
# 1. 安装
pip install meson ninja
# 2. 创建简单项目
mkdir myproject && cd myproject
cat > meson.build <<EOF
project('myapp', 'cpp')
executable('myapp', 'main.cpp')
EOF
# 3. 构建
meson setup build
meson compile -C build
# 4. 运行
./build/myapp
在软件开发中,好的工具应该让人几乎感觉不到它的存在。Meson 正在向这个目标迈进——让开发者专注于代码,而不是构建系统的复杂性。
资源推荐:
无论你是 C/C++ 新手还是经验丰富的开发者,Meson 都值得一试。它可能会改变你对构建系统的看法,甚至让你享受配置构建的过程。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online