Go中的交叉編譯問題
交叉編譯是指在一個硬件平臺生成另一個硬件平臺的可執(zhí)行文件。而Go提供了非常方便的交叉編譯方式。
如何編譯
Go交叉編譯,涉及到幾個環(huán)境變量的設(shè)置: GOARCH、GOOS和CGO_ENABLED。
GOARCH
:編譯目標(biāo)平臺的硬件體系架構(gòu)(amd64, 386, arm, ppc64等)。GOOS
:編譯目標(biāo)平臺上的操作系統(tǒng)(darwin, freebsd, linux, windows)。CGO_ENABLED
:代表是否開啟CGO,1表示開啟,0表示禁用。由于CGO不能支持交叉編譯,所以需要禁用。
GO中env的具體環(huán)境變量的注釋,可通過輸入命令go help environment查看。
~ $ go help environment ... GOARCH The architecture, or processor, for which to compile code. Examples are amd64, 386, arm, ppc64. ... GOOS The operating system for which to compile code. Examples are linux, darwin, windows, netbsd. ... CGO_ENABLED Whether the cgo command is supported. Either 0 or 1.
Mac 下編譯 Linux 和 Windows 64位可執(zhí)行程序
export CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go export CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
Linux 下編譯 Mac 和 Windows 64位可執(zhí)行程序
export CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go export CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
Windows 下編譯 Mac 和 Linux 64位可執(zhí)行程序
SET CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go SET CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
其他平臺或32位系統(tǒng)類似,這里就不再贅述。
GO是如何做到交叉編譯?
Go交叉編譯的實現(xiàn)通過在文件頂部增加構(gòu)建標(biāo)記,進(jìn)行選擇編譯。
// +build
注:Go源碼里的編譯器源碼位于$GOROOT/src/cmd/compile路徑下,鏈接器源碼位于$GOROOT/src/link路徑下。
我們的切入點從Go編譯器的main函數(shù)為入口,代碼位于$GOROOT/src/cmd/compile/main.go。
以環(huán)境變量GOARCH為例,看一下Go編譯器是如何通過構(gòu)建標(biāo)記來選擇對應(yīng)的體系架構(gòu)目標(biāo)進(jìn)行編譯。
package main ? // 引用了Go所能支持的所有架構(gòu)體系庫代碼,根據(jù)GOARCH選擇對應(yīng)的體系代碼 import ( "cmd/compile/internal/amd64" "cmd/compile/internal/arm" "cmd/compile/internal/arm64" .... "cmd/compile/internal/x86" ... ) ? // 初始化代碼 var archInits = map[string]func(*gc.Arch){ "386": x86.Init, "amd64": amd64.Init, "arm": arm.Init, "arm64": arm64.Init, ... } ? func main() { // disable timestamps for reproducible output log.SetFlags(0) log.SetPrefix("compile: ") ? // 通過objabi.GOARCH選擇對應(yīng)的架構(gòu)體系 archInit, ok := archInits[objabi.GOARCH] ... gc.Main(archInit) ... }
objabi.GOARCH是$GOROOT/src/cmd/internal/objabi/util.go中的變量GOARCH。
var ( defaultGOROOT string // set by linker ? ... GOROOT = envOr("GOROOT", defaultGOROOT) GOARCH = envOr("GOARCH", defaultGOARCH) GOOS = envOr("GOOS", defaultGOOS) ... )
defaultGOARCH是runtime包里的GOARCH值,如下所示。
// Code generated by go tool dist; DO NOT EDIT. ? package objabi ? import "runtime" ? ... const defaultGOOS = runtime.GOOS const defaultGOARCH = runtime.GOARCH ...
而該值又是通過sys.GOARCH賦值。$GOROOT/src/runtime/extern.go。
// GOARCH is the running program's architecture target: // one of 386, amd64, arm, s390x, and so on. const GOARCH string = sys.GOARCH
終于來到了重點!$GOROOT/src/runtime/internal/sys/agoarch_amd64.go
// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. ? // 我的機(jī)器平臺是amd64,且未對GOARCH的值做修改。因此這里的構(gòu)建標(biāo)簽是amd64. // +build amd64 ? package sys ? const GOARCH = `amd64`
通過構(gòu)建amd64的編譯標(biāo)簽,從而控制了Go編譯時需要選擇對應(yīng)的架構(gòu)代碼。即:如果不是amd64,例如arm,那對應(yīng)的編譯代碼就是$GOROOT/src/runtime/internal/sys/agoarch_arm.go。
如何利用交叉編譯?
雖然golang 可以跨平臺編譯,但卻無法解決系統(tǒng)的差異性。在靠近底層邏輯的項目中,我們需要直接調(diào)用操作系統(tǒng)函數(shù),例如同樣是實現(xiàn)IO多路服用,在darwin系統(tǒng)調(diào)用kqueue,而linux系統(tǒng)需調(diào)用epoll。
相同功能可以編寫類似xxx_windows.go xxx.Linux.go文件,根據(jù)操作系統(tǒng)編譯對應(yīng)源文件,而不是在文件中用if else規(guī)劃執(zhí)行路徑。
交叉編譯同樣可以理解為條件編譯,通過構(gòu)建的build標(biāo)簽,選擇需要編譯進(jìn)最終執(zhí)行二進(jìn)制文件的代碼。
這里給一個簡單的條件編譯示例,如下。
代碼文件
go.mod
main.go
myfunc.go
main.go:程序入口,調(diào)用位于myfunc.go中的speak函數(shù)。
package main ? import "fmt" ? func main() { fmt.Println("mike") speak("hello") }
myfunc.go: 構(gòu)建了build標(biāo)簽,需要build命令 帶上-tag speak,該代碼才能被編譯。
//+build speak ? package main ? func speak(s string) { println("speak:", s) }
執(zhí)行命令
$ go build -o main $ ./main
輸出
mike
可以看到,在main函數(shù)中的speak()函數(shù)并沒有被執(zhí)行,因為myfunc.go沒有被編譯。
如果需要將myfunc.go編譯進(jìn)最終的執(zhí)行代碼,則執(zhí)行命令
$ go build -tags speak -o main $ ./main
輸出
$ mike
$ speak: hello
上述條件編譯示例對你是否有啟發(fā)呢?
舉例:
項目開發(fā)中,如果想打印程序中的某些信息以便調(diào)試,而又不想打印相關(guān)代碼生成到最終的可執(zhí)行文件中,那么條件編譯便可派上用場。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Golan中?new()?、?make()?和簡短聲明符的區(qū)別和使用
Go語言中的new()、make()和簡短聲明符的區(qū)別和使用,new()用于分配內(nèi)存并返回指針,make()用于初始化切片、映射和通道,并返回初始化后的對象,簡短聲明符:=可以簡化變量聲明和初始化過程,感興趣的朋友一起看看吧2025-01-01Golang?HTTP服務(wù)超時控制實現(xiàn)原理分析
這篇文章主要介紹了Golang?HTTP服務(wù)超時控制實現(xiàn)原理,HTTP服務(wù)的超時控制是保障服務(wù)高可用性的重要措施之一,由于HTTP服務(wù)可能會遇到網(wǎng)絡(luò)延遲,資源瓶頸等問題,因此需要對請求進(jìn)行超時控制,以避免服務(wù)雪崩等問題,需要的朋友可以參考下2023-05-05Golang使用channel實現(xiàn)一個優(yōu)雅退出功能
最近補(bǔ)?Golang?channel?方面八股的時候發(fā)現(xiàn)用?channel?實現(xiàn)一個優(yōu)雅退出功能好像不是很難,之前寫的?HTTP?框架剛好也不支持優(yōu)雅退出功能,于是就參考了?Hertz?優(yōu)雅退出方面的代碼,為我的?PIANO?補(bǔ)足了這個?feature2023-03-03go?logger不侵入業(yè)務(wù)代碼使用slog替換zap并實現(xiàn)callerSkip詳解
這篇文章主要為大家介紹了go?logger不侵入業(yè)務(wù)代碼使用slog替換zap并實現(xiàn)callerSkip詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09