Go中的交叉編譯問(wèn)題
交叉編譯是指在一個(gè)硬件平臺(tái)生成另一個(gè)硬件平臺(tái)的可執(zhí)行文件。而Go提供了非常方便的交叉編譯方式。
如何編譯
Go交叉編譯,涉及到幾個(gè)環(huán)境變量的設(shè)置: GOARCH、GOOS和CGO_ENABLED。
GOARCH
:編譯目標(biāo)平臺(tái)的硬件體系架構(gòu)(amd64, 386, arm, ppc64等)。GOOS
:編譯目標(biāo)平臺(tái)上的操作系統(tǒng)(darwin, freebsd, linux, windows)。CGO_ENABLED
:代表是否開(kāi)啟CGO,1表示開(kāi)啟,0表示禁用。由于CGO不能支持交叉編譯,所以需要禁用。
GO中env的具體環(huán)境變量的注釋?zhuān)赏ㄟ^(guò)輸入命令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
其他平臺(tái)或32位系統(tǒng)類(lèi)似,這里就不再贅述。
GO是如何做到交叉編譯?
Go交叉編譯的實(shí)現(xiàn)通過(guò)在文件頂部增加構(gòu)建標(biāo)記,進(jìn)行選擇編譯。
// +build
注:Go源碼里的編譯器源碼位于$GOROOT/src/cmd/compile路徑下,鏈接器源碼位于$GOROOT/src/link路徑下。
我們的切入點(diǎn)從Go編譯器的main函數(shù)為入口,代碼位于$GOROOT/src/cmd/compile/main.go。
以環(huán)境變量GOARCH為例,看一下Go編譯器是如何通過(guò)構(gòu)建標(biāo)記來(lái)選擇對(duì)應(yīng)的體系架構(gòu)目標(biāo)進(jìn)行編譯。
package main ? // 引用了Go所能支持的所有架構(gòu)體系庫(kù)代碼,根據(jù)GOARCH選擇對(duì)應(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: ") ? // 通過(guò)objabi.GOARCH選擇對(duì)應(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 ...
而該值又是通過(guò)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
終于來(lái)到了重點(diǎn)!$GOROOT/src/runtime/internal/sys/agoarch_amd64.go
// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. ? // 我的機(jī)器平臺(tái)是amd64,且未對(duì)GOARCH的值做修改。因此這里的構(gòu)建標(biāo)簽是amd64. // +build amd64 ? package sys ? const GOARCH = `amd64`
通過(guò)構(gòu)建amd64的編譯標(biāo)簽,從而控制了Go編譯時(shí)需要選擇對(duì)應(yīng)的架構(gòu)代碼。即:如果不是amd64,例如arm,那對(duì)應(yīng)的編譯代碼就是$GOROOT/src/runtime/internal/sys/agoarch_arm.go。
如何利用交叉編譯?
雖然golang 可以跨平臺(tái)編譯,但卻無(wú)法解決系統(tǒng)的差異性。在靠近底層邏輯的項(xiàng)目中,我們需要直接調(diào)用操作系統(tǒng)函數(shù),例如同樣是實(shí)現(xiàn)IO多路服用,在darwin系統(tǒng)調(diào)用kqueue,而linux系統(tǒng)需調(diào)用epoll。
相同功能可以編寫(xiě)類(lèi)似xxx_windows.go xxx.Linux.go文件,根據(jù)操作系統(tǒng)編譯對(duì)應(yīng)源文件,而不是在文件中用if else規(guī)劃執(zhí)行路徑。
交叉編譯同樣可以理解為條件編譯,通過(guò)構(gòu)建的build標(biāo)簽,選擇需要編譯進(jìn)最終執(zhí)行二進(jìn)制文件的代碼。
這里給一個(gè)簡(jiǎ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ù)并沒(méi)有被執(zhí)行,因?yàn)閙yfunc.go沒(méi)有被編譯。
如果需要將myfunc.go編譯進(jìn)最終的執(zhí)行代碼,則執(zhí)行命令
$ go build -tags speak -o main $ ./main
輸出
$ mike
$ speak: hello
上述條件編譯示例對(duì)你是否有啟發(fā)呢?
舉例:
項(xiàng)目開(kāi)發(fā)中,如果想打印程序中的某些信息以便調(diào)試,而又不想打印相關(guān)代碼生成到最終的可執(zhí)行文件中,那么條件編譯便可派上用場(chǎng)。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Golan中?new()?、?make()?和簡(jiǎn)短聲明符的區(qū)別和使用
Go語(yǔ)言中的new()、make()和簡(jiǎn)短聲明符的區(qū)別和使用,new()用于分配內(nèi)存并返回指針,make()用于初始化切片、映射和通道,并返回初始化后的對(duì)象,簡(jiǎn)短聲明符:=可以簡(jiǎn)化變量聲明和初始化過(guò)程,感興趣的朋友一起看看吧2025-01-01go 異常處理panic和recover的簡(jiǎn)單實(shí)踐
在Go語(yǔ)言中,異常處理主要通過(guò)panic和recover這兩個(gè)內(nèi)建函數(shù)來(lái)實(shí)現(xiàn),本文主要介紹了go異常處理panic和recover的簡(jiǎn)單實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下2025-04-04Golang?HTTP服務(wù)超時(shí)控制實(shí)現(xiàn)原理分析
這篇文章主要介紹了Golang?HTTP服務(wù)超時(shí)控制實(shí)現(xiàn)原理,HTTP服務(wù)的超時(shí)控制是保障服務(wù)高可用性的重要措施之一,由于HTTP服務(wù)可能會(huì)遇到網(wǎng)絡(luò)延遲,資源瓶頸等問(wèn)題,因此需要對(duì)請(qǐng)求進(jìn)行超時(shí)控制,以避免服務(wù)雪崩等問(wèn)題,需要的朋友可以參考下2023-05-05Golang使用channel實(shí)現(xiàn)一個(gè)優(yōu)雅退出功能
最近補(bǔ)?Golang?channel?方面八股的時(shí)候發(fā)現(xiàn)用?channel?實(shí)現(xiàn)一個(gè)優(yōu)雅退出功能好像不是很難,之前寫(xiě)的?HTTP?框架剛好也不支持優(yōu)雅退出功能,于是就參考了?Hertz?優(yōu)雅退出方面的代碼,為我的?PIANO?補(bǔ)足了這個(gè)?feature2023-03-03Go語(yǔ)言學(xué)習(xí)之映射(map)的用法詳解
Map是一種無(wú)序的鍵值對(duì)的集合。這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中映射的用法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定的幫助,需要的可以參考一下2022-04-04go?logger不侵入業(yè)務(wù)代碼使用slog替換zap并實(shí)現(xiàn)callerSkip詳解
這篇文章主要為大家介紹了go?logger不侵入業(yè)務(wù)代碼使用slog替換zap并實(shí)現(xiàn)callerSkip詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09