Ubuntu下用linuxdeployqt打包Qt程序:依赖处理、避坑与实战 简介一份面向Ubuntu平台Qt开发者的PDF教程专注解决使用linuxdeployqt打包Qt程序后目标机器缺少运行依赖、无法启动的常见问题。适合需要将自研Qt应用分发到其他Linux主机的研发人员、运维人员及学习Qt部署的学生。教程从环境准备入手说明如何在~/.bashrc中配置PATH、LD_LIBRARY_PATH、QT_PLUGIN_PATH、QML2_IMPORT_PATH等变量使系统正确识别Qt路径。随后介绍下载linuxdeployqt源码、注释版本检测代码并用cmake/make编译安装的方法。打包部分给出了标准命令流程并对两个典型报错——patchelf工具缺失和libjasper库缺失——分别给出定位方法与apt安装命令同时建议利用ldd检查动态库依赖。资源为单个PDF文件大小58KB内容紧凑但步骤完整已有2723人学习下载。通过这份教程读者能快速复现成功的打包过程避开常见环境坑点有效提升Qt程序在不同Ubuntu版本下的可移植性与部署成功率。1. Ubuntu 下用 linuxdeployqt 打包 Qt 程序先分清它解决什么、不解决什么在 Ubuntu 上把 Qt 程序交给别人用最扎心的不是编译报错而是拷到干净机器上一运行就崩提示error while loading shared libraries: libQt5Core.so.5。Windows 下有 windeployqt 收拾这个场面Linux 上对应的就是 linuxdeployqt——把可执行文件依赖的 Qt 库、平台插件和 xcb 相关动态库全部收进一个 AppDir再封装成单个 AppImage目标机器完全不用预装 Qt 开发库。这个方案适合三类人给公司多台 Ubuntu 分发自研工具的内部运维、做商业客户端交付的开发者、以及写小工具想分享给朋友的独立开发者。我按原理、打包流程、踩坑记录、兜底补救的顺序讲列出的命令在 Ubuntu 20.04 和 22.04 上反复验证过照着走就能出包出问题也能照着索引自己定位。2. linuxdeployqt 的依赖处理逻辑与选型理由先理解它再下手2.1 linuxdeployqt 做的四件事依赖扫描、库复制、插件收集、rpath 改写linuxdeployqt 不是把可执行文件往包里一塞就完事的复制机。它先解析可执行文件的 ELF 动态段拿到DT_NEEDED里的库清单再把清单里属于 Qt 的库libQt5Core、libQt5Gui、libQt5Widgets 这些从 Qt 安装目录复制到 AppDir 下的usr/lib随后它会去 Qt 安装目录的plugins里把平台插件和格式插件复制进来platforms、imageformats、iconengines、xcbglintegrations这些目录都在收集范围内。最关键的一步在最后它用类似 patchelf 的手段改写这些库和可执行文件的 RPATH让程序启动时优先从这个相对目录加载而不是依赖系统ldconfig的缓存列表。这个处理顺序决定了排查方式的顺序程序起不来先看platforms目录下有没有 libqxcb.so再检查 RPATH 有没有指对最后看库文件权限和文件是否可读而不是一上来就怀疑工具坏了。有一点要记死linuxdeployqt 不会打包 glibc 和 libc 这种 C 基础库它把操作系统自带的基础库视为已存在。这一点是后面很多在我机器能跑到别人机器就崩事故的源头。细节上linuxdeployqt 会把libstdc.so.6和libgcc_s.so.1一起收进来。因为很多 Ubuntu 精简服务器版、容器镜像里没有完整的 gcc 运行库带上它们能救回一大批目标环境。还有它检查的是库文件是否存在不检查符号版本兼容性——也就是说就算文件复制齐了目标机器 glibc 版本不够照样启动失败。这一点决定了打包成功只代表文件齐全不代表运行兼容。2.2 Ubuntu 上的 Qt 程序为什么容易翻车系统 Qt、glibc、xcb 三方夹击Ubuntu 上 Qt 分发失败不是运气差是这个发行版的软件形态决定的。第一道坎是系统自带 Qt 版本和开发版本错位。Ubuntu 20.04 自带 Qt 5.12不少人开发用的是 Qt 5.15 甚至 Qt 6。同时存在意味着 PATH、qmake、LD_LIBRARY_PATH 一串环节上可能选中错误版本。第二道坎是 Qt 运行需要的 X11 扩展库在纯净系统上经常缺失缺libxcb-xinerama0是最常见的有时还有libxkbcommon-x11-0。桌面完整装机版一般没问题服务器版、精简 Docker 镜像上几乎必然缺。第三道坎是 glibc 版本。在 Ubuntu 22.04 上打包、目标机器是 20.04 时glibc 的差异会直接让程序起不来报错是version GLIBC_2.34 not found这种不好读的信息。三道坎叠加会产生一个很典型的现象双击 AppImage 没反应命令行运行直接退出退出码不是段错误就是符号解析失败。排查时第一件事是加环境变量QT_DEBUG_PLUGINS1再跑一次让 Qt 自己把插件加载过程打印出来看究竟卡在哪个动态库上。这一步输出可以直接定位是 xcb 插件问题还是系统库缺失比瞎猜有效率得多。2.3 打包工具选型linuxdeployqt 和 linuxdeploy、appimagetool 的定位差异经常有人搞不清楚 linuxdeployqt、linuxdeploy、appimagetool 到底什么关系。简单说appimagetool 只做最后封装把一个结构正确的 AppDir 压成单个 AppImage 文件linuxdeployqt 自己内置了调用 appimagetool 的能力一条命令完成从可执行文件到 AppImage 的转换linuxdeploy 是另一个更通用的依赖收集器依赖插件机制支持 Qt 之外的动态库。选型参考这张表工具依赖收集范围适合场景对 Qt 的支持方式linuxdeployqtQt 库与 Qt 插件纯 Qt、少数第三方库内置完整linuxdeploy通用 ELF 依赖带 OpenCV、FFmpeg、Halcon 等重型库需要单独装 qt 插件appimagetool无收集只封装已有现成 AppDir 时不直接参与依赖分析只做纯 Qt 程序时首选 linuxdeployqt命令行短、默认行为贴合 Qt。项目里带重型第三方库时linuxdeploy 加 Qt 插件的组合更合适。但也可以先用 linuxdeployqt 跑通再手动把第三方库按依赖链补进 AppDir两条路都能走通。官方工具本身不给包治百病的承诺把自身依赖边界画清楚才是关键。3. 在 Ubuntu 上跑通 linuxdeployqt 的最小命令从安装到出包3.1 安装 linuxdeployqt 与准备 Qt 环境的三个前置动作linuxdeployqt 不是一个 apt 包常规做法是从它的 release 页面下载编译好的可执行文件放到 PATH 里就完事。如果你的 Qt 是用官方在线安装器装的Qt 安装目录的Tools/linuxdeployqt下通常也会带一个那个版本跟随 Qt 库同步发布兼容性相对可靠。装完以后先做三个动作缺一个后面都会多折腾一小时which linuxdeployqt qmake -v ls /opt/Qt/5.15.2/gcc_64/plugins/platforms/第一行确认工具在 PATH 里。第二行确认 qmake 版本——如果输出是qmake version 3.1 ... Qt 5.12.8说明 PATH 被系统 Qt 抢先了需要调整 PATH 顺序。第三行确认 platforms 插件存在Qt 安装完整libqxcb.so和libqminimal.so是最低配置连它们都没有多半是 Qt 安装不完整。这三个动作做完再往下走才能保证打包脚本里引用的路径不是空目录。Ubuntu 24.04 我也跑过系统对 Qt 6 的集成比 20.04 更主动路径顺序检查要更严格但流程本质没有变。3.2 写一个可复用的打包脚本qmake 构建、依赖收集与 AppImage 生成下面这个脚本是我在 Ubuntu 20.04/22.04 上实际用的最小版本直接复制改工程名就行#!/bin/bash set -e QT_ROOT/opt/Qt/5.15.2/gcc_64 export PATH$QT_ROOT/bin:$PATH export LD_LIBRARY_PATH$QT_ROOT/lib:$LD_LIBRARY_PATH rm -rf build appdir mkdir build cd build qmake ../MyApp.pro -spec linux-g CONFIGrelease make -j$(nproc) cd .. linuxdeployqt ./build/MyApp -appimage \ -qmake$QT_ROOT/bin/qmake \ -bundle-non-qt-libs \ -verbose2脚本逻辑说明几处关键点set -e让任意一步失败就停避免带着坏产物继续QT_ROOT统一定义 Qt 安装路径后续 qmake、linuxdeployqt 都用全路径引用从源头杜绝 PATH 漂移-bundle-non-qt-libs处理第三方非 Qt 动态库-verbose2让打包过程打印出复制的每个文件和路径排查时直接看日志。运行成功后在脚本目录下会出现MyApp.AppImage和一个同名.AppDir目录.AppDir是中间产物留着排查依赖。如果工程里配置了install规则我会在make之后加一行make install INSTALL_ROOT../appdir让可执行文件和资源落在干净的目录结构里再让 linuxdeployqt 指向它。没配 install 规则时linuxdeployqt 会以可执行文件所在目录为基准生成 AppDir共用工程目录文件比较杂但不影响出包。注意-appimage参数内部会调用 appimagetool。如果你的 linuxdeployqt 构建版本没有捆绑它最后一步会报找不到工具此时把 appimagetool 也放进 PATH 再执行即可。还有一点最终 AppDir 和系统的/usr目录同构usr/lib放库、usr/bin放可执行文件、usr/share放资源后续手动补文件时要按这个习惯思考别把东西乱堆。3.3 移植项目最常见的编译报错msvc 路径串进 Ubuntu 工程在 Ubuntu 下用 Qt Creator 打开从 Windows 同步过来的工程常常看到这类报错:-1: error: dependent ..\..\..\..\..\..\qt\5.15.2\msvc2019_64\include\qtwidgets\qtwidgetsglobal.h does not exist.这个报错不是 linuxdeployqt 报的是 qmake 解析.pro工程文件时报的。看到路径里的msvc2019_64就应该明白.pro文件或它通过include()引入的.pri文件残留了 Windows 下配置的INCLUDEPATH .../msvc2019_64/include这类变量。Qt Creator 在 Windows 上写好的工程直接同步到 Ubuntu路径就串味了。解决办法是把.pro文件里INCLUDEPATH、LIBS中带msvc2019_64的片段全部删掉只保留QT core gui widgets这种模块声明让 qmake 自己推导头文件路径。如果项目是按 Qt Creator 套件Kit方式维护的到“项目 → 构建套件”里确认当前套件选的是 Ubuntu 下面的 gcc_64 Kit而不是迁移时残留下来的 MSVC 套件。一条经验我基本不手动在.pro里写绝对路径INCLUDEPATH /opt/Qt/5.15.2/gcc_64/include。项目要在不同机器上重建时绝对路径换个环境就失效要用$$[QT_INSTALL_HEADERS]让 qmake 根据当前版本自己拼接这样切版本、换机器都不会踩同样的坑。4. 避坑Ubuntu 下打包 Qt 程序的 5 个高频问题与排查方法4.1 双击 AppImage 没反应命令行运行直接退出现象打包成功AppImage 也生成了但双击没有窗口运行时进程秒退终端不打印任何错误。原因xcb 平台插件没加载上。在命令行加QT_DEBUG_PLUGINS1 ./MyApp.AppImage会看到Cannot load library .../platforms/libqxcb.so或xcb missing之类输出。多数情况下是 libqxcb.so 依赖的 X11 扩展库在目标环境缺失Ubuntu 服务器版或精简容器里尤其常见缺的是libxcb-xinerama0和libxkbcommon-x11-0。解决先在打包机确认依赖完整ldd /opt/Qt/5.15.2/gcc_64/plugins/platforms/libqxcb.so | grep not found有 not found 就先安装对应系统包再重新执行 linuxdeployqt。目标机器上则用apt install libxcb-xinerama0 libxkbcommon-x11-0补齐系统基础依赖。4.2 qmake 版本被系统 Qt 抢先打出来的包缺库缺插件现象打包过程没有报错但 AppDir 里的usr/lib少了好几个 Qt 库或者 plugins 目录不完整运行后找不到 Qt 插件。原因linuxdeployqt 靠-qmake参数或 PATH 里的 qmake 确定 Qt 安装路径。Ubuntu 20.04 自带 Qt 5.12加上开发用的 Qt 5.15两个 qmake 同时在 PATH 里系统 Qt 排前面就按 5.12 的模块清单去收集依赖程序却需要 5.15 的符号启动即崩。解决打包脚本里全路径指定 qmake写成-qmake/opt/Qt/5.15.2/gcc_64/bin/qmake并在脚本第一行执行qmake -v输出版本做确认。跑完打包后打开appdir/usr/lib扫一眼库文件看到libQt5Core.so.5.15.2而不是 5.12.x才算通过。4.3 拷贝到目标机器报 GLIBC 版本找不到现象在 Ubuntu 22.04 上打包拷贝到 20.04 或 18.04 目标机运行报version GLIBC_2.34 not found。原因glibc 是系统基础库linuxdeployqt 不打包它。打包机的 glibc 比目标机器新产物里所有依赖这个新符号的库在旧系统上都无法解析这不是缺文件是版本代差。解决换用较低版本的系统做构建机。常见做法是在 Ubuntu 20.04 容器里装 Qt 和 linuxdeployqt统一在容器里产包确保目标机器不用比容器版本旧的系统。如果目标环境是 Ubuntu 18.04 甚至更早建议直接评估换静态链接方案。这个坑最隐蔽的地方在于它不报缺库只报一个奇怪的符号版本很容易让人先折腾半天插件而不是去查 glibc。4.4 OpenCV、FFmpeg 这类第三方库的依赖没有收全现象程序用了 OpenCVlinuxdeployqt 带了-bundle-non-qt-libs但 AppImage 在目标机器上还是报缺libopencv_core.so.4.5。原因linuxdeployqt 只收集主程序 ldd 能直接看到的依赖。如果某个第三方库是运行时通过 dlopen 或插件机制加载的ldd 完全看不到收集自然不全。另一个常见情况是第三方库自己依赖的次级库不在 ld 默认路径里linuxdeployqt 没有递归处理到那个层级。解决换用 linuxdeploy 的递归收集插件或者手动补。手动补的做法是ldd appdir/usr/bin/MyApp找到缺失项再把对应库和它自己的依赖链复制到appdir/usr/lib用 patchelf 把 RPATH 指到$ORIGIN/../lib。补完以后重新执行一次打包让工具把新文件一并收进 AppImage。4.5 打包后的程序切不了中文输入法现象程序界面正常界面文字显示也没问题但 fcitx 或 ibus 输入法切不进去中文打不出来。原因Qt 程序的中文输入依赖platforminputcontexts插件桥接外部输入法框架。默认打包时这个插件目录里的文件可能不全而且插件链接的 fcitx/ibus 客户端库版本要和运行环境匹配。解决确认appdir/usr/plugins/platforminputcontexts/下有libfcitxplatforminputcontextplugin.so和libibusplatforminputcontextplugin.so缺失就从 Qt 安装目录对应位置复制。同时注意检查 fcitx 的客户端库libfcitx-qt5.so有没有进入appdir/usr/lib。补完后在真机上验证一次真实的输入切换因为这个坑在打包机上因为有完整桌面环境往往复现不出来。5. 让打包结果真正可分发补依赖、补插件、补桌面文件的三个实战调整5.1 手动补 xcb 平台插件依赖ldd 递归检查与库补齐linuxdeployqt 对平台的识别偶尔会漏。刚才提到 libqxcb.so 依赖的一串 X11 扩展库-bundle-non-qt-libs不一定能全部带进 AppDir因为它们属于系统库linuxdeployqt 对系统库和 Qt 库的处理策略不同。一个可靠的检查流程是find appdir -name *.so* | xargs -I{} ldd {} | grep not found | sort -u这条命令把所有 AppDir 里已经收进去的动态库逐个跑一遍 ldd把 not found 的结果汇总。看到哪些系统库缺失回到 Ubuntu 上确认这些库由哪个包提供然后安装后把库文件复制进appdir/usr/lib。注意只复制依赖的库本身不要连着一个几十 MB 的系统组件目录整块复制那样 AppImage 会膨胀得很离谱而且可能引入权限和软链接问题。另一个常见错觉是以为-bundle-non-qt-libs能处理一切。它处理的是 Qt 库之外、但不是系统恰恰必需的那些库判定边界比较模糊。多花三分钟跑一遍上面的递归检查比等到目标现场崩了再救要省事得多。5.2 第三方库的依赖传递写个小函数把整条链补进 AppDir带 OpenCV、FFmpeg、Halcon 这类链比较深的库时我通常会写一个小脚本把依赖链收进 AppDir因为 linuxdeploy 的递归逻辑在纯手工补库时用不上。脚本思路是用 ldd 递归解析每个库的依赖然后把文件复制过去#!/bin/bash declare -A seen copy_deps() { local lib$1 local real$(readlink -f $lib) local name$(basename $real) if [ ${seen[$name]:-} 1 ]; then return; fi seen[$name]1 cp -L $real appdir/usr/lib/ for dep in $(ldd $real | grep / | awk {print $3}); do copy_deps $dep done } copy_deps /usr/lib/x86_64-linux-gnu/libopencv_core.so脚本逻辑说明readlink -f处理软链接链避免复制多个版本cp -L把符号链接指向的真实文件复制进 AppDir避免链接断裂seen关联数组做去重防止同一个库被递归复制几十次。跑完以后要再执行一次 find/ldd 检查确认没有漏网之鱼。补完第三方库后还有一个细节这些库在 AppDir 里的位置要和 RPATH 匹配。linuxdeployqt 默认是相对路径方式库放usr/lib下程序通过$ORIGIN/../lib找到它们。如果你的脚本把库放到了别的目录记得同步修改 RPATH否则 ldd 检查能过运行照样找不到。5.3 生成可点击的桌面图标desktop 文件、图标命名与 AppImage 元信息AppImage 要能被桌面环境识别出图标和名称靠的是 AppDir 根目录下的.desktop文件。很多纯手工打包的人只把可执行文件塞进 AppDir生成的 AppImage 双击能跑但桌面上显示的图标是空白的文件管理里连类型都识别不出来。linuxdeployqt 在-appimage模式下会尝试自动生成 desktop 文件但它会使用可执行文件名称推导Name 字段可能不是你想要的显示名。常见做法是准备一个手写的.desktop文件[Desktop Entry] TypeApplication NameMyApp CommentMy App on Ubuntu ExecMyApp Iconmyapp CategoriesUtility;把这个文件命名为myapp.desktop放到 AppDir 根目录对应的图标myapp.png放到 AppDir 根目录或usr/share/icons/hicolor/256x256/apps/下。图标文件的命名必须和 desktop 里Icon字段完全一致而且建议准备 256x256 及以上尺寸的 PNG不应使用未缩放的 SVG 做桌面捷径图标部分桌面环境不渲染 SVG。之后再执行一次linuxdeployqt让工具把 desktop 文件和图标在 AppImage 内部注册最终的 AppImage 在 Ubuntu 自带桌面环境下就能正确显示名称和图标。6. 验证打包结果解包、环境变量、构建环境一致性与我的维护习惯拿到一个刚打出来的 AppImage直接双击运行不是完整的验证。我在打开发给任何用户之前至少跑三遍检查。第一遍在打包机上运行QT_DEBUG_PLUGINS1 ./MyApp.AppImage让 Qt 把所有插件加载过程打印出来确认平台插件、输入法插件、图片格式插件都正常加载第二遍用./MyApp.AppImage --appimage-extract解包对解出来的squashfs-root/usr/bin/MyApp执行ldd | grep not found排查有没有链接到缺失库。第三遍是最关键的一步找一台没装 Qt、甚至刚从 Ubuntu Server ISO 装完的干净机器或起一个低版本 Ubuntu 容器把 AppImage 放进去跑一遍完整功能流程。没有干净环境验证过的包我不认为它已经可交付。QT_DEBUG_PLUGINS1的输出里如果出现 Cannot load library 的提示先不要急着补库看它后面跟的依赖失败原因。缺系统库时它会明确写出libxcb-xinerama.so.0: cannot open shared object file指向性比插件目录缺文件要清楚得多。这个习惯帮我避免了不少无效排查。还有一条和打包有关但经常在最后才暴露构建机环境的一致性。同一个工程在两个版本不同的 Ubuntu 上打包产物可能差出几个库文件尤其是 glibc 和 OpenSSL 这类。我在交付期会固定一台构建机或固定一个容器镜像产物都从同一个环境出绝不今天在 22.04 上出包、明天在 24.04 上出包。容器方案的成本很低一个 Ubuntu 20.04 镜像、装好 Qt 和 linuxdeployqt打包脚本挂进去执行结果和本机一致。没有容器条件的至少记录下打包机的系统版本和 Qt 版本随包一起写在发布说明里。这些规则看起来琐碎但大多是血泪换来的。早期我拿到打包成功就以为完事后来不止一次在客户机器上看到闪退现场再回头一项项排查才发现都是验证环节偷了懒。现在我把这三步检查做成脚本放在打包目录里固定跑省下来的时间远比写脚本多。希望帮到你。本文还有配套的精品资源点击获取