起因: 最近在Ubuntu上編QEMU (for Windows);而我的目標機器是Win7 64bit的,所以自然就安裝了個 mingw64-x-gcc.
后來發現64bit的環境根本搭建不起來,好多依賴庫都不支持;於是只能重新安裝32bit的環境。
這時候問題就來了,/usr/bin下面有:
i586-mingw32msvc-gcc
i686-w64-mingw32-gcc
i686-w64-mingw32-gcc-4.6
x86_64-w64-mingw32-gcc
x86_64-w64-mingw32-gcc-4.6
然后我就懵逼了,不知道該用哪個了 (在這之前不知道有mingw-w64的存在)。
而韓國小哥也是個一知半解,
# install required packages $ sudo apt-get update $ sudo apt-get install mingw32-x-gcc For Windows 64bit If you want to build for windows 64 bit, you should install this package, mingw64-x-gcc |
嚴規正轉,現轉載科普文如下:
++++++++++++++++++++++
轉載: 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編譯選項。
EOF