2009年4月28日 星期二

Qt4.5.1 & 靜態編譯

一直想要試試看Qt靜態編譯的方法,看了很多文章,總覺得很麻煩,總希望那一天Qt(現在是Nokia的公司)出新的版本,
只要 qmake -static -project -o yes.pro 就搞定, CC...

不過我幻想的情況沒來,卻來了一個LGPL授權方式...
這個東西,簡單講,就是Qt所提供的GUI library,如果以動態連接的方式使用,則原程式設計者,可以擁有發佈形式的權利...
所以,對於商業應用的人來說,可以不需權利金,就能發展商業應用,看來是個雙贏的策略

因此,靜態編譯的方法,似乎吸引力就沒這麼大了...
但是既然看了很多文章,就這樣放水流,也可惜,還是記錄一下,順便把我那一堆書籤消化一下

目前Qt的4.5版本,已經採用了LGPL授權方式,最新版本4.5.1
下載 的Qt SDK包括了Qt. MinGW和Qt Creator

隨著Qt SDK下載回來的MinGW,在命令模式輸入gcc --version顯示3.4.2(mingw-special)

實驗記錄:
1. 下載qt-win-opensource-2009.02.exe ,安裝
2. 複製Qt下的"2009.02"目錄,取名叫"4.5.1-static"
3. 執行Qt Command Prompt,切換到"4.5.1-static\qt"目錄下
4. 輸入configure -static -release  -no-exceptions ,執行(大概25分鐘)
5. 輸入mingw32-make sub-src,執行(大約2個多小時)
6. 輸入mingw32-make clean,之後整個"4.5.1-static"目錄大約1GB
7. 砍掉"4.5.1-static\qt"目錄下所有.dll檔,之後整個目錄大約672MB
8. 請將這目錄"4.5.1-static"好好保存起來,不然再做一次,很浪費電...

若需要編譯成靜態連結的方式,就將目錄裡qt\bin設進PATH裡,就可以用了

目前編譯環境MinGW/MSYS...對於Qt Creator這個IDE,還不熟,我想應該不錯用...
至於Qt Creator的靜態連結設定,有機會再研究...

PS 1: 編譯完的library,只能編譯release版本(代表沒有debug資訊),且是靜態連接,符合一般發佈的情況,不過License必須使用GPL
PS 2: -no-exceptions是要拿掉mingwm10.dll相依,不過沒有exception會有啥影響,未知...
PS 3: 一些plugin型式的library,需要特別處理,請參考介紹的文章
PS 4: 在XP sp3沒法編譯完成,還有從相關文章報告,windows 7也有些問題,我是在XP sp2環境下完成

相關連結:

這裡有一位作者講解LGPL授權
http://jay-notebook.blogspot.com/2009/01/qt-45-lgpl-21.html

其他作者的文章,關於靜態編譯的方法
http://hi.baidu.com/jzinfo/blog/item/c17c3f6d4ff439fe431694c3.html
http://www.wretch.cc/blog/jaywang4/15097262
http://lists.trolltech.com/qt-interest/2006-08/thread00942-0.html
http://lists.trolltech.com/qt-interest/2005-10/thread00381-0.html#msg00553
http://www.linuxfans.org/bbs/thread-182853-1-1.html
http://www.dotblogs.com.tw/yotrew/archive/2009/03/19/7568.aspx
http://www.qtforum.org/article/26946/how-to-deploy-a-static-app-in-qt-4-5.html
http://blog.yam.com/kaizan/article/20066326
http://jarsing.blogspot.com/2009/03/qtqtdll.html

2009年4月24日 星期五

MinGW和VC2008E之間...DLL編譯

測試程式,和相關操作,請參考之前兩篇文章(參考1 ,參考2 )

1. VC2008E使用MinGW產生的DLL檔
MinGW編譯DLL,可以產生testdll.def檔,然後利用VC的工具轉成VC相容的testdll.lib,如下:
lib /machine:i386 /def:testdll.def
之後產生的testdll.lib,隨同testdll.h,testdll.dll就可以和VC的專案做編譯連結了

PS1:MinGW產生的.lib,格式和VC不相容
PS2:可以使用VC提供的"Visual Studio 2008 命令提示字元",來做.def to .lib的轉換,它提供一個乾淨的VC建置環境,不過DOS的指令要有些印象...
PS3:透過.def轉換到.lib,然後連結後,產生的執行檔,就會找和.def同主檔名的.dll

2. MinGW使用VC2008E產生的DLL檔
VC編譯的DLL,基本上MinGW就可以用,不過要注意,在VC的設定裡,"呼叫慣例"(calling convention),要設成__cdecl,
若是__stdcall,就需要其他處理...
在VC2008的屬性頁視窗,組態屬性->C/C++->進階,呼叫慣例,改成__cdecl

編譯好後,將testdll.lib, testdll.h, testdll.dll,放到MinGW的專案目錄,就可以編譯連結了

後記:
這篇基本上就是參考MinGW的說明,只是筆記

相關連結:
http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs
維尼的蜂巢:__stdcall ,__cdecl的區別
維尼的蜂巢:__stdcall ,__cdecl的區別 再次探討
http://msdn.microsoft.com/zh-tw/library/46t77ak2.aspx

2009年4月21日 星期二

VC2008 Express 編譯DLL

建立DLL專案:
檔案->新增->專案

選擇Win32,Win32專案

按"下一步"

選擇DLL,然後按完成

修改testdll.cpp,以及加入testdll.h在標頭檔分類裡,而其內容,如之前的文章
PS: testdll.h的加入,可以按右鍵在標頭檔,然後選擇加入->新增項目,選"標頭檔(.h)",名稱輸入testdll.h
可以在方案總管,看到VC幫我們加進來的檔案

修改屬性,在testdll的c++ project上按右鍵(就是標頭檔的上一個階層,或方案的下一個階層),選擇屬性
組態屬性->C/C++->前置處理器,在前置處理器定義裡加進DLL_EXPORT(用分號相隔),確定

選擇建置->建置方案,就可以建置DLL了,可以在方案下的Debug目錄看到testdll.dll和testdll.lib,以及在testdll的目錄裡看到testdll.h,這三個檔案是我們需要的


建置win32 主控台應用程式:檔案->新增->專案

選擇Win32,Win32主控台應用專案

按"完成"

可以在方案總管,看到vc幫我們加進來的檔案
修改testmain.cpp,如之前的文章 ,關於_tmain(...),覺得很詭異的話,之前有些文章有介紹


有三個檔案要加進來,首先
testdll.lib和testdll.h放到tsetmain目錄裡,(testdll.h可以加入標頭檔,方便修改,不過沒差)
lib的加入,進入屬性頁面,組態屬性->連結器->輸入,其他相依性,輸入testdll.lib,確定

選擇建置->建置方案,就可以建置了,不過執行會找不到dll...
將testdll.dll放到執行檔產生的目錄(例如Debug\),就ok了

問題:如果硬碟是FAT32的格式,會有一些問題,有其他高手有紀錄下來,及解決方式
這裡做個備忘
Step1 於系統功能表中點擊「專案」→「屬性」(或按Alt + F7)
Step2 展開「組態屬性」
Step3 展開「連結器」
Step4 點擊「偵錯」
Step5 把右方視窗裡的「產生對應檔」改為「是(/MAP)」
Step6 點擊「資訊清單工具」
Step7 把右方視窗裡的「使用 FAT32 解決辦法」改為「是」

後記:Visual Studio 2008 Express(包括了VC),她有分方案和專案,一個方案可以包括一個以上的專案,這些專案可以用VC或VB來寫,或是一個DLL的專案,上面的例子,是兩個方案(各擁有一個專案),透過手動的方式來組合,如果一個方案包括兩個專案,一個是testdll,一個是testmain,則這個IDE會幫你處理一些事,但使用者還是要有些設定...
Step1  在方案的階層,將這兩個專案建立或加入
Step2  testdll.h需要放在各自source(.cpp .h放的地方)的目錄,然後testdll專案需要定義DLL_EXPORT
Step3  testmain專案"設定為啟始專案","專案相依性"設定為testmain相依testdll
PS : 上面提到lib的加入(進入屬性頁面,組態屬性->連結器->輸入,其他相依性,輸入testdll.lib,確定),不需要...

寫這篇真的是服務的心態(對於初學者),也許也希望對於DLL的編譯有個完整性的介紹,不過抓這麼多圖,是有點無聊,佔頁面,不過沒辦法,不然就乾脆不寫了

最近"Google 文件"對於Blogger的輸出,一些bug,有改善了(像是之前圖檔會亂跑,增加許多空白行...),所以這篇文章也算是對"Google 文件"的測試,看來應該是可以重回"Google 文件"的懷抱了

相關連結:
http://genewince.blogspot.com/2008/03/visual-c-dll-gene.html
http://www.wretch.cc/blog/MtvBoy/21514680
http://logix4u.net/Programming/vc++/A_Tutorial_on_creating_DLLs_with_VC++.html

2009年4月19日 星期日

用MinGW編譯DLL

testdll.h:
#ifndef TESTDLL_H
#define TESTDLL_H

#ifdef DLL_EXPORT
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C" {
#endif

DLLAPI float funadd(float a, float b);

#ifdef __cplusplus
}
#endif

#endif


testmain.c:
#include <stdio.h>
#include "testdll.h"

int main(void)
{
float a,b,c;

    a=1.0;
    b=2.0;
    c=funadd(a,b);
    
    printf("%f \n",c);
    getchar();    
}


testdll.c:
#include "testdll.h"

float funadd(float a, float b)
{
    return (a+b);
}


gcc -c -DDLL_EXPORT (-fPIC) testdll.c
PS : 會產生testdll.o的檔案...還有x86平台,-fPIC會忽略

gcc -shared -o testdll.dll testdll.o -Wl,--output-def,testdll.def,--out-implib,testdll.lib
PS : 產生三個檔案:
testdll.dll:也就是程式執行時載入的dll檔
testdll.def:若有需要和VC編譯器連結,這個檔會需要
testdll.lib:export library,用來連結主程式,這樣主程式才能知道找哪個dll,那個dll裡有哪些函式跟參數....

gcc -c testmain.c
PS : 用來使用dll的主程式,會產生一個testmain.o的檔案

gcc -static -o test.exe testmain.o -L. -ltestdll
PS : 會讓主程式和export library(testdll.lib)靜態連結起來,產生test.exe的可執行檔


由於dll和主程式都是用MinGW產生的,所以可以簡化上面的步驟,不過上面的解說,可以對執行檔組成了解多一些,以下舉兩個例子

方法一:
gcc -shared -o testdll.dll testdll.c
PS : -shared是linker的參數,這裡直接產生testdll.dll

gcc -c testmain.c

PS : -c:只編譯不連結,產生testmain.o

gcc -o test.exe testmain.o testdll.dll

PS : 我也不知給什麼規則,反正MinGW搞得定這個testdll.dll就對了

方法二:
gcc -shared -o testdll.dll testdll.c
gcc -o test.exe testmain.c -L. -ltestdll

PS : -l:unix環境裡,會找libtestdll.a(-static) 或 libtestdll.so(-shared),在函式庫名稱的命名上,前面一定要有lib

不過MinGW對於這個慣例,放寬許多,來讓熟悉windows的人習慣
for (-static) : 會找libtestdll.a 或 testdll.lib
for (-shared) : 會找libtestdll.so . testdll.dll 或 libtestdll.dll

希望看完之後是感覺"條條道路通羅馬",而不是給它一整個亂...

這裡又牽扯出另一個疑問,那就是MinGW的make,它對於字尾規則和函式庫的處理,是否有加上這些擴充,有空在研究好了

PS:MinGW的官方網站,有提供一個Shell,叫MSYS,之前發現MinGW和MSYS都有自己的make,MinGW的make叫mingw32-make.exe,MSYS的make叫make.exe,其中MSYS的make是unix style,不能分辨'\',組成的路徑字串(windows是用'\',組成路徑字串),也許這兩個make除了這個區別,還包括了字尾規則和函式庫處理的差異...


testdll.h的補充說明:
關於DLL_EXPORT 的定義,應該要在編譯testdll.c時定義,來分別dllimport和dllexport,不過我沒加,也沒錯誤,我不知道MinGW有沒有支援__declspec(dllimport)和__declspec(dllexport) 這些關鍵字...

關於extern "C" {},如果使用gcc,且souce code的附檔名是.c,會有錯誤,不過__cplusplus沒有定義,會避開...
如果附檔名是.cpp,或gcc改g++,則__cplusplus會定義,就會使函數的Name Mangling使用C的形式(簡單的那一種),常看到...

這裡的紀錄,主要都是看了別人的文章,試出來的...我盡量有系統性,不過最好還是參考相關主要說明文件

相關連結:

MinGW官方整理的FAQ
http://www.mingw.org/wiki/FAQ

這裡有幾篇關於如何用MinGW編譯DLL
http://www.adp-gmbh.ch/win/misc/mingw/dll.html
http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs
http://sig9.com/node/35

用cygwin編譯DLL
http://mqjing.blogspot.com/2009/03/c-cygwin-windows-code.html
http://mqjing.blogspot.com/2009/04/c-gcc-library.html 

用gcc處理static and shared library
http://www.adp-gmbh.ch/cpp/gcc/create_lib.html

有關gcc和g++的區別
http://blog.chinaunix.net/u1/52901/showart_480159.html
http://www.yuanma.org/data/2007/0406/article_2498.htm