Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 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 | |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 17 | # buildifier: disable=bzl-visibility |
| 18 | load("//rust/private:utils.bzl", "name_to_crate_name") |
| 19 | |
| 20 | def 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 | |
| 38 | def 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([f.path for f in grpc_files]) |
| 84 | args.add_all([ |
| 85 | "--", |
| 86 | proto_toolchain.protoc.path, |
| 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 | |
| 120 | def _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. |
| 131 | PROTO_COMPILE_DEPS = [ |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 132 | Label("//proto/3rdparty/crates:protobuf"), |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 133 | ] |
| 134 | |
| 135 | # Default dependencies needed to compile gRPC stubs. |
| 136 | GRPC_COMPILE_DEPS = PROTO_COMPILE_DEPS + [ |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 137 | Label("//proto/3rdparty/crates:grpc"), |
| 138 | Label("//proto/3rdparty/crates:tls-api"), |
| 139 | Label("//proto/3rdparty/crates:tls-api-stub"), |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 140 | ] |
| 141 | |
| 142 | rust_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.", |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 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: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: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 = """\ |
| 178 | Declares a Rust Proto toolchain for use. |
| 179 | |
| 180 | This is used to configure proto compilation and can be used to set different \ |
| 181 | protobuf compiler plugin. |
| 182 | |
| 183 | Example: |
| 184 | |
| 185 | Suppose a new nicer gRPC plugin has came out. The new plugin can be \ |
| 186 | used in Bazel by defining a new toolchain definition and declaration: |
| 187 | |
| 188 | ```python |
| 189 | load('@rules_rust//proto:toolchain.bzl', 'rust_proto_toolchain') |
| 190 | |
| 191 | rust_proto_toolchain( |
| 192 | name="rust_proto_impl", |
| 193 | grpc_plugin="@rust_grpc//:grpc_plugin", |
| 194 | grpc_compile_deps=["@rust_grpc//:grpc_deps"], |
| 195 | ) |
| 196 | |
| 197 | toolchain( |
| 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 | |
| 209 | Then, either add the label of the toolchain rule to register_toolchains in the WORKSPACE, or pass \ |
| 210 | it to the `--extra_toolchains` flag for Bazel, and it will be used. |
| 211 | |
| 212 | See @rules_rust//proto:BUILD for examples of defining the toolchain. |
| 213 | """, |
| 214 | ) |