1# Copyright 2018 The Bazel Authors. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Toolchain for compiling rust stubs from protobuf and gRPC.""" 16 17# buildifier: disable=bzl-visibility 18load("//rust/private:utils.bzl", "name_to_crate_name") 19 20def generated_file_stem(file_path): 21 """Returns the basename of a file without any extensions. 22 23 Example: 24 ```python 25 content.append("pub mod %s;" % _generated_file_stem(f)) 26 ``` 27 28 Args: 29 file_path (string): A path to a file 30 31 Returns: 32 string: The file stem of the filename 33 """ 34 basename = file_path.rsplit("/", 2)[-1] 35 basename = name_to_crate_name(basename) 36 return basename.rsplit(".", 2)[0] 37 38def rust_generate_proto( 39 ctx, 40 transitive_descriptor_sets, 41 protos, 42 imports, 43 output_dir, 44 proto_toolchain, 45 is_grpc = False): 46 """Generate a proto compilation action. 47 48 Args: 49 ctx (ctx): rule context. 50 transitive_descriptor_sets (depset): descriptor generated by previous protobuf libraries. 51 protos (list): list of paths of protos to compile. 52 imports (depset): directory, relative to the package, to output the list of stubs. 53 output_dir (str): The basename of the output directory for for the output generated stubs 54 proto_toolchain (ToolchainInfo): The toolchain for rust-proto compilation. See `rust_proto_toolchain` 55 is_grpc (bool, optional): generate gRPC stubs. Defaults to False. 56 57 Returns: 58 list: the list of generate stubs (File) 59 """ 60 61 tools = [ 62 proto_toolchain.protoc, 63 proto_toolchain.proto_plugin, 64 ] 65 executable = proto_toolchain.protoc 66 args = ctx.actions.args() 67 68 if not protos: 69 fail("Protobuf compilation requested without inputs!") 70 paths = ["%s/%s" % (output_dir, generated_file_stem(i)) for i in protos.to_list()] 71 outs = [ctx.actions.declare_file(path + ".rs") for path in paths] 72 output_directory = outs[0].dirname 73 74 if is_grpc: 75 # Add grpc stubs to the list of outputs 76 grpc_files = [ctx.actions.declare_file(path + "_grpc.rs") for path in paths] 77 outs.extend(grpc_files) 78 79 # gRPC stubs is generated only if a service is defined in the proto, 80 # so we create an empty grpc module in the other case. 81 tools.append(proto_toolchain.grpc_plugin) 82 tools.append(ctx.executable._optional_output_wrapper) 83 args.add_all(grpc_files) 84 args.add_all([ 85 "--", 86 proto_toolchain.protoc, 87 "--plugin=protoc-gen-grpc-rust=" + proto_toolchain.grpc_plugin.path, 88 "--grpc-rust_out=" + output_directory, 89 ]) 90 executable = ctx.executable._optional_output_wrapper 91 92 args.add_all([ 93 "--plugin=protoc-gen-rust=" + proto_toolchain.proto_plugin.path, 94 "--rust_out=" + output_directory, 95 ]) 96 97 args.add_joined( 98 transitive_descriptor_sets, 99 join_with = ":", 100 format_joined = "--descriptor_set_in=%s", 101 ) 102 103 args.add_all(protos) 104 ctx.actions.run( 105 inputs = depset( 106 transitive = [ 107 transitive_descriptor_sets, 108 imports, 109 ], 110 ), 111 outputs = outs, 112 tools = tools, 113 progress_message = "Generating Rust protobuf stubs", 114 mnemonic = "RustProtocGen", 115 executable = executable, 116 arguments = [args], 117 ) 118 return outs 119 120def _rust_proto_toolchain_impl(ctx): 121 return platform_common.ToolchainInfo( 122 edition = ctx.attr.edition, 123 grpc_compile_deps = ctx.attr.grpc_compile_deps, 124 grpc_plugin = ctx.file.grpc_plugin, 125 proto_compile_deps = ctx.attr.proto_compile_deps, 126 proto_plugin = ctx.file.proto_plugin, 127 protoc = ctx.executable.protoc, 128 ) 129 130# Default dependencies needed to compile protobuf stubs. 131PROTO_COMPILE_DEPS = [ 132 Label("//proto/protobuf/3rdparty/crates:protobuf"), 133] 134 135# Default dependencies needed to compile gRPC stubs. 136GRPC_COMPILE_DEPS = PROTO_COMPILE_DEPS + [ 137 Label("//proto/protobuf/3rdparty/crates:grpc"), 138 Label("//proto/protobuf/3rdparty/crates:tls-api"), 139 Label("//proto/protobuf/3rdparty/crates:tls-api-stub"), 140] 141 142rust_proto_toolchain = rule( 143 implementation = _rust_proto_toolchain_impl, 144 attrs = { 145 "edition": attr.string( 146 doc = "The edition used by the generated rust source.", 147 ), 148 "grpc_compile_deps": attr.label_list( 149 doc = "The crates the generated grpc libraries depends on.", 150 cfg = "target", 151 default = GRPC_COMPILE_DEPS, 152 ), 153 "grpc_plugin": attr.label( 154 doc = "The location of the Rust protobuf compiler plugin to generate rust gRPC stubs.", 155 allow_single_file = True, 156 cfg = "exec", 157 default = Label("//proto/protobuf/3rdparty/crates:grpc-compiler__protoc-gen-rust-grpc"), 158 ), 159 "proto_compile_deps": attr.label_list( 160 doc = "The crates the generated protobuf libraries depends on.", 161 cfg = "target", 162 default = PROTO_COMPILE_DEPS, 163 ), 164 "proto_plugin": attr.label( 165 doc = "The location of the Rust protobuf compiler plugin used to generate rust sources.", 166 allow_single_file = True, 167 cfg = "exec", 168 default = Label("//proto/protobuf/3rdparty/crates:protobuf-codegen__protoc-gen-rust"), 169 ), 170 "protoc": attr.label( 171 doc = "The location of the `protoc` binary. It should be an executable target.", 172 executable = True, 173 cfg = "exec", 174 default = Label("@com_google_protobuf//:protoc"), 175 ), 176 }, 177 doc = """\ 178Declares a Rust Proto toolchain for use. 179 180This is used to configure proto compilation and can be used to set different \ 181protobuf compiler plugin. 182 183Example: 184 185Suppose a new nicer gRPC plugin has came out. The new plugin can be \ 186used in Bazel by defining a new toolchain definition and declaration: 187 188```python 189load('@rules_rust//proto/protobuf:toolchain.bzl', 'rust_proto_toolchain') 190 191rust_proto_toolchain( 192 name="rust_proto_impl", 193 grpc_plugin="@rust_grpc//:grpc_plugin", 194 grpc_compile_deps=["@rust_grpc//:grpc_deps"], 195) 196 197toolchain( 198 name="rust_proto", 199 exec_compatible_with = [ 200 "@platforms//cpu:cpuX", 201 ], 202 target_compatible_with = [ 203 "@platforms//cpu:cpuX", 204 ], 205 toolchain = ":rust_proto_impl", 206) 207``` 208 209Then, either add the label of the toolchain rule to register_toolchains in the WORKSPACE, or pass \ 210it to the `--extra_toolchains` flag for Bazel, and it will be used. 211 212See @rules_rust//proto:BUILD for examples of defining the toolchain. 213""", 214) 215