blob: 7225d0d1879b87fce8134086567edfdd8c0a8cb8 [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001# 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 Silvermancc09f182022-03-09 15:40:20 -080017# 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([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
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 = [
Brian Silverman5f6f2762022-08-13 19:30:05 -0700132 Label("//proto/3rdparty/crates:protobuf"),
Brian Silvermancc09f182022-03-09 15:40:20 -0800133]
134
135# Default dependencies needed to compile gRPC stubs.
136GRPC_COMPILE_DEPS = PROTO_COMPILE_DEPS + [
Brian Silverman5f6f2762022-08-13 19:30:05 -0700137 Label("//proto/3rdparty/crates:grpc"),
138 Label("//proto/3rdparty/crates:tls-api"),
139 Label("//proto/3rdparty/crates:tls-api-stub"),
Brian Silvermancc09f182022-03-09 15:40:20 -0800140]
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.",
Brian Silvermancc09f182022-03-09 15:40:20 -0800147 ),
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 = """\
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: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)