Skip to content

Commit

Permalink
Add rust_tonic_compile rule (typedb#391)
Browse files Browse the repository at this point in the history
## What is the goal of this PR?

We added the `rust_tonic_compile` rule which compiles `.proto` files
into Rust sources that depend on the `tonic` and `prost` crates.

## What are the changes implemented in this PR?

We had an existing Rust binary for compiling proto files into a Tonic
library - a Cargo build script in `typedb-protocol`. But Cargo users
shouldn't have to run `protoc` on their machine to compile
`typedb-client` (which is what happens with a Cargo build script).
Rather, the `typedb-protocol` crate should contain the generated Rust
gRPC + Protobuf sources.

So we've created a rule, `rust_tonic_compile`, that internally runs a
`rust_binary` (similarly to how many of our rules run Kotlin binaries),
which uses `tonic_build` to compile `.proto` files into Rust sources,
and exposes the outputs. Any `rust_library` can then depend on these
generated sources. The API is similar to Java and Python
(`java_grpc_compile` and `python_grpc_compile` respectively), and the
`antlr` rule from `rules_antlr` behaves similarly, too.

Ideally, we wouldn't need our own rule to do this, however, the Bazel
rules repos that contain Rust gRPC + Protobuf rules only support the
`grpc` crate, and not the more popular `tonic` crate. Moreover, all of
them already have open PRs for adding such functionality:

- rules-proto-grpc/rules_proto_grpc#143
- stackb/rules_proto#201
- bazelbuild/rules_rust#915

Given that our implementation is **very minimal** (~30 lines of code) it
should incur a very low maintenance effort.
  • Loading branch information
alexjpwalker authored and James Williams committed Dec 5, 2022
1 parent 8106b66 commit e908d52
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 3 deletions.
8 changes: 5 additions & 3 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ workspace(name = "vaticle_dependencies")
# Load @vaticle_dependencies #
################################

# Load //build/rust
# Load //builder/rust
load("//builder/rust:deps.bzl", rust_deps = "deps")
rust_deps()
load("@rules_rust//rust:repositories.bzl", "rust_repositories")
rust_repositories(version = "nightly", iso_date = "2021-07-01", edition="2018")

load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains")
rules_rust_dependencies()
rust_register_toolchains(edition = "2021", include_rustc_srcs = True)

# Load //builder/python
load("//builder/python:deps.bzl", python_deps = "deps")
Expand Down
33 changes: 33 additions & 0 deletions builder/grpc/rust/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#
# Copyright (C) 2022 Vaticle
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

load("@rules_rust//rust:defs.bzl", "rust_binary")
load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test")

rust_binary(
name = "compile",
srcs = ["compile.rs"],
deps = ["@vaticle_dependencies//library/crates:tonic_build"],
visibility = ["//visibility:public"]
)

checkstyle_test(
name = "checkstyle",
include = glob(["*"]),
license_type = "agpl-header",
size = "small",
)
62 changes: 62 additions & 0 deletions builder/grpc/rust/compile.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#
# Copyright (C) 2022 Vaticle
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

def _rust_tonic_compile_impl(ctx):
protos = [src[ProtoInfo].direct_sources[0] for src in ctx.attr.srcs]

inputs = ctx.attr.protoc.files.to_list() + protos
outputs = [ctx.actions.declare_file("{}.rs".format(package)) for package in ctx.attr.packages]

ctx.actions.run(
inputs = inputs,
outputs = outputs,
executable = ctx.executable._compile_script,
env = {
"OUT_DIR": outputs[0].dirname,
"PROTOC": ctx.attr.protoc.files.to_list()[0].path,
"PROTOS": ";".join([src.path for src in protos]),
"PROTOS_ROOT": ctx.attr.srcs[0][ProtoInfo].proto_source_root,
},
mnemonic = "RustTonicCompileAction"
)

return [DefaultInfo(files = depset(outputs))]

rust_tonic_compile = rule(
implementation = _rust_tonic_compile_impl,
attrs = {
"srcs": attr.label_list(
allow_files = True,
mandatory = True,
doc = "The .proto source files."
),
"packages": attr.string_list(
mandatory = True,
allow_empty = False,
doc = "The Protobuf package names. Each package name corresponds to a single output file."
),
"protoc": attr.label(
default = "@com_google_protobuf//:protoc",
doc = "The protoc executable."
),
"_compile_script": attr.label(
executable = True,
cfg = "host",
default = "@vaticle_dependencies//builder/grpc/rust:compile",
),
}
)
28 changes: 28 additions & 0 deletions builder/grpc/rust/compile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Copyright (C) 2022 Vaticle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

use std::env;

fn main() -> std::io::Result<()> {
let protos_raw = env::var("PROTOS").expect("PROTOS environment variable is not set");
let protos: Vec<&str> = protos_raw.split(";").filter(|&str| !str.is_empty()).collect();

tonic_build::configure()
.compile(&protos, &[
env::var("PROTOS_ROOT").expect("PROTOS_ROOT environment variable is not set")
])
}

0 comments on commit e908d52

Please sign in to comment.