| # 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.""" |
| |
| load("@rules_proto//proto:proto_common.bzl", proto_toolchains = "toolchains") |
| |
| # 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 |
| |
| # Throughout we use rules_rust as the name as the plugin, not rust, because rust is an unstable builtin language in protoc. |
| # If we use rust as the plugin name, it triggers protoc to try to use its in-built support, which is experimental. |
| # The naming here doesn't matter, it's arbitrary, just the plugin name and the out dir need to match, so we pick rules_rust. |
| |
| 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(grpc_files) |
| args.add_all([ |
| "--", |
| proto_toolchain.protoc, |
| "--plugin=protoc-gen-grpc-rules_rust=" + proto_toolchain.grpc_plugin.path, |
| "--grpc-rules_rust_out=" + output_directory, |
| ]) |
| executable = ctx.executable._optional_output_wrapper |
| |
| args.add_all([ |
| "--plugin=protoc-gen-rules_rust=" + proto_toolchain.proto_plugin.path, |
| "--rules_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): |
| if ctx.attr.protoc: |
| # buildifier: disable=print |
| print("WARN: rust_prost_toolchain's proto_compiler attribute is deprecated. Make sure your rules_proto dependency is at least version 6.0.0 and stop setting proto_compiler") |
| |
| proto_toolchain = proto_toolchains.find_toolchain( |
| ctx, |
| legacy_attr = "_legacy_proto_toolchain", |
| toolchain_type = "@rules_proto//proto:toolchain_type", |
| ) |
| |
| return platform_common.ToolchainInfo( |
| edition = ctx.attr.edition, |
| grpc_compile_deps = ctx.attr.grpc_compile_deps, |
| grpc_plugin = ctx.attr.protoc or ctx.file.grpc_plugin, |
| proto_compile_deps = ctx.attr.proto_compile_deps, |
| proto_plugin = ctx.file.proto_plugin, |
| protoc = ctx.executable.protoc or proto_toolchain.proto_compiler, |
| ) |
| |
| # Default dependencies needed to compile protobuf stubs. |
| PROTO_COMPILE_DEPS = [ |
| Label("//proto/protobuf/3rdparty/crates:protobuf"), |
| ] |
| |
| # Default dependencies needed to compile gRPC stubs. |
| GRPC_COMPILE_DEPS = PROTO_COMPILE_DEPS + [ |
| Label("//proto/protobuf/3rdparty/crates:grpc"), |
| Label("//proto/protobuf/3rdparty/crates:tls-api"), |
| Label("//proto/protobuf/3rdparty/crates:tls-api-stub"), |
| ] |
| |
| rust_proto_toolchain = rule( |
| implementation = _rust_proto_toolchain_impl, |
| attrs = dict({ |
| "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/protobuf/3rdparty/crates:grpc-compiler__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/protobuf/3rdparty/crates:protobuf-codegen__protoc-gen-rust"), |
| ), |
| "protoc": attr.label( |
| doc = "The location of the `protoc` binary. It should be an executable target. Note that this attribute is deprecated - prefer to use --incompatible_enable_proto_toolchain_resolution.", |
| executable = True, |
| cfg = "exec", |
| ), |
| }, **proto_toolchains.if_legacy_toolchain({ |
| "_legacy_proto_toolchain": attr.label( |
| default = "//proto/protobuf:legacy_proto_toolchain", |
| ), |
| })), |
| 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/protobuf: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. |
| """, |
| ) |