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