※Makefileに一部間違いがあったため、修正しました
bazelはビルドシステムの一つで、Googleが開発し社内でも使用している。makeなどの従来のビルドシステムと比べると、簡潔な設定で幅広い言語・ビルド対象に対応していて、ビルドが必要なものはほとんどbazelで解決できる。
Bazel - a fast, scalable, multi-language and extensible build system" - Bazel
例えば、Goのビルドと同時にdockerイメージもビルドしたい、protoもビルドしたいといった場合、通常であれば個別にビルドコマンドを打つ必要があるのだが、この作業をbazelに一本化でき、とても便利。
必要な物
以下のものを使用する:
1. bazel
2. rules_go
3. gazelle
4. rules_docker
rules_goはGo言語向けbazelルール。go_libraries、go_test、go_binaryなどを提供し、Go言語に関するビルド、テスト処理を記述できるようになる。
gazelleはrules_goを用いたビルド設定を自動生成するツール。importなどを見て、自動で依存を解決し、ビルド設定を吐き出してくれる。
rules_dockerは、bazelを用いてdockerイメージをビルドしたり、プッシュすることができるルール。今回はgoのバイナリを含めたイメージをビルドし、実行する。
オススメの構成
以下にサンプルのレポジトリを作成した。
GitHub - mjhd-devlion/bazel-gazelle-sample
ディレクトリ構造としては以下のようになっており、cmd以下のsampleが、今回動かす対象のプログラム。pkg/sampleパッケージを読み込み、Hello, Worldを出力する。depによるvendoringも行いたかったため、いつもよりキレイめにHello, Worldを表示している。
./
├── BUILD.bazel
├── Gopkg.lock
├── Gopkg.toml
├── Makefile
├── WORKSPACE
├── cmd
│ └── sample
│ ├── BUILD.bazel
│ └── main.go
├── pkg
│ └── sample
│ ├── BUILD.bazel
│ ├── sample.go
│ └── sample_test.go
└── vendor
また、以下のようなMakefileを用意している。make gazelleコマンドを実行することでプロジェクト全体のBUILD.bazelファイルを更新し、その後make runすることによってプログラムを実行することができる。
.PHONY: run
run:
bazel run //cmd/sample:go_image
.PHONY: build
build:
bazel build //cmd/sample:go_image
.PHONY: test
test:
bazel test //...
.PHONY: gazelle
gazelle:
bazel run //:gazelle
bazel run //:gazelle -- update-repos -from_file ./Gopkg.lock
ここで一つ注意点があり、Makefileのgazelleの項に、update-reposという記述をしている。
gazelleとvendoringは相性が悪く、特にprotoをビルドする際など、バージョンの違いによる問題が発生したりする。
ここ(go_proto_library dependency overhaul · Issue #1548 · bazelbuild/rules_go · GitHub)に詳しく載っているのだが、protoをビルドする際にrules_goが提供する依存のバージョンがvendoringしているものと衝突した場合、ビルド時、または実行時のエラーが発生する。
これを解決するためには、WORKSPACEにgo_repositoryルールを用いて依存するライブラリのバージョン指定をする必要があるのだが、手作業でやるのは流石に辛い。
ということで、実はgazelleはupdate-reposという「Gopkg.lockとgo_repositoryを同期する」プログラムを用意してくれている。(つい先日知った)
サンプルでは、このプログラムを用いてdepの解決した依存に、bazelの依存を同期し、安全にビルドが行えるよう、update-reposをgazelle実行時に呼ぶような設定を行っている。
微妙な点
実はvendorディレクトリを削除しても動く。依存はbazelがビルド時にダウンロードし、その後も管理をしてくれるため、vendorディレクトリの中身はビルド時に使用されていない。二重管理になってしまうため、vendorディレクトリを削除したいのだが、vendorディレクトリが無いとエディタが真っ赤になる…。
何かもっと良い方法をご存じの方、是非コメントいただけると嬉しいです。