Go語言有一個不(奇)錯(葩)的設計,就是build constraints(構建約束)。可以在源碼中通過注釋的方式指定編譯選項,比如只允許在linux下,或者在386的平台上編譯啊之類的;還可以通過文件名來約束構造,比如xxxx_linux.go,就是只允許在linux下編譯,xxx_windows_amd64.go就是只允許在windows x64下編譯。
構建約束可以在很多文件中使用,不單單是GO文件。但是必須要注意的是,通過注釋實施構建約束的話,比如要放在文件的開頭,要優先於空行或和其他注釋之前。也就是說必須在package語句的前面寫。這就有個很蛋疼的文件,因為GO的godoc是可以提取代碼中的注釋然后轉換為文檔的。在package語句之前寫的注釋會被認為是包級別的注釋。而構建約束又在所有注釋之前,那么為了區分包級別的注釋,就要在構建約束與包級別的注釋之間添加空行進行區分。(這個設計看上去不得不承認很囧)。
通過注釋實施的構建約束還可以進行邏輯表達。就是and, or之類的語義。GO的官方是這么定義的:如果構建約束中有空格,那么就是OR關系,如果是逗號分隔,那么就是AND關系。!表示not。比如
// +build linux,386 darwin,!cgo
就是表示(linux AND 386) OR (darwin AND (NOT cgo))
而且GO還支持多行的構建約束,多行之間是AND關系,比如
// +build linux darwin
// +build 386
就是表示(linux OR darwin) AND 386
GO官方還定義了常用的一些約束
- 限制目標操作系統,也就是要和runtime.GOOS一致
- 限制目標架構平台,也就是要和runtime.GOARCH一致
- GC或者GCCGO等編譯器支持的約束
- cgo約束,也就是說如果支持cgo的話,就可以參與編譯
- go1.1,表示從go1.1開始向前兼容
- go1.2,表示從1.2開始向前兼容
- go.13,表示從1.3開始向前兼容
- 自定義的約束
如果你想臨時讓某個文件不參與編譯,可以添加注釋約束下: // +build ignore
通過注釋來實現構建約束有點蛋疼,而且GO官方定義里還表示可以自定義約束,那么可以用來干嘛?學GO的人都知道GO內建了單元測試的框架,跑跑一般的單元測試還是非常嗨皮的,但是如果要做一些簡單的集成測試就令人拙計了,因為go test默認就是跑最基本的單元測試。那么怎么只執行集成測試的代碼呢?其實就可以通過構建約束來實施。比如我們在集成測試的GO文件中加上 // +build integration 然后運行命令 go test –tags=”integration”就可以只運行我們的集成測試代碼了。
雖然通過注釋的方式對構建進行了約束,但是文件名的構建約束反而讓人覺着很不錯呢,至少在工程里看着一目了然。文件名前綴只要含有_GOOS, _GOARCH, _GOOS_GOARCH的就可以了。比如xxx_linux.go yyy_windows_amd64.go , zzzz_386.s等等。