Go1-17 新特性:新版構建約束

大家好,我是 polarisxu。

今天給大家介紹 Go1.17 的一個新特性:構建約束 — Build Constraints。

確切來說,這個特性相關的工作在 1.16 時就加入,但處於過度階段,1.17 在各方面都更完善,更完整的支持,是時候瞭解它了。

01 什麼是構建約束

構建約束(build constraint),也叫做構建標記(build tag),是在 Go 源文件最開始的註釋行,比如:

// +build linux

看到這個,相信很多人都不陌生,因爲這是 Go 一開始就有的特性,在 Go 源碼中有很多這樣的註釋行。上面註釋行的意思,這個文件只在 Linux 系統會包含在包中,其他系統會忽略這個文件。

幾個注意點:

針對某個構建約束,可使用的詞如下:

此外,文件名可以通過 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