- 构建工具
【免费下载链接】meson
The Meson Build System
本文面向仍在维护 Qt4 遗留代码库、或需要理解 Meson Qt 模块统一抽象机制的开发者,系统讲解 Meson 中qt4模块的加载方式、moc/uic/rcc/lrelease工具集成、依赖声明与常见构建方案。读完本文,你将能在一个meson.build中完整驱动 Qt4 的元对象编译、界面文件转换、资源编译与翻译文件生成,并理解该模块在源码层的检测与回退逻辑。
模块概览:与 Qt5/Qt6 模块同构的 Qt4 支持
qt4模块由仓库中的 mesonbuild/modules/qt4.py 实现。从源码结构看,它并不是一份独立的实现,而是通过继承QtBaseModule(定义于 mesonbuild/modules/_qt.py)复用整套 Qt 模块逻辑,唯一差异是构造时传入qt_version=4:
class Qt4Module(QtBaseModule): INFO = ModuleInfo('qt4') def __init__(self, interpreter: Interpreter): QtBaseModule.__init__(self, interpreter, qt_version=4)因此,官方文档明确指出:Qt4 模块的使用方式与 Qt5 模块、Qt6 模块完全一致。在项目中通过如下方式加载并声明依赖:
qt4 = import('qt4') qt4_dep = dependency('qt4', modules: ['Core', 'Gui'])modules参数用于引入具体的 Qt 模块(如Core、Gui、Widgets等),可用模块列表以对应 Qt 版本的官方文档为准;private_headers参数(自 v0.47.0 起)允许使用 Qt 模块的私有头文件。更完整的依赖语义可参考 Dependencies 文档的 Qt 小节。
依赖与工具探测的底层逻辑
调用模块任何编译方法前,Meson 都会先执行工具探测。从 mesonbuild/modules/_qt.py 的_detect_tools与compilers_detect可以看到针对 Qt4 的关键处理:
- 依赖查找:通过
find_external_dependency('qt4', ...)查找 Qt4 依赖,默认method为auto(可显式指定pkgconfig或qmake)。 - 工具候选名:依次尝试
{bindir}/{tool}、{libexecdir}/{tool}、{tool}4、{tool}-qt4、无后缀的{tool}五类候选(对应moc、uic、rcc、lrelease)。 - 版本校验参数差异:Qt4 的
moc/uic/rcc用-v输出版本(Qt5+ 才是--version),lrelease用-version,Meson 会据此核对工具与 Qt 版本一致,避免混用不同大版本的工具。 - 无 Qt 依赖时的占位:若找不到 Qt4 依赖,会为工具创建
NonExistingExternalProgram,以moc-qt4、uic-qt4等名称给出可读的缺失提示。
此外,依赖侧的实现位于 mesonbuild/dependencies/qt.py,其中包含多处 Qt4 特有分支:
get_qmake_host_bins优先读QT_HOST_BINS,缺失时回退到 Qt4 时代的QT_INSTALL_BINS;- 私有头目录在 Qt4 中直接位于模块目录下(如
/usr/include/QtCore/private/),而非 Qt5 那样的模块/版本/模块/private三级结构; - 在 Windows 上 Qt4 模块库名带
4后缀(如QtCore4),在 macOS 上调试库带_debug后缀,_get_modules_lib_suffix会依据平台与 debug 类型自动拼接。
一个需要特别注意的限制:rcc的 depfile 支持需要 Qt >= 5.14(修复了 QTBUG-45460),moc的 depfile 支持需要 Qt >= 5.15。因此在 Qt4 场景下这两项均不可用,源码会给出对应警告。如果需要基于 qrc 文件解析依赖(见下文compile_resources),Qt4 下仍会通过 XML 解析方式静态提取<file>节点作为依赖,只是无法获得编译期动态生成的依赖信息。
编译资源文件:compile_resources
compile_resources(自 0.59.0 起)将 Qt 资源收集文件.qrc编译为可参与编译的 C++ 源文件,无位置参数,仅接受关键字参数:
name(string | empty):若提供,所有 qrc 文件会被合并生成单个.cpp文件;否则每个 qrc 各生成一个独立的.cpp文件。sources:必填,至少一个源,类型为File | string | custom_target | custom_target index | generator_output的数组(后三类自 0.60.0 起支持)。extra_args(string[]):直接透传给rcc的额外参数。method(string):Qt 依赖探测方式,语义同dependency()。
从 mesonbuild/modules/_qt.py 的实现看,name提供时命令形如rcc -name <name> -o @OUTPUT@ ...,输出固定为{name}.cpp;未提供时每个 qrc 文件对应一个命名为qt4-{basename}的 CustomTarget。_parse_qrc_deps会解析 qrc 的 XML 内容,把其中的<file>节点(绝对路径、构建目录内路径或相对 qrc 的相对路径)转换为真实依赖,保证资源文件变更后能正确触发重编译。注意:构建期动态生成的 qrc 文件无法用于本方法(因为需要在配置期扫描 XML),官方建议改用configure_file()在配置期生成。
典型用法:
qt4 = import('qt4') resources = qt4.compile_resources(sources: 'resources.qrc')编译界面文件:compile_ui
compile_ui(自 0.59.0 起)将 Qt Designer 生成的.ui文件转换为头文件,无位置参数,关键字参数如下:
sources:必填,待转换的.ui文件列表(类型同上,支持 custom_target 等生成源,自 0.60.0 起)。extra_args(string[]):透传给uic的额外参数。method(string):依赖探测方式。preserve_paths(bool,自 1.4.0 起):为true时输出文件保留在目标私有目录中的子目录结构——例如处理subdir/one.input会生成{target private directory}/subdir/one.out;默认false则展平为{target private directory}/one.out。
实现上,uic通过build.Generator包装,输出文件模式为ui_@BASENAME@.h(如mainwindow.ui生成ui_mainwindow.h)。配合preserve_paths,该 Generator 在_compile_ui_impl中会基于源码根目录与当前子目录计算保留路径的基准。
ui_headers = qt4.compile_ui(sources: ['mainwindow.ui', 'dialogs/settings.ui'])编译 MOC 文件:compile_moc
compile_moc(自 0.59.0 起)驱动 Qt 元对象编译器moc,将头文件转换为.cpp文件、或将源文件转换为供手动#include的.moc文件。headers与sources至少必须提供一个(源码中通过build.InvalidArguments强制校验,两者同时为空会直接报错),无位置参数:
headers:要转换为.cpp文件的头文件列表。sources:要转换为.moc文件(用于手动包含进实现文件)的源文件列表。extra_args(string[]):透传给moc的额外参数。method(string):依赖探测方式。dependencies:dependency 对象,其包含目录会被moc使用。include_directories(string | IncludeDirectory)[]:转换.moc文件时加入头文件搜索路径的目录。preserve_paths(bool,自 1.4.0 起):语义同compile_ui。
在 mesonbuild/modules/_qt.py 的_compile_moc_impl中可以看到细节:dependencies中的编译参数(仅取-I、-F、-D前缀项)与InternalDependency的包含目录会被拼进moc命令行;headers模式输出moc_@BASENAME@.cpp,sources模式输出@BASENAME@.moc,二者使用独立的 Generator 处理。示例:
qt4 = import('qt4') qt4_dep = dependency('qt4', modules: ['Core', 'Gui']) inc = include_directories('includes') moc_files = qt4.compile_moc(headers: 'myclass.h', extra_args: ['-DMAKES_MY_MOC_HEADER_COMPILE'], include_directories: inc, dependencies: qt4_dep)传统一体式方法:preprocess
preprocess是模块早期的统一入口,同时处理 moc、uic、rcc 三类输入并输出可直接参与编译的 C++ 文件。官方建议新项目优先使用compile_resources、compile_ui、compile_moc三个独立方法。
签名:qt.preprocess(name: str | None, *sources: str)
- 若传入
name,所有 rcc 文件将写入单一输出文件。 - 自 0.59.0 起,可变位置参数
*sources与sources关键字参数均已被弃用——它们只是原样透传文件,正确做法是把生成器输出追加到其他 sources 列表:
sources = files('a.cpp', 'main.cpp', 'bar.c') sources += qt4.preprocess(qresources: ['resources'])preprocess接受以下关键字参数:
qresources(string | File)[]:交给rcc编译器。ui_files(string | File | CustomTarget)[]:交给uic编译器。moc_sources(string | File | CustomTarget)[]:交给moc,转换为供#include的.moc文件。moc_headers(string | File | CustomTarget)[]:交给moc,转换为.cpp文件。include_directories(IncludeDirectories | string)[]:为moc追加的头文件搜索路径。moc_extra_arguments(string[],自 v0.44.0):额外传给moc的参数。uic_extra_arguments(string[],自 v0.49.0):额外传给uic的参数。rcc_extra_arguments(string[],自 v0.49.0):额外传给rcc的参数。dependencies(Dependency[],自 v0.48.0):moc所需的依赖对象。sources:原样加入输出的额外源列表(0.59.0 弃用)。preserve_paths(bool,自 1.4.0):语义同上。
该方法返回一个目标与源组成的数组,可直接传给编译目标。源码实现上,preprocess内部就是把各关键字参数拆解后分别转调_compile_resources_impl、_compile_ui_impl、_compile_moc_impl,再合并所有结果——这也解释了为何官方建议直接使用拆分的三个方法。
翻译文件:compile_translations
compile_translations(自 0.44.0 起)调用lrelease将 Qt Linguist 生成的.ts文件编译为.qm,无位置参数,关键字参数:
ts_files:输入翻译文件列表(自 0.60.0 起支持 custom_target / custom_target index / generator_output)。install(bool):为true时在安装步骤安装该目标(可选)。install_dir(string):安装目录(可选)。注意源码强制校验:install为真时必须同时给出install_dir,否则抛异常。build_by_default(bool):为true时目标会随meson compile默认构建,默认false。qresource(string,自 v0.56.0):从该 rcc 源文件中提取 ts_files,不能与ts_files同时使用。rcc_extra_arguments(string[],自 v0.56.0):与qresource配合时传给rcc的额外参数。
返回值有两种形态:普通情况下返回编译后翻译目标(custom target)的列表;使用qresource时返回单个custom target,其中包含处理后的源文件,应传给主构建目标。qresource路径的实现会先解析 qrc 中的.qm条目,反向推导出对应.ts文件名,并自动把install_tag标为i18n。
translations = qt4.compile_translations(ts_files: 'myTranslation_fr.ts', build_by_default: true)工具可用性检查:has_tools
has_tools(自 0.54.0 起)返回true当且仅当模块所需的所有工具都已找到,适合用于编写可选的 Qt 代码分支:
qt4 = import('qt4') if qt4.has_tools(required: get_option('qt_feature')) moc_files = qt4.preprocess(...) ... endif关键字参数:
required(bool | FeatureOption):默认为false;若设为true或启用的 feature 而工具缺失,Meson 将中止构建。method(string):查找 Qt 依赖的方式,默认auto。version(str | array[str],自 1.11.0):指定所需版本,形如带比较运算符的版本字符串。tools(自 1.6.0 起):指定要检查的工具子集,合法取值来自源码中定义的集合{'moc', 'uic', 'rcc', 'lrelease', 'qmlcachegen', 'qmltyperegistrar'};Qt4 场景下实际检查的是前四个(qmlcachegen/qmltyperegistrar需要较新的 Qt 版本,从_support_qml_module仅当 Qt >= 6.2.0 才置位的逻辑可以推断,Qt4 下这两者不会启用)。
完整示例与安装说明
以下示例完整串联 moc、uic、rcc 与翻译流程(结构参照官方 Qt 模块文档示例,适配 Qt4):
project('myqt4app', 'cpp', default_options: ['cpp_std=c++11']) qt4 = import('qt4') qt4_dep = dependency('qt4', modules: ['Core', 'Gui', 'Widgets']) inc = include_directories('includes') moc_files = qt4.compile_moc(headers: 'myclass.h', extra_args: ['-DMAKES_MY_MOC_HEADER_COMPILE'], include_directories: inc, dependencies: qt4_dep) ui_headers = qt4.compile_ui(sources: 'mainwindow.ui') resources = qt4.compile_resources(sources: 'resources.qrc') translations = qt4.compile_translations(ts_files: 'myapp_fr.ts', build_by_default: true) executable('myprog', 'main.cpp', 'myclass.cpp', moc_files, ui_headers, resources, translations, include_directories: inc, dependencies: qt4_dep)有时翻译会被直接嵌入二进制:此时.ts文件无需显式列出,Meson 会从 qresource 中列出的.qm文件自动推导对应的.ts:
qt4 = import('qt4') qt4_dep = dependency('qt4', modules: ['Core', 'Gui']) lang_cpp = qt4.compile_translations(qresource: 'lang.qrc') executable('myprog', 'main.cpp', lang_cpp, dependencies: qt4_dep)配置与构建流程与普通 Meson 项目无异:
meson setup builddir meson compile -C builddir meson install -C builddir需要说明的是,Qt4 本身已属生命周期结束的版本,本模块主要用于存量工程的构建维护;若条件允许,官方更推荐在 Qt5 模块或 Qt6 模块下进行迁移,两者的 API 完全同构,迁移成本主要体现在工具链与依赖层面。
小结
Meson 的qt4模块以极小的实现成本(qt4.py仅约 20 行)复用了完整的 Qt 工具抽象:通过compile_moc、compile_ui、compile_resources、compile_translations与has_tools覆盖 Qt4 项目构建的全部关键环节,并在 mesonbuild/dependencies/qt.py 中针对 Qt4 处理了私有头目录布局、qmake 变量回退与平台库后缀差异。理解这一模块,既能解决 Qt4 工程的实际构建需求,也能为理解 Meson 扩展模块体系(ExtensionModule +_detect_tools+build.Generator/CustomTarget)提供一份简洁的参考范本。
- 构建工具
【免费下载链接】meson
The Meson Build System
相关推荐
使用 Meson Qt5 模块自动化构建 Qt 5 项目:moc、uic、rcc 与翻译文件全流程
使用 Meson Qt5 模块自动化构建 Qt 5 项目:moc、uic、rcc 与翻译文件全流程 本指南以 Meson 官方文档 Qt5 module.md
构建工具Meson External Project 模块实战指南:在 Meson 构建系统中集成 Autotools 等外部构建系统子项目
Meson External Project 模块实战指南:在 Meson 构建系统中集成 Autotools 等外部构建系统子项目 Meson 的 unsta
构建工具Waybar开发工具链终极配置:Clang与Meson实战指南
Waybar开发工具链终极配置:Clang与Meson实战指南 Waybar是专为Wayland桌面环境设计的高度可定制状态栏工具,为Sway和wlroots合
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考