"""This file implements an experimental, do-not-use-kind of rust_proto_library. Disclaimer: This project is experimental, under heavy development, and should not be used yet.""" load("@rules_proto//proto:defs.bzl", "ProtoInfo", "proto_common") load("@rules_rust//rust:defs.bzl", "rust_common") load( "//rust:aspects.bzl", "RustProtoInfo", "label_to_crate_name", "proto_rust_toolchain_label", "rust_cc_proto_library_aspect", "rust_upb_proto_library_aspect", ) visibility([ "//experimental/...", "//src/google/protobuf/...", "//rust/...", ]) def rust_proto_library(name, deps, **args): """Declares all the boilerplate needed to use Rust protobufs conveniently. Hopefully no user will ever need to read this code. Args: name: name of the Rust protobuf target. deps: proto_library target for which to generate Rust gencode. **args: other args passed to the rust__proto_library targets. """ if not name.endswith("_rust_proto"): fail( "{}: Name rust_proto_library target should end with `_rust_proto`, but was '{}'" .format(name), ) name = name.removesuffix("_rust_proto") alias_args = {} if "visibility" in args: alias_args["visibility"] = args.pop("visibility") native.alias( name = name + "_rust_proto", actual = select({ "//rust:use_upb_kernel": name + "_upb_rust_proto", "//conditions:default": name + "_cpp_rust_proto", }), **alias_args ) rust_upb_proto_library( name = name + "_upb_rust_proto", deps = deps, visibility = ["//visibility:private"], **args ) rust_cc_proto_library( name = name + "_cpp_rust_proto", deps = deps, visibility = ["//visibility:private"], **args ) def _user_visible_label(ctx): label = str(ctx.label) label = label.removesuffix("_cpp_rust_proto") label = label.removesuffix("_upb_rust_proto") return label + "_rust_proto" def _rust_proto_library_impl(ctx): if not ctx.label.name.endswith("_rust_proto"): fail( "{}: Name of rust_proto_library target should end with `_rust_proto`." .format(_user_visible_label(ctx)), ) deps = ctx.attr.deps if not deps: fail( "{}: Exactly 1 dependency in `deps` attribute expected, none were provided." .format(_user_visible_label(ctx)), ) if len(deps) > 1: fail( "{}: Exactly 1 dependency in `deps` attribute expected, too many were provided." .format(_user_visible_label(ctx)), ) dep = deps[0] rust_proto_info = dep[RustProtoInfo] dep_variant_info = rust_proto_info.dep_variant_info crate_info = dep_variant_info.crate_info # Change the crate name from the hame of the proto_library to the name of the rust_proto_library. # # When the aspect visits proto_libraries, it doesn't know and cannot deduce the name of the # rust_proto_library (although the name of rust_proto_libraries is consistently ending with # _rust_proto, we can't rely on all proto_libraires to have a name consistently ending with # _proto), therefore we have to modify it after the fact here. # # Since Starlark providers are frozen once they leave the _impl function that defines them, # we have to create a shallow copy. toolchain = ctx.toolchains["@rules_rust//rust:toolchain_type"] fields = {field: getattr(crate_info, field) for field in dir(crate_info)} pkg, name = _user_visible_label(ctx).rsplit(":") label = struct(**{"name": name, "pkg": pkg}) fields["name"] = label_to_crate_name(ctx, label, toolchain) crate_info_with_rust_proto_name = rust_common.crate_info(**fields) return [ crate_info_with_rust_proto_name, dep_variant_info.dep_info, dep_variant_info.cc_info, DefaultInfo(files = dep_variant_info.crate_info.srcs), ] def _make_rust_proto_library(is_upb): return rule( implementation = _rust_proto_library_impl, attrs = { "deps": attr.label_list( mandatory = True, providers = [ProtoInfo], aspects = [rust_upb_proto_library_aspect if is_upb else rust_cc_proto_library_aspect], ), "_proto_lang_toolchain": attr.label( default = Label(proto_rust_toolchain_label(is_upb)), ), }, toolchains = [ "@rules_rust//rust:toolchain_type", ], ) rust_upb_proto_library = _make_rust_proto_library(is_upb = True) rust_cc_proto_library = _make_rust_proto_library(is_upb = False)