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