1# gRPC Client & Server 2 3This example shows how to build a gRPC server and client in Rust with Bazel. 4There is a Cargo Workspace configuration and a Bazelmod configuration. Furthermore, 5all binary targets apply optimization from the [compiler optimization example](../03-comp-opt). 6 7To run the example with Cargo, open one terminal and start the server with: 8 9` 10cargo run --bin grpc_server 11` 12 13And, in a second terminal, to run the client: 14 15` 16cargo run --bin grpc_client 17` 18 19The equivalent Bazel targets are: 20 21Server: 22 23`bazel run //grpc_server:bin` 24 25Client: 26 27`bazel run //grpc_client:bin` 28 29## Setup 30 31The Prost and Tonic rules do not specify a default toolchain in order to avoid mismatched dependency issues. 32While the Tonic toolchain works out of the box when its dependencies are matched, however, 33Prost requires a custom toolchain that you have to define. 34 35The setup requires three steps to complete: 361. Configure rules and dependencies in MODULE.bazel 372. Configure a custom Prost toolchain 383. Register custom Prost toolchain. 39 40To keep the build hermetic, we use the LLVM Clang compiler to compile all C/C++ dependencies. 41 42### 1) Configure rules and dependencies 43 44In your MODULE.bazel, you add the following: 45 46```starlark 47# rules for proto 48############################################################################### 49# https://github.com/bazelbuild/rules_proto/releases 50bazel_dep(name = "rules_proto", version = "6.0.2") 51# https://github.com/aspect-build/toolchains_protoc/releases 52bazel_dep(name = "toolchains_protoc", version = "0.3.1") 53# https://registry.bazel.build/modules/protobuf 54bazel_dep(name = "protobuf", version = "27.1") 55# rules for LLVM 56# https://github.com/bazel-contrib/toolchains_llvm 57bazel_dep(name = "toolchains_llvm", version = "1.0.0") 58 59# 1 Register LLVM 60############################################################################### 61# L L V M 62# https://github.com/bazel-contrib/toolchains_llvm/blob/master/tests/MODULE.bazel 63############################################################################### 64llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm") 65LLVM_VERSIONS = { "": "16.0.0",} 66 67# LLVM toolchain. 68llvm.toolchain( 69 name = "llvm_toolchain", 70 llvm_versions = LLVM_VERSIONS, 71) 72use_repo(llvm, "llvm_toolchain", "llvm_toolchain_llvm") 73register_toolchains("@llvm_toolchain//:all") 74 75# 2 Register Proto toolchain 76############################################################################### 77# Proto toolchain 78register_toolchains("@rules_rust//proto/protobuf:default-proto-toolchain") 79 80# Custom Prost toolchain will be added later. See next section 81 82# 3 Register proto / prost / tonic crates 83############################################################################### 84crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate") 85 86# protobufs / gRPC 87crate.spec( 88 package = "prost", 89 version = "0.12", 90) 91crate.spec( 92 default_features = False, 93 package = "prost-types", 94 version = "0.12", 95) 96crate.spec( 97 features = ["transport"], 98 package = "tonic", 99 version = "0.11", 100) 101crate.spec( 102 package = "tonic-build", 103 version = "0.11", 104) 105crate.spec( 106 package = "protoc-gen-prost", 107 version = "0.3.1", 108) 109crate.annotation( 110 crate = "protoc-gen-prost", 111 gen_binaries = ["protoc-gen-prost"], 112) 113crate.spec( 114 package = "protoc-gen-tonic", 115 version = "0.4.0", 116) 117crate.annotation( 118 crate = "protoc-gen-tonic", 119 gen_binaries = ["protoc-gen-tonic"], 120) 121 122# Other Rust dependencies ... 123 124crate.from_specs() 125use_repo(crate, "crates") 126``` 127 128### 2) Configure a custom Prost toolchain 129 130Configuring a custom Prost toolchain is straightforward, you create a new folder with an empty BUILD.bazl file, and add 131the toolchain definition. 132As your Bazel setup grows over time, it is a best practice to put all custom macros, rules, and toolchains in a 133dedicated folder, for example: `build/`. 134 135Suppose you have your BUILD.bazl file in `build/prost_toolchain/BUILD.bazel`, then add the following content: 136 137```starlark 138load("@rules_rust//proto/prost:defs.bzl", "rust_prost_toolchain") 139load("@rules_rust//rust:defs.bzl", "rust_library_group") 140 141rust_library_group( 142 name = "prost_runtime", 143 deps = [ 144 "@crates//:prost", 145 ], 146) 147 148rust_library_group( 149 name = "tonic_runtime", 150 deps = [ 151 ":prost_runtime", 152 "@crates//:tonic", 153 ], 154) 155 156rust_prost_toolchain( 157 name = "prost_toolchain_impl", 158 prost_plugin = "@crates//:protoc-gen-prost__protoc-gen-prost", 159 prost_runtime = ":prost_runtime", 160 prost_types = "@crates//:prost-types", 161 proto_compiler = "@protobuf//:protoc", 162 tonic_plugin = "@crates//:protoc-gen-tonic__protoc-gen-tonic", 163 tonic_runtime = ":tonic_runtime", 164) 165 166toolchain( 167 name = "prost_toolchain", 168 toolchain = "prost_toolchain_impl", 169 toolchain_type = "@rules_rust//proto/prost:toolchain_type", 170) 171``` 172 173The Prost and Tonic dependencies are pulled from the previously configured 174crate dependencies in the MODULE file. With this custom toolchain in place, the last step is to register it. 175 176### 3. Register custom Prost toolchain. 177 178In your MODULE.bazel file, locate your toolchains and add the following entry right below the proto toolchain. 179 180```starlark 181# 2 Register Proto toolchain 182############################################################################### 183# Proto toolchain 184register_toolchains("@rules_rust//proto/protobuf:default-proto-toolchain") 185 186# Custom Prost toolchain 187register_toolchains("@//build/prost_toolchain") 188``` 189 190Pay attention to the path, `build/prost_toolchain` because if your toolchain 191is in a different folder, you have to update this path to make the build work. 192 193## Usage 194 195Once the setup has been completed, you use the proto & prost targets as you normally do. For example, to configure rust 196bindings for a proto file, just add the target: 197 198```starlark 199load("@rules_proto//proto:defs.bzl", "proto_library") 200load("@rules_rust//proto/prost:defs.bzl", "rust_prost_library") 201 202# Build proto files 203# https://bazelbuild.github.io/rules_rust/rust_proto.html#rust_proto_library 204proto_library( 205 name = "proto_bindings", 206 srcs = [ 207 "proto/helloworld.proto", 208 ], 209) 210 211# Generate Rust bindings from the generated proto files 212# https://bazelbuild.github.io/rules_rust/rust_proto.html#rust_prost_library 213rust_prost_library( 214 name = "rust_proto", 215 proto = ":proto_bindings", 216 visibility = ["//visibility:public"], 217) 218``` 219 220From there, you 221just [follow the target documentation](https://bazelbuild.github.io/rules_rust/rust_proto.html#rust_proto_library). 222