ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Qt打包实战:从依赖收集到安装包制作全解析

Qt打包实战:从依赖收集到安装包制作全解析 Qt 程序写起来爽发布的时候是真头大。编译调试大半天好不容易出了 exe拷到同事电脑上一双击要么弹“由于找不到 Qt5Core.dll”要么报一串qt_qpa_platform_plugin_path之类的插件路径错直接劝退一批人。Qt 官方并没有提供一个“一键出安装包”的傻瓜按钮而是把部署能力拆散在各种工具里于是大家就陷入了打包工具的选择困难windeployqt、linuxdeployqt、CQtDeployer、AppImage、Inno Setup、NSIS、Qt Installer Framework……一个一个看介绍越看越懵。这篇内容我尽量用大白话把这摊事讲清楚。我会从 Qt 打包的本质说起把主流工具的使用场景、优缺点、实测命令和常见坑都过一遍最后给你一套可以直接照抄的打包方案。不管你是刚接触 Qt 的新手还是被发布流程折磨过几次的熟手这篇应该都能帮你省下不少时间。1. 打包的本质你到底在打什么包1.1 Qt 程序不是单个 exe是一堆依赖的集合很多新手以为编译通过了那个 exe 就是全部。实际上 Windows 下一个典型的 Qt Widgets 程序运行起来至少要这几类东西Qt 自身动态库比如 Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dllQt 6 里则是对应的 Qt6Core.dll 等你用了哪些模块就得带哪些库。平台插件也就是platforms/qwindows.dll。没有它就会出现那句经典报错“could not find or load the Qt platform plugin windows”热搜词里的qt_qpa_platform_plugin_path基本也绕不开它。图像格式插件、样式插件、TLS 插件imageformats/下的一堆 dlltls/下的 qcertonlybackend.dll 等按需携带。编译器运行库你用 MSVC 编译就带 vcruntime140.dll、msvcp140.dll用 MinGW 就带 libgcc_s_seh-1.dll、libstdc-6.dll、libwinpthread-1.dll。QML 相关模块如果你的界面是 Qt Quick 写的那还要带上qml/整个目录里的各种模块漏一个就报 “module QtQuick is not installed”。第三方依赖OpenSSLlibssl、libcrypto、MySQL 客户端库libmysql.dll、各种解码库等这些 windeployqt 大多数不会帮你管得自己拷。这么说吧Qt 程序的发布本质是“依赖收集 运行时环境重建”。你在开发机上跑得欢是因为 Qt 的 bin 目录、编译器目录都在 PATH 或系统环境里换一台干净的机器这些关联全断了自然起不来。1.2 发布产物有哪几种形态对应什么需求打包工具这么多根源在于产物形态不同解决的是不同层面的问题绿色目录版一个文件夹里面是 exe dll 插件目录压缩成 zip 发给对方就能跑。最常见win 下用 windeployqt 生成的就是这种。单文件版把所有 dll 和资源打成一个 exe双击直接运行靠 Enigma Virtual Box 这类工具实现。适合内部工具、临时演示启动速度和杀软误报率是个隐患。安装包版exe/msi向导式安装带开始菜单、桌面图标、卸载入口给正式客户交付时基本是标配Inno Setup、NSIS、Qt Installer Framework 都在干这件事。Linux 下的 AppImage/deb/rpmAppImage 类似绿色版deb/rpm 类似安装包不同发行版有不同偏好。搞清楚自己到底要哪种产物再选工具就不会陷入“听起来都很厉害但不知道用哪个”的局面。1.3 打包工具其实是两类东西把工具拆开看就清晰了一类是依赖收集器负责把 Qt 的 dll、插件、QML 模块从你的 Qt 安装目录里拉到应用目录典型就是 windeployqt、linuxdeployqt、macdeployqt、CQtDeployer另一类是安装包封装器负责把你整理好的目录变成带界面的安装程序典型是 Inno Setup、NSIS、Qt Installer Framework、AppImage 工具链。很多人问“windeployqt 和 Inno Setup 选哪个”这问题本身就不成立因为它俩根本不在一个层级。正确姿势是先用 windeployqt 把绿色目录做出来再用 Inno Setup 把目录包装成安装包。这个关系理清了后面所有对比都顺了。2. 主流 Qt 打包工具全景对比2.1 官方三件套windeployqt、macdeployqt、linuxdeployqt官方自带的是 Windows 的 windeployqt 和 macOS 的 macdeployqt。这俩质量最稳毕竟 Qt 自己维护对库依赖的解析最准确命令也简单在 Qt 的bin目录下直接就有。windeployqt 的基本用法C:\Qt\5.15.2\msvc2019_64\bin\windeployqt.exe D:\release\MyApp.exe它会扫描 MyApp.exe 的导入表自动把依赖的 Qt dll、platforms 插件、imageformats、qml 模块等复制到 exe 所在目录。macdeployqt 同理处理 .app 包的 framework 和插件。linuxdeployqt 就尴尬一点——它不是 Qt 官方正式维护的。这个项目最早由 probonopd 发起后来官方收录在 Qt 官方 GitHub 下但维护节奏一直不稳定目前处于归档状态官方推荐用新的 linuxdeploy 配合 qt 插件。不过老项目里 linuxdeployqt 用得还是很多网上的教程也绝大多数在讲它所以我们后面 Linux 部分两个方案都会提到。2.2 社区利器CQtDeployer、linuxdeploy、AppImageCQtDeployer 是一个我很看好的跨平台工具支持 Windows、Linux、macOS一条命令就能把可执行文件变成可分发的目录cqtdeployer -bin MyApp -qmake /path/to/qmake它自动处理 Qt 依赖、插件目录、翻译文件还支持生成 AppImage、deb、rpm 等格式算是目前社区里最接近“一站式”的方案。缺点是新东西资料少团队小遇到定制需求得自己啃源码。linuxdeploy 是 Linux 下的新一代部署工具配合 qt 插件使用。它不依赖 Qt 安装目录里的部署脚本而是自己分析依赖灵活度高linuxdeploy -e MyApp --plugin qt --appdir AppDirAppImage 则不是严格意义上的“打包工具”更准确说是一种打包格式和运行时规范。它把整个应用目录塞进一个 squashfs 镜像运行时通过 FUSE 挂载跑起来好处是一个文件到处跑坏处是某些精简系统没有 FUSE老版本 AppImage 跑不动得加--appimage-extract-and-run参数绕过去。2.3 安装包封装Inno Setup、NSIS、Qt Installer Framework依赖收集做完接下来就是封装层。这里三个主流选手各有脾气Inno Setup闷声干大事的典范。脚本语法简洁文档齐全编译出的安装包小而稳定支持 lzma2 压缩、卸载器、注册表操作Windows 下我首选。NSIS老牌插件生态丰富但脚本语法比较古董写复杂逻辑容易头大。如果你的安装逻辑很特殊比如自定义页面、网络下载组件NSIS 的可扩展性更强。Qt Installer FrameworkQtIFWQt 亲儿子主打组件化安装和在线/离线更新。适合大型项目、需要增量更新、需要按模块勾选安装的场景。缺点是学习曲线陡脚本基于 XML官方文档写得晦涩第一次配置容易劝退。还有一个 Enigma Virtual Box它不做安装包而是把程序和 dll 虚拟化打进单文件 exe。内部小工具发布很方便但正式产品我不推荐杀软误报和启动时的解压损耗都是问题。2.4 怎么选先看发布目标再定工具链我列一个决策速查表你照着对号入座就行场景首选方案备选方案Windows 内部工具zip 发给同事windeployqt 手动整理CQtDeployerWindows 正式发布给客户安装windeployqt Inno Setupwindeployqt NSISWindows 大型软件需要组件化安装和在线升级windeployqt QtIFWwindeployqt NSIS 自研更新器Linux 通用桌面发布linuxdeploy AppImagelinuxdeployqt AppImageLinux 发行版原生包打 deb/rpm依赖交给包管理器linuxdeploy deb 插件跨平台项目想统一工具链CQtDeployer各平台官方 deploy 工具PySide6/PyQt6 项目PyInstaller windeployqt 二次处理Nuitka windeployqt核心思路就一句话依赖收集用官方或社区部署工具安装封装再根据平台选安装器两层分工不要混着比。3. Windows 下完整打包实战3.1 用 windeployqt 做出可运行的绿色目录我在 Qt 5.15.2 MSVC2019 环境下的标准操作全程命令行干净利落mkdir D:\deploy\MyApp copy /y D:\build\release\MyApp.exe D:\deploy\MyApp\ cd /d D:\deploy\MyApp C:\Qt\5.15.2\msvc2019_64\bin\windeployqt.exe MyApp.exe --no-translations注意几点不传--debug就默认 release 模式别 debug 和 release 混用否则一堆带 d 后缀的调试库打进去运行还要 VC debug 运行库纯属自找麻烦。--no-translations的意思是不要 Qt 自带的语言翻译文件如果你界面是中文且没加载qt_zh_CN.qm加上这个能省 20 多 MB。默认参数会把 MSVC 运行库 vcruntime140.dll、msvcp140.dll 一起拷进来。想在目标机器上走“安装 VC 运行库”这条路可以加--no-system-dll但新手我不建议这么干很容易漏。还是老老实实默认全带体积大点不影响正确性。跑完看一眼目录正常应该长这样MyApp.exe Qt5Core.dll Qt5Gui.dll Qt5Widgets.dll platforms\ qwindows.dll styles\ qmodernwindowsstyle.dll imageformats\ qjpeg.dll qgif.dll ...如果用了 Qt Quick还会多出qml\目录。这一步做完理论上去一台没装 Qt 的机器就能跑了。3.2 手动补齐第三方库和运行时windeployqt 管不了第三方依赖这一步得自己上。我自己遇到过最典型的是 OpenSSL。用 Qt5Network 访问 HTTPS 接口程序在开发机上没事发布后一请求就报 TLS 初始化失败。原因就是没带libcrypto-1_1-x64.dll和libssl-1_1-x64.dll。解决方式从本机 OpenSSL 安装目录或者 Qt 自带的tools/OpenSSL目录把这两个 dll 拷到 exe 目录。数据库驱动同理MySQL 的libmysql.dll拷到 exe 目录同时确认 Qt 的plugins/sqldrivers/qsqlmysql.dll在绿色目录里。Qt 5.12 之后的 MySQL 驱动还要匹配 MySQL 8 的认证插件客户端库版本别太老。还有一个高频的坑程序用到了 WebEngine 或高级 OpenGL 功能目标机器提示缺少d3dcompiler_47.dll。这个文件在 Windows 10 1903 之后系统自带但 Win7/老版本 Win10 上经常缺失。稳妥做法是把C:\Windows\System32\d3dcompiler_47.dll直接拷到应用目录。这类“系统库缺失”基本都靠这一步兜底。另外强烈建议在 exe 旁边放一个qt.conf[Paths] Prefix. Pluginsplugins Importsimports Qml2Importsqml Translationstranslations别看它简单作用很大告诉 Qt 以 exe 所在目录为基准去找插件和 QML 模块。没有它程序在某些环境下可能因为当前工作目录不对又跑回“找不到平台插件”的老路。3.3 用 Inno Setup 把绿色目录变成安装包绿色目录自测没问题后下一步封装安装包。我用的 Inno Setup 脚本长这样[Setup] AppNameMyApp AppVersion1.0.0 DefaultDirName{autopf}\MyApp OutputDirinstaller OutputBaseFilenameMyAppSetup Compressionlzma2 SolidCompressionyes ArchitecturesInstallIn64BitModex64compatible [Files] Source: D:\deploy\MyApp\*; DestDir: {app}; Flags: recursesubdirs createallsubdirs [Icons] Name: {autoprograms}\MyApp; Filename: {app}\MyApp.exe Name: {autodesktop}\MyApp; Filename: {app}\MyApp.exe; Tasks: desktopicon [Tasks] Name: desktopicon; Description: Create desktop icon; GroupDescription: Additional icons:几个要点Source里的recursesubdirs必须写它会带着 platforms、imageformats、qml 这些子目录一起进安装包。漏了这个标志装到用户机器上照样缺插件报错。ArchitecturesInstallIn64BitModex64compatible的意思是让 64 位程序默认装到 Program Files。注意老版本 Inno Setup 不认识x64compatible用x64也可以但新版推荐前者。如果应用还带了自己生成的数据文件、日志目录建议把程序目录和用户数据目录分开不要都塞{app}下。编译完生成MyAppSetup.exe找个干净虚拟机装一遍验证卸载和图标是否正常。3.4 发布前的自检清单我在 Windows 上做最终检查按下面这几条过一遍一台干净的不带 Qt 的机器虚拟机就行上跑绿色目录版双击 exe确认能启动。运行QT_DEBUG_PLUGINS1 MyApp.exe观察控制台输出。如果哪个插件加载失败会清清楚楚列出来比瞎猜快得多。检查platforms\qwindows.dll存在检查qml\目录用 Qt Quick 的话存在检查第三方 dll 都在根目录。用 Process Explorer 打开运行中的进程查看加载的 dll 列表对比开发机上缺哪些。这一步基本能定位所有“暗缺依赖”。4. Linux 下打包实战从 Ubuntu 到麒麟4.1 老牌方案 linuxdeployqt 的实操和局限Linux 下的依赖收集比 Windows 麻烦因为动态库的依赖链更深还要关心 glibc 版本、xcb 插件依赖的系统库。老方案 linuxdeployqt 的用法wget https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage chmod x linuxdeployqt-continuous-x86_64.AppImage export PATH/opt/Qt/5.15.2/gcc_64/bin:$PATH mkdir -p AppDir/usr/bin cp MyApp AppDir/usr/bin/ ./linuxdeployqt-continuous-x86_64.AppImage AppDir/usr/bin/MyApp -appimage它会把 Qt 的 .so 收集到AppDir/usr/lib并把插件放好最后生成 AppImage。这里有几个坑必须提前设置 PATH让工具能找到 qmake。找不到 qmake 时它会报 “Could not find qmake” 然后罢工。AppDir 目录结构有约定可执行文件要放在usr/bin下不要直接扔 AppDir 根目录。linuxdeployqt 已经归档在新系统上跑可能出现 Python 脚本兼容问题这是旧方案最大的隐患。4.2 更推荐的新方案 linuxdeploy qt 插件现在社区主流推荐 linuxdeploy它是模块化设计Qt 支持通过插件实现wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage chmod x linuxdeploy-x86_64.AppImage linuxdeploy-plugin-qt-x86_64.AppImage export QMAKE/opt/Qt/5.15.2/gcc_64/bin/qmake mkdir -p AppDir/usr/bin cp MyApp AppDir/usr/bin/ ./linuxdeploy-x86_64.AppImage -e MyApp --plugin qt --appdir AppDirlinuxdeploy 会自己扫描 Qt 插件和依赖不太依赖你安装 Qt 时的部署文件。最后再./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage就能拿到 AppImage。注意--plugin qt必须在安装 Qt 插件的情况下才有效这个插件是通过环境变量QMAKE找到 Qt 库路径的。4.3 AppImage 跑不起来的常见原因AppImage 在不同发行版之间跑不起来多半是这几个原因目标机器没装 FUSE。新版 AppImage 需要 fuse3老系统只有 fuse2。可以在启动时加--appimage-extract-and-run但把这个问题抛给用户显然不专业。glibc 版本过低。你在 Ubuntu 22.04 上打的包拿到 CentOS 7 上往往跑不起来原因是 glibc 版本低于构建机。解决方案是找一台最低版本的目标系统做构建机或者用 Docker 拉一个旧版 base image 来打包。字体、中文输入法缺失。Qt 程序在精简 Linux 上容易显示方块字需要带字体或者在系统里安装fonts-wqy-zenhei等中文字体打包时把字体文件放进 AppDir 并设置QTPATH相关环境变量也很常见。4.4 麒麟 x86 系统离线部署思路国产麒麟系统x86 架构本质是 Linux 发行版Qt 打包逻辑和 Ubuntu 大同小异但有几个实际区别很多内网环境不能上网你开发的 Qt 是离线安装的目标机器上也没有包管理器源。此时最好的方案不是丢一个 AppImage而是做一个 deb 包把依赖写好离线安装时用dpkg -i配上本地依赖包一起装。麒麟的 xcb 相关系统库不一定装全常见缺libxcb-xinerama0、libxkbcommon-x11-0。在你自己的打包机上先ldd MyApp扫一遍缺什么就在构建脚本里写清楚或者打进安装包。如果目标机器是飞腾/鲲鹏的 ARM 架构那就必须用 ARM 版 Qt 交叉编译或者直接在 ARM 机器上编译x86 的产物跑不了这个和工具选型无关纯架构约束。我的建议是麒麟 x86 环境优先做 deb 包用 fpm 或者手动打把libqt5core5a、libqt5gui5等写明依赖内网用本地源装。图省事就 ld 脚本收集依赖做一个绿色目录直接拷贝运行但要接受不同机器系统库版本不一致的隐患。5. Qt 打包经典报错速查表5.1 高频报错一览我整理了平时遇到最多、群里问得最多的几个直接对照处理报错信息原因解决办法could not find or load the Qt platform plugin windowsplatforms/qwindows.dll 缺失或路径不对用 windeployqt 重新部署或写 qt.conf 指定 Plugins 路径提示 qt_qpa_platform_plugin_path 相关的路径错插件目录没放到预期位置环境变量被设置成开发机路径检查是否自定义了 QT_QPA_PLATFORM_PLUGIN_PATH清除或改为相对路径由于找不到 Qt5Core.dllQt 库没带全windeployqt 部署后确认 Qt5Core.dll 在 exe 目录缺少 VCRUNTIME140.dll 或 msvcp140.dllVC 运行库缺失打包时默认带编译器运行库或安装 vc_redist.x64.exe缺少 d3dcompiler_47.dll老系统缺 DirectX 相关库从 System32 拷一份到应用目录module QtQuick version 2.15 is not installedQML 模块没带全把整个 qml 目录拷到应用目录用 qt.conf 指到 Qml2ImportsQMYSQL driver not loaded数据库驱动插件事务缺失或 libmysql 缺失检查 plugins/sqldrivers/qsqlmysql.dll拷贝 libmysql.dll启动闪退无任何提示第三方 dll 缺失或插件加载失败用 QT_DEBUG_PLUGINS1 看插件日志用 ldd 查 Linux 依赖编译时报 dependent ......\allinstall\qt\5.15.2\msvc2019\include\qtw....pro 或 CMake 写死了开发机绝对路径Qt 安装路径漂移清理构建缓存改用 $$[QT_INSTALL_HEADERS] 等 Qt 内置变量AppImage 在别的机器上打不开FUSE 缺失或 glibc 版本不匹配加 --appimage-extract-and-run 应急长期方案是低版本系统构建5.2 排查依赖的实用工具和方法遇到诡异问题先别急着重新打包按这个排查顺序来Windows 下用 Process Explorer 打开进程右键“Properties”查看“Image”列表能看到所有加载的 dll。对照开发机的加载列表缺哪个一目了然。Linux 下用ldd MyApp看动态库依赖输出里出现 “not found” 就是缺系统库。再用readelf -d MyApp | grep NEEDED看更深层依赖。万能环境变量QT_DEBUG_PLUGINS1Windows 和 Linux 都支持启动程序后控制台会打印每个插件加载路径和结果几乎所有“平台插件”类问题都能在这里看到答案。自己写个脚本遍历 release 目录把文件清单导出来用QDir::entryInfoList递归收集目录内容是可以的但更省事的方式是用dir /s /b或者在工程里加一个小工具函数把依赖文件列表输出成文本方便比对。排查的错误见多了就会发现90% 的打包问题根源只有三个插件路径不对、依赖库缺失、版本不匹配。定位到具体是哪个解决起来其实都很快。6. 我的一点个人经验踩过几次坑之后我的习惯已经固化了Windows 产品发布固定用 windeployqt 做依赖收集再用 Inno Setup 包一层脚本写进 CI每次构建产物直接出安装包再也不手工拷 dll。Linux 那边我用 linuxdeploy 打 AppImage 应对“到处跑”的需求给麒麟系统交付时单独打 deb 包。两者互不冲突都在一条流水线里。最后再分享一个小技巧发布前别光在自己 64 位 Windows 11 上测试准备一台 Win10 的干净虚拟机跑一遍绿色版和安装包。很多你以为“不可能缺”的 dll在人家机器上就是缺了。打包工具再智能也不如一次真实环境的验证来得可靠。
返回列表