Edited at

GoのBuild Constraintsに関するメモ

More than 3 years have passed since last update.

GoのBuild Constraintsを使おうとしてハマったのでメモ。


Build Constraintsの後には空行が必須

Goでは、同一ディレクトリ以下のソースコードは全てビルド対象になります。これに対し「Build Constraints」というコメント的なものを使うことで、環境ごとにビルド対象に含めたり外したりといったことができます。

参照:Package build - Build Constraints

これによれば、コード先頭に次のように書けば、そのソースコードはビルド対象から外れそうに読めます。

// +build ignore

しかし、次のように書いたソースコードは何事もなかったかのようにリンクされます。

// +build ignore

package main // ダメ

実は、Build Constraintsの後には必ず空行を入れる必要があるのです。

// +build ignore

package main // バッチリ

これはよく見るとドキュメントにも書いてあります。


To distinguish build constraints from package documentation, a series of build constraints must be followed by a blank line.



Build Constraintsの詳細

以下のものがBuild Constraintsとして記述できます。(build/build.goのコメントから引用)


  • OS名($GOOSの値、linux, windows, darwinなど)

  • アーキテクチャ名($GOARCHの値、arm, 386, amd64など)


  • cgo (cgoが有効なとき)


  • !cgo (cgoが無効なとき)

  • コンパイラ名(go, gccgo

  • !コンパイラ名

  • tag (ctxt.BuildTagsctxt.ReleaseTags に含まれるタグ)

  • !tag (ctxt.BuildTagsctxt.ReleaseTags に含まれないタグ)

  • 上記のカンマ区切りリスト(AND条件)

ただし、OS名にlinuxを指定した場合は例外的にAndroidも含まれます。Androidを除くLinuxを指定したい場合はlinux,!androidとする必要があります。

ReleaseTagsはGo 1.4だと次のような値になっています。これを使えばGoの特定バージョン以降・以前という表現ができます。

        c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4"}


ファイル名によるBuild Constraints

ところで、このBuild Constraintsの一部はファイル名で表現することもできます。OS名とアーキテクチャ名に限り、次のようなファイル名で記述可能です。


  • foo_[OS名].go

  • foo_[アーキテクチャ名].go

  • foo_[OS名]_[アーキテクチャ名].go

これにマッチしないファイルはビルド対象になりません。これを逆手にとって、foo.goをビルド対象から外したいときに_foo.goにリネームするというテクニックが使えます。もちろん、foo_ignore.goなどとしてもよいでしょう。

このように単純なBuild Constraintsについてはファイル名で指定するのが良いように思います。同じことをするにしても、コメントによるBuild Constraintsの場合は全ファイルの先頭を眺めないと挙動がわからないため、保守性が下がるのではないでしょうか。ファイル名で実現できる場合はファイル名で実現し、複雑なBuild Constraintsに限りコメントで実現すべき、というのが筆者の個人的な意見になります。


文句

空行必須とか何なの。初見殺しにもほどがあるでしょ…。

コメントやファイル名でビルドの挙動を変えること自体、あまり良い選択だとは思えない。不親切というかやっつけというか。文句言いつつ慣れたころに仕様変更されそうで怖い。