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.modmain.gomyfunc.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)文章
golang validator參數(shù)校驗(yàn)的實(shí)現(xiàn)
這篇文章主要介紹了golang validator參數(shù)校驗(yàn)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
Goland使用Go Modules創(chuàng)建/管理項(xiàng)目的操作
這篇文章主要介紹了Goland使用Go Modules創(chuàng)建/管理項(xiàng)目的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
Go語(yǔ)言如何使用golang-jwt/jwt/v4進(jìn)行JWT鑒權(quán)詳解
最近項(xiàng)目中需要用到鑒權(quán)機(jī)制,golang中jwt可以用,這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言如何使用golang-jwt/jwt/v4進(jìn)行JWT鑒權(quán)的相關(guān)資料,需要的朋友可以參考下2022-09-09
golang中的三個(gè)點(diǎn) ''...''的用法示例詳解
這篇文章主要介紹了golang中的三個(gè)點(diǎn) '...' 的用法示例詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
Go語(yǔ)言中的復(fù)合類(lèi)型詳細(xì)介紹
這篇文章主要介紹了Go語(yǔ)言中的復(fù)合類(lèi)型詳細(xì)介紹,復(fù)合類(lèi)型包括:結(jié)構(gòu)體、數(shù)組、切片、Maps,需要的朋友可以參考下2014-10-10
Go 語(yǔ)言 JSON 標(biāo)準(zhǔn)庫(kù)的使用
今天通過(guò)本文給大家介紹Go 語(yǔ)言 JSON 標(biāo)準(zhǔn)庫(kù)的使用小結(jié),包括序列化和反序列化的相關(guān)知識(shí),感興趣的朋友跟隨小編一起看看吧2021-10-10

