- 构建工具
【免费下载链接】meson
The Meson Build System
导读
本文基于 Meson 构建系统 0.40.0 版本的官方发布说明(docs/markdown/Release-notes-for-0.40.0.md),系统梳理该版本引入的核心新特性,并结合当前仓库源码逐一验证底层实现。阅读本文后,你将掌握:如何在 Visual Studio 后端使用生成器输出、用compute_int与get_define在配置期求值、用wrap-mode控制依赖下载、用override_options按目标覆盖全局选项、用link_whole整库链接、按输出分别指定安装目录,以及如何为依赖显式指定获取方法等实战能力。
一、生成器输出可用于 VS 后端的 custom target
在 Ninja 后端中,生成器(generator)产生的输出早已可以作为自定义目标(custom target)的输入使用。0.40.0 将这一能力扩展到了 Visual Studio 后端,现在两种后端行为一致。这意味着使用--backend=vs2017、--backend=vs2019等 VS 系列后端构建项目时,custom_target()可以放心地引用generator.process()生成的中间文件,不再受后端差异限制。
从源码结构看,各 VS 后端统一继承自 vs2010backend.py 中的Vs2010Backend基类,后续的 VS2012/2013/2015/2017/2019/2022/2026 后端(见该文件 L47-L70)都复用同一套生成器与自定义目标处理逻辑,因此该特性对所有 VS 版本后端同时生效。
二、编译器对象的compute_int方法
0.40.0 为编译器对象新增了compute_int()方法,用于在配置期直接计算一个 C/C++(或 Fortran)表达式的整数值:
cc = meson.get_compiler('c') two = cc.compute_int('1 + 1') # A very slow way of adding two numbers.底层实现原理
从源码看,该方法的基类声明位于 mesonbuild/compilers/compilers.py,而 C 系编译器的实际实现在 mesonbuild/compilers/mixins/clike.py:Meson 会生成一段包含<stddef.h>与<stdio.h>的 C 测试程序,用printf("%ld\n", (long)(<expression>))输出表达式求值结果,再解析程序输出得到整数值。Fortran 编译器(mesonbuild/compilers/fortran.py)则生成program test并用print '(i0)', <expression>输出。因此compute_int实际执行的是"编译并运行一小段代码",而非纯语法解析——这正是文档注释中"一种非常慢的加法"的由来。
该方法常用于在配置期推导平台相关的常量,例如cc.compute_int('sizeof(void*)')或架构位宽判断。
三、Visual Studio 2017 支持与通用 VS 后端
0.40.0 带来了两项后端能力:
- 专门的
--backend=vs2017; - 通用的
--backend=vs,自动探测当前环境中激活的 VS 版本。
通用后端的版本分派逻辑体现在 vs2010backend.py:依次尝试导入Vs2012Backend、Vs2013Backend、Vs2015Backend、Vs2017Backend、Vs2019Backend、Vs2022Backend、Vs2026Backend,并返回首个可用的后端类。这使得用户无需关心本机安装的具体 VS 版本,一条命令即可完成工程文件生成。
四、git submodule 子项目的自动初始化
如果subprojects/目录下的某个子目录是 git submodule,且构建文件引用了它,Meson 会自动初始化该 submodule——无需提供 wrap 文件,因为 git 本身已包含全部所需信息。
源码级验证
该逻辑实现在 mesonbuild/wrap/wrap.py 的resolve_git_submodule()中,具体判断流程为:
- 检查 git 是否安装(
GIT变量),并确认目录存在; - 通过
git rev-parse确认处于 git 仓库中; - 用
git submodule status .查询 submodule 状态:- 前缀
+:submodule 可能已过期(输出警告但仍继续); - 前缀
U:存在合并冲突,直接抛出异常; - 前缀
-:未初始化,自动执行git submodule update --init .; - 前缀 :已初始化但内容可能不完整,执行
git submodule update .与git checkout .补齐; - 空输出:普通目录而非 submodule,返回 false。
- 前缀
该机制由WrapResolver在解析子项目时自动调用(见 wrap.py),对用户完全透明。
五、Wrap 的"禁止下载"模式(wrap-mode)
对于发行版打包者和需要严格禁止配置/构建期间联网的场景,0.40.0 新增了wrap-mode选项:
meson setup builddir --wrap-mode=nodownload当设置为nodownload后,任何尝试下载依赖项目的操作都会直接报错。
全部取值与语义
从 mesonbuild/wrap/init.py 中WrapMode枚举及其注释可以看到完整的模式语义:
| 取值 | 含义 |
|---|---|
default | 默认行为:允许下载 |
nofallback | 禁止为dependency(..., fallback: ...)下载 wrap(不执行回退下载) |
nodownload | 禁止所有subproject()调用的下载,包括 fallback(即既不下载也不回退) |
forcefallback | 忽略系统提供的依赖,强制使用 fallback |
nopromote | 不自动提升子项目为 copylib |
注释还给出了选型建议:从 release 源码包构建时,上游通常已附带全部源码,可安全使用nodownload;从 git 仓库构建时则建议使用nofallback,以便 copylib 类 wrap 仍能按需下载。执行层面,wrap.py 在WrapMode.nodownload下直接抛出"Automatic wrap-based subproject downloading is disabled"错误。另外注意:wrap-mode不影响 git submodule 形式的子项目——注释明确说明这些 submodule 仅在 git 仓库中可用,几乎总是应当被下载(相关判断见 mesonbuild/wrap/wrap.py 上方逻辑)。
六、按目标覆盖选项:override_options
构建目标新增了override_options关键字参数,允许针对单个目标覆盖 Meson 内置选项。例如某个目标已知无法在-Werror下编译,可以这样做:
executable('foo', 'foo.c', override_options : ['werror=false'])需要特别注意的是:它只影响来自 Meson 自身的选项(语言标准、unity 构建等),不影响项目自定义选项(即在meson_options.txt中定义的 option)。
从实现看,override_options贯穿解释层与后端:其类型定义与校验位于 mesonbuild/interpreter/type_checking.py 与 mesonbuild/interpreter/kwargs.py,目标对象在 mesonbuild/build.py 中保存覆盖值,而后端在展开编译参数时(例如 mesonbuild/backend/backends.py 的选项读取路径)会优先采用目标级覆盖结果,从而实现"全局配置 + 目标级豁免"的灵活组合。
七、编译器对象get_define方法
get_define()返回指定预处理宏的字符串值:
cc = meson.get_compiler('c') one = cc.get_define('__linux__') # 在 Linux 主机上返回 '1'如果宏未定义,则返回None。该方法对跨平台特性探测非常实用——例如在配置期区分_WIN32、__APPLE__等平台宏。
实现细节
实现在 mesonbuild/compilers/mixins/clike.py:Meson 生成如下预处理代码,用两个特殊分隔符夹住目标宏:
#define MESON_GET_DEFINE_DELIMITER_START #ifdef __linux__ # define __linux__ MESON_GET_DEFINE_UNDEFINED_SENTINEL #endif "MESON_GET_DEFINE_DELIMITER_START"__linux__"MESON_GET_DEFINE_DELIMITER_END"以CompileCheckMode.PREPROCESS模式交给编译器预处理,再解析分隔符之间的文本即为宏值。注意代码中先用#ifndef ... #define <dname> MESON_GET_DEFINE_UNDEFINED_SENTINEL兜底,未定义宏会落入哨兵值并被识别为None。同时该方法支持结果缓存(disable_cache参数)与extra_args/dependencies参数,保证与其他编译器检测函数一致的接口风格。基类声明见 mesonbuild/compilers/compilers.py。
八、Cygwin 支持
0.40.0 起 Meson 可在 Cygwin 环境下正常工作,并已加入 CI 测试矩阵(见仓库 ci 目录下的持续集成配置)。这意味着在 Windows 上通过 Cygwin 使用 POSIX 风格工具链的开发者,可以直接用 Meson 构建项目。
九、多个安装目录:按输出分别指定
此前 custom target 的多个输出只能安装到同一目录。0.40.0 起,可以为每个输出分别指定安装目录:
custom_target('two_out', output : ['diff.h', 'diff.sh'], command : [creator, '@OUTDIR@'], install : true, install_dir : ['dir1', 'dir2'])- 输出列表与
install_dir列表按位置一一对应; - 为了向后兼容与简洁,若只指定一个目录,则所有输出都安装到该目录;
- 该特性在源码中以
FeatureNew标注自 0.40.0 引入,见 mesonbuild/interpreter/interpreter.py。
Vala 目标的扩展用法
同一能力也适用于 Vala 构建目标。例如把 valac 构建的共享库、生成的头文件和生成的 VAPI 分别安装到默认位置:
shared_library('valalib', 'mylib.vala', install : true, install_dir : [true, true, true])规则为:true表示安装到该类型的默认目录,字符串表示安装到自定义目录,false表示不安装。三个元素分别对应共享库、生成头文件与生成 VAPI。若像旧版那样只传一个字符串,则只会安装库本身——这是一个完全向后兼容的变更。
十、显式指定依赖获取方法(method关键字)
某些依赖有多种提供方式,例如 Qt 既可以通过pkg-config检测,也可以通过qmake检测。此前 Meson 依靠启发式算法选择,偶尔会选错。0.40.0 起可用method关键字显式指定:
qt5_dep = dependency('qt5', modules : 'core', method : 'qmake')method的取值由DependencyMethods枚举定义(如auto、pkgconfig、qmake、system、config-tool等),默认值为AUTO,相关解析逻辑见 mesonbuild/dependencies/base.py 与 mesonbuild/dependencies/detect.py。以 Qt 为例,mesonbuild/dependencies/qt.py 中会按指定的qmake路径执行检测,从而精确控制依赖的来源,避免启发式误判。
十一、整库链接:link_whole
静态库的默认链接行为会丢弃未被直接引用的符号,这可能丢失导出符号。多数编译器支持"whole archive"链接,把静态库的全部符号与代码都纳入最终产物。0.40.0 通过link_whole关键字暴露该能力:
shared_library('foo', 'foo.c', link_whole : some_static_library)- 可用于将静态库整体并入共享库或可执行文件,典型场景是确保插件/导出符号不丢失;
- 注意:Visual Studio 编译器仅在 VS2015 及更新版本支持该功能。
后端实现佐证
后端在生成链接命令时专门处理link_whole_targets:
- Ninja 后端在 mesonbuild/backend/ninjabackend.py 的
get_link_whole_args()中收集目标的link_whole_targets,并通过linker.get_link_whole_for()生成平台相关的整库链接参数(例如 GCC/Clang 的-Wl,--whole-archive ... -Wl,--no-whole-archive,MSVC 的/WHOLEARCHIVE); - VS 后端在 mesonbuild/backend/vs2010backend.py 中同样将
link_whole_targets中的静态库标记为整库链接。
十二、Unity 构建仅用于子项目
此前 unity 构建要么对所有目标生效、要么全部关闭。0.40.0 起可将unity选项设为subprojects,让更新频率低的子项目启用 unity 合并编译,而频繁改动的主项目保持普通编译,从而在享受 unity 构建加速的同时减少主项目变更引发的整体重编译:
meson setup builddir -Dunity=subprojects该选项的类型定义见 mesonbuild/options.py:UserComboOption('unity', 'Unity build', 'off', choices=['on', 'off', 'subprojects'])。解释器在判断某目标是否采用 unity 编译时,会依据unity == 'subprojects'且目标属于子项目(tobj.subproject != '')来判定,见 mesonbuild/interpreter/interpreterobjects.py。
十三、脚本可用的MESONINTROSPECT环境变量
Meson 运行脚本时,除了设置MESON_SOURCE_ROOT等变量外,现在还会设置MESONINTROSPECT环境变量,保证它指向正确的mesonintrospect脚本——这对于从 git 未安装状态运行 Meson,或PATH配置不正确的情况尤为重要。
具体地,以下 Meson 函数会设置该变量:
meson.add_install_script()meson.add_postconf_script()run_command()run_target()
从源码看,该变量在多个执行路径中被注入环境:
- 后端在生成运行目标命令时设置
'MESONINTROSPECT': self.get_introspect_command()(mesonbuild/backend/backends.py); - 解释器层在构造脚本执行环境时以
shlex.quote转义后写入(mesonbuild/interpreter/interpreterobjects.py); - 安装阶段(mesonbuild/minstall.py)同样在
meson.add_install_script()执行环境中设置该变量。
因此,自定义脚本内部可以放心地通过os.environ['MESONINTROSPECT']调用 introspection 接口,获取目标的源文件、编译参数等构建信息。
小结
Meson 0.40.0 是一个面向后端完善与配置灵活性的版本:Visual Studio 生态获得了 VS2017 支持、通用vs后端与生成器输出的一致处理;配置期编程能力通过compute_int、get_define得到增强;依赖管理在wrap-mode、method与 git submodule 自动初始化三个维度上更加可控;构建目标层面新增override_options、link_whole、多安装目录与unity=subprojects等实用特性;同时补齐了 Cygwin 支持与MESONINTROSPECT环境变量。上述全部特性在当前仓库源码中均有对应实现可查证,相关代码路径已在各小节中给出,便于读者深入研读。
- 构建工具
【免费下载链接】meson
The Meson Build System
相关推荐
Slang 诊断目录测试包:为每个诊断代码生成 DIAGNOSTIC_TEST 的系统化方案
Slang 诊断目录测试包:为每个诊断代码生成 DIAGNOSTIC_TEST 的系统化方案 导读 Slang 编译器将用户可见的诊断(错误、警告、提示)组织成
构建工具Meson 依赖系统完全指南:dependency() 的查找机制、检测方法与内置特殊依赖详解
Meson 依赖系统完全指南:dependency 的查找机制、检测方法与内置特殊依赖详解 导读 本文围绕 Meson 构建系统中 dependency 函数及
构建工具MOOTDX:Python量化投资的终极通达信数据接口完整指南
MOOTDX:Python量化投资的终极通达信数据接口完整指南 MOOTDX是一个专为Python量化投资设计的开源数据接口,它提供了简单高效的通达信数据访问能
金融科技数据分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考