blob: 519243633fc8b238d082dfd863626f14dbe67a71 [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
17load("//rust:defs.bzl", "rust_common")
18
19# buildifier: disable=bzl-visibility
20load("//rust/private:utils.bzl", "name_to_crate_name")
21
22def 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
40def 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
122def _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.
133PROTO_COMPILE_DEPS = [
134 Label("//proto/raze:protobuf"),
135]
136
137# Default dependencies needed to compile gRPC stubs.
138GRPC_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
144rust_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 = """\
181Declares a Rust Proto toolchain for use.
182
183This is used to configure proto compilation and can be used to set different \
184protobuf compiler plugin.
185
186Example:
187
188Suppose a new nicer gRPC plugin has came out. The new plugin can be \
189used in Bazel by defining a new toolchain definition and declaration:
190
191```python
192load('@rules_rust//proto:toolchain.bzl', 'rust_proto_toolchain')
193
194rust_proto_toolchain(
195 name="rust_proto_impl",
196 grpc_plugin="@rust_grpc//:grpc_plugin",
197 grpc_compile_deps=["@rust_grpc//:grpc_deps"],
198)
199
200toolchain(
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
212Then, either add the label of the toolchain rule to register_toolchains in the WORKSPACE, or pass \
213it to the `--extra_toolchains` flag for Bazel, and it will be used.
214
215See @rules_rust//proto:BUILD for examples of defining the toolchain.
216""",
217)