Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rust_tonic_compile rule #391

Merged
merged 1 commit into from
Nov 21, 2022
Merged

Conversation

alexjpwalker
Copy link
Member

@alexjpwalker alexjpwalker commented Nov 21, 2022

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:

Given that our implementation is very minimal (~30 lines of code) it should incur a very low maintenance effort.

@typedb-bot
Copy link
Member

typedb-bot commented Nov 21, 2022

PR Review Checklist

Do not edit the content of this comment. The PR reviewer should simply update this comment by ticking each review item below, as they get completed.


Code

  • Packages, classes, and methods have a single domain of responsibility.
  • Packages, classes, and methods are grouped into cohesive and consistent domain model.
  • The code is canonical and the minimum required to achieve the goal.
  • Modules, libraries, and APIs are easy to use, robust (foolproof and not errorprone), and tested.
  • Logic and naming has clear narrative that communicates the accurate intent and responsibility of each module (e.g. method, class, etc.).
  • The code is algorithmically efficient and scalable for the whole application.

Architecture

  • Any required refactoring is completed, and the architecture does not introduce technical debt incidentally.
  • Any required build and release automations are updated and/or implemented.
  • Any new components follows a consistent style with respect to the pre-existing codebase.
  • The architecture intuitively reflects the application domain, and is easy to understand.
  • The architecture has a well-defined hierarchy of encapsulated components.
  • The architecture is extensible and scalable.

@@ -0,0 +1,33 @@
#
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package contains the rust_tonic_compile rule and the rust_binary that powers it.

Ideally, we'd get this rule from an external repo such as rules_rust or rules_proto_grpc, but they don't have tonic implementations yet.

We may want to relocate this rule to another repo so it can be used by other Rust proto repos. We may also want to ask the rules_proto_grpc maintainers if they'd accept it as a PR. They do have an existing open PR that adds rust_tonic_compile (and more), so we'd start by commenting there.

@@ -0,0 +1,28 @@
//
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We pass in all the tonic_build configuration via environment variables, which are set by the rust_tonic_compile rule.

@@ -0,0 +1,62 @@
#
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rule implementation is quite straightforward:

  1. Define the inputs for the compile script, namely all .proto sources and the protoc executable.
  2. Define the outputs of the compile script. One output is generated for each unique package defined in our .proto files. Bazel can't (easily!) get these package names, so the user is required to supply them as a parameter.
  3. Run the compile script and expose the outputs as source files that any rust_library can depend on.

(The rust_library is then expected to depend on the tonic and prost crates, otherwise it will fail to compile. This is in line with Java, where we expect our java_library to depend on the correct gRPC + Protobuf JARs.)


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to the latest Rust (fixes build issue in ortools, and also is just generally good practice)

"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,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OUT_DIR and PROTOC are both used internally by the tonic_build crate. So, for convenience, we use environment variables to represent the other necessary input arguments as well.

@alexjpwalker alexjpwalker merged commit 49de586 into typedb:master Nov 21, 2022
@alexjpwalker alexjpwalker deleted the rust-tonic branch November 21, 2022 18:43
haikalpribadi pushed a commit to typedb/typedb-protocol that referenced this pull request Nov 21, 2022
## What is the goal of this PR?

We added crate distribution targets (`assemble_crate` and
`deploy_crate`) for Rust, and added CI workflows for deploying snapshot
and release crates.

## What are the changes implemented in this PR?

We recently tried adding an `assemble_crate` rule to our existing Rust
proto codebase to distribute Rust proto bindings, but it didn't work -
it didn't contain any protobuf source code, because the `assemble_crate`
rule doesn't support Cargo build scripts.

But instead of actually adding that support, we had a rethink of the
build architecture. The old idea was (following Tonic's own docs) for
`typedb-protocol` to come bundled with the `.proto` sources and a Cargo
build script that would allow the end-user to compile the protobufs
on-the-fly. This would require them to have `protoc` installed in their
environment, and would dramatically increase their compilation time and
overhead.

A better option would be to bundle the generated Rust sources. So we've
created the `rust_tonic_compile` rule that enables just that in
typedb/typedb-dependencies#391. Now, both
`rust_library` and `assemble_crate` rules can depend on
`typedb_protocol` seamlessly, so we can depend on `typedb_protocol`
either through Bazel or Cargo.

As this PR stabilises the release process, we've gone ahead and added
the relevant CI workflows: `deploy-crate-snapshot` and
`deploy-crate-release`.
jamesreprise pushed a commit to jamesreprise/vaticle-dependencies that referenced this pull request Dec 5, 2022
## 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants