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