Golangのビルドタグについて
Golangのビルドタグというものを知ったのでその話。
ビルドタグとは
Golangはクロスプラットフォームに対応しているので、基本的には環境に依存しないプログラムを書くべきですが、ときにはどうしても環境依存の処理を書きたかったり、開発環境と本番環境で別のスタブを差し込みたかったり、とにかくターゲット環境に合わせてどのソースコードを使ってビルドを指定したくなるときがあります。
そういうときは
$ go build a.go b.go c.go
みたいな感じでビルドに含めたいファイルだけを列挙すればできますが、これは明らかに煩雑なので使いたくないです。
このような問題はGolangではビルドタグを使うことで解決することができます。
ビルド制約と呼ばれることもあるらしい(厳密にはビルド制約をビルドタグを使って記述している)
使い方
ファイルの先頭に次のようなコメントを書くとビルドタグになります。
// +build
buildに続いて記述した内容がビルドタグとして認識され、ソースの扱いを定めることができます。
コンマ区切りでAND、空白区切りでOR、!でNOT、改行して別のビルドタグを記述した場合もORになります。
例えば、
// +build linux,386 darwin,!cgo // +build develop
をファイルの先頭に記述した場合のビルド制約は
( ( linux AND 386 ) OR ( darwin AND ( NOT cgo ) ) ) AND develop
となります。
OS,CPUアーキテクチャごとに指定する
GOOSとGOARCHをビルドタグに指定することで、特定のOSやCPUアーキテクチャに向けて指定することができます。
指定できるGOOSとGOARCHについては公式のドキュメントを参照してください。
急にGOOSとGOARCHをビルドタグに指定とか言われても分かりづらいと思いますが、とりあえず使ってみるとなんとなく分かると思います。
$ ls Print1.go Print2.go main.go $ cat main.go package main import ( "fmt" ) func main() { fmt.Println(str) } $ cat Print1.go // +build 386 package main var str = "this is 386 stub" $ cat Print2.go // +build amd64 package main var str = "this is amd64 stub" $ GOARCH=amd64 go build -o Print_amd64 $ GOARCH=386 go build -o Print_386 $ ./Print_amd64 this is amd64 stub $ ./Print_386 this is 386 stub
GOARCHがamd64のとき、ビルドタグにamd64を指定したPrint1.goが、GOARCHが386のとき、ビルドタグに386を指定したPrint2.goがビルドに使用されていることが分かります。もしビルドタグを使用しなかった場合は変数strの二重宣言でコンパイルエラーになりますが、このようにビルド制約を課すことで特定のファイルをビルドに使用できることができます。
またこの場合は、ビルドタグを使わなくてもファイル名にGOOS、GOARCHのサフィックスをつけることでも同様のことが実現できます。
具体的には
- *_$GOOS.go
- *_$GOOS_test.go
- *_$GOARCH.go
- *_$GOARCH_test.go
- *_$GOOS_$GOARCH.go
- *_$GOOS_$GOARCH_test.go
の6種類の指定方法があります。$GOOS、$GOARCHにはターゲットの環境に合わせてよしなに置換してください。
$ ls Print_386.go Print_amd64.go main.go $ cat Print_386.go package main var str = "this is 386 stub" $ cat Print_amd64.go package main var str = "this is amd64 stub" $ GOARCH=amd64 go build -o Print_amd64 $ GOARCH=386 go build -o Print_386 $ ./Print_amd64 this is amd64 stub $ ./Print_386 this is 386 stub
これはファイルの命名だけで制御できるので便利な反面、意図せずこの機能を使ってしまうと、かなり気づきにくい気がするので気をつけないとなぁ(実際にそんなサフィックスのついたファイル命名をするかどうかは置いておいて)
tagsオプションによる指定
GOOSやGOARCHのような予約語としてのビルドタグだけでなく、ユーザ定義のタグを使用することができます。
ユーザ定義のタグはビルド時にtagsオプションで指定することで有効にすることができます。
$ ls develop.go main.go release.go $ cat develop.go // +build develop package main var str = "this is develop stub" $ cat release.go // +build release package main var str = "this is release stub" $ go build -tags=release -o release $ go build -tags=develop -o develop $ ./release this is release stub $ ./develop this is develop stub
開発環境と本番環境で差し替えたいときとかは便利ですね。
飛び道具的な使い方
tagsオプションによる指定を使った場合、ビルド時にオプションで追加したタグのみが有効となりますが、これは逆に言うとオプションで追加しない限り必ず無効になるということです。
このユーザ定義タグの性質を利用すると、「このファイルは常にビルドに使わないことを明示する」という使い方もできます。コンパイルエラーを大量に吐くファイルをとりあえず除外したいときなんかは便利かもしれません。
このような使い方をするときは慣習的にignoreをビルドタグに指定することが多いようです。
またファイル名をアンダースコアから始めても同様のことが実現できます。
まとめ
真面目に開発するときはビルドタグのお世話になる機会は多そうですね。特にDevelopとReleaseを切り替えるやつ。Makefileとの相性も良さげなので、積極的に使っていきたいと思います。
あとGolangはテスト然り、ファイル名についてはけっこう制約を課してくるタイプの言語であることがだんだん分かってきたので、ファイル名の付け方には割と注意しておく必要がありそうですね。