BazelでGoプロジェクトのビルド。Gazelleのgo_repositoryで外部ライブラリの依存とBazelのgo_testでテスト。
BazelをつかったGoプロジェクトのビルドをまとめている。前回のエントリではバイナリのビルドとDockerイメージのビルドをまとめた。
今回は外部ライブラリをGoプロジェクトに依存させる方法とテストの方法をまとめていく。
Gazelleのgo_repositoryで外部のライブラリを依存させる
外部ライブラリの依存はGazelleのgo_repositoryをつかう。
bazel-gazelle/repository.rst at master · bazelbuild/bazel-gazelle · GitHub
依存させるライブラリはDIツールのwireを選んだ。
WORKSPACE
WORKSPACEにgo_repositoryを有効にしてruleを追加する。
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") gazelle_dependencies() go_repository( name = "com_github_google_cloud", commit = "2152f209f3c907645f7ebdcacdb2c18cd89e6fa8", importpath = "github.com/google/go-cloud", )
name
はWORKSPACE内の固有をつける。commit
はgoogle/go-cloud 0.5.0 バージョンのハッシュ値を設定する。import_path
はGoコードでwireを利用するときにimport文のパスをセットする。
wireをつかってDIするコードを用意する。
簡易的なstruceのインスタンスをDIするコードを用意する。
# pkg/public_go/usecase/greet_usecase.go package usecase import ( "github.com/google/go-cloud/wire" ) var GreetUsecaseSet = wire.NewSet(ProvideUseCase) type GreetUsecase struct { Msg string } func ProvideUseCase(msg string) GreetUsecase { return GreetUsecase{ Msg: msg, } }
上記のコードを追加してgazelleコマンドを実行する。
(bazel-multiprojects) $ bazel run gazelle
コマンドを実行するとGazelleはWORKSPACEに追加したgo_repositoryの依存とgreet_usecase.goのwireの利用を理解して次のようなBUILD.bazelファイルを生成してくれる。
# pkg/public_go/usecase/BUILD.bazel load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", srcs = ["greet_usecase.go"], importpath = "github.com/soushin/bazel-multiprojects/pkg/public_go/usecase", visibility = ["//visibility:public"], deps = ["@com_github_google_cloud//wire:go_default_library"], )
greet_usecase.goをソースにしたライブラリのruleが定義されている。depsにはWORKSPACEで定義したcom_github_google_cloud
のレポジトリが参照されている。
Gazelleの魅力
Bazel + Gazelleの魅力はこのようにWORKSPACEで定義した依存とコードの中間の役割のBUILDファイルを自動で生成してくれるところだ。
最終的にはmainパッケージからusecaseパッケージを参照することになる。wireで生成したwire_gen.goコードは次のようになる。
# pkg/public_go/wire_gen.go // Code generated by Wire. DO NOT EDIT. //go:generate wire //+build !wireinject package main import ( "context" "github.com/soushin/bazel-multiprojects/pkg/public_go/usecase" ) // Injectors from injector.go: func initializeGreetUsecase(ctx context.Context, greet string) (usecase.GreetUsecase, error) { greetUsecase := usecase.ProvideUseCase(greet) return greetUsecase, nil }
mainパッケージにusecaseが依存した状態で再度、gazelleコマンドを実行するとmainパッケージのBUILD.bazelは次のような差分になる。
diff --git a/pkg/public_go/BUILD.bazel b/pkg/public_go/BUILD.bazel index 06f7a04..b50739a 100644 --- a/pkg/public_go/BUILD.bazel +++ b/pkg/public_go/BUILD.bazel @@ -2,10 +2,16 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( name = "go_default_library", - srcs = ["main.go"], + srcs = [ + "main.go", + "wire_gen.go", + ], importpath = "github.com/soushin/bazel-multiprojects/pkg/public_go", visibility = ["//visibility:private"], - deps = ["//pkg/common_go/util:go_default_library"], + deps = [ + "//pkg/common_go/util:go_default_library", + "//pkg/public_go/usecase:go_default_library", + ], ) go_binary(
depsに//pkg/public_go/usecase:go_default_library
が追加されている。usecaseパッケージに定義しているgo_library
を参照している。
Gazelleは各パッケージの依存とWORKSPACEの外部依存を理解してBUILDファイルを生成するだけでなくパッケージ間の依存も理解をしてくれる。
Bazelのgo_testでテスト
テストのビルドもBazelで行う。
次のようなstringのテストコードを用意する。
# pkg/common_go/util/string_test.go package util import "testing" func TestAdd(t *testing.T) { actual := Add("test") expected := "test - built by Bazel!" if actual != expected { t.Errorf("invalid text: got %s want %s", actual, expected) } }
このテストコードを配置した状態でgazelleコマンドを実行するとutilパッケージのBUILD.bazelは次のような差分になる。
diff --git a/pkg/common_go/util/BUILD.bazel b/pkg/common_go/util/BUILD.bazel index ce90025..d48f3ca 100644 --- a/pkg/common_go/util/BUILD.bazel +++ b/pkg/common_go/util/BUILD.bazel @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", @@ -6,3 +6,9 @@ go_library( importpath = "github.com/soushin/bazel-multiprojects/pkg/common_go/util", visibility = ["//visibility:public"], ) + +go_test( + name = "go_default_test", + srcs = ["string_test.go"], + embed = [":go_default_library"], +)
Gazelleがパッケージ内のテストコードを解釈してgo_test
のruleを追加してくれる。このgo_testは次のように実行する
(bazel-multiprojects) $ bazel run //pkg/common_go/util:go_default_test INFO: Analysed target //pkg/common_go/util:go_default_test (4 packages loaded). INFO: Found 1 target... Target //pkg/common_go/util:go_default_test up-to-date: bazel-bin/pkg/common_go/util/darwin_amd64_stripped/go_default_test INFO: Elapsed time: 0.244s, Critical Path: 0.01s INFO: 0 processes. INFO: Build completed successfully, 1 total action INFO: Build completed successfully, 1 total action exec ${PAGER:-/usr/bin/less} "$0" || exit 1 Executing tests from //pkg/common_go/util:go_default_test ----------------------------------------------------------------------------- PASS
まとめ
- Gazelleを使ったGoプロジェクトの外部ライブラリ依存とテストの方法をまとめた。
- GoプロジェクトにおいてはGazelleの働きが素晴らしくビルドルールの追加からビルドまでストレスフリーで開発が行える印象。
- KotlinプロジェクトにおいてもGazelleが生成するBUILDファイルに沿ってルールの追加をしていくと捗りそう。