MinGW + MSVC + CEF 源代碼編譯 - 4.使用 MinGW 編譯 libcef_dll_wrapper
終于到最后一步了——編譯 libcef_dll_wrapper。正如第一篇所說,兩個編譯器(編譯環(huán)境)都可以,但不能交叉連接——即用 MinGW 連接?MSVC 生成的靜態(tài)庫,或者反之。
其實上面這段話一次性挖了三個坑,下面將一一說明。另,多圖警告(大概)。

雖然盡量避開了這類稱呼,但想了想還是提一下吧。
首先是編譯器和編譯環(huán)境的問題。MinGW 屬于編譯環(huán)境,包含了 GCC 編譯器;而 MSVC?本身就是編譯器工具鏈,由 cl.exe 負責(zé)編譯,link.exe 負責(zé)連接。

然后是連接問題。由于 MinGW?缺少 Propsys.lib 與 BufferOverflowU.lib 等依賴庫,因此無法連接?MSVC 生成的 cef_sandbox.lib。
當(dāng)然,最重要的是二者生成的靜態(tài)庫 ABI(Application Binary Interface,應(yīng)用程序二進制接口)不同,這是 C++ 的特性導(dǎo)致的。C++ 是一種復(fù)雜的編程語言,支持繼承和多態(tài),因此編譯器要想保證準(zhǔn)確調(diào)用函數(shù),就需要確定其調(diào)用約定(函數(shù)名區(qū)分、參數(shù)輸入、棧管理等)、返回類型及參數(shù)列表。為使函數(shù)唯一化,不妨從函數(shù)名稱下手,通常采用名稱修飾(名稱粉碎)機制。經(jīng)過修飾的名稱包含了上述幾個部分,形成了此函數(shù)的唯一標(biāo)識,這就是 ABI 的組成部分(ABI 其實有很多內(nèi)容,這里只討論名稱修飾)。連接時,通過尋找這個標(biāo)識就知道是否存在對應(yīng)函數(shù)。然而,C++ 標(biāo)準(zhǔn)并未定義修飾規(guī)則,導(dǎo)致不同編譯器修飾后的名稱不同,ABI 也不同。所以說,C 就不存在 ABI 不兼容的問題(樂
靜態(tài)庫 .lib / .a 都是由許多中間文件組成的,如 .obj,對應(yīng) MSVC 生成的 .lib 或 MinGW 生成的 .a;又如 .o,對應(yīng) Linux 下的 .a。連接時,通過修飾后的名稱,才能實現(xiàn)不同中間文件的函數(shù)調(diào)用,最終生成可執(zhí)行文件。這樣,問題就來了:要是不同中間文件的 ABI 不同怎么辦?還能怎么辦,找不到唄(嗯我懂你的意思.jpg)。這也就是出現(xiàn)大量“undefined reference to '*'”第二常見的原因(第一見末尾)。
有人會說,把修飾后的名稱轉(zhuǎn)換一下不就大功告成了嗎?正確的,中肯的。可惜的是,這只存在于 MinGW?中(還記得第一篇嗎,未特殊說明均為?MinGW-w64。這里指的是過時的、只能生成 32 位程序的 MinGW),即 reimp 命令。至于現(xiàn)在的?MinGW-w64,已經(jīng)明確表示不支持 MSVC 生成的靜態(tài)庫,鏈接如下:
https://sourceforge.net/p/mingw-w64/wiki2/Answer%2064%20bit%20MSVC-generated%20x64%20.lib/
好家伙,這方面的資料又復(fù)雜又混亂,看了一晚上看得頭疼,所以上面的內(nèi)容可能有錯??傊?,cef_sandbox.lib 在 MinGW 上算是寄了。

說完這些,最后來看一下 .lib 文件。
MSVC 生成的 .lib 文件主要有兩種,一種是靜態(tài)鏈接庫,另一種是(動態(tài)鏈接庫的)導(dǎo)入庫。有什么不同呢?直觀感受,靜態(tài)庫大(如 cef_sandbox.lib),導(dǎo)入庫小(如 libcef.lib)。進一步看,靜態(tài)庫包含了代碼實現(xiàn)及地址符號表,導(dǎo)入庫則不包含代碼實現(xiàn)(相當(dāng)于頭文件)。MSVC 的靜態(tài)庫 .lib 相當(dāng)于 MinGW 的 .a,而導(dǎo)入庫 .lib 相當(dāng)于 MinGW 的 .dll.a;前者不能通用,后者則可以。
因此,cef_sandbox.lib 與 libcef_dll_wrapper.lib 在 MinGW 中均不可用,只能使用 libcef.lib 與 libcef_dll_wrapper.a。希望有才之士編譯出 MinGW 能用的 cef_sandbox 靜態(tài)庫......

下面解決如何使用 MinGW 編譯 libcef_dll_wrapper 的問題(MSVC 就不用了,飯都喂嘴里了還不會吃?)。在開始之前,找到?cef_binary 開頭的文件夾(打包之后生成),頂層目錄結(jié)構(gòu)大致如下:

這里,我把文件夾重命名為 libcef,放在了 D:\Data 下(之后均為相對路徑)。
現(xiàn)在要做的事,是修改 CMakeLists.txt,僅留下 libcef_dll_wrapper 所在行(即 add_subdirectory 語句)及之前的內(nèi)容,后面全部注釋掉。寫到最后才想起來沒有 tests(撓頭

接下來是修改?cmake\cef_variables.cmake:
第一步,尋找 if(OS_LINUX),看到下面熟悉的 GCC 參數(shù)了嗎?先別急,向下找到?CEF_STANDARD_LIBS,一個大大的“X11”告訴你這與 GCC?和?Windows 都無關(guān)了(X11 是 Linux 下的圖形化窗口管理系統(tǒng),提供 GUI 的)。這之間的內(nèi)容,就是 GCC 的相關(guān)參數(shù),全部復(fù)制到 if(OS_WINDOWS) 下,覆蓋掉對應(yīng)內(nèi)容(包括 GEN_NINJA 等無關(guān)內(nèi)容),參考下圖(可能有些許差異)。




值得一提的是,下面的 MSVC 靜態(tài)庫(.lib)都有對應(yīng)的 MinGW 靜態(tài)庫(.a),如 comctl32.lib 對應(yīng) libcomctl32.a 等——除了 Propsys.lib(明明有頭文件),估計 MinGW 不需要吧。
從覆蓋處繼續(xù)向下,刪除 ATL 相關(guān)內(nèi)容,參考下圖(可能有些許差異)。

第二步,來到 if(OS_WINDOWS) 開頭,找到?CEF_LIBTYPE?所在行,并改為?set(CEF_LIBTYPE STATIC) 以生成靜態(tài)庫(動態(tài)庫總是出錯,煩內(nèi))。
第三步,向下,在?CEF_COMPILER_FLAGS 中刪除?-fstack-protector(會影響 libcef_dll_wrapper 的連接,見末尾)。
第四步,繼續(xù)向下,在 CEF_CXX_COMPILER_FLAGS 中修改?-std 選項為 -std=c++17。
最后,完整的?if(OS_WINDOWS) 語句塊如下(可能有較大差異,不要直接復(fù)制):
修改完成,準(zhǔn)備編譯。

這次不需要開發(fā)者命令行了,直接打開 cmd,輸入以下內(nèi)容:
成功后將提示(這里出錯說明 CMake 環(huán)境配置有問題):

繼續(xù)輸入:
接下來,大概率會出現(xiàn)以下錯誤:

按下圖修改 include\internal\cef_string_wrappers.h:

這里選擇在用戶自定義的頭文件之前添加,能夠避免可能的重復(fù)修改。

看到以下提示,說明編譯完成,libcef_dll_wrapper.a 位于 build\libcef_dll_wrapper 處。

祝賀你,現(xiàn)在可以編譯你的程序了!
如果要編譯 cefsimple(自行下載),建議重新編寫?cefsimple 下的 CMakeLists.txt,因為原有的?CMakeLists.txt 使用變量太分散,容易出錯。最終效果如下:

遺憾的是,cef_sandbox.lib?不可用(原因見開頭),也就是說可以刪除(使用?MSVC?的也可留下)。
由于?cefclient?缺少?DirectXMath.h,無法確定?MinGW 編譯出來的靜態(tài)庫是否正?!辽倩竟δ苓€是?OK?的,要啥自行車(
所以投靠?VS?才是治本之道(可是手動構(gòu)建項目和沒有模板限制真的很香?。?/span>(又不是不能用.jpg)
湯暖暖的,連夜轉(zhuǎn)戰(zhàn)?Linux

下面是一些常見錯誤及其解決辦法(我是不是說過這句話?)。
連接程序時出現(xiàn)大量“undefined?reference?to?'*'”:使用 target_link_libraries / gcc -l 時,必須把 libcef_dll_wrapper 寫在前面(庫文件引用要求:越靠后越底層)。
連接程序時出現(xiàn)“undefined?reference?to?'__stack_chk_fail'”:由于不存在相關(guān)庫文件,直接禁用棧保護即可(刪除?-fstack-protector)。

結(jié)束了?結(jié)束了。
第一次寫專欄 /?Blog,感覺寫出來的跟不上自己想的,三千字,還是太多了
“謝謝,寫了很久,已經(jīng)倒下了orz”