
簡(jiǎn)介這份PDF文檔面向在Ubuntu環(huán)境下開(kāi)發(fā)Qt程序、需要將程序打包部署到無(wú)Qt環(huán)境機(jī)器的開(kāi)發(fā)者重點(diǎn)解決使用linuxdeployqt工具打包時(shí)遇到的各類問(wèn)題。內(nèi)容涵蓋Qt環(huán)境變量配置、linuxdeployqt源碼編譯、程序打包流程以及patchelf缺失、libjasper.so.1找不到等典型報(bào)錯(cuò)的排查與修復(fù)思路適合具備一定Linux與Qt基礎(chǔ)的讀者參考。資源包內(nèi)僅含1個(gè)PDF文件大小約58KB篇幅精煉便于快速查閱關(guān)鍵配置與命令。目前已有2723人學(xué)習(xí)下載說(shuō)明該問(wèn)題在Qt跨平臺(tái)部署中較為普遍。讀者可從中獲得環(huán)境變量設(shè)置模板、源碼編譯注意事項(xiàng)、依賴庫(kù)缺失的定位方法以及借助ldd輸出排查庫(kù)依賴的通用思路幫助減少打包過(guò)程中的試錯(cuò)成本。1. 為什么在 Ubuntu 上打包 Qt 程序linuxdeployqt 總在最后一步翻車(chē)如果你在 Ubuntu 上寫(xiě) Qt遲早會(huì)撞上同一個(gè)場(chǎng)景程序在自己機(jī)器上跑得好好的拷到同事或客戶的機(jī)器上就報(bào)error while loading shared libraries: libQt5Core.so.5: cannot open shared object file。這不是代碼問(wèn)題是動(dòng)態(tài)庫(kù)依賴沒(méi)跟著走。linuxdeployqt 就是干這件事的——把 Qt 程序依賴的庫(kù)、插件、QML 模塊一股腦塞進(jìn) AppDir最后產(chǎn)出一個(gè)能雙擊運(yùn)行的 AppImage。它解決的是「我編譯出來(lái)的二進(jìn)制怎么在沒(méi)裝 Qt 的 Ubuntu 上跑起來(lái)」這個(gè)最實(shí)際的訴求。適合誰(shuí)適合用 qmake 或 CMake 構(gòu)建、準(zhǔn)備把桌面工具交付出去、又不想讓用戶手動(dòng)裝一堆依賴的 Qt 開(kāi)發(fā)者。但它的坑也集中Qt 版本混裝、qmake 路徑不對(duì)、插件掃描不全隨便一個(gè)都能讓你卡半天。2. 先把 linuxdeployqt 的依賴解析邏輯摸清楚2.1 它到底做了什么從 ldd 到 AppDir 的搬運(yùn)過(guò)程linuxdeployqt 的核心不是編譯是「掃描 拷貝 打補(bǔ)丁」。你給它一個(gè)可執(zhí)行文件它先用ldd遞歸解析出所有動(dòng)態(tài)庫(kù)依賴然后按規(guī)則篩選出哪些屬于 Qt、哪些屬于系統(tǒng)基礎(chǔ)庫(kù)glibc、libstdc 這類默認(rèn)不打包再把 Qt 相關(guān)的庫(kù)復(fù)制進(jìn) AppDir 的usr/lib目錄。接著它會(huì)掃描 Qt 插件目錄把 platforms、imageformats、sqldrivers 這些按需拷進(jìn)去最后改寫(xiě)可執(zhí)行文件的 RPATH讓程序運(yùn)行時(shí)優(yōu)先從 AppDir 內(nèi)部找?guī)?。理解這條鏈路很關(guān)鍵因?yàn)楹竺嫠袌?bào)錯(cuò)基本都能對(duì)應(yīng)到某一環(huán)ldd解析失敗 → 庫(kù)找不到插件沒(méi)拷 → 運(yùn)行時(shí)報(bào)could not find the Qt platform pluginRPATH 沒(méi)改對(duì) → 還是去系統(tǒng)路徑找?guī)?。我一般?huì)先手動(dòng)跑一遍ldd ./myapp | grep Qt看看依賴長(zhǎng)什么樣心里有底再交給 linuxdeployqt。2.2 環(huán)境準(zhǔn)備qmake、Qt 版本和 linuxdeployqt 三者的對(duì)齊最常見(jiàn)的翻車(chē)根源是 Qt 版本混裝。你系統(tǒng)里可能同時(shí)有 apt 裝的 Qt5、官網(wǎng) installer 裝的 Qt5.15、還有 Qt6qmake -v指向的和你編譯時(shí)用的不是同一個(gè)。linuxdeployqt 會(huì)調(diào)用qmake來(lái)定位 Qt 安裝路徑所以 qmake 必須指向你實(shí)際編譯用的那套 Qt。# 確認(rèn)當(dāng)前 qmake 指向哪套 Qt which qmake qmake -v # 輸出示例QMake version 3.1 / Using Qt version 5.15.2 in /opt/Qt/5.15.2/gcc_64/lib # 如果不是你要的那套臨時(shí)用絕對(duì)路徑或改 PATH export PATH/opt/Qt/5.15.2/gcc_64/bin:$PATH qmake -v參數(shù)說(shuō)明which qmake看的是 PATH 里第一個(gè) qmakeqmake -v里的Using Qt version ... in ...才是真正決定 linuxdeployqt 行為的路徑。如果你用 CMake 構(gòu)建編譯時(shí)用的CMAKE_PREFIX_PATH也要和這個(gè) qmake 指向同一套 Qt否則會(huì)出現(xiàn)「編譯用 5.15、打包用 5.12」的錯(cuò)位運(yùn)行時(shí)報(bào)cannot mix incompatible Qt library。下載 linuxdeployqt 本身常見(jiàn)做法是去它的 GitHub Releases 拿編譯好的 AppImage 版本賦予執(zhí)行權(quán)限后直接用# 假設(shè)下載到當(dāng)前目錄 chmod x linuxdeployqt-continuous-x86_64.AppImage # 驗(yàn)證能跑 ./linuxdeployqt-continuous-x86_64.AppImage --version注意linuxdeployqt 官方推薦用 AppImage 形式分發(fā)不要試圖從源碼編譯除非你有特殊需求否則依賴鏈會(huì)讓你懷疑人生。2.3 最小可復(fù)現(xiàn)打包流程從編譯到 AppImage先準(zhǔn)備一個(gè)干凈的構(gòu)建目錄用 release 模式編譯避免把 debug 符號(hào)和 debug 版 Qt 庫(kù)帶進(jìn)去。# 1. 編譯以 qmake 為例 mkdir build cd build qmake ../myapp.pro CONFIGrelease make -j$(nproc) # 2. 確認(rèn)產(chǎn)物 ls -lh myapp ldd myapp | grep -i qt | head編譯完成后把可執(zhí)行文件單獨(dú)拷到一個(gè)干凈的 AppDir 結(jié)構(gòu)里這是 linuxdeployqt 要求的輸入形態(tài)# 3. 建立 AppDir 骨架 mkdir -p /tmp/MyApp.AppDir/usr/bin cp myapp /tmp/MyApp.AppDir/usr/bin/ # 4. 準(zhǔn)備一個(gè) desktop 文件AppImage 需要 cat /tmp/MyApp.AppDir/myapp.desktop EOF [Desktop Entry] TypeApplication NameMyApp Execmyapp Iconmyapp CategoriesUtility; EOF # 5. 準(zhǔn)備圖標(biāo)沒(méi)有會(huì)警告但不致命 cp ../myapp.png /tmp/MyApp.AppDir/myapp.png然后調(diào)用 linuxdeployqt關(guān)鍵是-appimage參數(shù)讓它直接產(chǎn)出 AppImage# 6. 執(zhí)行打包 ./linuxdeployqt-continuous-x86_64.AppImage \ /tmp/MyApp.AppDir/usr/bin/myapp \ -appimage \ -verbose2 \ -qmake/opt/Qt/5.15.2/gcc_64/bin/qmake參數(shù)說(shuō)明第一個(gè)位置參數(shù)是 AppDir 內(nèi)的可執(zhí)行文件路徑不是 AppDir 本身-appimage觸發(fā) AppImage 生成-verbose2輸出詳細(xì)日志排錯(cuò)必開(kāi)-qmake顯式指定 qmake避免它自己找錯(cuò)。執(zhí)行完你會(huì)看到MyApp-x86_64.AppImage出現(xiàn)在當(dāng)前目錄chmod x后直接運(yùn)行測(cè)試。2.4 驗(yàn)證打包結(jié)果別只看能不能跑打包成功不等于交付成功。我習(xí)慣做三層驗(yàn)證第一層在打包機(jī)上直接跑 AppImage確認(rèn)基本功能第二層用ldd檢查 AppImage 解壓后的庫(kù)是否都指向內(nèi)部路徑第三層找一個(gè)沒(méi)裝 Qt 的干凈 Ubuntu 環(huán)境虛擬機(jī)或容器實(shí)測(cè)。# 解壓 AppImage 看內(nèi)部結(jié)構(gòu) ./MyApp-x86_64.AppImage --appimage-extract ls squashfs-root/usr/lib/ | grep -i qt # 檢查可執(zhí)行文件的 RPATH readelf -d squashfs-root/usr/bin/myapp | grep -i rpath如果 RPATH 里出現(xiàn)$ORIGIN/../lib這類相對(duì)路徑說(shuō)明改寫(xiě)成功如果還是指向/opt/Qt/...絕對(duì)路徑那換臺(tái)機(jī)器必掛。這一步是很多人忽略的后悔藥等交付后才發(fā)現(xiàn)就晚了。3. 插件、QML 和第三方庫(kù)那些默認(rèn)掃不到的依賴3.1 Qt 插件掃描機(jī)制與 platforms 插件缺失linuxdeployqt 默認(rèn)會(huì)掃描 Qt 安裝目錄下的plugins文件夾但它的掃描是有條件的——只拷貝它認(rèn)為「被用到」的插件。問(wèn)題在于它判斷「被用到」靠的是可執(zhí)行文件的導(dǎo)入符號(hào)而 platforms 插件比如libqxcb.so是通過(guò)運(yùn)行時(shí)字符串加載的靜態(tài)分析掃不到。結(jié)果就是程序能打包但一運(yùn)行就報(bào)qt.qpa.plugin: Could not find the Qt platform plugin xcb。解決辦法是顯式告訴它要帶哪些插件。常見(jiàn)做法是在 desktop 文件同級(jí)放一個(gè)qt.conf或者直接用-extra-plugins參數(shù)./linuxdeployqt-continuous-x86_64.AppImage \ /tmp/MyApp.AppDir/usr/bin/myapp \ -appimage \ -extra-pluginsplatforms/libqxcb.so,imageformats/libqjpeg.so,iconengines/libqsvgicon.so \ -qmake/opt/Qt/5.15.2/gcc_64/bin/qmake參數(shù)說(shuō)明-extra-plugins接受逗號(hào)分隔的相對(duì)路徑相對(duì)于 Qt 的plugins目錄。platforms 里的libqxcb.so是 X11 環(huán)境必須的如果你還要支持 Wayland得把libqwayland-*.so也帶上。imageformats 按你實(shí)際用到的圖片格式加別一股腦全塞AppImage 體積會(huì)失控。3.2 QML 模塊的依賴收集qmlimportscanner 的角色純 Widgets 程序相對(duì)簡(jiǎn)單一旦用了 QML依賴就變成「QML 模塊 對(duì)應(yīng)的 C 插件 qmldir 文件」三層結(jié)構(gòu)。linuxdeployqt 內(nèi)部會(huì)調(diào)用qmlimportscanner去解析 QML 文件的 import 語(yǔ)句但前提是它能找到你的 QML 源碼目錄。如果你只把編譯后的可執(zhí)行文件丟進(jìn) AppDirQML 源碼不在旁邊掃描就會(huì)漏。我一般會(huì)在打包前把 QML 源文件目錄也拷進(jìn) AppDir 的對(duì)應(yīng)位置或者用-qmldir顯式指定./linuxdeployqt-continuous-x86_64.AppImage \ /tmp/MyApp.AppDir/usr/bin/myapp \ -appimage \ -qmldir/path/to/your/qml/sources \ -qmake/opt/Qt/5.15.2/gcc_64/bin/qmake參數(shù)說(shuō)明-qmldir指向包含main.qml等入口文件的目錄qmlimportscanner 會(huì)從這里遞歸解析所有 import。如果用了自定義 QML 模塊帶qmldir文件的確保這些模塊目錄也在掃描路徑下否則運(yùn)行時(shí)會(huì)報(bào)module MyModule is not installed。3.3 第三方動(dòng)態(tài)庫(kù)與系統(tǒng)庫(kù)的取舍邊界不是所有.so都該打包。glibc、libstdc、libGL 這些系統(tǒng)基礎(chǔ)庫(kù)打包進(jìn)去反而容易在新舊系統(tǒng)間產(chǎn)生沖突——你在 Ubuntu 22.04 上打包的 glibc拿到 20.04 上跑可能直接段錯(cuò)誤。linuxdeployqt 默認(rèn)會(huì)排除一批系統(tǒng)庫(kù)但它的排除列表不一定覆蓋你項(xiàng)目里引入的所有第三方庫(kù)。判斷標(biāo)準(zhǔn)很簡(jiǎn)單這個(gè)庫(kù)是不是目標(biāo)系統(tǒng)默認(rèn)就有如果是比如libpng、libz可以不打包依賴目標(biāo)系統(tǒng)如果是你項(xiàng)目特有的比如某個(gè)自編譯的算法庫(kù)必須打包??梢杂?exclude-libs手動(dòng)排除也可以用-no-translations等開(kāi)關(guān)精簡(jiǎn)體積。# 查看 AppDir 里最終帶了哪些庫(kù)人工過(guò)一遍 find /tmp/MyApp.AppDir/usr/lib -name *.so* | sort這一步別偷懶我見(jiàn)過(guò)有人把libc.so.6都打進(jìn)去了結(jié)果 AppImage 在部分機(jī)器上直接起不來(lái)。血淚經(jīng)驗(yàn)系統(tǒng)庫(kù)能不碰就不碰。4. 避坑與排查linuxdeployqt 最常見(jiàn)的五類翻車(chē)4.1 報(bào)錯(cuò) cannot mix incompatible Qt library現(xiàn)象運(yùn)行打包后的程序終端輸出fatal: cannot mix incompatible Qt library (version 0x50601) with this library (version 0x50a00)。原因AppDir 里混進(jìn)了兩個(gè)不同版本的 Qt 庫(kù)。通常是編譯時(shí)用了一套 Qtlinuxdeployqt 打包時(shí)又通過(guò) qmake 找到了另一套把兩套庫(kù)都拷了進(jìn)去。解決先qmake -v確認(rèn)版本再用-qmake顯式指定和編譯時(shí)一致的 qmake。打包后find AppDir -name libQt5Core.so*檢查是否只有一個(gè)版本。如果有多個(gè)刪掉 AppDir 重新打包別試圖手動(dòng)刪庫(kù)容易漏。4.2 運(yùn)行時(shí)報(bào) could not find the Qt platform plugin xcb現(xiàn)象AppImage 雙擊沒(méi)反應(yīng)命令行運(yùn)行報(bào)qt.qpa.plugin: Could not find the Qt platform plugin xcb in 。原因platforms 插件沒(méi)被打包進(jìn)去或者打包了但路徑不對(duì)程序找不到。解決用-extra-pluginsplatforms/libqxcb.so顯式帶上。打包后檢查AppDir/usr/plugins/platforms/libqxcb.so是否存在。如果存在還報(bào)錯(cuò)檢查qt.conf里的 Plugins 路徑配置確保指向usr/plugins。4.3 AppImage 體積異常膨脹到幾百 MB現(xiàn)象一個(gè)簡(jiǎn)單工具打包出來(lái) 300MB。原因debug 版 Qt 庫(kù)、多余的插件、翻譯文件、QML 模塊全被塞進(jìn)去了。解決編譯時(shí)確保CONFIGrelease打包時(shí)加-no-translations去掉翻譯用-extra-plugins精確控制插件而不是全量掃描。打包后用du -sh AppDir/usr/lib/*看哪個(gè)庫(kù)占大頭針對(duì)性排除。4.4 在打包機(jī)能跑換臺(tái)機(jī)器就報(bào)庫(kù)找不到現(xiàn)象本機(jī)測(cè)試正常拷到另一臺(tái) Ubuntu 上運(yùn)行報(bào)error while loading shared libraries。原因RPATH 沒(méi)改對(duì)程序還在找系統(tǒng)路徑或絕對(duì)路徑的庫(kù)。解決readelf -d AppDir/usr/bin/myapp | grep RPATH檢查正常應(yīng)該是$ORIGIN/../lib。如果不是說(shuō)明 linuxdeployqt 的 RPATH 改寫(xiě)沒(méi)生效常見(jiàn)于可執(zhí)行文件本身帶了-Wl,-rpath硬編碼。重新編譯時(shí)去掉硬編碼 rpath或者用patchelf手動(dòng)改。4.5 qmake 找不到或指向錯(cuò)誤路徑現(xiàn)象linuxdeployqt 報(bào)qmake: could not find a Qt installation或打包出來(lái)的庫(kù)版本不對(duì)。原因PATH 里沒(méi)有 qmake或者有多個(gè) qmake 但指向了系統(tǒng) apt 裝的那套。解決export PATH/opt/Qt/5.15.2/gcc_64/bin:$PATH把正確的 Qt bin 放最前或者每次都用-qmake絕對(duì)路徑。我習(xí)慣在打包腳本里寫(xiě)死-qmake不依賴環(huán)境變量省得換終端就翻車(chē)。5. 進(jìn)階用腳本固化打包流程讓每次交付都可復(fù)現(xiàn)手動(dòng)敲命令打包第一次能成第三次就忘了參數(shù)。我的做法是寫(xiě)一個(gè)build_appimage.sh把編譯、清理、打包、驗(yàn)證串起來(lái)每次交付走同一個(gè)腳本出問(wèn)題也好回溯。#!/bin/bash set -e # 任何一步失敗就停別帶著錯(cuò)誤往下走 QT_DIR/opt/Qt/5.15.2/gcc_64 APP_NAMEMyApp BUILD_DIR$(pwd)/build APPDIR/tmp/${APP_NAME}.AppDir LDQ$(pwd)/linuxdeployqt-continuous-x86_64.AppImage # 1. 清理舊產(chǎn)物 rm -rf $BUILD_DIR $APPDIR mkdir -p $BUILD_DIR $APPDIR/usr/bin # 2. 編譯 cd $BUILD_DIR $QT_DIR/bin/qmake ../${APP_NAME}.pro CONFIGrelease make -j$(nproc) # 3. 組裝 AppDir cp $APP_NAME $APPDIR/usr/bin/ cp ../${APP_NAME}.desktop $APPDIR/ cp ../${APP_NAME}.png $APPDIR/ # 4. 打包 $LDQ $APPDIR/usr/bin/$APP_NAME \ -appimage \ -verbose2 \ -qmake$QT_DIR/bin/qmake \ -extra-pluginsplatforms/libqxcb.so,imageformats/libqjpeg.so \ -no-translations # 5. 驗(yàn)證 RPATH echo RPATH check readelf -d $APPDIR/usr/bin/$APP_NAME | grep -i rpath || echo WARN: no RPATH found # 6. 驗(yàn)證 Qt 庫(kù)版本唯一性 echo Qt lib versions find $APPDIR -name libQt5Core.so* -exec ls -l {} \; echo Done: $(ls ${APP_NAME}-x86_64.AppImage)這個(gè)腳本里幾個(gè)關(guān)鍵點(diǎn)值得說(shuō)set -e保證編譯失敗不會(huì)繼續(xù)打包出半成品-no-translations砍掉用不上的翻譯文件打包后立刻做 RPATH 和 Qt 庫(kù)版本檢查把問(wèn)題攔在交付前。參數(shù)方面QT_DIR和-qmake必須指向同一套 Qt這是整個(gè)流程的錨點(diǎn)改一處就要改另一處。驗(yàn)證方法上除了腳本里的檢查我還會(huì)在 Docker 里跑一個(gè)干凈的 Ubuntu 鏡像做最終測(cè)試docker run --rm -v $(pwd):/app ubuntu:22.04 /app/MyApp-x86_64.AppImage --version如果容器里能正常輸出版本號(hào)說(shuō)明依賴打包是完整的。這一步能抓到 90% 的「本機(jī)能跑、別人不能跑」問(wèn)題。從那以后我每次打包 Qt 程序都強(qiáng)制先跑一遍qmake -v和ldd | grep Qt確認(rèn)版本對(duì)齊再動(dòng)手省下的排錯(cuò)時(shí)間比打包本身多得多。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取