MinGW vs MinGW-W64及其它
現轉載科普文如下:
++++++++++++++++++++++
轉載: http://www.cnblogs.com/foohack/p/3877276.html
部分參照備忘錄原文: bitbucket.org/FrankHB/yslib/src/50c3e6344a5a24b2382ce3398065f2197c2bd57e/doc/Workflow.Annual2014.txt?at=master 452行。
試試問答體。首先得繞個遠路,從Win32開始說起,否則之後容易亂……
Q:什麼是Win32?
A:嘛,32自然是指32位了?不一定。
正式地說,Win32主要是指跑在Windows NT核心上的Win32子系統。現在x64的Windows上的大部分程式也是跑在這個子系統上的,system32目錄也沒叫成system64。
儘管32的語源的確來自於“32位”。
Q:那麼為什麼還有Win64?
這倒可以肯定,這裡的64是指64位目標平臺,因為沒有上面的那種歧義。
有一點值得注意,在MSVC中,32位環境(當然是說跑的Intel 相容CPU的PC)預定義巨集_WIN32,但64位環境同時預定義了_WIN32和_WIN64。
順便,通常64位主要指x86_64(微軟稱為AMD64,這個相容x86的基礎架構一開始的確是AMD先搞出來的,後來才有Intel EM64T)。
64位Itanium也有_WIN64,不過一般見不到且跟MinGW沒什麼關係且現在都不正式支援了,不管了……
對於MinGW來說,這裡也有類似的坑:預定義巨集得先優先檢查64位的。實際情況更加複雜,另說。
Q:MinGW和MinGW-W64有什麼區別?
這個是個關鍵問題,但是……是個很長的故事。沒有鋪墊不好回答。
首先,MinGW是GNU工具(包括編譯器GCC和GNU binutils和偵錯程式GDB等)在Win32上的一個移植,是從Cygwin裡fork出來的。當初只考慮32位。和Cygwin相比,不強調POSIX相容性而相對強調效能和減小依賴。
具體來說MinGW除了以上工具外,還提供了一個適配於Win32的執行時環境。其中C標準庫實現用的直接是微軟隨Windows分發的MSVCRT。MinGW自己的執行時庫依賴於MSVCRT和其它系統庫。
而MinGW GCC依賴於MinGW執行時以及libgcc和其它系統庫。編譯出來的程式一般也要依賴這些庫,所以才會寫死在預設specs裡(可以用gcc -dumpspecs檢視)免得使用者隨便編譯連結個程式還得手動指定一大堆-l選項。
用三元組表示目標平臺,當年的MinGW是指i386-pc-mingw32。這裡i386也可以是i486等等……總之是32位x86指令集架構的名稱。中間的pc可選,表示廠商名。mingw32表示系統名。
特別注意,事實上成為標準的“專有名詞”mingw32裡的32是固定的。另外,所有這些大小寫一般也是固定的。GCC等的原始碼配置裡面也有硬編碼進去。
然後,因為只支援32位,有人覺得不夠用。這裡的一個主要人物,就是現在MinGW-W64的主要維護者Kai Tietz。因為工作需要他想MinGW提供擴充x64支援,但對方態度很不友好。於是憤而fork出來,這就是MinGW-W64的由來。
可見,MinGW-W64和原版MinGW有所淵源,但是獨立的兩個專案。
W64雖然用意是Win64,但是也算是個專有名詞,在三元組裡佔據廠商名,例如常見的:i686-w64-mingw32。(在GCC原始碼的配置檔案中,*-w64-mingw32和*-pc-mingw32是分別對待的。)
MinGW-W64是同時支援32位和64位的。甚至還支援32位和64位的交叉編譯(啟用multilib支援的MinGW發行版例如mingw-builds可以用-m32或-m64指定)。
顯然,W64和支援的架構無關。上面i686就不是64位的平臺(而且可以看出這裡的32也和架構沒關係)。支援64為的對應三元組是x86_64-w64-mingw32。
……容易讓人頭疼的是,這兩個專案現在都沒死,偏偏還很容易因為這些字面上的原因搞錯。為了下文描述方便,原版MinGW稱為MinGW.org。
這裡有一點非常重要:只有MinGW-W64是GCC官方支援的(儘管mingw32平臺是二等公民)。Kai Tietz擁有GCC官方repo的提交許可權。
所以,使用MinGW-W64的GCC一般比MinGW.org有更新更全面的支援,所以現在一般推薦MinGW-W64發行版。
說到這裡……維護mingw.org的Keith Marshall還和Kai Tietz等GCC官方人員在bugzilla上對噗過:gcc.gnu.org/bugzilla/show_bug.cgi?id=52015。
其中Keith Marshall對MinGW-W64使用MinGW一名造成混淆表示憤慨。嘛,這倒也是事實。
當然,也不是說MinGW.org就一無是處了。*-w64-mingw32原則上向後相容*-pc-mingw32,不過也有一些介面上的差別。BSD流的DT_*在MinGW.org上能用,在MinGW-W64的就沒有。(雖然DT_*也不怎麼推薦用就是了……)
Q:什麼是MinGW發行版(distribution) ?
這個說法習慣上可以說是從Linux等軟體中借用過來的。
類似Linux核心,不管MinGW.org還是MinGW-W64,本身都是相對集中於特定軟體包(MinGW執行時庫)開發的專案,並不著力於提供整個開箱即用的環境。
因此除了官方的一些編譯版本外,有很多人基於MinGW執行時上進行定製封裝供使用者下載整個環境,有的還提供包管理服務等。這就是發行版。一般提供直接解壓加上PATH就能用的環境和/或安裝包。
早期比較著名的有TDM-GCC、rubenvb等。
以前用的MinGW.org,不過現在主要轉到MinGW-W64上來了。
比較新的發行版,一開始就著眼於MinGW-W64。最著名的發行版之一應該是mingw-builds,基本上近年來(GCC4.7以後)Windows上能用支援最新版本最快的,支援交叉編譯。
mingw-builds一開始在sf.net上有自己的專案,不過後來表示要求加入MinGW-W64專案作為official builds,所以停更了,更新都在MinGW-W64裡面,不過殘念的是好像到現在MinGW-W64看來都不提供唯一的官方發行版,所以還是叫做personal builds。
另外提一下還有微軟VC++標準庫Dinkumware維護者之一Mr.STL(Stephan T. Lavavej) 個人的發行版,預設specs里加了-std=c++11。
還有MSYS2專案的MinGW發行版(這裡可能有新的混亂,下文再說),也是mingw-builds一夥人搞的,最近(4.9.1)比mingw-builds更新還快幾個小時。
其它發行版可以參照mingw-w64.sourceforge.net,更新相對沒那麼快。
最後,不嫌閒著蛋疼也可以自己編譯。不過迫不得已外最好別這樣做(GCC的編譯過程和hacking實在無力吐槽)。重複一遍,非常不推薦。
Q:上面為什麼要強調更新呢?
如果不想使用新的特性生成更高質量的程式碼,其實也沒必要盯著上面這麼多版本混亂的MinGW了。即便要相容性,也可以用古董嘛(逃……
對於C++前端來說MinGW尤為重要,現階段根本沒有能頂替的。作為系統預設ABI新銳代表的MSVC2013——前端還是殘的……各種bug。
GCC也有各種傻缺bug,不過至少在前端來說,程度上絕逼打不過cl(Microsoft C&C++ Optimizing Compiler)。
VC++除錯支援當然好得多,但是編譯器一坑爹整合除錯再好也沒用了。
嘛,Clang++?libc++什麼時候能在Windows上跑順再說——即便這樣MinGW相容的還是得依賴MinGW的libgcc。至於和VC++相容的clang-cl,看起來還在折騰微軟的坑爹ABI黑箱(這沒像大部分平臺上GCC用的Itanium ABI公開文件),一年半載別指望了。
Q:什麼是異常模型和執行緒模型,用哪種比較好?
這兩個都是對於C++實現(G++、libgcc、libsup++等等)而言的。
首先,異常模型。C++標準沒規定異常怎麼實現。MinGW GCC使用的Itanium ABI也沒規定必須怎麼實現(但規定了一些公共介面),這部分由實現自行考慮。
GCC一般提供了SjLj(C的setjmp/longjmp)實現的stub。對於x86,允許使用Dwarf2除錯資訊的實現。兩者的區別在於sjlj比較通用,但是即便不丟擲捕獲異常而只是使用異常中立的風格隱式傳遞異常,也有執行時開銷。而Dwarf2相容性(考慮多層C++和C的DLL互相呼叫來看)相對較差,但沒有這種開銷。
兩者ABI並不相容(知道C++坑爹了吧,不僅不同實現不相容,同一個編譯器同一個平臺自己都能跟自己不相容……)——前者依賴類似libgcc_s_sjlj.dll這樣的dll,後者則是類似libgcc_s_dw2.dll這樣的。舊一點的可能也沒有這種字尾差異。
另外,libstdc++作為C++標準庫實現顯然依賴異常,但名字一樣的dll可能依賴的不是同一種。所以混用多個版本MinGW GCC且沒把path清理乾淨的時候容易出現找不到符號定義導致連結失敗。這還不是最坑的,有時候gdb載入不同位置的dll在執行時掛掉,還不只是一個PATH的問題……這種情況下先拿system internals的process exporler之類的工具看看程序載入的DLL是不是預期的再說。
為什麼說要有這麼坑爹不相容的,像VC++一樣用一種不就好了……其實Win32 x86上最理想的應該是和VC++一樣基於SEH(Windows結構化異常處理)的實現,但是Borland關於這個的專利才沒過期幾天……所以你懂的。
x64上沒專利的麻煩,有sjlj和SEH的實現,一般還是SEH。
第二,執行緒模型。
Windows執行緒API和POSIX(pthread) 有很大不同,而ISO C++的std::thread為代表的介面是很接近pthread的。
所以在libstdc++上實現這些介面,首先依賴的是pthread在Win32上的移植libwinpthread,也就是POSIX執行緒模型。因此釋出的時候需要帶上libwinpthread-1.dll之類的dll。
至於Win32執行緒模型,GCC mailing list是有提過,不過到現在還是沒實現。也就是說ISO C++的實現是殘的,沒法用。如果只打算用Win32多執行緒API倒是的可以用。
所以取決於具體需要。要相容性好點的一般還是POSIX。
Q:什麼是MSYS,和MinGW有什麼區別?
MSYS是提供一套“系統”,三元組是*-pc-msys。
和MinGW相比,MSYS更接近Cygwin(強調POSIX相容性),提供了一個sysroot(下面有/bin啊/etc什麼的),因此移植POSIX環境的程式一般更方便。
代價也是有的。MSYS環境下原生編譯的程式一般需要多依賴MSYS執行時庫(當然比Cygwin要輕量多了)。
所以常規的實踐是,如果只是開發Windows程式,能用MinGW就不要用MSYS原生的編譯器來構建。當然,使用MSYS上的sh等工具還是沒問題,跟GNU工具配套怎麼說比cmd總好用。(雖然也有不少瑣碎坑爹bug。)
Q:什麼是MSYS2,MSYS2上的MinGW發行版是怎麼回事?
字面意思,MSYS 2.0。比起1.0來說更加像Cygwin(例如/etc/fstab配置)。專案在sf.net上託管。
一個特色是基礎系統附帶ArchLinux移植的包管理器pacman,可以同時獨立部署/mingw32(i686-w64-mingw32)和/mingw64(x86-w64-mingw32)下的開發和執行環境。
下載依賴相當方便(就是沒有靠譜的映象,網速可能非常拙計)。具體使用參考ArchLinux Wiki。
雖然不支援交叉編譯,不過可以分別裝所以不是什麼問題,比mingw-builds的-m32和-m64來說更加穩定靠譜。
只提供Dwarf2異常模型和POSIX執行緒模型對於成套系統也不是什麼大問題。包雖然比不上ArchLinux那麼豐富不過常用的很多都有,免去自己編譯的麻煩。打算長期使用MinGW和相關工具的,推薦使用。
雖然滾掛了也沒多大事,不過版本是個問題。如果需要特定版本的GCC就不適用(比如規避GCC 4.9的坑爹bug……),除非有耐心自己找到.xz手動安裝。
Q:部署程式需要提供哪些檔案?
Windows預設安裝自然不帶MinGW執行時環境,所以除了編譯出來的程式和可能附帶的資料,一些dll是要準備好的——除非有耐心折騰全部靜態連結。
不同版本不同語言不同編譯器編譯出來的東西都不太一樣。最簡單暴力也可靠(?)的方法就是複製可執行程式到沒裝環境的白板測試機上看少了哪些東西(不過未必一目瞭然)。
簡單可靠的方式是用Dependency Walker等工具檢視依賴。
對於C++,除非不用POSIX thread可以省掉libwinpthread,一般至少得確保上面異常模型和執行緒模型討論中提到的三個dll(注意就算你不顯示使用標準庫,編譯器生成的程式碼也可能用到——典型的如預設::operator new,所以得帶上libstdc++)。
Q:現在還有什麼新坑?
就提一個GCC 4.9的問題。
GCC 4.9的LTO(連結時優化)預設使用新的目標檔案格式,生成的檔案不包含冗餘的二進位制程式碼。
但是LTO有特定的phase(內部會多編譯幾個pass),傳統的靜態連結器(ar) 不知道這裡的約定,所以原來好好的東西,升級4.9以後開了-flto就可能找不到符號連結失敗。
現在MinGW發行版應該都沒實現gcc-ar(執行會提示沒支援linker plugin)。相容舊版本的行為還得加上-ffat-lto-objects編譯選項。