W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
原文鏈接:https://chai2010.cn/advanced-go-programming-book/ch2-cgo/ch2-10-link.html
編譯和鏈接參數(shù)是每一個 C/C++ 程序員需要經(jīng)常面對的問題。構(gòu)建每一個 C/C++ 應(yīng)用均需要經(jīng)過編譯和鏈接兩個步驟,CGO 也是如此。 本節(jié)我們將簡要討論 CGO 中經(jīng)常用到的編譯和鏈接參數(shù)的用法。
編譯參數(shù)主要是頭文件的檢索路徑,預(yù)定義的宏等參數(shù)。理論上來說 C 和 C++ 是完全獨(dú)立的兩個編程語言,它們可以有著自己獨(dú)立的編譯參數(shù)。 但是因為 C++ 語言對 C 語言做了深度兼容,甚至可以將 C++ 理解為 C 語言的超集,因此 C 和 C++ 語言之間又會共享很多編譯參數(shù)。 因此 CGO 提供了 CFLAGS/CPPFLAGS/CXXFLAGS 三種參數(shù),其中 CFLAGS 對應(yīng) C 語言編譯參數(shù) (以 .c
后綴名)、 CPPFLAGS 對應(yīng)
C/C++ 代碼編譯參數(shù) (.c,.cc,.cpp,.cxx)、CXXFLAGS 對應(yīng)純 C++ 編譯參數(shù) (.cc,.cpp,*.cxx)。
鏈接參數(shù)主要包含要鏈接庫的檢索目錄和要鏈接庫的名字。因為歷史遺留問題,鏈接庫不支持相對路徑,我們必須為鏈接庫指定絕對路徑。 cgo 中的 ${SRCDIR} 為當(dāng)前目錄的絕對路徑。經(jīng)過編譯后的 C 和 C++ 目標(biāo)文件格式是一樣的,因此 LDFLAGS 對應(yīng) C/C++ 共同的鏈接參數(shù)。
為不同 C/C++ 庫提供編譯和鏈接參數(shù)是一項非常繁瑣的工作,因此 cgo 提供了對應(yīng) pkg-config
工具的支持。 我們可以通過 #cgo pkg-config xxx
命令來生成 xxx 庫需要的編譯和鏈接參數(shù),其底層通過調(diào)用 pkg-config xxx --cflags
生成編譯參數(shù),通過 pkg-config xxx --libs
命令生成鏈接參數(shù)。
需要注意的是 pkg-config
工具生成的編譯和鏈接參數(shù)是 C/C++ 公用的,無法做更細(xì)的區(qū)分。
pkg-config
工具雖然方便,但是有很多非標(biāo)準(zhǔn)的 C/C++ 庫并沒有實現(xiàn)對其支持。 這時候我們可以手工為 pkg-config
工具創(chuàng)建對應(yīng)庫的編譯和鏈接參數(shù)實現(xiàn)支持。
比如有一個名為 xxx 的 C/C++ 庫,我們可以手工創(chuàng)建 /usr/local/lib/pkgconfig/xxx.pc
文件:
Name: xxx
Cflags:-I/usr/local/include
Libs:-L/usr/local/lib –lxxx2
其中 Name 是庫的名字,Cflags 和 Libs 行分別對應(yīng) xxx 使用庫需要的編譯和鏈接參數(shù)。如果 pc
文件在其它目錄, 可以通過 PKG_CONFIG_PATH 環(huán)境變量指定 pkg-config
工具的檢索目錄。
而對應(yīng) cgo 來說,我們甚至可以通過 PKG_CONFIG 環(huán)境變量可指定自定義的 pkg-config 程序。 如果是自己實現(xiàn) CGO 專用的 pkg-config 程序,只要處理 --cflags
和 --libs
兩個參數(shù)即可。
下面的程序是 macos 系統(tǒng)下生成 Python3 的編譯和鏈接參數(shù):
// py3-config.go
func main() {
for _, s := range os.Args {
if s == "--cflags" {
out, _ := exec.Command("python3-config", "--cflags").CombinedOutput()
out = bytes.Replace(out, []byte("-arch"), []byte{}, -1)
out = bytes.Replace(out, []byte("i386"), []byte{}, -1)
out = bytes.Replace(out, []byte("x86_64"), []byte{}, -1)
fmt.Print(string(out))
return
}
if s == "--libs" {
out, _ := exec.Command("python3-config", "--ldflags").CombinedOutput()
fmt.Print(string(out))
return
}
}
}
然后通過以下命令構(gòu)建并使用自定義的 pkg-config
工具:
$ go build -o py3-config py3-config.go
$ PKG_CONFIG=./py3-config go build -buildmode=c-shared -o gopkg.so main.go
具體的細(xì)節(jié)可以參考 Go 實現(xiàn) Python 模塊章節(jié)。
在使用 go get
獲取 Go 語言包的同時會獲取包依賴的包。比如 A 包依賴 B 包,B 包依賴 C 包,C 包依賴 D 包: pkgA -> pkgB -> pkgC -> pkgD -> ...
。再 go get 獲取 A 包之后會依次線獲取 BCD 包。 如果在獲取 B 包之后構(gòu)建失敗,那么將導(dǎo)致鏈條的斷裂,從而導(dǎo)致 A 包的構(gòu)建失敗。
鏈條斷裂的原因有很多,其中常見的原因有:
仔細(xì)分析可以發(fā)現(xiàn),失敗的原因中和 CGO 相關(guān)的問題占了絕大多數(shù)。這并不是偶然現(xiàn)象, 自動化構(gòu)建 C/C++ 代碼一直是一個世界難題,到目前位置也沒有出現(xiàn)一個大家認(rèn)可的統(tǒng)一的 C/C++ 管理工具。
因為用了 cgo,比如 gcc 等構(gòu)建工具是必須安裝的,同時盡量要做到對主流系統(tǒng)的支持。 如果依賴的 C/C++ 包比較小并且有源代碼的前提下,可以優(yōu)先選擇從代碼構(gòu)建。
比如 github.com/chai2010/webp
包通過為每個 C/C++ 源文件在當(dāng)前包建立關(guān)鍵文件實現(xiàn)零配置依賴:
// z_libwebp_src_dec_alpha.c
#include "./internal/libwebp/src/dec/alpha.c"
因此在編譯 z_libwebp_src_dec_alpha.c
文件時,會編譯 libweb 原生的代碼。 其中的依賴是相對目錄,對于不同的平臺支持可以保持最大的一致性。
官方文檔說明導(dǎo)出的 Go 函數(shù)要放 main 包,但是真實情況是其它包的 Go 導(dǎo)出函數(shù)也是有效的。 因為導(dǎo)出后的 Go 函數(shù)就可以當(dāng)作 C 函數(shù)使用,所以必須有效。但是不同包導(dǎo)出的 Go 函數(shù)將在同一個全局的名字空間,因此需要小心避免重名的問題。 如果是從不同的包導(dǎo)出 Go 函數(shù)到 C 語言空間,那么 cgo 自動生成的 _cgo_export.h
文件將無法包含全部導(dǎo)出的函數(shù)聲明, 我們必須通過手寫頭文件的方式聲明導(dǎo)出的全部函數(shù)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: