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