资讯详情

资讯详情

Qt程序发布打包全攻略:跨平台依赖处理与安装包制作

1. 从开发机到用户桌面Qt程序发布到底难在哪很多人第一次用Qt写完一个带界面的小工具在本机双击运行一切正常发给同事或者客户之后对方要么提示缺少一堆dll要么界面直接闪退要么图标变成白板。这个场景几乎每个Qt开发者都经历过。问题不在于代码写得对不对而在于开发环境和运行环境的差异——你的机器上装了完整的Qt SDK、编译器运行时、各种系统库而用户的机器上什么都没有。Qt程序的发布打包本质上就是解决三件事依赖库的收集、运行环境的适配、安装包的制作。听起来简单但实际操作中坑非常多。比如同样是Windows平台用MinGW编译和用MSVC编译需要携带的运行时库完全不同同样是Qt6windeployqt的行为和Qt5时代也有不少变化再比如你用了OpenGL、多媒体、数据库插件这些模块的依赖往往不会自动被扫描到。这篇文章面向的是已经能用Qt写出可运行程序的开发者不管你是刚做完第一个小工具想分享给朋友还是在公司里需要正式发布一个商业软件下面这些内容都能直接参考。我会从依赖分析讲起然后分别说清楚Windows、macOS、Linux三个平台的打包策略再讲安装包制作和版本管理最后分享一些我踩过的坑和实际项目中的经验。整个流程我会尽量给出可以直接复制执行的命令和配置让你少走弯路。需要提前说明的是Qt的发布方式取决于你的编译套件MinGW还是MSVC、Qt版本5还是6、目标平台Windows/macOS/Linux以及你是否使用了第三方库。不同的组合对应不同的处理方式我会在每一节里把分支情况都讲清楚。2. 发布之前必须搞清楚的依赖关系2.1 Qt程序运行时到底需要哪些文件一个Qt程序在运行时需要的文件大致可以分为四类。第一类是Qt核心库比如Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll这些是最基本的只要用了Qt就少不了。第二类是Qt插件最常见的是平台插件platforms目录下的qwindows.dll没有它程序直接起不来还有图片格式插件imageformats目录、数据库驱动sqldrivers目录、多媒体插件等。第三类是编译器运行时库MinGW需要libgcc_s_seh-1.dll、libstdc-6.dll、libwinpthread-1.dllMSVC需要对应的VC运行库。第四类是第三方库和系统库比如你用了OpenCV、FFmpeg、SQLite这些都要单独处理。很多人以为用windeployqt跑一遍就万事大吉了实际上它只能扫描到直接链接的Qt库和部分插件对于通过QLibrary动态加载的库、或者第三方库自己依赖的东西它是扫描不到的。我遇到过一个典型情况程序里用了Qt的SQL模块连接SQLitewindeployqt把qsqlite.dll复制过来了但SQLite本身的依赖没问题可程序还是连不上数据库最后发现是sqldrivers目录的路径不对。2.2 用Dependency Walker和objdump做依赖分析在Windows上最传统的依赖分析工具是Dependency Walker但它对现代程序的支持不太好经常误报。我更推荐用objdump或者dumpbin来查看可执行文件的导入表。如果你用的是MinGW套件objdump就在Qt的Tools目录下objdump -p your_app.exe | grep DLL Name这条命令会列出exe直接依赖的所有dll。然后你需要对每一个非系统dll再跑一遍递归地把整棵依赖树找出来。这个过程手动做很繁琐所以实际项目中我一般写个简单的脚本来自动化#!/bin/bash # 递归查找依赖的简易脚本 analyze_deps() { local file$1 objdump -p $file | grep DLL Name | awk {print $3} | while read dll; do if [[ ! $dll ~ ^(KERNEL32|USER32|ADVAPI32|SHELL32|ole32|OLEAUT32|GDI32|COMCTL32|msvcrt|WS2_32|IMM32|WINMM|VERSION|UxTheme|dwmapi|netapi32|userenv|bcrypt|d3d11|dxgi|D3DCOMPILER) ]]; then echo $dll fi done }这个脚本会过滤掉Windows系统自带的dll只输出你需要额外携带的。实际使用时你需要对每个找到的dll再递归分析直到没有新的非系统dll出现。在Linux上对应的工具是lddldd your_app | grep | awk {print $3}macOS上则是otoolotool -L your_app这些工具输出的结果需要你人工判断哪些是系统自带的、哪些是需要打包的。一般来说Linux上libc、libm、libpthread、libdl这些是系统库不用管但libQt5Core.so.5这种就要打包进去。2.3 静态编译和动态编译的取舍Qt支持静态编译就是把Qt库直接编译进你的可执行文件里这样发布的时候只需要一个exe文件不需要带一堆dll。听起来很美好但有几个现实问题第一静态编译Qt需要你自己从源码编译过程比较耗时而且容易出错第二静态编译后如果用了LGPL版本的Qt按照许可证要求你需要提供目标文件以便用户重新链接这在商业项目里比较麻烦第三静态编译的程序体积会很大一个简单的窗口程序可能就有几十MB。动态编译就是默认的方式exe和dll分开发布时把需要的dll一起打包。这种方式灵活、体积可控、许可证问题也简单是绝大多数项目的选择。我的建议是除非你有特别强烈的单文件需求否则优先用动态编译加打包工具的方式。注意如果你用的是Qt商业许可证静态编译没有许可证方面的顾虑但编译过程依然需要不少时间。开源项目用LGPL的话动态链接是最省心的方案。3. Windows平台打包的完整操作链路3.1 windeployqt的正确使用姿势windeployqt是Qt官方提供的部署工具位于Qt安装目录的bin文件夹下。最基本的用法是windeployqt --release your_app.exe它会在exe所在目录下自动创建platforms、imageformats等文件夹并把需要的Qt库复制过来。但直接这样用往往不够我一般会加上几个参数windeployqt --release --no-translations --no-system-d3d-compiler --no-opengl-sw --compiler-runtime your_app.exe这里解释一下各个参数的作用。--no-translations是不复制翻译文件如果你不需要多语言支持可以加上能省不少体积。--no-system-d3d-compiler和--no-opengl-sw是不复制D3D编译器和软件OpenGL实现如果你的程序不用OpenGL可以省掉。--compiler-runtime是自动复制编译器运行时库MinGW套件下这个很有用。但windeployqt有个问题它默认只处理exe直接依赖的Qt模块。如果你的程序通过插件方式加载了某个模块比如运行时才加载的图片格式插件它可能扫描不到。这时候你需要用--add-plugin参数手动指定windeployqt --release --add-plugin imageformats/qwebp.dll your_app.exe还有一个常见问题是路径问题。windeployqt需要能找到Qt的安装目录如果你在命令行里直接运行它确保Qt的bin目录在PATH里或者用完整路径调用。我习惯在项目里写一个批处理脚本把路径都写死避免换机器之后找不到echo off set QT_DIRC:\Qt\6.5.0\mingw_64 set PATH%QT_DIR%\bin;%PATH% %QT_DIR%\bin\windeployqt.exe --release --compiler-runtime %13.2 MinGW和MSVC套件的差异处理MinGW和MSVC在打包时最大的区别在于运行时库。MinGW需要携带libgcc_s_seh-1.dll、libstdc-6.dll、libwinpthread-1.dll这三个文件它们通常在Qt安装目录的Tools\mingw*\bin下面。windeployqt加上--compiler-runtime参数会自动处理但有时候版本对不上会导致问题。MSVC则需要VC运行库通常是msvcp140.dll、vcruntime140.dll、vcruntime140_1.dll这几个。这些文件在Windows系统里可能已经有了但用户的机器上不一定装了对应版本的VC运行库。有两种处理方式一是把这些dll直接打包进去二是让安装包检测并安装VC运行库。我倾向于直接打包虽然微软的许可协议允许再分发这些运行库但要注意版本匹配。还有一个容易忽略的点调试版和发布版的混用。如果你用debug模式编译了程序但用windeployqt的release模式去部署或者反过来会出现各种奇怪的崩溃。一定要确保编译模式和部署模式一致。我一般会在pro文件里加一个判断CONFIG(debug, debug|release) { DESTDIR $$PWD/bin/debug } else { DESTDIR $$PWD/bin/release }这样debug和release的输出目录分开避免混淆。3.3 第三方库和插件的处理第三方库的处理是打包中最麻烦的部分。假设你用了OpenCV那么除了OpenCV本身的dll还要考虑它依赖的IPP库、TBB库等。这些库windeployqt是不认识的需要你手动复制。我的做法是建一个third_party目录把所有第三方库的dll按类别放好然后在打包脚本里统一复制xcopy /Y /E third_party\opencv\*.dll dist\ xcopy /Y /E third_party\ffmpeg\*.dll dist\对于Qt插件除了windeployqt自动复制的还要检查一下你的程序是否用了这些插件插件类型目录常见文件使用场景平台插件platformsqwindows.dll所有Windows GUI程序必需图片格式imageformatsqjpeg.dll, qgif.dll加载对应格式图片时数据库驱动sqldriversqsqlite.dll, qmysql.dll使用Qt SQL模块时多媒体multimediawindowsmediaplugin.dll使用Qt Multimedia时样式stylesqwindowsvistastyle.dll需要原生外观时如果你不确定某个插件是否需要一个简单的判断方法是在开发机上把程序复制到一个干净的虚拟机里运行看报什么错缺什么补什么。这个过程虽然笨但最可靠。4. macOS和Linux平台的打包策略4.1 macOS的app bundle结构解析macOS上的程序打包和Windows完全不同它使用app bundle的形式。一个app bundle本质上是一个目录结构如下YourApp.app/ Contents/ Info.plist MacOS/ YourApp Frameworks/ QtCore.framework QtGui.framework ... PlugIns/ platforms/ libqcocoa.dylib imageformats/ ... Resources/ your_icon.icnsQt提供了一个macdeployqt工具用法和windeployqt类似macdeployqt YourApp.app -dmg它会自动把Qt框架复制到Frameworks目录修正install_name路径并生成一个dmg磁盘映像。但macdeployqt有几个需要注意的地方第一它默认只处理Qt框架第三方库需要手动用install_name_tool修正路径第二如果你的程序用了非系统自带的库需要确保它们的install_name是rpath或者executable_path开头的相对路径否则在用户机器上会找不到。修正install_name的命令是install_name_tool -change /old/path/libfoo.dylib rpath/libfoo.dylib YourApp.app/Contents/MacOS/YourApp这个操作需要对每个依赖的第三方库都做一遍比较繁琐。我一般会写个脚本自动处理#!/bin/bash APP$1 FRAMEWORKS_DIR$APP/Contents/Frameworks find $FRAMEWORKS_DIR -name *.dylib | while read lib; do install_name_tool -id rpath/$(basename $lib) $lib done4.2 Linux下的打包方式选择Linux的打包方式比较多样常见的有AppImage、deb/rpm包、tar.gz压缩包几种。AppImage的好处是一个文件就能运行不需要安装适合分发给普通用户deb/rpm适合集成到系统包管理器里tar.gz最原始但兼容性最好。制作AppImage需要用到linuxdeployqt工具它的用法是linuxdeployqt your_app -appimage它会自动收集依赖、创建AppDir目录、生成AppImage文件。但linuxdeployqt对系统的glibc版本有要求如果你的程序是在较新的系统上编译的生成的AppImage在旧系统上可能跑不起来。这是因为glibc的向后兼容性——新系统编译的程序不能在旧系统上运行。解决这个问题的办法是在较旧的基础系统上编译和打包比如用CentOS 7或者Ubuntu 18.04的虚拟机来构建。这样生成的二进制文件能在更新的系统上运行。如果必须在新系统上编译可以考虑用静态链接glibc的方式但这会带来其他问题一般不推荐。对于deb包的制作可以用dpkg-deb命令mkdir -p myapp/DEBIAN mkdir -p myapp/usr/bin mkdir -p myapp/usr/lib/myapp cp your_app myapp/usr/bin/ cp *.so myapp/usr/lib/myapp/ cat myapp/DEBIAN/control EOF Package: myapp Version: 1.0.0 Architecture: amd64 Maintainer: your_name Description: A Qt application EOF dpkg-deb --build myapp这个control文件是deb包的元数据必须包含Package、Version、Architecture、Maintainer、Description这几个字段。4.3 跨平台打包的自动化脚本设计实际项目中我一般会写一个统一的打包脚本根据当前平台自动选择对应的工具。脚本的大致结构是#!/bin/bash PLATFORM$(uname -s) APP_NAMEYourApp BUILD_DIRbuild/release case $PLATFORM in Linux*) linuxdeployqt $BUILD_DIR/$APP_NAME -appimage ;; Darwin*) macdeployqt $BUILD_DIR/$APP_NAME.app -dmg ;; MINGW*|MSYS*|CYGWIN*) windeployqt --release --compiler-runtime $BUILD_DIR/$APP_NAME.exe ;; esac这个脚本可以放在项目的根目录配合CI/CD工具使用。每次发布新版本时只需要在三个平台上分别跑一遍就能生成对应的安装包。提示跨平台打包时注意文件路径的分隔符。Windows用反斜杠Linux和macOS用正斜杠。在脚本里尽量用正斜杠Qt和大多数工具都能识别。5. 安装包制作与版本信息管理5.1 Inno Setup制作Windows安装包Windows上最常用的安装包制作工具是Inno Setup免费、开源、脚本化。一个基本的脚本长这样[Setup] AppNameYourApp AppVersion1.0.0 DefaultDirName{autopf}\YourApp DefaultGroupNameYourApp OutputDirinstaller OutputBaseFilenameYourApp_Setup_1.0.0 Compressionlzma2 SolidCompressionyes [Files] Source: dist\*; DestDir: {app}; Flags: recursesubdirs [Icons] Name: {group}\YourApp; Filename: {app}\YourApp.exe Name: {commondesktop}\YourApp; Filename: {app}\YourApp.exe [Run] Filename: {app}\YourApp.exe; Description: Launch YourApp; Flags: nowait postinstall skipifsilent这个脚本会把dist目录下的所有文件打包成一个安装程序并在开始菜单和桌面创建快捷方式。Inno Setup支持多语言、注册表操作、环境变量设置等高级功能可以根据需要扩展。一个容易忽略的点是卸载时清理残留。如果你的程序在运行时会生成配置文件、日志文件卸载时应该询问用户是否保留。可以在脚本里加[UninstallDelete] Type: filesandordirs; Name: {app}\logs Type: files; Name: {app}\config.ini5.2 版本号、图标和元信息的设置版本信息在Windows上体现在exe文件的属性里需要用一个rc文件来设置。在Qt的pro文件里可以这样写RC_ICONS app.ico VERSION 1.0.0.0 QMAKE_TARGET_COMPANY Your Company QMAKE_TARGET_PRODUCT YourApp QMAKE_TARGET_DESCRIPTION A Qt Application QMAKE_TARGET_COPYRIGHT Copyright 2024这样编译出来的exe在属性里就能看到完整的版本信息。图标文件需要是ico格式包含多个尺寸16x16、32x32、48x48、256x256这样在不同视图下都能清晰显示。macOS上的版本信息在Info.plist里设置macdeployqt会自动生成一个基本的plist但你可能需要自定义一些字段keyCFBundleShortVersionString/key string1.0.0/string keyCFBundleVersion/key string1/string keyNSHighResolutionCapable/key true/Linux下没有统一的版本信息机制一般是在桌面快捷方式文件.desktop里体现[Desktop Entry] NameYourApp Exec/usr/bin/yourapp Icon/usr/share/icons/yourapp.png TypeApplication CategoriesUtility;5.3 自动更新机制的简单实现如果你的程序需要自动更新有几种方案可选。最简单的是检查更新手动下载程序启动时请求一个版本信息接口比较版本号如果有新版本就弹窗提示用户去下载。这种方式实现简单不需要额外的服务器组件。稍微复杂一点的是增量更新只下载变化的文件。这需要你维护每个版本的文件清单和哈希值实现起来工作量大但用户体验好。我一般用bsdiff算法做二进制差分生成的补丁文件很小。还有一种方案是用Qt Installer Framework这是Qt官方提供的安装包框架支持在线安装和更新。它的配置文件是XML格式可以定义组件、依赖关系、更新源等。不过它的学习曲线比较陡适合大型项目。不管用哪种方案版本号的规范都很重要。我推荐用语义化版本SemVer主版本号.次版本号.修订号。主版本号在有不兼容的API变更时增加次版本号在增加功能时增加修订号在修复bug时增加。这样用户一看版本号就知道升级的影响范围。6. 那些年我踩过的打包坑6.1 插件加载失败的各种原因插件加载失败是Qt打包中最常见的问题表现是程序启动时报could not find or load the Qt platform plugin windows。这个错误的根本原因是Qt找不到platforms目录。Qt在启动时会按以下顺序查找插件环境变量QT_PLUGIN_PATH指定的路径可执行文件所在目录下的plugins目录可执行文件所在目录下的platforms目录Qt安装目录下的plugins目录在开发机上Qt安装目录存在所以能找到插件。到了用户机器上Qt安装目录不存在就必须把插件放在exe旁边的platforms目录里。windeployqt会自动做这件事但如果你手动复制文件很容易漏掉。还有一种情况是插件版本不匹配。比如你用Qt 6.5编译的程序但复制了Qt 6.2的qwindows.dll就会加载失败。插件的版本必须和Qt库的版本完全一致。我一般会在打包脚本里加一个校验步骤# 检查qwindows.dll的版本 strings platforms/qwindows.dll | grep Qt 6.5如果输出的版本号和你的Qt版本不一致就说明复制错了。6.2 路径中含中文或空格的坑这个问题在Windows上特别常见。如果你的程序安装在C:\Program Files\My App这样的路径下而程序内部又用相对路径去加载资源就可能因为空格导致路径解析错误。Qt的QDir和QFile对空格的处理是没问题的但如果你调用了外部程序或者用了系统API就可能出问题。更麻烦的是中文路径。有些第三方库对Unicode路径支持不好遇到中文路径会打开文件失败。解决办法是在程序内部统一用UTF-8编码处理路径在Windows上调用系统API时转换成宽字符。Qt的QString内部就是Unicode所以只要不经过窄字符的转换一般不会有问题。我在一个项目里遇到过这样的情况程序在开发机上运行正常到了用户机器上就报找不到配置文件。排查了半天发现用户的安装路径里有中文而程序里用std::ifstream去读配置文件这个标准库函数在Windows上不支持中文路径。后来改成用QFile读取就正常了。6.3 杀毒软件误报和数字签名Qt程序被杀毒软件误报的概率不算高但如果你用了UPX之类的加壳工具误报率会大幅上升。我一般不建议对Qt程序加壳因为Qt的dll本身就比较大加壳带来的体积收益有限反而容易触发杀毒软件。如果你的程序需要正式发布建议购买代码签名证书对exe和安装包进行数字签名。这样不仅能减少杀毒软件误报还能让用户在安装时看到发布者信息提升信任度。签名工具用Windows SDK里的signtoolsigntool sign /f certificate.pfx /p password /t http://timestamp.digicert.com your_app.exe时间戳服务器的作用是让签名在证书过期后仍然有效。如果不加时间戳证书一过期之前签名的程序也会显示签名无效。6.4 不同Windows版本的兼容性测试Windows的版本碎片化比较严重从Windows 7到Windows 11不同版本的系统库有差异。Qt 6官方支持的最低版本是Windows 10如果你需要支持Windows 7只能用Qt 5。即使都用Qt 5在Windows 7上也可能遇到API缺失的问题比如某些新的系统调用在旧系统上不存在。我的做法是准备几个虚拟机分别装Windows 7、Windows 10、Windows 11每次发布前都跑一遍。重点测试程序能否启动、界面是否正常、文件对话框是否可用、网络功能是否正常。如果条件允许还可以用AppVerifier工具检查程序是否有内存泄漏、句柄泄漏等问题。还有一个容易忽略的点是高DPI缩放。在高分屏上如果程序没有正确处理DPI界面会模糊或者控件错位。Qt 5.6以后支持高DPI但需要在main函数里设置QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);Qt 6默认就开启了高DPI支持不需要手动设置。7. 发布流程的工程化与持续集成7.1 用脚本串联整个发布流程手动打包容易出错我一般会把整个流程写成脚本从编译到打包到生成安装包一气呵成。一个典型的发布脚本包含这些步骤#!/bin/bash set -e # 1. 清理旧的构建目录 rm -rf build/release dist installer # 2. 编译 mkdir -p build/release cd build/release qmake ../../YourApp.pro CONFIGrelease make -j$(nproc) cd ../.. # 3. 部署Qt依赖 windeployqt --release --compiler-runtime build/release/YourApp.exe # 4. 复制第三方库 cp -r third_party/* build/release/ # 5. 复制到dist目录 mkdir -p dist cp -r build/release/* dist/ # 6. 生成安装包 iscc installer.iss echo 发布完成installer/YourApp_Setup.exe这个脚本用set -e确保任何一步失败就停止避免生成不完整的安装包。在实际项目中我还会加上版本号自动递增、生成更新日志、上传到分发服务器等步骤。7.2 在CI/CD中集成打包任务如果项目托管在代码平台上可以用CI/CD工具自动打包。以GitHub Actions为例一个Windows打包的workflow大致是name: Build and Package on: push: tags: - v* jobs: build-windows: runs-on: windows-latest steps: - uses: actions/checkoutv3 - name: Install Qt uses: jurplel/install-qt-actionv3 with: version: 6.5.0 - name: Build run: | qmake YourApp.pro CONFIGrelease nmake - name: Deploy run: windeployqt --release release/YourApp.exe - name: Package run: iscc installer.iss - name: Upload uses: actions/upload-artifactv3 with: name: windows-installer path: installer/*.exe这样每次打tag推送代码CI就会自动编译、打包、上传安装包。Linux和macOS的配置类似只是编译命令和打包工具不同。7.3 发布后的验证清单安装包生成之后不要急着发给用户先在一个干净的虚拟机里测试一遍。我一般会检查这些项目程序能否正常启动界面是否完整显示所有功能是否正常特别是文件读写、网络请求、数据库操作图标和版本信息是否正确显示安装和卸载是否干净有没有残留文件在不同DPI设置下界面是否正常杀毒软件是否报毒这个清单看起来简单但每次发布前都过一遍能避免很多低级问题。我见过不少开发者因为忘了测试某个功能导致用户拿到安装包后发现用不了再重新打包发布浪费时间和信誉。提示测试用的虚拟机最好每次都用快照恢复到初始状态确保测试环境干净。如果虚拟机里已经装过旧版本可能会因为残留文件导致测试结果不准确。8. 一些提高效率的实用技巧8.1 用CMake替代qmake的打包差异现在越来越多的Qt项目用CMake构建CMake的打包流程和qmake有些差异。CMake本身不提供类似windeployqt的工具但你可以用CMake的install命令配合CPack来打包install(TARGETS YourApp RUNTIME DESTINATION bin) install(DIRECTORY plugins/ DESTINATION bin/plugins) set(CPACK_GENERATOR NSIS) set(CPACK_PACKAGE_NAME YourApp) set(CPACK_PACKAGE_VERSION 1.0.0) include(CPack)然后在构建目录里运行cpack就能生成安装包。CMake的优点是跨平台一致性好同一套配置在三个平台上都能用。缺点是NSIS生成器的定制能力不如直接写Inno Setup脚本。如果你用CMakewindeployqt依然可以用只是需要在构建后手动调用add_custom_command(TARGET YourApp POST_BUILD COMMAND windeployqt --release $TARGET_FILE:YourApp )这样每次编译完会自动部署依赖省去手动执行的步骤。8.2 减小发布体积的几种手段Qt程序体积偏大是公认的一个简单的窗口程序可能就有几十MB。减小体积有几种手段第一裁剪Qt模块。只链接你实际用到的模块不要图省事把整个Qt都链接进去。在pro文件里精确指定QT core gui widgets而不是QT core gui widgets network sql multimedia。第二用编译优化。在pro文件里加上QMAKE_CXXFLAGS_RELEASE -Os-Os是优化体积的选项比-O2生成的代码更小。第三去掉不需要的插件和翻译。windeployqt的--no-translations参数可以去掉翻译文件--no-opengl-sw可以去掉软件OpenGL实现。如果不用数据库sqldrivers目录整个删掉。第四用UPX压缩。虽然前面说UPX容易触发杀毒软件但如果你的程序不涉及敏感操作UPX能把体积压缩30%到50%。用之前先测试一下杀毒软件的反应。8.3 多语言和资源文件的处理如果你的程序支持多语言Qt的翻译文件.qm需要打包进去。windeployqt默认会复制translations目录但里面包含所有语言的翻译体积不小。你可以只保留需要的语言# 只保留中文和英文 find translations -name *.qm ! -name *_zh_CN.qm ! -name *_en.qm -delete资源文件图片、图标、配置文件如果编译进了qrc就不需要额外处理。但如果用了外部资源文件要确保它们被正确复制到安装目录。我一般会在pro文件里用INSTALLS变量指定resources.files resources/* resources.path $$OUT_PWD/resources INSTALLS resources这样make install的时候会自动复制资源文件。8.4 用户反馈和崩溃日志的收集程序发布之后难免会遇到用户反馈崩溃或者功能异常。如果没有日志排查问题会很困难。我一般会在程序里集成一个简单的崩溃捕获机制用QBreakpad或者自己写一个异常处理器在程序崩溃时生成dump文件。对于非崩溃的异常可以用日志系统记录关键操作。Qt的qInstallMessageHandler可以自定义日志输出void messageHandler(QtMsgType type, const QMessageLogContext context, const QString msg) { QFile logFile(app.log); logFile.open(QIODevice::Append); QTextStream stream(logFile); stream QDateTime::currentDateTime().toString() msg \n; }然后在main函数里注册qInstallMessageHandler(messageHandler);这样所有的qDebug、qWarning输出都会写入日志文件。用户反馈问题时让他们把日志文件发过来就能快速定位问题。注意日志文件不要无限增长要定期清理或者限制大小。我一般会保留最近7天的日志超过的自动删除。9. 从实际项目中学到的经验打包这件事说难不难说简单也不简单。核心就是把开发机上有的、用户机器上没有的东西正确地复制过去。但实际操作中因为平台差异、版本差异、第三方库的复杂性总会遇到各种意外。我印象最深的一次是给一个客户做交付程序在我这里测试了无数遍都没问题到了客户那里就是启动不了。排查了一整天最后发现客户的机器上装了一个旧版本的Qt环境变量QT_PLUGIN_PATH指向了那个旧版本的插件目录导致我的程序加载了错误的插件。解决办法是在程序启动时清除或覆盖这个环境变量qputenv(QT_PLUGIN_PATH, QCoreApplication::applicationDirPath().toUtf8());这样程序只会从自己的目录加载插件不受系统环境的影响。还有一次是macOS上的问题程序在开发机上运行正常打包成dmg之后用户打开提示文件已损坏。这是因为macOS的Gatekeeper机制未签名的app会被阻止运行。解决办法是用codesign命令签名codesign --deep --force --sign Developer ID Application: Your Name YourApp.app如果暂时没有开发者证书可以先用ad-hoc签名codesign --deep --force --sign - YourApp.app这样至少在本机测试时不会报错但分发给用户还是需要正式证书。最后分享一个我常用的打包检查脚本在发布前跑一遍能发现大部分常见问题#!/bin/bash APP$1 echo 检查平台插件... [ -f $(dirname $APP)/platforms/qwindows.dll ] || echo 缺少qwindows.dll echo 检查编译器运行时... [ -f $(dirname $APP)/libgcc_s_seh-1.dll ] || echo 缺少libgcc echo 检查Qt核心库... [ -f $(dirname $APP)/Qt6Core.dll ] || echo 缺少Qt6Core.dll echo 检查完成这个脚本可以根据你的项目实际情况扩展比如检查特定的第三方库、检查资源文件等。把它集成到发布流程里能避免很多低级错误。打包发布是Qt开发中最后一步也是最容易被忽视的一步。代码写得再好如果用户装不上或者跑不起来一切都是白费。希望上面这些内容能帮你少踩一些坑顺利把程序交到用户手里。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →