背景
いつの間にかprotocでprotoc-gen-goプラグインを使うと以下のようなエラーが出るようになりました。
2021/01/05 06:19:01 WARNING: Missing 'go_package' option in "xxx/xxx.proto", please specify it with the full Go package path as a future release of protoc-gen-go will require this be specified. See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information.
現時点ではwarningですが、メッセージにあるように将来的にrequiredになるようなので早めに対応する方が良さそうです。
ドキュメントでも以下のようにgo_package
にフルパスのimportを指定するように書かれています。
The
.proto
file should contain a go_package option specifying the full import path of the Go package that contains the generated code.
ref: Go Generated Code | Protocol Buffers | Google Developers
環境
- protoc v3.14.0
- protoc-gen-go v1.25.0
- Go v1.15.6
- protoc-gen-go-grpc v1.0.1
go_package
optionはなぜ必要?
まずgo_package
が必要なケースを説明します。
go_package
がないと
背景としては以下のようにあるprotoが別ディレクトリにあるprotoファイルを参照したい時に
. ├── person │ └── person.proto └── team └── team.proto
単純に以下のようにしてimportするようにしてみます。
person.proto
syntax = "proto3"; package person; message Person { string Name = 1; uint32 Age = 2; }
team.proto
syntax = "proto3"; package team; import "person/person.proto"; // ココ message Team { repeated person.Person people = 1; }
team.pb.go
するとprotocでのgoファイル生成はできるものの、生成されたgoファイルのimport pathがpackage名のみで上手く参照できていないことが分かります。
// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.23.0 // protoc v3.14.0 // source: team/team.proto package team import ( proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" person "person" // ココ reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
なのでgo自体のビルドはコケてしまいます。
go_package
があると
ここで以下のようにgo_package
を指定すると、
person.proto
syntax = "proto3"; option go_package = "github.com/jun06t/grpc-sample/go-package-option/after/person"; // ココ package person; message Person { string Name = 1; uint32 Age = 2; }
team.proto
syntax = "proto3"; option go_package = "github.com/jun06t/grpc-sample/go-package-option/after/team"; // ココ package team; import "person/person.proto"; message Team { repeated person.Person people = 1; }
team.pb.go
生成されたgoファイルでもきちんとimport pathが設定されます。
// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.23.0 // protoc v3.14.0 // source: team/team.proto package team import ( proto "github.com/golang/protobuf/proto" person "github.com/jun06t/grpc-sample/go-package-option/after/person" // ココ protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
単純にgo_packageを使用すると起きる問題
ここで1つ問題が起きます。
go_package
を指定するとgo_out
で指定したディレクトリをルートにサブディレクトリが作成されてそこにファイルが置かれてしまうのです。
after/ ├── github.com │ └── jun06t │ └── grpc-sample │ └── go-package-option │ └── after │ ├── person │ │ └── person.pb.go │ └── team │ └── team.pb.go ├── person │ └── person.proto └── team └── team.proto
これは結構前から問題視されていて、以下のIssueで議論されていました。
対処法
では上記の対応方法について説明します。
GOPATHを使っている場合
初期から挙がっていたwork aroundです。
go_out
で指定する出力先を$GOPATH/src
にするやり方です。
protoc \ -I=./after \ --go_out=:${GOPATH}/src \ ./after/team/*.proto \ ./after/person/*.proto
こうすればgoファイルが参照できる位置にファイルが生成されるようになります。
しかし現在goはGo modulesの導入によりGOPATHは要らない世界を推進していますので、やり方としてはよろしくないでしょう。
GOPATHを使っていない場合(Go modulesの場合)
ドキュメントにもありますが、--go_opt=module=$PREFIX
というフラグが追加されているのでこれを使います。
ref: Go Generated Code | Protocol Buffers | Google Developers
$PREFIXに書いた部分のディレクトリが省略されます。
protoc \ -I=./after \ --go_out=:./after \ --go_opt=module=github.com/jun06t/grpc-sample/go-package-option/after \ ./after/team/*.proto \ ./after/person/*.proto
こうすることで以下のように期待する位置にファイルが生成されます。
after/ ├── person │ ├── person.pb.go │ └── person.proto └── team ├── team.pb.go └── team.proto
その他
ディレクトリ名とpackage名が異なる場合
例えばproto
というディレクトリで管理しているけれど、goのpackage名として別で管理したい場合、protoファイルでpackage helloworld
としても
syntax = "proto3"; option go_package = "github.com/my-user/my-repo/proto"; package helloworld; service Greeter { rpc SayHello(HelloRequest) returns (HelloReply) {} }
生成されたファイルのpackageはgo_packageで上書きされてしまいます。
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. package proto // ココ import ( context "context"
これはgo_package
の最後のディレクトリ名をpackage名にする仕様のためです。
対応方法
対処方法として;
でリネームできます。
syntax = "proto3"; option go_package = "github.com/my-user/my-repo/proto;helloworld"; package helloworld; service Greeter { rpc SayHello(HelloRequest) returns (HelloReply) {} }
コンパイルします。
// Code generated by protoc-gen-go. DO NOT EDIT. package helloworld // ココ import ( context "context"
サンプルコード
検証したコードはこちら
まとめ
go_package
optionの必要性と、--go_opt=module=$PREFIX
を用いた利用方法を紹介しました。