- 构建工具
【免费下载链接】meson
The Meson Build System
本篇技术指南基于 Meson 官方文档 docs/markdown/howtox.md 整理并深度扩充,面向所有使用 Meson 构建系统的开发者。文中汇总了日常项目中最高频的配置需求——如何选择编译器与链接器、设置语言标准、启用线程与数学库、注入外部编译参数、生成覆盖率报告、使用 AddressSanitizer、Clang 静态分析器与 clippy、执行 PGO 优化等,并给出可直接复制运行的最小示例。读完本文,你将能够在不编写任何自定义构建逻辑的情况下,用最惯用、最可移植的方式解决上述全部问题。
选择编译器
在首次运行 Meson 时,可以通过环境变量指定编译器:
$ CC=mycc meson <options>需要特别注意的是,在交叉编译场景中,CC这类环境变量只作用于宿主平台(host machine),即最终安装运行程序的机器。用于构建「在构建机器(build machine)上运行的工具」的编译器,则需要通过CC_FOR_BUILD指定。例如构建交叉编译工具链或代码生成器时就会用到它。
不过,Meson 官方明确指出:环境变量从来不是使用 Meson 的习惯方式。更推荐的做法是使用 native 文件与 cross 文件;而且在交叉编译时,宿主平台的工具只能通过 cross 文件指定,环境变量无能为力。
所有受支持的环境变量汇总见 Reference-tables.md 的编译器与链接器选择变量表,其中完整列出了 C(CC/CC_LD)、C++(CXX/CXX_LD)、D(DC/DC_LD)、Fortran(FC/FC_LD)、Objective-C、Rust(RUSTC/RUSTC_LD)、Vala、C#、Cython、nasm 等语言的对应变量。从 1.3.0 起,若编译器路径中包含空格(如 Windows 上 MSVC 的cl.exe完整路径),需要用双引号包裹后导出:
export CC='"C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.34.31933/bin/Hostx64/x64/cl.exe"'自 0.54.0 起,影响机器特定配置的环境变量均成对出现:裸变量FOO只影响宿主配置,带_FOR_BUILD后缀的FOO_FOR_BUILD只影响构建机器配置(详见 Reference-tables.md 的 per-machine 环境变量章节),这与 Meson 内置选项中使用build.前缀表达的含义一致。
选择链接器
链接器的选择方式与编译器类似,可通过<编译器变量>_LD环境变量,或 native/cross 文件中的<编译器条目>_ld条目指定。使用前必须先弄清楚一个关键差异:当前编译器是自己调用链接器,还是被直接当作链接器使用。
- GCC 与 Clang 属于前者:
c_ld/CC_LD应填一个传给编译器特殊参数的值,例如 clang/gcc 的-fuse-ld参数值; - MSVC 及模仿它的编译器(如 Clang-Cl)属于后者:此时
c_ld/CC_LD应填一个可执行文件,例如lld-link.exe。
环境变量方式:
$ CC=clang CC_LD=lld meson <options>或(Clang-Cl + MSVC link.exe):
$ CC=clang-cl CC_LD=link meson <options>machine 文件方式(Machine-files.md 的 [binaries] 章节 中有多个c_ld使用实例):
[binaries] c = 'clang' c_ld = 'lld'版本注意:在 Meson 0.53.0 中曾使用裸
ld条目与LD环境变量,导致大量回归,0.53.1 起改为<lang>_ld与<comp variable>_LD。
设置默认的 C/C++ 语言标准
在project()的default_options中一次性声明全局默认语言标准:
project('myproj', 'c', 'cpp', default_options : ['c_std=c11', 'cpp_std=c++11'])语言标准也可以在单个目标上覆盖全局设置:
executable(..., override_options : ['c_std=c11'])这种default_options(全局默认)与override_options(目标级覆盖)的组合方式,与处理其他内置选项(如unity)的模式完全一致。
启用线程支持
很多人习惯用cc.find_library('pthread')手动查找 pthread 库——请不要这样做,它不可移植(Windows 等平台没有 pthread)。正确做法是使用 Meson 内建线程依赖:
thread_dep = dependency('threads') executable(..., dependencies : thread_dep)Meson 会自动为当前平台选择正确的线程实现(例如 Windows 上的原生线程),无需你关心平台差异。
从外部注入额外的编译与链接参数
在构建发行版软件包等场景下,需要在首次配置时通过环境变量注入额外参数,行为与其他构建系统一致。注意:这些变量只在首次配置时生效,若源码需要重新构建,不要依赖它们。
$ CFLAGS=-fsomething LDFLAGS=-Wl,--linker-flag meson <options>只对特定编译器使用某参数
先检测编译器类型,再决定参数列表:
if meson.get_compiler('c').get_id() == 'clang' extra_args = ['-fclang-flag'] else extra_args = [] endif然后应用到某个目标:
executable(..., c_args : extra_args)若希望对所有目标生效,则使用add_global_arguments:
if meson.get_compiler('c').get_id() == 'clang' add_global_arguments('-fclang-flag', language : 'c') endif把命令输出写入配置数据
运行外部命令并把其 stdout 写入configuration_data,供configure_file生成配置头文件:
txt = run_command('script', 'argument', check: true).stdout().strip() cdata = configuration_data() cdata.set('SOMETHING', txt) configure_file(...)从文件读取内容生成配置数据
fs模块(Fs-module)提供read函数,可把任意文件的内容塞进配置数据。其实现位于 mesonbuild/modules/fs.py(read方法定义见 232 行附近),支持读取构建机器本地的任意文本文件:
fs = import('fs') cdata = configuration_data() copyright = fs.read('LICENSE') cdata.set('COPYRIGHT', copyright) if build_machine.system() == 'linux' os_release = fs.read('/etc/os-release') cdata.set('LINUX_BUILDER', os_release) endif configure_file(...)用configure_file生成可执行脚本
configure_file会保留源文件的元数据:如果模板文件本身带有可执行权限,生成出来的文件也会带有相同权限,因此可以直接用它生成可运行的 shell 脚本,无需额外的chmod步骤。
生成覆盖率报告
覆盖率属于 Meson 的 builtin 选项(对应源码 mesonbuild/options.py 中的b_coverage定义:Enable coverage tracking,默认false)。首先开启覆盖率:
$ meson setup <other flags> -Db_coverage=true然后编译、测试并生成报告:
$ meson compile $ meson test $ ninja coverage-html (或 coverage-xml)报告输出在构建目录的meson-logs子目录下。从 mesonbuild/backend/ninjabackend.py 的generate_coverage_legacy_rules(1415 行起)可以看到,Meson 会生成coverage、coverage-html、coverage-xml、coverage-text以及(gcovr ≥ 4.2 时的)coverage-sonarqube等多个 phony target。工具层面,mesonbuild/scripts/coverage.py 支持 gcovr 与 llvm-cov 两条工具链(--gcov-executable llvm-cov gcov桥接 llvm-cov)。
自 0.55.0 起支持配合 clang 使用llvm-cov。
给 debug 构建加点优化
debug 构建默认不启用任何优化,这通常是理想行为,但部分项目受益于少量优化。GCC 专门提供了-Og标志,启用方式:
$ meson configure -Dc_args=-Og执行后,后续所有构建都会自动携带该参数(这是持久化的构建目录级配置,不是一次性环境变量)。
使用 AddressSanitizer
Clang 与 GCC 自带地址消毒器等分析工具,Meson 通过b_sanitizebuiltin 选项原生支持(mesonbuild/options.py 中定义为Code sanitizer to use,默认空列表):
$ meson setup <other options> -Db_sanitize=addressClang 用户可能还需要设置-Db_lundef=false:否则 Meson 会传入-Wl,--no-undefined(即-Wl,-z,defs),这与 clang 构建共享库时的 address sanitizer 不兼容。这一约束同样体现在解释器层——mesonbuild/interpreter/interpreter.py 在 3420 行附近会在b_lundef与b_sanitize同时启用且使用 Clang 时给出显式报错,提示改为b_lundef=false。
之后只需编译并运行测试套件,存在内存问题的可执行文件会被 sanitizer 直接中止,表现为测试失败。
使用 Clang 静态分析器
安装scan-build程序后:
$ meson setup builddir $ ninja -C builddir scan-build可用SCANBUILD环境变量指定 scan-build 可执行文件:
$ SCANBUILD=<your exe> ninja -C builddir scan-build如需向 scan-build 传递参数,可以包一层脚本(mesonbuild/scripts/scanbuild.py 的scanbuild()函数会把额外参数追加给meson_cmd,并调用detect_scanbuild()解析SCANBUILD变量,见 mesonbuild/tooldetect.py):
#!/bin/sh scan-build -v --status-bugs "$@"通过变量传入脚本(务必使用绝对路径):
$ SCANBUILD=$(pwd)/my-scan-build.sh ninja -C builddir scan-build对 Rust 目标运行 clippy
项目包含 Rust 目标时,可以直接调用 clippy:
$ meson setup builddir $ ninja -C builddir clippy- clippy 会遵循
werror内置选项(见 Builtin-options.md 的 core options):-Dwerror=true时 clippy 告警同样按错误处理。 - 默认情况下 Meson 使用与机器核心数相同的并发进程,可通过环境变量
MESON_NUM_PROCESSES覆盖。 - Meson 会在
rustc同目录下查找clippy-driver,若rustc指向 rustup 安装的二进制,则尝试通过rustup run方式调用(mesonbuild/compilers/rust.py 的get_rust_tool/get_rustup_run_and_args实现了这一探测逻辑,clippy.py的ClippyDriver则负责为目标收集工具链)。 - 若
clippy-driver未被正确探测到,可将其加入 machine 文件 的[binaries]段。
使用 Profile Guided Optimization(PGO)
GCC 的 PGO 是两阶段操作。第一阶段:开启 profile 生成并编译:
$ meson setup <Meson options, 如 --buildtype=debugoptimized> -Db_pgo=generate $ meson compile -C builddir第二阶段:用有代表性的输入运行程序(此步取决于你的项目),然后切换为使用生成的 profile 数据并重新编译:
$ meson configure -Db_pgo=use $ meson compile完成上述步骤后,生成的二进制即得到完整优化。b_pgo在 mesonbuild/options.py 中被定义为三选一选项(off/generate/use,默认off)。注意 MSVC 上b_pgo要求同时启用b_lto(见 mesonbuild/backend/vs2010backend.py 中相关校验)。
可移植地链接数学库-lm
部分平台(如 Linux)有独立的数学库,其他平台则没有。用find_library的required: false优雅解决:
cc = meson.get_compiler('c') m_dep = cc.find_library('m', required : false) executable(..., dependencies : m_dep)库不存在时该依赖自动降级为空,构建照常进行。
把可执行文件安装到libexecdir
executable(..., install : true, install_dir : get_option('libexecdir'))libexecdir是 Meson 的标准目录选项之一,与bindir、libdir等一样可通过-Dlibexecdir=...在配置期调整。
复用现有的Find<name>.cmake文件
若系统安装了 CMake,Meson 可以接入 CMake 的find_package()生态。要使用项目内自定义的Find<name>.cmake脚本,通过cmake_module_path指向脚本所在目录。例如cmake子目录中有一个FindCmakeOnlyDep.cmake:
cm_dep = dependency('CmakeOnlyDep', cmake_module_path : 'cmake')cmake_module_path只对自定义 CMake 脚本是必需的——系统级 CMake 脚本会被自动找到。更多细节见 Dependencies.md 的 CMake 章节。
获取一个默认「未找到」的依赖
有时需要一个默认值,但稍后可能被覆盖:
null_dep = dependency('', required : false)典型用法是按平台选择性覆盖:
# Windows 上不需要! my_dep = dependency('', required : false) if host_machine.system() in ['freebsd', 'netbsd', 'openbsd', 'dragonfly'] my_dep = dependency('some dep', required : false) elif host_machine.system() == 'linux' my_dep = dependency('some other dep', required : false) endif executable( 'myexe', my_sources, deps : [my_dep] )把某些文件排除出 unity 构建
如果你的项目支持 unity 构建,通常应修复源码拼接后暴露出的问题。但有些情况确实无法做到——例如生成的文件。此时可以把它们放进独立的静态库目标,并在该目标上关闭 unity:
generated_files = ... unityproof_lib = static_library('unityproof', generated_files, override_options : ['unity=off']) main_exe = executable('main', main_sources, link_with : unityproof_lib)若需要把这个静态库链接进另一个库目标,可能需要用link_whole而不是link_with,以确保其中的生成代码被完整收录。
小结
Meson 的设计哲学是「用声明的、可移植的方式描述构建意图」,而不是依赖脚本化的临时处理。本文覆盖的每个技巧都遵循这一原则:
- 编译器/链接器选择优先走 machine 文件,环境变量仅在首次配置时兜底;
- 依赖解析(线程、数学库、CMake 依赖)交给 Meson 的依赖系统,绝不手写
find_libraryhack; - 质量工具(覆盖率、sanitizer、scan-build、clippy)全部是内置选项或内置 target,一条命令即可启用;
- 目标级覆盖(
override_options、c_args、install_dir)让特殊文件与特殊目标可以局部偏离全局配置。
以上命令与代码片段均可在当前仓库 test cases 与 unittests 中找到对应验证场景,动手实验时也可以直接参考。
- 构建工具
【免费下载链接】meson
The Meson Build System
相关推荐
10个Meson Build System条件编译技巧:掌握配置选项控制代码包含
10个Meson Build System条件编译技巧:掌握配置选项控制代码包含 Meson Build System作为现代构建工具,通过配置选项提供了强大的
构建工具sqlc 预编译查询(Prepared Queries)实战指南:从 `emit_prepared_queries` 配置到生成代码解析
sqlc 预编译查询(Prepared Queries)实战指南:从 emit_prepared_queries 配置到生成代码解析 导读 本文围绕 sqlc
开发工具代码生成数据库你的数字记忆正在流失?3种格式永久保存微信聊天记录的技术方案
你的数字记忆正在流失?3种格式永久保存微信聊天记录的技术方案 你是否曾有过这样的体验:手机更换后,那些与挚友深夜长谈的记录、与家人温馨的对话、重要的工作会议纪要
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考