Go1-17 新特性:新版構建約束
大家好,我是 polarisxu。
今天給大家介紹 Go1.17 的一個新特性:構建約束 — Build Constraints。
確切來說,這個特性相關的工作在 1.16 時就加入,但處於過度階段,1.17 在各方面都更完善,更完整的支持,是時候瞭解它了。
01 什麼是構建約束
構建約束(build constraint),也叫做構建標記(build tag),是在 Go 源文件最開始的註釋行,比如:
// +build linux
看到這個,相信很多人都不陌生,因爲這是 Go 一開始就有的特性,在 Go 源碼中有很多這樣的註釋行。上面註釋行的意思,這個文件只在 Linux 系統會包含在包中,其他系統會忽略這個文件。
幾個注意點:
-
約束可以出現在任何源文件中,比如
.go
、.s
等; -
必須在文件頂部附近,它的前面只能有空行或其他註釋行;可見包子句也在約束之後;
-
約束可以有多行;
-
爲了區別約束和包文檔,在約束之後必須有空行;
針對某個構建約束,可使用的詞如下:
-
特定操作系統,對應 runtime.GOOS 的可用值,比如 linux、windows 等;
-
特定的架構,對應 runtime.GOARCH 的可用值,比如 386、amd64 等;
-
使用的編譯器,比如 gc、gccgo;
-
支持 cgo 命令時,可以使用 cgo;
-
Go 的主要發佈版本,比如 go1.17、go1.16 等;(測試版本和 fixbug 版本不支持)
-
自定義的 tag,編譯時通過
-tags
傳遞的值; -
可以加入任意值,一般用 ignore 來忽略構建;
此外,文件名可以通過 GOOS 和 GOARCH 來做構建約束。
02 舊版構建約束
從上面看到,構建約束的語法是 // +build
這種形式,如果多個條件組合,通過空格、逗號或多行構建約束表示。比如:
// +build linux,386
你知道什麼意思嗎?表示在 linux AND 386。逗號表示 AND,空格表示 OR。那看一個複雜的:
// +build linux,386 darwin,!cgo
是不是有點懵?我也有點懵!它表示的意思是:(linux AND 386) OR (darwin AND (NOT cgo)) 。
有些時候,多個約束分成多行書寫,會更易讀些:
// +build linux darwin
// +build amd64
這相當於:(linux OR darwin) AND amd64 。
是不是很複雜,很難記憶?
正因爲太複雜,很容易出錯。而且,Go 中有不少註釋是有特殊意義的,也爲了一致性考慮,因此有了新版的構建約束。
03 新版構建約束
在 Go 源碼中,經常會見到類似下面開頭的註釋:
//go:link
新版的構建約束,也使用了 //go:
開頭:
//go:build
注意 //
和 go 之間不能有空格。
同時新版語法使用布爾表達式,而不是逗號、空格等。布爾表達式,會更清晰易懂,出錯可能性大大降低。
比如舊語法:
// +build linux,386
對應的新語法:
//go:build linux && 386
構建標記的基礎語法與其當前形式沒有變化,但是構建標記的組合現在是用 Go 的 || 、 && 和 ! 運算符和括號。(請注意,構建標記並不總是有效的 Go 表達式,即使它們共享操作符,因爲標記並不總是有效的標識符。例如:”go1.1"。)
新語法可以使用 Go spec 的 EBNF 標記來表示:
BuildLine = "//go:build" Expr
Expr = OrExpr
OrExpr = AndExpr { "||" AndExpr }
AndExpr = UnaryExpr { "&&" UnaryExpr }
UnaryExpr = "!" UnaryExpr | "(" Expr ")" | tag
tag = tag_letter { tag_letter }
tag_letter = unicode_letter | unicode_digit | "_" | "."
採用新語法後,一個文件只能有一行構建語句,而不是像舊版那樣有多行。這樣可以避免多行的關係到底是什麼的問題。
Go1.17 中,gofmt 工具會自動根據舊版語法生成對應的新版語法,爲了兼容性,兩者都會保留。比如原來是這樣的:
// +build !windows,!plan9
執行 Go1.17 的 gofmt 後,變成了這樣:
//go:build !windows && !plan9
// +build !windows,!plan9
如果文件中已經有了這兩種約束形式,gofmt 會根據 //go:buid
自動覆蓋 // +build
的形式,確保兩者表示的意思一致。如果只有新版語法,不會自動生成舊版的,這時,你需要注意,它不兼容舊版本了。
另外,Vet 工具現在能夠檢測出兩種語法的不一致。所以,建議大家在編輯器中保存文件時自動執行 gofmt。
早在 Go1.16 時就新增了一個包:go/build/constraint,專門處理新版構建約束。
關於新版約束的設計文檔請移步:https://go.googlesource.com/proposal/+/master/design/draft-gobuild.md。
04 總結
新版本的構建約束可讀性更強,更容易書寫,不容易出錯。有興趣的可以自己針對構建約束,同時書寫兩種形式,體會下新版的好處。
最後提醒一點,新版約束中,一定要注意 //
和 go 之間不能有空格!
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Bt1gex5492TmsNf9rbvkLg