Qt應用打包發布,部署真正的Qt程序Linux&Windows

最近在發布Qt應用時遇到了一些困難,Windows還好,在Linux上面發布遇到了不少的麻煩(實際Linux應該簡單才對),經過在網絡搜索發現帖子不少,但都比較片面,現把Qt應用程序在Linux&Windows打包部署總結如下。

核心

應用部署的核心是加載庫,一個Qt應用程序至少包含以下庫:

Windows

Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll

Linux

libQt5Core.so.5、libQt5Gui.so、libQt5Widgets.so


與其他應用程序一樣,Qt應用程序也依賴于操作系統來加載這些庫文件,這意味著它們必須放置在操作系統可以找到它們的位置。在Windows上,這意味著在.exe文件同一目錄中或搜索路徑中指定的目錄(例如C:\ Windows \ System32)。將DLL復制到未安裝Qt的另一臺PC上時,我們通常不想弄亂其他計算機上的搜索路徑,而是將DLL與.exe文件放在一起,這可能是最好的解決方案。

在Linux上,將.so文件放在可執行文件同一目錄中不會像Windows中那樣自動加載它們。但大多數Linux系統都預先安裝好了Qt,例如在Ubuntu上,通常在/ usr / lib / i686-linux-gnu或/ usr / lib / x86_64-linux-gnu中安裝了Qt,因此我們的應用仍然可以運行(但是請注意版本問題,Ubuntu提供的Qt可能太舊了,我們的應用無法啟動)。

還有一點在部署Qt應用比較容易出錯的是Qt的插件機制,除了上面提到的幾個Qt核心庫之外,程序想要運行還必須提供一些插件,例如:platforms、sqldrivers、imageformats,這些是插件目錄,名字是Qt定的(當然可以修改,但是不建議修改)。其中最重要的是platforms插件目錄,里面提供一些平臺插件,所有Qt應用程序都需要這些庫,對于Windows默認的是qwindows.dll,對于Linux,則為libqxcb.so。這是Qt的QPA(Qt Platform Abstraction層)它負責許多特定于OS的事情,例如將調用轉換setWindowState(Qt::WindowMaximized)為Windows / Linux特定的系統調用。

插件設置

上面提到了一些Qt插件,這些插件Qt是如何加載的呢?默認Qt會在可執行文件所在的目錄查找并加載這些插件。但是為了我們的程序目錄更加簡潔,可以自定義插件目錄。

設置環境變量QT_PLUGIN_PATH

使用環境變量可能是比較容易的選擇,例如:

export QT_PLUGIN_PATH=plugins

Windows使用set

設置上面環境變量,QT將在plugins\platforms尋找qwindows.dll。當然,也可以通過這種方式設置絕對路徑。而且,如果想獲得大量的插件目錄,則可以附加以;分隔的其他路徑。(在Linux中是:)。

在代碼中指定插件路徑:

#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
    QApplication::addLibraryPath("plugins");//設置插件目錄
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

可以在代碼中指定插件路徑,但是由于Qt在構造QApplication時會加載插件,因此設置此項的機會就是在調用QApplication的構造函數之前進行。上述代碼和設置環境變量效果一樣。

創建一個qt.conf文件:

這是一種設置插件路徑比較的流行方法。

[Paths]
Plugins=plugins

此文件與應用可執行文件位于同一目錄中,Qt將讀取該文件并將plugins= path路徑添加到其插件目錄列表中。上面的例子實現效果和之前的一樣。

請注意,如果您使用的是Windows:該Plugins=設置中的反斜杠不起作用,應該使用Linux風格的正斜杠。

插件問題排查:

最后,如果仍然遇到插件加載問題,可以通過將環境變量QT_DEBUG_PLUGINS設置為非零值來打開插件加載過程的log

export QT_DEBUG_PLUGINS=1

然后從Terminal/CMD窗口啟動應用程序。對于Linux,在終端中會看到類似QFactoryLoader :: QFactoryLoader()checking...這樣的行,但是在Windows Qt中,輸出會將輸出路由到OutputDebugString()API,因此CMD窗口中將不會顯示任何內容。使用Visual Studio或Qt Creator可以看到輸出,如果在非開發PC上遇到插件問題,這沒有太大幫助。另一種選擇是下載一個實用程序,并在啟動應用程序之前將其打開。

Windows部署

首先使用Release模式構建應用得到可執行文件myapp.exe。使用windeployqt.exe工具可以自動拷貝Qt庫和插件到應用程序目錄,但是這個工具會拷貝多于程序需要的好多東西。通常都需要手動刪除一些程序不需要的庫。

Windows部署Qt應用程序通常至少包含以下文件:


windows

platforms目錄內包含qwindows.dll,這樣簡單的應用程序是可以運行的,并且可以打包分發。
這是一個最基礎的應用,通常我們的應用會更復雜,比如需要插件sqldrivers、imageformats、translations。比如:

更多插件

所有插件都放在一個目錄會顯得很亂,現在我們創建一個qt.conf,內容如下:

[Paths]
Plugins=plugins

然后我們新建目錄plugins,把所有插件移動到plugins。使我們的應用程序目錄變得的更加簡潔:


注意:plugins是Qt默認就會查找的目錄,所以沒有qt.conf也無所謂,但是如果想要指定一個特殊的插件目錄名稱,則必須使用qt.conf,如:third。

Linux部署

幾乎所有的Linux發行版都會預裝Qt,所以如果我們的應用是為特定發行版編譯的,我們幾乎是不需要進行任何配置,直接打包分發可執行文件即可(如果你是這種情況,就不需要看下面內容了)。

但是如果目標Linux沒有安裝Qt或者版本比較老,那我們的應用程序就可能不會運行了。這也是我遇到的問題,我的應用使用Qt5.12.5開發并編譯,但目前非常主流的Ubuntu 18.04 lts最高只可使用Qt5.9.5。

那就沒辦法了么?是不是可以升級Linux系統的Qt安裝呢?網上有相關文章,需要破壞系統基礎配置,對于只想運行一個應用程序,就需要破壞系統環境配置,這是得不償失的。能不能像Windows那樣拷貝Qt5.12.5的動態庫到其他安裝了低版本的Qt,甚至沒有安裝Qt的Linux上面運行呢,答案是肯定的。下面內容就是說明如何完全體部署Qt應用到Linux。

修改連接庫(不推薦)

就是把系統Qt庫鏈接到新版本的Qt庫,網上有類似方法,這種方式非常不推薦,原因是Linux發行版在開發時對所有庫進行了預設,強行修改可能會造成一個應用程序好用了,十個應用程序崩潰了的情況。

帶動態庫一起打包發布(推薦)

首先同樣使用Release模式構建應用,得到可執行文件myapp。GitHub上面有一個linuxdeployqt工具可以幫助構建.AppImage格式的Linux可執行程序,但是它的宗旨是構建可在所有平臺運行的Qt程序,因此就會有同樣的問題,如果我們編譯環境過于新,也就無法使用這個工具了。

自己動手,豐衣足食

網上好多文章使用ldd命令來查看我們的應用依賴了哪些庫,并且還有腳本可以自動復制這些庫到應用目錄,這通常是不可行的。因為ldd列表出來的大部分庫并不是我們需要的,有些甚至是我們構建系統獨有的庫,并且我們并不能很好的確定該刪除哪些庫,還有就是ldd只能檢查我們的應用程序直接依賴的哪些庫,并不能查出依賴庫的間接依賴,這些只有Qt自己知道,比如:libqxcb.so依賴libQt5DBus.so.5。

鏈接庫

.so文件放在可執行文件旁邊并不會像Windows加載DLL那樣自動被系統加載,Linux有幾個可以配置加載動態庫的方法,我這里使用設置環境變量LD_LIBRARY_PATH的方法,其他方法通常都需要root權限,并且破壞了系統原有運行模式,所以對于部署一個獨立運行的應用程序使用LD_LIBRARY_PATH是最好的方法。

以下是我的應用程序在Linux下的目錄,這是一個最基礎應用程序的目錄結構,你幾乎不能刪除其中任何文件。


linux

./bin 可執行文件目錄
./bin/qt.conf qt特殊配置文件
./bin/myapp 可執行文件
./plugins 插件目錄
./plugins/platforms
./plugins/platforms/libqxcb.so
./lib 動態庫加載目錄
./lib/libQt5Gui.so.5
./lib/libQt5Widgets.so.5
./lib/libQt5XcbQpa.so.5
./lib/libQt5DBus.so.5
./lib/libicudata.so.56
./lib/libicui18n.so.56
./lib/libQt5Core.so.5
./lib/libicuuc.so.56
./myapp.sh 應用啟動腳本

啟動腳本

因為我們的程序包含了所需要的庫,所以需要讓操作系統來加載我們自己的庫,基于此我們需要使用腳本的方式啟動我們的應用程序,來代替直接運行可執行文件。使用LD_LIBRARY_PATH來指定動態庫加載目錄,這里需要指定到了lib目錄。

myapp.sh內容如下:

#!/bin/sh
appname=`basename $0 | sed s,\.sh$,,`

dirname=`dirname $0`
tmp="${dirname#?}"

if [ "${dirname%$tmp}" != "/" ]; then
dirname=$PWD/$dirname
fi
LD_LIBRARY_PATH=$dirname/lib
export LD_LIBRARY_PATH
$dirname/bin/$appname "$@"

如果你是用以上腳本來啟動應用程序,只需要修改腳本名稱即可。

bin目錄

bin目錄存放可執行文件和qt.conf
qt.conf內容如下:

[Paths]
Prefix = ../
Plugins = plugins

Prefix指定程序工作目錄,相對于可執行文件路徑。默認為.當前目錄,這里我為了是目錄更加整潔,指定為..上級目錄。
Plugins指定插件加載目錄,相對于Prefix目錄。上面這種指定方式,Qt就會在我的plugins目錄下尋找插件了,例如plugins/platforms/libqxcb.so

打包發布

以上目錄結構和各種依賴即是一個Qt發布Linux環境的最小集了,可以使用以上目錄針對各種不同Linux發行版進行構建打包,或者直接壓縮進行分發。這樣應用就可以在裝有低版本Qt甚至沒有安裝Qt的環境上面運行了。

你還可以編寫自己的.desktop文件使用myapp.sh作為應用啟動命令。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379