透過一個編譯報錯,總結(jié)兩個Go程序編譯的重要知識
本文轉(zhuǎn)載自微信公眾號「網(wǎng)管叨bi叨」,作者網(wǎng)管。轉(zhuǎn)載本文請聯(lián)系網(wǎng)管叨bi叨公眾號。
最近調(diào)研了一下某個做 APM 的廠商的 Go 探針程序,說是引入一個包,全程不用再修改其他代碼就能在項目里引入探針。沒想到在剛引入包試著構(gòu)建了一下就翻車了。
- main.go:10:2: build constraints exclude all Go files in /xxx/github.com/xxx/agnet/xxxx
編譯器編譯的時候直接排除了某個包下的所有文件,啥意思呢?就是這個包下沒有能在當(dāng)前構(gòu)建環(huán)境下構(gòu)建的 Go 文件。猜測應(yīng)該是這個包源碼的構(gòu)建標(biāo)簽上聲明了不允許在Mac 環(huán)境下構(gòu)建。打開源碼看了看,確實(shí)是,所有文件的構(gòu)建標(biāo)簽都是這么聲明的。
- // +build linux
- // +build amd64
這個叫做條件編譯,或者約束編譯。那想在Mac下編譯linux上才能運(yùn)行的執(zhí)行文件該怎么辦呢?Go 里邊還支持一個特性叫做交叉編譯,就是干跨平臺編譯這個事兒的。具體怎么用呢,比如這個例子里是需要在Mac環(huán)境下編譯能在Linux系統(tǒng)amd64架構(gòu)下運(yùn)行的執(zhí)行文件,就得醬嬸進(jìn)行編譯:
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
不過我后來想研究下為啥不讓在 Mac 上編譯,看了看這個包的探針是用CGO實(shí)現(xiàn)的調(diào)用了linux系統(tǒng)下的一個C語言實(shí)現(xiàn)的工具命令??吹竭@我已經(jīng)不想繼續(xù)研究這個包了,那么為了讓此篇文章水的不那么明顯:),接下來咱們就把Go語言的交叉編譯和條件編譯這兩個知識點(diǎn)再復(fù)習(xí)一遍吧。
交叉編譯
交叉編譯是用來在一個平臺上生成另一個平臺的可執(zhí)行程序。Go 的命令集是原生支持交叉編譯的,使用方法也很簡單,比如上面已經(jīng)演示過的
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
參數(shù)說明
- CGO_ENABLED : CGO 表示golang中的工具,CGO_ENABLED 表示CGO禁用,交叉編譯中不能使用CGO的
- GOOS: 目標(biāo)平臺
- mac 對應(yīng) darwin
- linux 對應(yīng) linux
- windows 對應(yīng) windows
- GOARCH :目標(biāo)平臺的體系架構(gòu)【386,amd64,arm】, 目前市面上的個人電腦一般都是amd64架構(gòu)的
- 386 也稱 x86 對應(yīng) 32位操作系統(tǒng)
- amd64 也稱 x64 對應(yīng) 64位操作系統(tǒng)
- arm 這種架構(gòu)一般用于嵌入式開發(fā)。比如 Android , IOS , Win mobile , TIZEN 等
了解完這幾個參數(shù)后,我們在看下Mac、Linux、Windows這三個平臺上執(zhí)行交叉編譯的例子,Windows的因為家境貧寒,條件不允許我沒有試過,命令網(wǎng)上找的,如果有錯誤還請同學(xué)們在評論里留言幫我改正一下。
Mac 下編譯, Linux 或者 Windows 的可執(zhí)行程序
- # linux可執(zhí)行程序
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
- # Windows 可執(zhí)行程序
- CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
Linux 下編譯 , Mac 或者 Windows 下去執(zhí)行
- # Mac 平臺可執(zhí)行程序
- CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go
- # Windows可執(zhí)行程序
- CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
Windows 下執(zhí)行 , Mac 或 Linux 下去執(zhí)行
需要寫一個批處理程序,在里面去設(shè)置,因為windows 下的 terminal 不支持shell , 這跟 Mac 和 Linux下的有點(diǎn)不同
- # Mac 下執(zhí)行
- SET CGO_ENABLED=0
- SET GOOS=darwin
- SET GOARCH=amd64
- go build main.go
- # Linux 去執(zhí)行
- SET CGO_ENABLED=0
- SET GOOS=linux
- SET GOARCH=amd64
- go build main.go
條件編譯
交叉編譯只是為了能在一個平臺上編譯出其他平臺可運(yùn)行的程序,Go 作為一個跨平臺的語言,它提供的類庫勢必也是跨平臺的,比如說程序的系統(tǒng)調(diào)用相關(guān)的功能,能根據(jù)所處環(huán)境選擇對應(yīng)的源碼進(jìn)行編譯。讓編譯器只對滿足條件的代碼進(jìn)行編譯,將不滿足條件的代碼舍棄,這就是另外一個概念叫---條件編譯。
在 Go 中,也稱之為Build Constraints 編譯約束,添加編譯約束的方式有2種分別:
- 編譯標(biāo)簽(build tag)
- 文件后綴
編譯標(biāo)簽
編譯標(biāo)簽是一種通過在源碼文件頂部添加注釋,來決定文件是否參與編譯的約束方式。其格式如下:
- // +build <tags>
注意:// +build的下一行必須是空行,否則會被解析為包注釋。
- // +build linux
- // main package comment
- package main
tags說明:
- 以空格分開表示 AND
- 以逗號分開表示 OR
- !表示 NOT
標(biāo)簽可以指定為以下內(nèi)容:
- 操作系統(tǒng),環(huán)境變量中GOOS的值,如:linux、darwin、windows等等。
- 操作系統(tǒng)的架構(gòu),環(huán)境變量中GOARCH的值,如:arch64、x86、i386等等。
- 使用的編譯器,gc或者gccgo。
- 是否開啟CGO,cgo。
- golang版本號:比如Go Version 1.1為go1.1,Go Version 1.12版本為go1.12,以此類推。
- 其它自定義標(biāo)簽,通過go build -tags指定的值。
例如,編譯條件為(linux AND 386) OR (darwin AND (NOT cgo))
- // +build linux,386 darwin,!cgo
另外一個文件可以有多個編譯約束,比如條件為(linux OR darwin) AND amd64
- // +build linux darwin
- // +build amd64
也可以使用ignore標(biāo)簽將一個文件從編譯中排除。
- // +build ignore
文件后綴
除了編譯標(biāo)簽,第二種添加編譯約束的方法是通過源碼文件的文件名實(shí)現(xiàn)的,這種方案比構(gòu)造標(biāo)簽方案更簡單。編譯器也會根據(jù)文件后綴來自動選擇編譯文件:
- $filename_$GOOS.go
- $filename_$GOARCH.go
- $filename_$GOOS_$GOARCH.go
- $filename: 源文件名稱。
- $GOOS: 表示操作系統(tǒng),從環(huán)境變量中獲取。
- $GOARCH: 表示系統(tǒng)架構(gòu),從環(huán)境變量中獲取。
后綴的順序記住不要顛倒,后綴中同時出現(xiàn)系統(tǒng)和架構(gòu)名時,需要保持$filename_$GOOS_$GOARCH.go的順序。
在 Go 的每個內(nèi)置庫里都有很多以不同系統(tǒng)名結(jié)尾的文件。下面是Go的os內(nèi)置庫源代碼的部分截圖:
文件后綴添加編譯約束
兩種添加編譯限制的方式該如何選擇
構(gòu)建標(biāo)簽和文件名后綴在功能上是重疊的。比如,一個名為mypkg_linux.go的文件,再包含構(gòu)建標(biāo)簽// +build linux會顯得多余。
通常來說,當(dāng)只有一個特定平臺需要指定時,我們選擇文件名后綴的方式。比如:
- mypkg_linux.go // 只在 linux 系統(tǒng)編譯
- mypkg_windows_amd64.go // 只在 windows amd 64位 平臺編譯
相反,如果你的文件需要指定給多個平臺或體系架構(gòu)使用,或者你需要排除某個特定平臺時,我們選擇構(gòu)建標(biāo)簽的方式。比如:
- // 在所有類unix平臺編譯
- // +build darwin dragonfly freebsd linux netbsd openbsd
- // 在非Windows平臺編譯
- // +build !windows
一個編譯器報錯,居然水了一篇文章....啊...(咳嗽聲)引出來的交叉編譯和條件編譯(編譯約束)這兩個非常重要的知識點(diǎn),其實(shí)這兩個知識點(diǎn)在很早之前我也寫過篇文章,這次相當(dāng)于從實(shí)際遇到問題帶出從頭開始再分析一遍,希望大家能喜歡。
參考鏈接
http://www.oskip.com/post/golang/golang-build/
https://juejin.cn/post/6844903944808824845
https://mp.weixin.qq.com/s/Ys8o4arwIFYB6DPCdiGNNQ