bazelでWORKSPACEの外にあるprotoファイルをインクルードしてgrpc c++プロジェクトをビルドする

# 目的

既存のgrpcプロジェクトで使用されているprotoファイルを、bazelをつかったC++プロジェクトにインクルードしたい。ただし、protoファイルは別リポジトリ（つまりbazel WORKSPACEの外）で管理されているので、そこにBUILDやWORKSPACEファイルを設置したくないという制約がある。

# 解決方法

WORKSPACEにnew_local_repositoryを追加する。



# 手順

## 1. WORKSPACEにnew_local_repositoryを追加する

WORKSPACEに以下を追記する

```
new_local_repository(
    name = "proto",
    path = "path/to/proto/dir",
    build_file = "path/to/proto.BUILD",
)
```

nameは`proto`としたが何でもよい。この名前のディレクトリにC++のヘッダーファイルが生成される。

`path/to/proto/dir`がprotoファイルの場所であり、マニュアルによると絶対パスでなければならないと書いてあるが、bazel 4.0.0ではWORKSPACEからの相対パスでも動作した。

## 2. new_local_repository用のBUILDファイルを作成する

WORKSPACE配下の任意の場所（上記の例では path/to/proto.BUILD）BUILDファイルをつくる。
内容は通常のproto/grpcビルド用の内容でよい。
以下の内容はgrpc公式のgreeterサンプルを使う場合のもの。

```
package(default_visibility = ["//visibility:public"])

load("@rules_proto//proto:defs.bzl", "proto_library")
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_proto_library")
load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library")

proto_library(
    name = "helloworld_proto",
    srcs = ["helloworld.proto"],
)

cc_proto_library(
    name = "helloworld_cc_proto",
    deps = [":helloworld_proto"],
)

cc_grpc_library(
    name = "helloworld_cc_grpc",
    srcs = [":helloworld_proto"],
    grpc_only = True,
    deps = [":helloworld_cc_proto"],
)
```

## 3. protoをインクルードしたい側のBUILDファイルの依存に追加

```
cc_binary(
    name = "greeter_client",
    srcs = ["greeter_client.cc"],
    defines = ["BAZEL_BUILD"],
    deps = [
        "@proto//:helloworld_cc_grpc",
        "@com_github_grpc_grpc//:grpc++",
    ],
)
```

new_local_repositoryの`name`で指定した名前で `@proto` のように参照できる。

## 4. C++ソースコードでインクルードする

```
#include "external/proto/helloworld.grpc.pb.h"
```

前述のように、`name`のディレクトリにprotoの生成物ができる。externalディレクトリの配下になることに注意。

# トラブルシューティング

ビルドが失敗するようなら、各ビルドステップ毎にビルドしてみるとよい

```
$ bazel build @proto//:helloworld_proto
INFO: Analyzed target @proto//:helloworld_proto (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target @proto//:helloworld_proto up-to-date:
  bazel-bin/external/proto/helloworld_proto-descriptor-set.proto.bin
INFO: Elapsed time: 0.138s, Critical Path: 0.01s
INFO: 2 processes: 1 internal, 1 processwrapper-sandbox.
INFO: Build completed successfully, 2 total actions

$ bazel build @proto//:helloworld_cc_proto
INFO: Analyzed target @proto//:helloworld_cc_proto (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target @proto//:helloworld_cc_proto up-to-date:
  bazel-bin/external/proto/helloworld.pb.h
  bazel-bin/external/proto/helloworld.pb.cc
  bazel-bin/external/proto/libhelloworld_proto.a
  bazel-bin/external/proto/libhelloworld_proto.so
INFO: Elapsed time: 1.148s, Critical Path: 0.02s
INFO: 5 processes: 3 internal, 2 processwrapper-sandbox.
INFO: Build completed successfully, 5 total actions

$ bazel build @proto//:helloworld_cc_grpc
INFO: Analyzed target @proto//:helloworld_cc_grpc (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target @proto//:helloworld_cc_grpc up-to-date:
  bazel-bin/external/proto/libhelloworld_cc_grpc.a
  bazel-bin/external/proto/libhelloworld_cc_grpc.so
INFO: Elapsed time: 0.652s, Critical Path: 0.03s
INFO: 5 processes: 3 internal, 2 processwrapper-sandbox.
INFO: Build completed successfully, 5 total actions
```
# 備考

* protoをおいてある場所にBUILDやWORKSPACEをおいてよいなら、`new_local_repository`ではなく`local_repository`をつかうとよい
* protobufで提供されている定義google/protobuf/any.proto 等を使いたい場合は、depsに `"@com_google_protobuf//:any_proto"` などを追加すればよい。https://github.com/cgrushko/proto_library/blob/beae7b78b85b3af51d3ea54663c421ebde97dc10/src/BUILD#L42


# refs

* https://docs.bazel.build/versions/master/be/workspace.html#new_local_repository.path
* https://docs.bazel.build/versions/master/be/protocol-buffer.html#proto_library
* https://blog.bazel.build/2017/02/27/protocol-buffers.html
* https://pencilflip.medium.com/getting-started-with-grpc-and-bazel-24725fd9e5c2