淺談 Qt 靜態(tài)編譯
Qt 靜態(tài)編譯是本文要介紹的內(nèi)容,首先,你應(yīng)該該知道什么叫靜態(tài)引用編譯、什么叫動(dòng)態(tài)引用編譯。我這里只是簡(jiǎn)單的提提,具體的可以google一下。
動(dòng)態(tài)引用編譯,是指相關(guān)的庫,以dll的形式引用庫。動(dòng)態(tài)編譯的Exe程序尺寸比較小,因?yàn)橄嚓P(guān)的庫都沒有包含進(jìn)來。當(dāng)然,程序發(fā)布的時(shí)候,還要把相關(guān)的庫也一并發(fā)布出去。
靜態(tài)引用編譯,是指把相關(guān)的庫也一并引入Exe文件。這是程序的尺寸就會(huì)很大,不過,程序發(fā)布就會(huì)變得簡(jiǎn)單很多。
其次,你可能會(huì)注意到我標(biāo)題上寫了“真正”這兩個(gè)字。為什么我要強(qiáng)調(diào)真正這兩個(gè)字呢?因?yàn)槭褂肰C編譯的C或者C++程序,都需要相關(guān)的C runtime庫才能運(yùn)行。如果你是VC6,相應(yīng)的庫就叫MSVCR,如果是vc2005,那就是MSVCR08,vc2008就是MSVCR09。我這里假設(shè)你安裝的是VC2005,請(qǐng)進(jìn)入如下目錄:${VS Install Dir}\VC\redist\x86 和 ${System Driver}:\windows\WinSxS,你就會(huì)發(fā)現(xiàn)下面有很多很多的庫。沒錯(cuò),這里相當(dāng)一部分就是C runtime庫。
好了,言歸正傳,首先,我們用VC2005寫了一個(gè)不使用MFC的存C或者C++的程序,怎么發(fā)布給最終用戶呢?有兩個(gè)方法:
(1)靜態(tài)引用C runtime庫:打開“項(xiàng)目”->“XXX屬性”->“配置屬性”->“C/C++”->“代碼生成”->“運(yùn)行時(shí)庫”??吹搅税??這里一共有四個(gè)選項(xiàng),其中MT開頭的是靜態(tài)引用,MD開頭的是動(dòng)態(tài)引用,d結(jié)尾的是Debug調(diào)試版本,沒有d的是Release發(fā)布版本,所以就一共有四個(gè)選項(xiàng)。我們選擇/MT,然后編譯程序(生成的程序應(yīng)該不小),把這個(gè)程序發(fā)給用戶,然后用戶就可以直接運(yùn)行了。
(2)動(dòng)態(tài)引用C runtime庫:跟上面差不多,不過是用/MD選項(xiàng)編譯(程序應(yīng)該只是幾十K),然后發(fā)給用戶。這時(shí),用戶是不能運(yùn)行這個(gè)程序的,會(huì)報(bào)個(gè)什么程序引導(dǎo)失敗,重裝系統(tǒng)可能會(huì)修復(fù)問題之類的提示。這是我們還要把C runtime庫一并發(fā)過去。把${VS Install Dir}\VC\redist\x86\Microsoft.VC80.CRT下的所有文件(注意,是所有,包括那個(gè).manifest文件)發(fā)給用戶,用戶把這些文件放在我們的程序的同一個(gè)目錄,然后再次運(yùn)行,這時(shí),程序就起來了(VC2005之后,C runtime庫的引用改變了很多,建議google一下)。
說完C runtime庫,就來說說Qt庫了,這里我假設(shè)你用的是***的Qt4.4.3。我們編譯Qt的時(shí)候,configure.exe有很多參數(shù),大家可以configure.exe --help來看看,其中,默認(rèn)生成的Qt庫(這里默認(rèn)的意思,是指沒有加-share或者-static參數(shù))是動(dòng)態(tài)引用的,也就是說,編譯完后,在QtDir的lib目錄下除了一大堆lib文件外,還有一大堆的dll文件。我們發(fā)布我們的Exe程序的時(shí)候,需要把相應(yīng)的Qt庫的Dll也一并發(fā)給用戶。
按照Qt的安裝手冊(cè)和網(wǎng)上一大堆大牛的說法,加上-static參數(shù)后,Qt就可以靜態(tài)編譯了,也就是說,lib目錄下之后一大堆lib文件,沒有dll文件。是否?我們做個(gè)試驗(yàn):
首先是設(shè)置變量:
- set QTDIR=%CD%
- set PATH=%PATH%;%QTDIR%\bin
- set QMAKESPEC=win32-msvc2005
- "C:\Program Files\Microsoft Visual Studio 8\VC\vcvarsall.bat"" x86
配置makefile:
- configure -release -static -fast -qt-sql-odbc -qt-sql-sqlite -no-webkit
(這里的參數(shù)就不一一說明了,建議讀者打入--help認(rèn)真查查,特別注明一下,之所以-no-webkit,是因?yàn)樾掳娴膓t加上了Webkit,而這個(gè)東東編譯的時(shí)候非常耗時(shí)間,編譯后也很大,有100多M,并且我基本不會(huì)用到這個(gè)東東,所以忽略它)
然后
- cd src (我之所以直接進(jìn)入src目錄nmake,是因?yàn)椴幌雖ake其它不相干的模塊,節(jié)省時(shí)間)
- nmake
漫長的等待之后,我們發(fā)現(xiàn)lib下果然只有一大堆lib文件了,而且每個(gè)lib文件的尺寸都在M以上,似乎已經(jīng)成功了。然后我們?cè)诎惭b了qt-vsintegration的VC2005新建一個(gè)Qt工程,然后編譯一個(gè)release版本。編譯的時(shí)候,問題來了。我們選擇/MD選項(xiàng),這時(shí)鏈接就可以通過,但如果我們想要用/MT選項(xiàng)來使用靜態(tài)C runtime庫,就會(huì)報(bào)一大堆某某函數(shù)鏈接重復(fù)之類的錯(cuò)誤。經(jīng)驗(yàn)告訴我們,之所以不能使用/MT來編譯,是因?yàn)榱硗庖粋€(gè)庫——Qt庫使用了另外一種引用方式/MD(原則上來說,一個(gè)程序里面的所有模塊,都應(yīng)該使用同一種引用方式,具體可以google一下)。很顯然,我們編譯的所謂靜態(tài)Qt程序,一樣要背著微軟的C Runtime庫到處跑,還不夠“真正”的靜態(tài)。
怎么才能做成完全的靜態(tài)呢?記得之前編譯wxWidgets的時(shí)候,它除了有SHARED=0或者1的選項(xiàng)之外,還有一個(gè)RUNTIME_LIBS = static or dynamic的選項(xiàng),很顯然,這個(gè)RUNTIME_LIBS的選項(xiàng)就是我們想要的選項(xiàng)。不過我翻遍了Qt的安裝手冊(cè)以及網(wǎng)上大牛的文章,都沒有提及這個(gè)問題,我當(dāng)時(shí)心里就覺得奇怪,難道沒人遇到過這個(gè)問題?我又認(rèn)真翻查了configure.exe的help,也沒有類似的選項(xiàng),問題一下就僵住了。
回憶一下剛才我們編譯的時(shí)候,屏幕上調(diào)用cl.exe編譯的時(shí)候,有這樣一個(gè)參數(shù):cl.exe .... -MD .... xxx.cpp,眼利的朋友一下就會(huì)發(fā)現(xiàn),這個(gè)-MD就是c runtime動(dòng)態(tài)引用的選項(xiàng)。然而,怎么把這個(gè)-MD改成-MT呢?我們翻開剛才我們編譯的qt的src目錄下,隨便找個(gè)目錄進(jìn)去,打開Makefile.Release,我們就會(huì)看到CFLAGS=-MD ........,沒錯(cuò),就是這里。我們只要在這里把-MD改成-MT,就會(huì)使用靜態(tài)c runtime庫編譯Qt了。我們當(dāng)然不可能一個(gè)一個(gè)地替換這些makefile,關(guān)鍵是找出生成這些參數(shù)的模板文件。很顯然,它肯定在qt的mkspecs目錄,我們直奔win32-msvc2005目錄,果然找到一個(gè)qmake.conf文件,果然找到一個(gè)QMAKE_CFLAGS_RELEASE = -O2 -MD,把這里的-MD換成-MT,然后清理一下剛才的生成的配置信息(網(wǎng)上又說用nmake confclean來清空,不過我沒有成功,貌似是使用了-fast參數(shù)的緣故,不過沒關(guān)系,把這個(gè)目錄刪掉,重新解壓一份源代碼就可以了,然后把win32-msvc2005目錄下的qmake.conf的-MD換成-MT),重新
- configure -release -static -fast -qt-sql-odbc -qt-sql-sqlite -no-webkit
然后nmake
又是漫長的等待。不過我們不要干等,看看出來的編譯命令,cl.exe .... -MT .... xxx.cpp,果然變成靜態(tài)c運(yùn)行庫了。
編譯完之后,像剛才那樣,在VC2005建一個(gè)Qt的工程,然后用/MT這個(gè)選項(xiàng)編譯,OK,編譯成功,出來的Exe文件大小是4.95M,貌似已經(jīng)把C runtime庫嵌進(jìn)來了。然后把這個(gè)程序放到用戶那里運(yùn)行,OK單個(gè)Exe文件運(yùn)行成功了。
至此,編譯真正靜態(tài)的Qt程序試驗(yàn)完成??偨Y(jié)一下整個(gè)過程,首先是要有耐性,因?yàn)?strong>編譯一次Qt都至少兩個(gè)小時(shí)(當(dāng)然,用一些技巧,例如-fast,-no-qmake,只編譯src等等的技巧可以縮短很多時(shí)間),我來回就編譯了五次Qt;其次熟悉一些常見的編譯、鏈接的錯(cuò)誤,例如一見到XXX庫已經(jīng)引用之類的錯(cuò)誤,馬上就聯(lián)想到應(yīng)該是引用不同的庫導(dǎo)致的;***,要善于發(fā)現(xiàn)問題,查找問題。