blob: 80c5cccd50aae06499129e825d293b5cf0925bef [file] [log] [blame]
# Copyright 2018 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Toolchain for compiling rust stubs from protobuf and gRPC."""
# buildifier: disable=bzl-visibility
load("//rust/private:utils.bzl", "name_to_crate_name")
def generated_file_stem(file_path):
"""Returns the basename of a file without any extensions.
Example:
```python
content.append("pub mod %s;" % _generated_file_stem(f))
```
Args:
file_path (string): A path to a file
Returns:
string: The file stem of the filename
"""
basename = file_path.rsplit("/", 2)[-1]
basename = name_to_crate_name(basename)
return basename.rsplit(".", 2)[0]
def rust_generate_proto(
ctx,
transitive_descriptor_sets,
protos,
imports,
output_dir,
proto_toolchain,
is_grpc = False):
"""Generate a proto compilation action.
Args:
ctx (ctx): rule context.
transitive_descriptor_sets (depset): descriptor generated by previous protobuf libraries.
protos (list): list of paths of protos to compile.
imports (depset): directory, relative to the package, to output the list of stubs.
output_dir (str): The basename of the output directory for for the output generated stubs
proto_toolchain (ToolchainInfo): The toolchain for rust-proto compilation. See `rust_proto_toolchain`
is_grpc (bool, optional): generate gRPC stubs. Defaults to False.
Returns:
list: the list of generate stubs (File)
"""
tools = [
proto_toolchain.protoc,
proto_toolchain.proto_plugin,
]
executable = proto_toolchain.protoc
args = ctx.actions.args()
if not protos:
fail("Protobuf compilation requested without inputs!")
paths = ["%s/%s" % (output_dir, generated_file_stem(i)) for i in protos.to_list()]
outs = [ctx.actions.declare_file(path + ".rs") for path in paths]
output_directory = outs[0].dirname
if is_grpc:
# Add grpc stubs to the list of outputs
grpc_files = [ctx.actions.declare_file(path + "_grpc.rs") for path in paths]
outs.extend(grpc_files)
# gRPC stubs is generated only if a service is defined in the proto,
# so we create an empty grpc module in the other case.
tools.append(proto_toolchain.grpc_plugin)
tools.append(ctx.executable._optional_output_wrapper)
args.add_all([f.path for f in grpc_files])
args.add_all([
"--",
proto_toolchain.protoc.path,
"--plugin=protoc-gen-grpc-rust=" + proto_toolchain.grpc_plugin.path,
"--grpc-rust_out=" + output_directory,
])
executable = ctx.executable._optional_output_wrapper
args.add_all([
"--plugin=protoc-gen-rust=" + proto_toolchain.proto_plugin.path,
"--rust_out=" + output_directory,
])
args.add_joined(
transitive_descriptor_sets,
join_with = ":",
format_joined = "--descriptor_set_in=%s",
)
args.add_all(protos)
ctx.actions.run(
inputs = depset(
transitive = [
transitive_descriptor_sets,
imports,
],
),
outputs = outs,
tools = tools,
progress_message = "Generating Rust protobuf stubs",
mnemonic = "RustProtocGen",
executable = executable,
arguments = [args],
)
return outs
def _rust_proto_toolchain_impl(ctx):
return platform_common.ToolchainInfo(
edition = ctx.attr.edition,
grpc_compile_deps = ctx.attr.grpc_compile_deps,
grpc_plugin = ctx.file.grpc_plugin,
proto_compile_deps = ctx.attr.proto_compile_deps,
proto_plugin = ctx.file.proto_plugin,
protoc = ctx.executable.protoc,
)
# Default dependencies needed to compile protobuf stubs.
PROTO_COMPILE_DEPS = [
Label("//proto/raze:protobuf"),
]
# Default dependencies needed to compile gRPC stubs.
GRPC_COMPILE_DEPS = PROTO_COMPILE_DEPS + [
Label("//proto/raze:grpc"),
Label("//proto/raze:tls_api"),
Label("//proto/raze:tls_api_stub"),
]
rust_proto_toolchain = rule(
implementation = _rust_proto_toolchain_impl,
attrs = {
"edition": attr.string(
doc = "The edition used by the generated rust source.",
),
"grpc_compile_deps": attr.label_list(
doc = "The crates the generated grpc libraries depends on.",
cfg = "target",
default = GRPC_COMPILE_DEPS,
),
"grpc_plugin": attr.label(
doc = "The location of the Rust protobuf compiler plugin to generate rust gRPC stubs.",
allow_single_file = True,
cfg = "exec",
default = Label("//proto:protoc_gen_rust_grpc"),
),
"proto_compile_deps": attr.label_list(
doc = "The crates the generated protobuf libraries depends on.",
cfg = "target",
default = PROTO_COMPILE_DEPS,
),
"proto_plugin": attr.label(
doc = "The location of the Rust protobuf compiler plugin used to generate rust sources.",
allow_single_file = True,
cfg = "exec",
default = Label("//proto:protoc_gen_rust"),
),
"protoc": attr.label(
doc = "The location of the `protoc` binary. It should be an executable target.",
executable = True,
cfg = "exec",
default = Label("@com_google_protobuf//:protoc"),
),
},
doc = """\
Declares a Rust Proto toolchain for use.
This is used to configure proto compilation and can be used to set different \
protobuf compiler plugin.
Example:
Suppose a new nicer gRPC plugin has came out. The new plugin can be \
used in Bazel by defining a new toolchain definition and declaration:
```python
load('@rules_rust//proto:toolchain.bzl', 'rust_proto_toolchain')
rust_proto_toolchain(
name="rust_proto_impl",
grpc_plugin="@rust_grpc//:grpc_plugin",
grpc_compile_deps=["@rust_grpc//:grpc_deps"],
)
toolchain(
name="rust_proto",
exec_compatible_with = [
"@platforms//cpu:cpuX",
],
target_compatible_with = [
"@platforms//cpu:cpuX",
],
toolchain = ":rust_proto_impl",
)
```
Then, either add the label of the toolchain rule to register_toolchains in the WORKSPACE, or pass \
it to the `--extra_toolchains` flag for Bazel, and it will be used.
See @rules_rust//proto:BUILD for examples of defining the toolchain.
""",
)