Carpe Diem

備忘録

protoeasyでprotobufのコンパイルを簡単にする

概要

gRPCなどで使われているprotobufですが、.protoの定義は簡単でもコンパイルにちょっと学習コストがかかります。
gogoprotogrpc-gatewayといった他のライブラリも使うと、中々にカオスなコマンドになります。

grpc-gatewayの例

protoc -I/usr/local/include -I. \
  -I$GOPATH/src \
  -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
  --grpc-gateway_out=logtostderr=true:. \
  path/to/your_service.proto

ref: https://github.com/grpc-ecosystem/grpc-gateway

この辺の手間を解決してくれるのが次のツールです。

github.com

面倒なコマンドをオプションで簡単に使えるようにしてくれます。

環境

インストール

go getで入れます。

$ go get -u go.pedge.io/protoeasy/cmd/protoeasy

依存する他のものもインストールします。
今回はgogoprotogrpc-gatewayを使いたいので以下をインストールしてます。ここは使いたいライブラリに応じて調整してください。

cat <<EOF | xargs go get -u
github.com/golang/protobuf/proto
github.com/golang/protobuf/protoc-gen-go
google.golang.org/grpc
github.com/gogo/protobuf/proto
github.com/gogo/protobuf/protoc-gen-gogofast
github.com/gogo/protobuf/gogoproto
github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
EOF

使い方

基本

以下のfoo.protoファイルがあるとします。

syntax = "proto3";

package foo;

message One {
  int64 i = 1;
}

通常のコンパイル

$ protoeasy --go .

でOKです。

gRPCのコードを生成

grpcのexampleであるhelloworld.protoコンパイルする場合、

syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

というIDLに対して

$ protoeasy --go --grpc .

だけでOKです。

応用

以下の要件で作ります。

  • gogoprotoで高速なmarshalerを使う
  • gRPCを使う
  • grpc-gatewayでproxyも生成する
  • 別の独自protoファイルをimportする

上の要件を満たす、以下のbar.protoファイルがあるとします。

syntax = "proto3";

import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "gogoproto/gogo.proto";
import "foo.proto";

option (gogoproto.marshaler_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.populate_all) = true;
option (gogoproto.equal_all) = true;

package bar;

message Two {
  foo.One one = 1;
  int64 j = 2;
}

service API {
  rpc Do(Two) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      post: "/do"
      body: "*"
    };
  }
}

ディレクトリの構成は以下とします。

.
├── bar
│   └── bar.proto
└── foo.proto

ルートディレクトリ上でコンパイルします。

$ protoeasy --gogo \
  --go-import-path=github.com/jun06t/protoeasy-sample \
  --grpc --grpc-gateway .

それぞれのオプションを解説すると、以下の通りです。

オプション 解説
--gogo gogoprotoを使う
import "gogoproto/gogo.proto";と各種optionの追加を忘れずに
--go-import-path 他の.protoをimportする際に相対パスが使えるように
今回だとimport "foo.proto";があります
--grpc gRPCを使う
--grpc-gateway grpc-gatewayを使う

まとめ

protoeasyを使うと面倒だったコンパイルの書き方が非常にシンプルになったと思います。
マイクロサービス化を進めて.protoファイルが色んなリポジトリに散乱すると、こういったコンパイルの仕方がリポジトリ毎に異なってメンテが大変になるのでこのようなツールの導入をお勧めします。

ソース